From a023b710ea918b966987f1081fa68369fc1596c9 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Fri, 30 May 2014 23:49:56 +0300 Subject: [PATCH 001/378] ath10k: remove unused len variables from wmi process rx functions These len variables are not used anywhere. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/wmi.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 4b7782a529ac..6f83cae57655 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -2106,7 +2106,6 @@ static void ath10k_wmi_main_process_rx(struct ath10k *ar, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; enum wmi_event_id id; - u16 len; cmd_hdr = (struct wmi_cmd_hdr *)skb->data; id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID); @@ -2114,8 +2113,6 @@ static void ath10k_wmi_main_process_rx(struct ath10k *ar, struct sk_buff *skb) if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL) return; - len = skb->len; - trace_ath10k_wmi_event(id, skb->data, skb->len); switch (id) { @@ -2225,7 +2222,6 @@ static void ath10k_wmi_10x_process_rx(struct ath10k *ar, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; enum wmi_10x_event_id id; - u16 len; cmd_hdr = (struct wmi_cmd_hdr *)skb->data; id = MS(__le32_to_cpu(cmd_hdr->cmd_id), WMI_CMD_HDR_CMD_ID); @@ -2233,8 +2229,6 @@ static void ath10k_wmi_10x_process_rx(struct ath10k *ar, struct sk_buff *skb) if (skb_pull(skb, sizeof(struct wmi_cmd_hdr)) == NULL) return; - len = skb->len; - trace_ath10k_wmi_event(id, skb->data, skb->len); switch (id) { From 4e0561e775e0f76a2032a2c4ed9fde20deda29bb Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 30 May 2014 23:49:58 +0300 Subject: [PATCH 002/378] ath10k: print Kconfig options Print Kconfig options enabled/disabled in the build. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 82017f56e661..68bed4e5c9f4 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -798,7 +798,7 @@ int ath10k_core_start(struct ath10k *ar) ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1; INIT_LIST_HEAD(&ar->arvifs); - if (!test_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags)) + if (!test_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags)) { ath10k_info("%s (0x%08x, 0x%08x) fw %s api %d htt %d.%d\n", ar->hw_params.name, ar->target_version, @@ -807,6 +807,12 @@ int ath10k_core_start(struct ath10k *ar) ar->fw_api, ar->htt.target_version_major, ar->htt.target_version_minor); + ath10k_info("debug %d debugfs %d tracing %d dfs %d\n", + config_enabled(CONFIG_ATH10K_DEBUG), + config_enabled(CONFIG_ATH10K_DEBUGFS), + config_enabled(CONFIG_ATH10K_TRACING), + config_enabled(CONFIG_ATH10K_DFS_CERTIFIED)); + } __set_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags); From f5a9f0ca40c74547e28e0cb30973df5577dfbaec Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Fri, 30 May 2014 23:49:57 +0300 Subject: [PATCH 003/378] ath10k: remove unnecessary htt rx corruption check While fixing a bug reported by Avery I went ahead and added a warning suspecting there might be something more to the bug. This ended up with people reporting they see warnings during heavy traffic. This bought me some time and helped me understand the problem better - apparently fw/hw can report a chained msdus as follows: 1 msdu, 1 chained, 1 msdu (0 length). The patch removes the extra check but leaves the other change that fixed the original skb_push panic bug (msdu_chaining was overwritten in an unfortunate way which made the above example to be treated as non-chained case). Reported-by: Yeoh Chun-Yeow Reported-by: Tim Harvey Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/htt_rx.c | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 6c102b1312ff..eebc860c3655 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -312,7 +312,6 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, int msdu_len, msdu_chaining = 0; struct sk_buff *msdu; struct htt_rx_desc *rx_desc; - bool corrupted = false; lockdep_assert_held(&htt->rx_ring.lock); @@ -439,9 +438,6 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, last_msdu = __le32_to_cpu(rx_desc->msdu_end.info0) & RX_MSDU_END_INFO0_LAST_MSDU; - if (msdu_chaining && !last_msdu) - corrupted = true; - if (last_msdu) { msdu->next = NULL; break; @@ -456,20 +452,6 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt, if (*head_msdu == NULL) msdu_chaining = -1; - /* - * Apparently FW sometimes reports weird chained MSDU sequences with - * more than one rx descriptor. This seems like a bug but needs more - * analyzing. For the time being fix it by dropping such sequences to - * avoid blowing up the host system. - */ - if (corrupted) { - ath10k_warn("failed to pop chained msdus, dropping\n"); - ath10k_htt_rx_free_msdu_chain(*head_msdu); - *head_msdu = NULL; - *tail_msdu = NULL; - msdu_chaining = -EINVAL; - } - /* * Don't refill the ring yet. * From dfa413de1e4388818f7dcdce0a90d6212e74895b Mon Sep 17 00:00:00 2001 From: Bartosz Markowski Date: Mon, 2 Jun 2014 21:19:45 +0300 Subject: [PATCH 004/378] ath10k: fix 8th virtual AP interface with DFS Firmware 10.x supports up to 8 virtual AP interfaces, but in a DFS channel it was possible to create only 7 interfaces as ath10k internal creates a monitor interface for DFS. Previous vdev map initialization was missing enough space for 8 + 1 vdevs due to wrong define used and that's why there was no space for 8th interface. Use the correct define TARGET_10X_NUM_VDEVS with 10.x firmware to make it possible to create the 8th virtual interface. Signed-off-by: Bartosz Markowski Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 82017f56e661..e6c56c5bb0f6 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -795,7 +795,11 @@ int ath10k_core_start(struct ath10k *ar) if (status) goto err_htc_stop; - ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1; + if (test_bit(ATH10K_FW_FEATURE_WMI_10X, ar->fw_features)) + ar->free_vdev_map = (1 << TARGET_10X_NUM_VDEVS) - 1; + else + ar->free_vdev_map = (1 << TARGET_NUM_VDEVS) - 1; + INIT_LIST_HEAD(&ar->arvifs); if (!test_bit(ATH10K_FLAG_FIRST_BOOT_DONE, &ar->dev_flags)) From b62b65055bcc5372d5c3f4103629176cb8db3678 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 5 Jun 2014 12:19:54 +0300 Subject: [PATCH 005/378] Bluetooth: Fix incorrectly overriding conn->src_type The src_type member of struct hci_conn should always reflect the address type of the src_member. It should never be overridden. There is already code in place in the command status handler of HCI_LE_Create_Connection to copy the right initiator address into conn->init_addr_type. Without this patch, if privacy is enabled, we will send the wrong address type in the SMP identity address information PDU (it'll e.g. contain our public address but a random address type). Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann Cc: stable@vger.kernel.org --- net/bluetooth/hci_conn.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 8671bc79a35b..b9b2bd464bec 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -610,11 +610,6 @@ static void hci_req_add_le_create_conn(struct hci_request *req, if (hci_update_random_address(req, false, &own_addr_type)) return; - /* Save the address type used for this connnection attempt so we able - * to retrieve this information if we need it. - */ - conn->src_type = own_addr_type; - cp.scan_interval = cpu_to_le16(hdev->le_scan_interval); cp.scan_window = cpu_to_le16(hdev->le_scan_window); bacpy(&cp.peer_addr, &conn->dst); From e694788d73efe139b24f78b036deb97fe57fa8cb Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 10 Jun 2014 09:54:24 +0300 Subject: [PATCH 006/378] Bluetooth: Fix check for connection encryption The conn->link_key variable tracks the type of link key in use. It is set whenever we respond to a link key request as well as when we get a link key notification event. These two events do not however always guarantee that encryption is enabled: getting a link key request and responding to it may only mean that the remote side has requested authentication but not encryption. On the other hand, the encrypt change event is a certain guarantee that encryption is enabled. The real encryption state is already tracked in the conn->link_mode variable through the HCI_LM_ENCRYPT bit. This patch fixes a check for encryption in the hci_conn_auth function to use the proper conn->link_mode value and thereby eliminates the chance of a false positive result. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann Cc: stable@vger.kernel.org --- net/bluetooth/hci_conn.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index b9b2bd464bec..ca01d1861854 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -889,7 +889,7 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) /* If we're already encrypted set the REAUTH_PEND flag, * otherwise set the ENCRYPT_PEND. */ - if (conn->key_type != 0xff) + if (conn->link_mode & HCI_LM_ENCRYPT) set_bit(HCI_CONN_REAUTH_PEND, &conn->flags); else set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags); From ba15a58b179ed76a7e887177f2b06de12c58ec8f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 9 Jun 2014 13:58:14 +0300 Subject: [PATCH 007/378] Bluetooth: Fix SSP acceptor just-works confirmation without MITM From the Bluetooth Core Specification 4.1 page 1958: "if both devices have set the Authentication_Requirements parameter to one of the MITM Protection Not Required options, authentication stage 1 shall function as if both devices set their IO capabilities to DisplayOnly (e.g., Numeric comparison with automatic confirmation on both devices)" So far our implementation has done user confirmation for all just-works cases regardless of the MITM requirements, however following the specification to the word means that we should not be doing confirmation when neither side has the MITM flag set. Signed-off-by: Johan Hedberg Tested-by: Szymon Janc Signed-off-by: Marcel Holtmann Cc: stable@vger.kernel.org --- net/bluetooth/hci_event.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 21e5913d12e0..ff11f4a1ada3 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3628,8 +3628,11 @@ static void hci_user_confirm_request_evt(struct hci_dev *hdev, /* If we're not the initiators request authorization to * proceed from user space (mgmt_user_confirm with - * confirm_hint set to 1). */ - if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags)) { + * confirm_hint set to 1). The exception is if neither + * side had MITM in which case we do auto-accept. + */ + if (!test_bit(HCI_CONN_AUTH_PEND, &conn->flags) && + (loc_mitm || rem_mitm)) { BT_DBG("Confirming auto-accept as acceptor"); confirm_hint = 1; goto confirm; From 4ad51a75c70ba1ba6802fa7ff2ee6829b1c6e61a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 9 Jun 2014 14:41:25 +0300 Subject: [PATCH 008/378] Bluetooth: Add clarifying comment for conn->auth_type When responding to an IO capability request when we're the initiators of the pairing we will not yet have the remote IO capability information. Since the conn->auth_type variable is treated as an "absolute" requirement instead of a hint of what's needed later in the user confirmation request handler it's important that it doesn't have the MITM bit set if there's any chance that the remote device doesn't have the necessary IO capabilities. This patch adds a clarifying comment so that conn->auth_type is left untouched in this scenario. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_event.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index ff11f4a1ada3..3183edc25769 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3537,7 +3537,11 @@ static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb) cp.authentication = conn->auth_type; /* Request MITM protection if our IO caps allow it - * except for the no-bonding case + * except for the no-bonding case. + * conn->auth_type is not updated here since + * that might cause the user confirmation to be + * rejected in case the remote doesn't have the + * IO capabilities for MITM. */ if (conn->io_capability != HCI_IO_NO_INPUT_OUTPUT && cp.authentication != HCI_AT_NO_BONDING) From fff3490f47810e2d34b91fb9e31103e923b11c2f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 10 Jun 2014 15:19:50 +0300 Subject: [PATCH 009/378] Bluetooth: Fix setting correct authentication information for SMP STK When we store the STK in slave role we should set the correct authentication information for it. If the pairing is producing a HIGH security level the STK is considered authenticated, and otherwise it's considered unauthenticated. This patch fixes the value passed to the hci_add_ltk() function when adding the STK on the slave side. Signed-off-by: Johan Hedberg Tested-by: Marcin Kraglak Signed-off-by: Marcel Holtmann Cc: stable@vger.kernel.org --- net/bluetooth/smp.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 3d1cc164557d..f2829a7932e2 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -544,7 +544,7 @@ static u8 smp_random(struct smp_chan *smp) hci_le_start_enc(hcon, ediv, rand, stk); hcon->enc_key_size = smp->enc_key_size; } else { - u8 stk[16]; + u8 stk[16], auth; __le64 rand = 0; __le16 ediv = 0; @@ -556,8 +556,13 @@ static u8 smp_random(struct smp_chan *smp) memset(stk + smp->enc_key_size, 0, SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); + if (hcon->pending_sec_level == BT_SECURITY_HIGH) + auth = 1; + else + auth = 0; + hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, - HCI_SMP_STK_SLAVE, 0, stk, smp->enc_key_size, + HCI_SMP_STK_SLAVE, auth, stk, smp->enc_key_size, ediv, rand); } From 50143a433b70e3145bcf8a4a4e54f0c11bdee32b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 10 Jun 2014 14:05:57 +0300 Subject: [PATCH 010/378] Bluetooth: Fix indicating discovery state when canceling inquiry When inquiry is canceled through the HCI_Cancel_Inquiry command there is no Inquiry Complete event generated. Instead, all we get is the command complete for the HCI_Inquiry_Cancel command. This means that we must call the hci_discovery_set_state() function from the respective command complete handler in order to ensure that user space knows the correct discovery state. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann Cc: stable@vger.kernel.org --- net/bluetooth/hci_event.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 3183edc25769..640c54ec1bd2 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -48,6 +48,10 @@ static void hci_cc_inquiry_cancel(struct hci_dev *hdev, struct sk_buff *skb) smp_mb__after_atomic(); /* wake_up_bit advises about this barrier */ wake_up_bit(&hdev->flags, HCI_INQUIRY); + hci_dev_lock(hdev); + hci_discovery_set_state(hdev, DISCOVERY_STOPPED); + hci_dev_unlock(hdev); + hci_conn_check_pending(hdev); } From 21a60d307ddc2180cfa542a995d943d1034cf5c5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 10 Jun 2014 14:05:58 +0300 Subject: [PATCH 011/378] Bluetooth: Refactor discovery stopping into its own function We'll need to reuse the same logic for stopping discovery also when cleaning up HCI state when powering off. This patch refactors the code out to its own function that can later (in a subsequent patch) be used also for the power off case. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann Cc: stable@vger.kernel.org --- net/bluetooth/mgmt.c | 93 +++++++++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 44 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 0fce54412ffd..be6f03219121 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1047,6 +1047,43 @@ static void clean_up_hci_complete(struct hci_dev *hdev, u8 status) } } +static void hci_stop_discovery(struct hci_request *req) +{ + struct hci_dev *hdev = req->hdev; + struct hci_cp_remote_name_req_cancel cp; + struct inquiry_entry *e; + + switch (hdev->discovery.state) { + case DISCOVERY_FINDING: + if (test_bit(HCI_INQUIRY, &hdev->flags)) { + hci_req_add(req, HCI_OP_INQUIRY_CANCEL, 0, NULL); + } else { + cancel_delayed_work(&hdev->le_scan_disable); + hci_req_add_le_scan_disable(req); + } + + break; + + case DISCOVERY_RESOLVING: + e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, + NAME_PENDING); + if (!e) + return; + + bacpy(&cp.bdaddr, &e->data.bdaddr); + hci_req_add(req, HCI_OP_REMOTE_NAME_REQ_CANCEL, sizeof(cp), + &cp); + + break; + + default: + /* Passive scanning */ + if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) + hci_req_add_le_scan_disable(req); + break; + } +} + static int clean_up_hci_state(struct hci_dev *hdev) { struct hci_request req; @@ -3574,8 +3611,6 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data, { struct mgmt_cp_stop_discovery *mgmt_cp = data; struct pending_cmd *cmd; - struct hci_cp_remote_name_req_cancel cp; - struct inquiry_entry *e; struct hci_request req; int err; @@ -3605,52 +3640,22 @@ static int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data, hci_req_init(&req, hdev); - switch (hdev->discovery.state) { - case DISCOVERY_FINDING: - if (test_bit(HCI_INQUIRY, &hdev->flags)) { - hci_req_add(&req, HCI_OP_INQUIRY_CANCEL, 0, NULL); - } else { - cancel_delayed_work(&hdev->le_scan_disable); + hci_stop_discovery(&req); - hci_req_add_le_scan_disable(&req); - } - - break; - - case DISCOVERY_RESOLVING: - e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, - NAME_PENDING); - if (!e) { - mgmt_pending_remove(cmd); - err = cmd_complete(sk, hdev->id, - MGMT_OP_STOP_DISCOVERY, 0, - &mgmt_cp->type, - sizeof(mgmt_cp->type)); - hci_discovery_set_state(hdev, DISCOVERY_STOPPED); - goto unlock; - } - - bacpy(&cp.bdaddr, &e->data.bdaddr); - hci_req_add(&req, HCI_OP_REMOTE_NAME_REQ_CANCEL, sizeof(cp), - &cp); - - break; - - default: - BT_DBG("unknown discovery state %u", hdev->discovery.state); - - mgmt_pending_remove(cmd); - err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY, - MGMT_STATUS_FAILED, &mgmt_cp->type, - sizeof(mgmt_cp->type)); + err = hci_req_run(&req, stop_discovery_complete); + if (!err) { + hci_discovery_set_state(hdev, DISCOVERY_STOPPING); goto unlock; } - err = hci_req_run(&req, stop_discovery_complete); - if (err < 0) - mgmt_pending_remove(cmd); - else - hci_discovery_set_state(hdev, DISCOVERY_STOPPING); + mgmt_pending_remove(cmd); + + /* If no HCI commands were sent we're done */ + if (err == -ENODATA) { + err = cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY, 0, + &mgmt_cp->type, sizeof(mgmt_cp->type)); + hci_discovery_set_state(hdev, DISCOVERY_STOPPED); + } unlock: hci_dev_unlock(hdev); From f8680f128b01212895a9afb31032f6ffe91bd771 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 10 Jun 2014 14:05:59 +0300 Subject: [PATCH 012/378] Bluetooth: Reuse hci_stop_discovery function when cleaning up HCI state When cleaning up the HCI state as part of the power-off procedure we can reuse the hci_stop_discovery() function instead of explicitly sending HCI command related to discovery. The added benefit of this is that it takes care of canceling name resolving and inquiry which were not previously covered by the code. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann Cc: stable@vger.kernel.org --- net/bluetooth/mgmt.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index be6f03219121..6107e037cd8e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1100,9 +1100,7 @@ static int clean_up_hci_state(struct hci_dev *hdev) if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) disable_advertising(&req); - if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) { - hci_req_add_le_scan_disable(&req); - } + hci_stop_discovery(&req); list_for_each_entry(conn, &hdev->conn_hash.list, list) { struct hci_cp_disconnect dc; From 7ab56c3a6eccb215034b0cb096e0313441cbf2a4 Mon Sep 17 00:00:00 2001 From: Jukka Taimisto Date: Thu, 12 Jun 2014 10:15:13 +0000 Subject: [PATCH 013/378] Bluetooth: Fix deadlock in l2cap_conn_del() A deadlock occurs when PDU containing invalid SMP opcode is received on Security Manager Channel over LE link and conn->pending_rx_work worker has not run yet. When LE link is created l2cap_conn_ready() is called and before returning it schedules conn->pending_rx_work worker to hdev->workqueue. Incoming data to SMP fixed channel is handled by l2cap_recv_frame() which calls smp_sig_channel() to handle the SMP PDU. If smp_sig_channel() indicates failure l2cap_conn_del() is called to delete the connection. When deleting the connection, l2cap_conn_del() purges the pending_rx queue and calls flush_work() to wait for the pending_rx_work worker to complete. Since incoming data is handled by a worker running from the same workqueue as the pending_rx_work is being scheduled on, we will deadlock on waiting for pending_rx_work to complete. This patch fixes the deadlock by calling cancel_work_sync() instead of flush_work(). Signed-off-by: Jukka Taimisto Signed-off-by: Marcel Holtmann Cc: stable@vger.kernel.org --- net/bluetooth/l2cap_core.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 6eabbe05fe54..323f23cd2c37 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1663,7 +1663,13 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err) kfree_skb(conn->rx_skb); skb_queue_purge(&conn->pending_rx); - flush_work(&conn->pending_rx_work); + + /* We can not call flush_work(&conn->pending_rx_work) here since we + * might block if we are running on a worker from the same workqueue + * pending_rx_work is waiting on. + */ + if (work_pending(&conn->pending_rx_work)) + cancel_work_sync(&conn->pending_rx_work); l2cap_unregister_all_users(conn); From c73f94b8c093a615ce80eabbde0ac6eb9abfe31a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 13 Jun 2014 10:22:28 +0300 Subject: [PATCH 014/378] Bluetooth: Fix locking of hdev when calling into SMP code The SMP code expects hdev to be unlocked since e.g. crypto functions will try to (re)lock it. Therefore, we need to release the lock before calling into smp.c from mgmt.c. Without this we risk a deadlock whenever the smp_user_confirm_reply() function is called. Signed-off-by: Johan Hedberg Tested-by: Lukasz Rymanowski Signed-off-by: Marcel Holtmann Cc: stable@vger.kernel.org --- net/bluetooth/mgmt.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 6107e037cd8e..af8e0a6243b7 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3031,8 +3031,13 @@ static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev, } if (addr->type == BDADDR_LE_PUBLIC || addr->type == BDADDR_LE_RANDOM) { - /* Continue with pairing via SMP */ + /* Continue with pairing via SMP. The hdev lock must be + * released as SMP may try to recquire it for crypto + * purposes. + */ + hci_dev_unlock(hdev); err = smp_user_confirm_reply(conn, mgmt_op, passkey); + hci_dev_lock(hdev); if (!err) err = cmd_complete(sk, hdev->id, mgmt_op, From 92d1372e1a9fec00e146b74e8b9ad7a385b9b37f Mon Sep 17 00:00:00 2001 From: Marcin Kraglak Date: Fri, 13 Jun 2014 14:08:22 +0200 Subject: [PATCH 015/378] Bluetooth: Allow change security level on ATT_CID in slave role Kernel supports SMP Security Request so don't block increasing security when we are slave. Signed-off-by: Marcin Kraglak Acked-by: Johan Hedberg Signed-off-by: Marcel Holtmann Cc: stable@vger.kernel.org --- net/bluetooth/l2cap_sock.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index ade3fb4c23bc..e1378693cc90 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -787,11 +787,6 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, /*change security for LE channels */ if (chan->scid == L2CAP_CID_ATT) { - if (!conn->hcon->out) { - err = -EINVAL; - break; - } - if (smp_conn_security(conn->hcon, sec.level)) break; sk->sk_state = BT_CONFIG; From f15ec3451daf137a63d9cdc65ac5f863ce91fce5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sat, 31 May 2014 19:40:45 +0200 Subject: [PATCH 016/378] b43: disable 5 GHz on G-PHY MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes regression introduced by adding some G-PHY devices to the list of dual band devices. There is simply no support for 5 GHz on G-PHY devices in b43. It results in: WARNING: CPU: 0 PID: 79 at drivers/net/wireless/b43/phy_g.c:75 b43_gphy_channel_switch+0x125/0x130 [b43]() b43-phy1 ERROR: PHY init: Channel switch to default failed Regression was introduced by the following commit: commit 773cfc508f4d64c14547ff8751b5cbd473124364 Author: Rafał Miłecki Date: Mon May 19 23:18:55 2014 +0200 b43: add more devices to the bands database Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 32538ac5f7e4..0d6a0bb1f876 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -5221,6 +5221,7 @@ static int b43_wireless_core_attach(struct b43_wldev *dev) /* We don't support 5 GHz on some PHYs yet */ switch (dev->phy.type) { case B43_PHYTYPE_A: + case B43_PHYTYPE_G: case B43_PHYTYPE_N: case B43_PHYTYPE_LP: case B43_PHYTYPE_HT: From bca463e80825433203f0c0de4bf6518f50a9b30d Mon Sep 17 00:00:00 2001 From: Chin-Ran Lo Date: Fri, 6 Jun 2014 19:37:10 -0700 Subject: [PATCH 017/378] mwifiex: fix tx_info/rx_info overlap with PCIe dma_mapping On PCIe Tx data path, network interface specific tx_info parameters such as bss_num and bss_type are saved at "skb->cb + sizeof(dma_addr_t)" (returned by MWIFIEX_SKB_TXCB). Later mwifiex_map_pci_memory() called from mwifiex_pcie_send_data() will memcpy sizeof(struct mwifiex_dma_mapping) bytes to save PCIe DMA address and length information at beginning of skb->cb. This accidently overwrites bss_num and bss_type saved in skb->cb previously because bss_num/bss_type and mwifiex_dma_mapping data overlap. Similarly, on PCIe Rx data path, rx_info parameters overlaps with PCIe DMA address and length information too. Fix it by defining mwifiex_cb structure and having MWIFIEX_SKB_TXCB and MWIFIEX_SKB_RXCB return the correct address of tx_info/rx_info using the structure members. Also add a BUILD_BUG_ON to maks sure that mwifiex_cb structure doesn't exceed the size of skb->cb. Reviewed-by: Aaron Durbin Signed-off-by: Chin-Ran Lo Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/pcie.c | 4 +-- drivers/net/wireless/mwifiex/util.h | 51 +++++++++++++++++++++-------- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 574d4b597468..2cc9b6fca490 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -50,7 +50,7 @@ mwifiex_map_pci_memory(struct mwifiex_adapter *adapter, struct sk_buff *skb, return -1; } mapping.len = size; - memcpy(skb->cb, &mapping, sizeof(mapping)); + mwifiex_store_mapping(skb, &mapping); return 0; } @@ -60,7 +60,7 @@ static void mwifiex_unmap_pci_memory(struct mwifiex_adapter *adapter, struct pcie_service_card *card = adapter->card; struct mwifiex_dma_mapping mapping; - MWIFIEX_SKB_PACB(skb, &mapping); + mwifiex_get_mapping(skb, &mapping); pci_unmap_single(card->dev, mapping.addr, mapping.len, flags); } diff --git a/drivers/net/wireless/mwifiex/util.h b/drivers/net/wireless/mwifiex/util.h index ddae57021397..caadb3737b9e 100644 --- a/drivers/net/wireless/mwifiex/util.h +++ b/drivers/net/wireless/mwifiex/util.h @@ -20,32 +20,55 @@ #ifndef _MWIFIEX_UTIL_H_ #define _MWIFIEX_UTIL_H_ -static inline struct mwifiex_rxinfo *MWIFIEX_SKB_RXCB(struct sk_buff *skb) -{ - return (struct mwifiex_rxinfo *)(skb->cb + sizeof(dma_addr_t)); -} - -static inline struct mwifiex_txinfo *MWIFIEX_SKB_TXCB(struct sk_buff *skb) -{ - return (struct mwifiex_txinfo *)(skb->cb + sizeof(dma_addr_t)); -} - struct mwifiex_dma_mapping { dma_addr_t addr; size_t len; }; -static inline void MWIFIEX_SKB_PACB(struct sk_buff *skb, - struct mwifiex_dma_mapping *mapping) +struct mwifiex_cb { + struct mwifiex_dma_mapping dma_mapping; + union { + struct mwifiex_rxinfo rx_info; + struct mwifiex_txinfo tx_info; + }; +}; + +static inline struct mwifiex_rxinfo *MWIFIEX_SKB_RXCB(struct sk_buff *skb) { - memcpy(mapping, skb->cb, sizeof(*mapping)); + struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; + + BUILD_BUG_ON(sizeof(struct mwifiex_cb) > sizeof(skb->cb)); + return &cb->rx_info; +} + +static inline struct mwifiex_txinfo *MWIFIEX_SKB_TXCB(struct sk_buff *skb) +{ + struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; + + return &cb->tx_info; +} + +static inline void mwifiex_store_mapping(struct sk_buff *skb, + struct mwifiex_dma_mapping *mapping) +{ + struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; + + memcpy(&cb->dma_mapping, mapping, sizeof(*mapping)); +} + +static inline void mwifiex_get_mapping(struct sk_buff *skb, + struct mwifiex_dma_mapping *mapping) +{ + struct mwifiex_cb *cb = (struct mwifiex_cb *)skb->cb; + + memcpy(mapping, &cb->dma_mapping, sizeof(*mapping)); } static inline dma_addr_t MWIFIEX_SKB_DMA_ADDR(struct sk_buff *skb) { struct mwifiex_dma_mapping mapping; - MWIFIEX_SKB_PACB(skb, &mapping); + mwifiex_get_mapping(skb, &mapping); return mapping.addr; } From 8edcb0ba0d56f5914eef11eda6db8bfe74eb9ca8 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Tue, 10 Jun 2014 12:51:06 +0200 Subject: [PATCH 018/378] rt2x00: disable TKIP on USB On USB we can not get atomically TKIP key. We have to disable support for TKIP acceleration on USB hardware to avoid bug as showed bellow. [ 860.827243] BUG: scheduling while atomic: hostapd/3397/0x00000002 [ 860.827280] Call Trace: [ 860.827282] [] dump_stack+0x4d/0x66 [ 860.827284] [] __schedule_bug+0x47/0x55 [ 860.827285] [] __schedule+0x733/0x7b0 [ 860.827287] [] schedule+0x29/0x70 [ 860.827289] [] schedule_timeout+0x15a/0x2b0 [ 860.827291] [] ? ftrace_raw_event_tick_stop+0xc0/0xc0 [ 860.827294] [] ? __module_text_address+0x12/0x70 [ 860.827296] [] wait_for_completion_timeout+0xb3/0x140 [ 860.827298] [] ? wake_up_state+0x20/0x20 [ 860.827301] [] usb_start_wait_urb+0x7d/0x150 [ 860.827303] [] usb_control_msg+0xc5/0x110 [ 860.827305] [] rt2x00usb_vendor_request+0xc6/0x160 [rt2x00usb] [ 860.827307] [] rt2x00usb_vendor_req_buff_lock+0x75/0x150 [rt2x00usb] [ 860.827309] [] rt2x00usb_vendor_request_buff+0xa3/0xe0 [rt2x00usb] [ 860.827311] [] rt2x00usb_register_multiread+0x33/0x40 [rt2800usb] [ 860.827314] [] rt2800_get_tkip_seq+0x39/0x50 [rt2800lib] [ 860.827321] [] ieee80211_get_key+0x218/0x2a0 [mac80211] [ 860.827322] [] ? __nlmsg_put+0x6c/0x80 [ 860.827329] [] nl80211_get_key+0x22e/0x360 [cfg80211] Cc: stable@vger.kernel.org Reported-and-tested-by: Peter Wu Reported-and-tested-by: Pontus Fuchs Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2x00mac.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/rt2x00/rt2x00mac.c b/drivers/net/wireless/rt2x00/rt2x00mac.c index 212ac4842c16..004dff9b962d 100644 --- a/drivers/net/wireless/rt2x00/rt2x00mac.c +++ b/drivers/net/wireless/rt2x00/rt2x00mac.c @@ -487,6 +487,8 @@ int rt2x00mac_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, crypto.cipher = rt2x00crypto_key_to_cipher(key); if (crypto.cipher == CIPHER_NONE) return -EOPNOTSUPP; + if (crypto.cipher == CIPHER_TKIP && rt2x00_is_usb(rt2x00dev)) + return -EOPNOTSUPP; crypto.cmd = cmd; From 740bb82b7655770c09a19af5528a131ac741eaf1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 10 Jun 2014 13:32:05 +0200 Subject: [PATCH 019/378] b43: fix typo in Kconfig (make B43_BUSES_BCMA_AND_SSB the default for real) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reported-by: Paul Bolle Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/b43/Kconfig b/drivers/net/wireless/b43/Kconfig index e3f67b8d3f80..40fd9b7b1426 100644 --- a/drivers/net/wireless/b43/Kconfig +++ b/drivers/net/wireless/b43/Kconfig @@ -36,7 +36,7 @@ config B43_SSB choice prompt "Supported bus types" depends on B43 - default B43_BCMA_AND_SSB + default B43_BUSES_BCMA_AND_SSB config B43_BUSES_BCMA_AND_SSB bool "BCMA and SSB" From de51b35d506f9f9e8ab7bb5987ed5d50a76bd906 Mon Sep 17 00:00:00 2001 From: Michael Braun Date: Thu, 12 Jun 2014 19:33:36 +0200 Subject: [PATCH 020/378] rt2800usb:fix efuse detection The device 057c:8501 (AVM Fritz! WLAN v2 rev. B) currently does not load. One thing observed is that the vendors driver detects EFUSE mode for this device, but rt2800usb does not. This is due to rt2800usb lacking a check for the firmware mode present in the vendors driver, that this patch adopts for rt2800usb. With this patch applied, the 'RF chipset' detection does no longer fail. Signed-off-by: Michael Braun Acked-by: Stanislaw Gruszka Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800usb.c | 30 ++++++++++++++++++++++++- drivers/net/wireless/rt2x00/rt2x00usb.h | 1 + 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index a49c3d73ea2c..11583ca3facb 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -229,6 +229,27 @@ static enum hrtimer_restart rt2800usb_tx_sta_fifo_timeout(struct hrtimer *timer) /* * Firmware functions */ +static int rt2800usb_autorun_detect(struct rt2x00_dev *rt2x00dev) +{ + __le32 reg; + u32 fw_mode; + + /* cannot use rt2x00usb_register_read here as it uses different + * mode (MULTI_READ vs. DEVICE_MODE) and does not pass the + * magic value USB_MODE_AUTORUN (0x11) to the device, thus the + * returned value would be invalid. + */ + rt2x00usb_vendor_request(rt2x00dev, USB_DEVICE_MODE, + USB_VENDOR_REQUEST_IN, 0, USB_MODE_AUTORUN, + ®, sizeof(reg), REGISTER_TIMEOUT_FIRMWARE); + fw_mode = le32_to_cpu(reg); + + if ((fw_mode & 0x00000003) == 2) + return 1; + + return 0; +} + static char *rt2800usb_get_firmware_name(struct rt2x00_dev *rt2x00dev) { return FIRMWARE_RT2870; @@ -735,11 +756,18 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry, /* * Device probe functions. */ +static int rt2800usb_efuse_detect(struct rt2x00_dev *rt2x00dev) +{ + if (rt2800usb_autorun_detect(rt2x00dev)) + return 1; + return rt2800_efuse_detect(rt2x00dev); +} + static int rt2800usb_read_eeprom(struct rt2x00_dev *rt2x00dev) { int retval; - if (rt2800_efuse_detect(rt2x00dev)) + if (rt2800usb_efuse_detect(rt2x00dev)) retval = rt2800_read_eeprom_efuse(rt2x00dev); else retval = rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom, diff --git a/drivers/net/wireless/rt2x00/rt2x00usb.h b/drivers/net/wireless/rt2x00/rt2x00usb.h index e7bcf62347d5..831b65f93feb 100644 --- a/drivers/net/wireless/rt2x00/rt2x00usb.h +++ b/drivers/net/wireless/rt2x00/rt2x00usb.h @@ -93,6 +93,7 @@ enum rt2x00usb_mode_offset { USB_MODE_SLEEP = 7, /* RT73USB */ USB_MODE_FIRMWARE = 8, /* RT73USB */ USB_MODE_WAKEUP = 9, /* RT73USB */ + USB_MODE_AUTORUN = 17, /* RT2800USB */ }; /** From b663cd10f0ba351ae520fa3627e13061d7f8e714 Mon Sep 17 00:00:00 2001 From: Michael Braun Date: Thu, 12 Jun 2014 19:33:41 +0200 Subject: [PATCH 021/378] rt2800usb:fix hang during firmware load The device 057c:8501 (AVM Fritz! WLAN v2 rev. B) boots into a state that does not actually require loading a firmware file. The vendors driver finds out about this by checking a firmware state register, so this patch adds this here. Finally, with this patch applied, my wifi dongle actually becomes useful (scan + connect to wpa network works). Signed-off-by: Michael Braun Acked-by: Stanislaw Gruszka Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800usb.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index 11583ca3facb..e11dab2216c6 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -278,8 +278,13 @@ static int rt2800usb_write_firmware(struct rt2x00_dev *rt2x00dev, /* * Write firmware to device. */ - rt2x00usb_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE, - data + offset, length); + if (rt2800usb_autorun_detect(rt2x00dev)) { + rt2x00_info(rt2x00dev, + "Firmware loading not required - NIC in AutoRun mode\n"); + } else { + rt2x00usb_register_multiwrite(rt2x00dev, FIRMWARE_IMAGE_BASE, + data + offset, length); + } rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_CID, ~0); rt2x00usb_register_write(rt2x00dev, H2M_MAILBOX_STATUS, ~0); From 2fc68eb122c7ea6cd5be1fe7d6650c0beb2f4f40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Thu, 12 Jun 2014 22:28:22 +0200 Subject: [PATCH 022/378] b43: fix frequency reported on G-PHY with /new/ firmware MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support for firmware rev 508+ was added years ago, but we never noticed it reports channel in a different way for G-PHY devices. Instead of offset from 2400 MHz it simply passes channel id (AKA hw_value). So far it was (most probably) affecting monitor mode users only, but the following recent commit made it noticeable for quite everybody: commit 3afc2167f60a327a2c1e1e2600ef209a3c2b75b7 Author: Emmanuel Grumbach Date: Tue Mar 4 16:50:13 2014 +0200 cfg80211/mac80211: ignore signal if the frame was heard on wrong channel Reported-by: Aaro Koskinen Signed-off-by: Rafał Miłecki Cc: stable@vger.kernel.org Tested-by: Aaro Koskinen Signed-off-by: John W. Linville --- drivers/net/wireless/b43/xmit.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/b43/xmit.c b/drivers/net/wireless/b43/xmit.c index 4f38f19b8e3d..6e6ef3fc2247 100644 --- a/drivers/net/wireless/b43/xmit.c +++ b/drivers/net/wireless/b43/xmit.c @@ -811,9 +811,13 @@ void b43_rx(struct b43_wldev *dev, struct sk_buff *skb, const void *_rxhdr) break; case B43_PHYTYPE_G: status.band = IEEE80211_BAND_2GHZ; - /* chanid is the radio channel cookie value as used - * to tune the radio. */ - status.freq = chanid + 2400; + /* Somewhere between 478.104 and 508.1084 firmware for G-PHY + * has been modified to be compatible with N-PHY and others. + */ + if (dev->fw.rev >= 508) + status.freq = ieee80211_channel_to_frequency(chanid, status.band); + else + status.freq = chanid + 2400; break; case B43_PHYTYPE_N: case B43_PHYTYPE_LP: From 616a8394b5df8c88f4dd416f4527439a4e365034 Mon Sep 17 00:00:00 2001 From: Stanislaw Gruszka Date: Mon, 16 Jun 2014 18:45:15 +0200 Subject: [PATCH 023/378] rt2x00: fix rfkill regression on rt2500pci As reported by Niels, starting rfkill polling during device probe (commit e2bc7c5, generally sane change) broke rfkill on rt2500pci device. I considered that bug as some initalization issue, which should be fixed on rt2500pci specific code. But after several attempts (see bug report for details) we fail to find working solution. Hence I decided to revert to old behaviour on rt2500pci to fix regression. Additionally patch also unregister rfkill on device remove instead of ifconfig down, what was another issue introduced by bad commit. Bug report: https://bugzilla.kernel.org/show_bug.cgi?id=73821 Fixes: e2bc7c5f3cb8 ("rt2x00: Fix rfkill_polling register function.") Cc: stable@vger.kernel.org Bisected-by: Niels Reported-and-tested-by: Niels Signed-off-by: Stanislaw Gruszka Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2500pci.c | 7 ++++++- drivers/net/wireless/rt2x00/rt2x00.h | 1 + drivers/net/wireless/rt2x00/rt2x00dev.c | 24 +++++++++++++++++++++--- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2500pci.c b/drivers/net/wireless/rt2x00/rt2500pci.c index 2f1cd929c6f6..a511cccc9f01 100644 --- a/drivers/net/wireless/rt2x00/rt2500pci.c +++ b/drivers/net/wireless/rt2x00/rt2500pci.c @@ -1681,8 +1681,13 @@ static int rt2500pci_init_eeprom(struct rt2x00_dev *rt2x00dev) /* * Detect if this device has an hardware controlled radio. */ - if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) + if (rt2x00_get_field16(eeprom, EEPROM_ANTENNA_HARDWARE_RADIO)) { __set_bit(CAPABILITY_HW_BUTTON, &rt2x00dev->cap_flags); + /* + * On this device RFKILL initialized during probe does not work. + */ + __set_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags); + } /* * Check if the BBP tuning should be enabled. diff --git a/drivers/net/wireless/rt2x00/rt2x00.h b/drivers/net/wireless/rt2x00/rt2x00.h index 010b76505243..d13f25cd70d5 100644 --- a/drivers/net/wireless/rt2x00/rt2x00.h +++ b/drivers/net/wireless/rt2x00/rt2x00.h @@ -693,6 +693,7 @@ enum rt2x00_capability_flags { REQUIRE_SW_SEQNO, REQUIRE_HT_TX_DESC, REQUIRE_PS_AUTOWAKE, + REQUIRE_DELAYED_RFKILL, /* * Capabilities diff --git a/drivers/net/wireless/rt2x00/rt2x00dev.c b/drivers/net/wireless/rt2x00/rt2x00dev.c index 2bde6729f5e6..4fa43a2eeb73 100644 --- a/drivers/net/wireless/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/rt2x00/rt2x00dev.c @@ -1126,9 +1126,10 @@ static void rt2x00lib_uninitialize(struct rt2x00_dev *rt2x00dev) return; /* - * Unregister extra components. + * Stop rfkill polling. */ - rt2x00rfkill_unregister(rt2x00dev); + if (test_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags)) + rt2x00rfkill_unregister(rt2x00dev); /* * Allow the HW to uninitialize. @@ -1166,6 +1167,12 @@ static int rt2x00lib_initialize(struct rt2x00_dev *rt2x00dev) set_bit(DEVICE_STATE_INITIALIZED, &rt2x00dev->flags); + /* + * Start rfkill polling. + */ + if (test_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags)) + rt2x00rfkill_register(rt2x00dev); + return 0; } @@ -1375,7 +1382,12 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) rt2x00link_register(rt2x00dev); rt2x00leds_register(rt2x00dev); rt2x00debug_register(rt2x00dev); - rt2x00rfkill_register(rt2x00dev); + + /* + * Start rfkill polling. + */ + if (!test_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags)) + rt2x00rfkill_register(rt2x00dev); return 0; @@ -1390,6 +1402,12 @@ void rt2x00lib_remove_dev(struct rt2x00_dev *rt2x00dev) { clear_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); + /* + * Stop rfkill polling. + */ + if (!test_bit(REQUIRE_DELAYED_RFKILL, &rt2x00dev->cap_flags)) + rt2x00rfkill_unregister(rt2x00dev); + /* * Disable radio. */ From a2b23bacb315d3873ed90029fd2b68c95de734c0 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 20 Jun 2014 12:34:28 +0200 Subject: [PATCH 024/378] Revert "Bluetooth: Add a new PID/VID 0cf3/e005 for AR3012." This reverts commit ca58e594da2486c1d28e7ad547d82266604ec4ce. For some unclear reason this patch tries to add suport for the product ID 0xe005, but it ends up adding product ID 0x3005 to all the tables. This is obviously wrong and causing multiple issues. The original patch seemed to be fine, but what ended up in 3.15 is not what the patch intended. The commit 0a3658cccdf53 is already present and adds support for this hardware. This means only revert of this broken commit is requird. Signed-off-by: Marcel Holtmann Reported-by: Alexander Holler Cc: stable@vger.kernel.org # 3.15.x --- drivers/bluetooth/ath3k.c | 2 -- drivers/bluetooth/btusb.c | 1 - 2 files changed, 3 deletions(-) diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index f98380648cb3..f50dffc0374f 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -90,7 +90,6 @@ static const struct usb_device_id ath3k_table[] = { { USB_DEVICE(0x0b05, 0x17d0) }, { USB_DEVICE(0x0CF3, 0x0036) }, { USB_DEVICE(0x0CF3, 0x3004) }, - { USB_DEVICE(0x0CF3, 0x3005) }, { USB_DEVICE(0x0CF3, 0x3008) }, { USB_DEVICE(0x0CF3, 0x311D) }, { USB_DEVICE(0x0CF3, 0x311E) }, @@ -140,7 +139,6 @@ static const struct usb_device_id ath3k_blist_tbl[] = { { USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0CF3, 0x0036), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0cf3, 0x3005), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x3008), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x311D), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x311E), .driver_info = BTUSB_ATH3012 }, diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index a1c80b0c7663..6250fc2fb93a 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -162,7 +162,6 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x0b05, 0x17d0), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x0036), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x3004), .driver_info = BTUSB_ATH3012 }, - { USB_DEVICE(0x0cf3, 0x3005), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x3008), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x311d), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x0cf3, 0x311e), .driver_info = BTUSB_ATH3012 }, From c7262e711ae6e466baeb9ddc21d678c878469b1f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 17 Jun 2014 13:07:37 +0300 Subject: [PATCH 025/378] Bluetooth: Fix overriding higher security level in SMP When we receive a pairing request or an internal request to start pairing we shouldn't blindly overwrite the existing pending_sec_level value as that may actually be higher than the new one. This patch fixes the SMP code to only overwrite the value in case the new one is higher than the old. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index f2829a7932e2..0189ec8b68d1 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -669,7 +669,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_pairing rsp, *req = (void *) skb->data; struct smp_chan *smp; - u8 key_size, auth; + u8 key_size, auth, sec_level; int ret; BT_DBG("conn %p", conn); @@ -695,7 +695,9 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) /* We didn't start the pairing, so match remote */ auth = req->auth_req; - conn->hcon->pending_sec_level = authreq_to_seclevel(auth); + sec_level = authreq_to_seclevel(auth); + if (sec_level > conn->hcon->pending_sec_level) + conn->hcon->pending_sec_level = sec_level; build_pairing_cmd(conn, req, &rsp, auth); @@ -838,6 +840,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) struct smp_cmd_pairing cp; struct hci_conn *hcon = conn->hcon; struct smp_chan *smp; + u8 sec_level; BT_DBG("conn %p", conn); @@ -847,7 +850,9 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) if (!(conn->hcon->link_mode & HCI_LM_MASTER)) return SMP_CMD_NOTSUPP; - hcon->pending_sec_level = authreq_to_seclevel(rp->auth_req); + sec_level = authreq_to_seclevel(rp->auth_req); + if (sec_level > hcon->pending_sec_level) + hcon->pending_sec_level = sec_level; if (smp_ltk_encrypt(conn, hcon->pending_sec_level)) return 0; @@ -901,9 +906,12 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) if (smp_sufficient_security(hcon, sec_level)) return 1; + if (sec_level > hcon->pending_sec_level) + hcon->pending_sec_level = sec_level; + if (hcon->link_mode & HCI_LM_MASTER) - if (smp_ltk_encrypt(conn, sec_level)) - goto done; + if (smp_ltk_encrypt(conn, hcon->pending_sec_level)) + return 0; if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) return 0; @@ -918,7 +926,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) * requires it. */ if (hcon->io_capability != HCI_IO_NO_INPUT_OUTPUT || - sec_level > BT_SECURITY_MEDIUM) + hcon->pending_sec_level > BT_SECURITY_MEDIUM) authreq |= SMP_AUTH_MITM; if (hcon->link_mode & HCI_LM_MASTER) { @@ -937,9 +945,6 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) set_bit(SMP_FLAG_INITIATOR, &smp->flags); -done: - hcon->pending_sec_level = sec_level; - return 0; } From 581370cc74ea75426421a1f5851ef05e2e995b01 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 17 Jun 2014 13:07:38 +0300 Subject: [PATCH 026/378] Bluetooth: Refactor authentication method lookup into its own function We'll need to do authentication method lookups from more than one place, so refactor the lookup into its own function. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 0189ec8b68d1..7156f4720644 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -385,6 +385,16 @@ static const u8 gen_method[5][5] = { { CFM_PASSKEY, CFM_PASSKEY, REQ_PASSKEY, JUST_WORKS, OVERLAP }, }; +static u8 get_auth_method(struct smp_chan *smp, u8 local_io, u8 remote_io) +{ + /* If either side has unknown io_caps, use JUST WORKS */ + if (local_io > SMP_IO_KEYBOARD_DISPLAY || + remote_io > SMP_IO_KEYBOARD_DISPLAY) + return JUST_WORKS; + + return gen_method[remote_io][local_io]; +} + static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, u8 local_io, u8 remote_io) { @@ -401,14 +411,11 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, BT_DBG("tk_request: auth:%d lcl:%d rem:%d", auth, local_io, remote_io); /* If neither side wants MITM, use JUST WORKS */ - /* If either side has unknown io_caps, use JUST WORKS */ /* Otherwise, look up method from the table */ - if (!(auth & SMP_AUTH_MITM) || - local_io > SMP_IO_KEYBOARD_DISPLAY || - remote_io > SMP_IO_KEYBOARD_DISPLAY) + if (!(auth & SMP_AUTH_MITM)) method = JUST_WORKS; else - method = gen_method[remote_io][local_io]; + method = get_auth_method(smp, local_io, remote_io); /* If not bonding, don't ask user to confirm a Zero TK */ if (!(auth & SMP_AUTH_BONDING) && method == JUST_CFM) From 2ed8f65ca262bca778e60053f667ce11b32db6b8 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 17 Jun 2014 13:07:39 +0300 Subject: [PATCH 027/378] Bluetooth: Fix rejecting pairing in case of insufficient capabilities If we need an MITM protected connection but the local and remote IO capabilities cannot provide it we should reject the pairing attempt in the appropriate way. This patch adds the missing checks for such a situation to the smp_cmd_pairing_req() and smp_cmd_pairing_rsp() functions. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 7156f4720644..e33a982161c1 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -706,6 +706,16 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) if (sec_level > conn->hcon->pending_sec_level) conn->hcon->pending_sec_level = sec_level; + /* If we need MITM check that it can be acheived */ + if (conn->hcon->pending_sec_level >= BT_SECURITY_HIGH) { + u8 method; + + method = get_auth_method(smp, conn->hcon->io_capability, + req->io_capability); + if (method == JUST_WORKS || method == JUST_CFM) + return SMP_AUTH_REQUIREMENTS; + } + build_pairing_cmd(conn, req, &rsp, auth); key_size = min(req->max_key_size, rsp.max_key_size); @@ -752,6 +762,16 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) if (check_enc_key_size(conn, key_size)) return SMP_ENC_KEY_SIZE; + /* If we need MITM check that it can be acheived */ + if (conn->hcon->pending_sec_level >= BT_SECURITY_HIGH) { + u8 method; + + method = get_auth_method(smp, req->io_capability, + rsp->io_capability); + if (method == JUST_WORKS || method == JUST_CFM) + return SMP_AUTH_REQUIREMENTS; + } + get_random_bytes(smp->prnd, sizeof(smp->prnd)); smp->prsp[0] = SMP_CMD_PAIRING_RSP; From 1d56dc4f5f7cdf0ba99062d974b7586a28fc5cf4 Mon Sep 17 00:00:00 2001 From: Lukasz Rymanowski Date: Tue, 17 Jun 2014 13:04:20 +0200 Subject: [PATCH 028/378] Bluetooth: Fix for ACL disconnect when pairing fails When pairing fails hci_conn refcnt drops below zero. This cause that ACL link is not disconnected when disconnect timeout fires. Probably this is because l2cap_conn_del calls l2cap_chan_del for each channel, and inside l2cap_chan_del conn is dropped. After that loop hci_chan_del is called which also drops conn. Anyway, as it is desrcibed in hci_core.h, it is known that refcnt drops below 0 sometimes and it should be fine. If so, let disconnect link when hci_conn_timeout fires and refcnt is 0 or below. This patch does it. This affects PTS test SM_TC_JW_BV_05_C Logs from scenario: [69713.706227] [6515] pair_device: [69713.706230] [6515] hci_conn_add: hci0 dst 00:1b:dc:06:06:22 [69713.706233] [6515] hci_dev_hold: hci0 orig refcnt 8 [69713.706235] [6515] hci_conn_init_sysfs: conn ffff88021f65a000 [69713.706239] [6515] hci_req_add_ev: hci0 opcode 0x200d plen 25 [69713.706242] [6515] hci_prepare_cmd: skb len 28 [69713.706243] [6515] hci_req_run: length 1 [69713.706248] [6515] hci_conn_hold: hcon ffff88021f65a000 orig refcnt 0 [69713.706251] [6515] hci_dev_put: hci0 orig refcnt 9 [69713.706281] [8909] hci_cmd_work: hci0 cmd_cnt 1 cmd queued 1 [69713.706288] [8909] hci_send_frame: hci0 type 1 len 28 [69713.706290] [8909] hci_send_to_monitor: hdev ffff88021f0c7000 len 28 [69713.706316] [5949] hci_sock_recvmsg: sock ffff8800941a9680, sk ffff88012bf4d000 [69713.706382] [5949] hci_sock_recvmsg: sock ffff8800941a9680, sk ffff88012bf4d000 [69713.711664] [8909] hci_rx_work: hci0 [69713.711668] [8909] hci_send_to_monitor: hdev ffff88021f0c7000 len 6 [69713.711680] [8909] hci_rx_work: hci0 Event packet [69713.711683] [8909] hci_cs_le_create_conn: hci0 status 0x00 [69713.711685] [8909] hci_sent_cmd_data: hci0 opcode 0x200d [69713.711688] [8909] hci_req_cmd_complete: opcode 0x200d status 0x00 [69713.711690] [8909] hci_sent_cmd_data: hci0 opcode 0x200d [69713.711695] [5949] hci_sock_recvmsg: sock ffff8800941a9680, sk ffff88012bf4d000 [69713.711744] [5949] hci_sock_recvmsg: sock ffff8800941a9680, sk ffff88012bf4d000 [69713.818875] [8909] hci_rx_work: hci0 [69713.818889] [8909] hci_send_to_monitor: hdev ffff88021f0c7000 len 21 [69713.818913] [8909] hci_rx_work: hci0 Event packet [69713.818917] [8909] hci_le_conn_complete_evt: hci0 status 0x00 [69713.818922] [8909] hci_send_to_control: len 19 [69713.818927] [5949] hci_sock_recvmsg: sock ffff8800941a9680, sk ffff88012bf4d000 [69713.818938] [8909] hci_conn_add_sysfs: conn ffff88021f65a000 [69713.818975] [6450] bt_sock_poll: sock ffff88005e758500, sk ffff88010323b800 [69713.818981] [6515] hci_sock_recvmsg: sock ffff88005e75a080, sk ffff88010323ac00 ... [69713.819021] [8909] hci_dev_hold: hci0 orig refcnt 10 [69713.819025] [8909] l2cap_connect_cfm: hcon ffff88021f65a000 bdaddr 00:1b:dc:06:06:22 status 0 [69713.819028] [8909] hci_chan_create: hci0 hcon ffff88021f65a000 [69713.819031] [8909] l2cap_conn_add: hcon ffff88021f65a000 conn ffff880221005c00 hchan ffff88020d60b1c0 [69713.819034] [8909] l2cap_conn_ready: conn ffff880221005c00 [69713.819036] [5949] hci_sock_recvmsg: sock ffff8800941a9680, sk ffff88012bf4d000 [69713.819037] [8909] smp_conn_security: conn ffff880221005c00 hcon ffff88021f65a000 level 0x02 [69713.819039] [8909] smp_chan_create: [69713.819041] [8909] hci_conn_hold: hcon ffff88021f65a000 orig refcnt 1 [69713.819043] [8909] smp_send_cmd: code 0x01 [69713.819045] [8909] hci_send_acl: hci0 chan ffff88020d60b1c0 flags 0x0000 [69713.819046] [5949] hci_sock_recvmsg: sock ffff8800941a9900, sk ffff88012bf4e800 [69713.819049] [8909] hci_queue_acl: hci0 nonfrag skb ffff88005157c100 len 15 [69713.819055] [5949] hci_sock_recvmsg: sock ffff8800941a9900, sk ffff88012bf4e800 [69713.819057] [8909] l2cap_le_conn_ready: [69713.819064] [8909] l2cap_chan_create: chan ffff88005ede2c00 [69713.819066] [8909] l2cap_chan_hold: chan ffff88005ede2c00 orig refcnt 1 [69713.819069] [8909] l2cap_sock_init: sk ffff88005ede5800 [69713.819072] [8909] bt_accept_enqueue: parent ffff880160356000, sk ffff88005ede5800 [69713.819074] [8909] __l2cap_chan_add: conn ffff880221005c00, psm 0x00, dcid 0x0004 [69713.819076] [8909] l2cap_chan_hold: chan ffff88005ede2c00 orig refcnt 2 [69713.819078] [8909] hci_conn_hold: hcon ffff88021f65a000 orig refcnt 2 [69713.819080] [8909] smp_conn_security: conn ffff880221005c00 hcon ffff88021f65a000 level 0x01 [69713.819082] [8909] l2cap_sock_ready_cb: sk ffff88005ede5800, parent ffff880160356000 [69713.819086] [8909] le_pairing_complete_cb: status 0 [69713.819091] [8909] hci_tx_work: hci0 acl 10 sco 8 le 0 [69713.819093] [8909] hci_sched_acl: hci0 [69713.819094] [8909] hci_sched_sco: hci0 [69713.819096] [8909] hci_sched_esco: hci0 [69713.819098] [8909] hci_sched_le: hci0 [69713.819099] [8909] hci_chan_sent: hci0 [69713.819101] [8909] hci_chan_sent: chan ffff88020d60b1c0 quote 10 [69713.819104] [8909] hci_sched_le: chan ffff88020d60b1c0 skb ffff88005157c100 len 15 priority 7 [69713.819106] [8909] hci_send_frame: hci0 type 2 len 15 [69713.819108] [8909] hci_send_to_monitor: hdev ffff88021f0c7000 len 15 [69713.819119] [8909] hci_chan_sent: hci0 [69713.819121] [8909] hci_prio_recalculate: hci0 [69713.819123] [8909] process_pending_rx: [69713.819226] [6450] hci_sock_recvmsg: sock ffff88005e758780, sk ffff88010323d400 ... [69713.822022] [6450] l2cap_sock_accept: sk ffff880160356000 timeo 0 [69713.822024] [6450] bt_accept_dequeue: parent ffff880160356000 [69713.822026] [6450] bt_accept_unlink: sk ffff88005ede5800 state 1 [69713.822028] [6450] l2cap_sock_accept: new socket ffff88005ede5800 [69713.822368] [6450] l2cap_sock_getname: sock ffff8800941ab700, sk ffff88005ede5800 [69713.822375] [6450] l2cap_sock_getsockopt: sk ffff88005ede5800 [69713.822383] [6450] l2cap_sock_getname: sock ffff8800941ab700, sk ffff88005ede5800 [69713.822414] [6450] bt_sock_poll: sock ffff8800941ab700, sk ffff88005ede5800 ... [69713.823255] [6450] l2cap_sock_getname: sock ffff8800941ab700, sk ffff88005ede5800 [69713.823259] [6450] l2cap_sock_getsockopt: sk ffff88005ede5800 [69713.824322] [6450] l2cap_sock_getname: sock ffff8800941ab700, sk ffff88005ede5800 [69713.824330] [6450] l2cap_sock_getsockopt: sk ffff88005ede5800 [69713.825029] [6450] bt_sock_poll: sock ffff88005e758500, sk ffff88010323b800 ... [69713.825187] [6450] l2cap_sock_sendmsg: sock ffff8800941ab700, sk ffff88005ede5800 [69713.825189] [6450] bt_sock_wait_ready: sk ffff88005ede5800 [69713.825192] [6450] l2cap_create_basic_pdu: chan ffff88005ede2c00 len 3 [69713.825196] [6450] l2cap_do_send: chan ffff88005ede2c00, skb ffff880160b0b500 len 7 priority 0 [69713.825199] [6450] hci_send_acl: hci0 chan ffff88020d60b1c0 flags 0x0000 [69713.825201] [6450] hci_queue_acl: hci0 nonfrag skb ffff880160b0b500 len 11 [69713.825210] [8909] hci_tx_work: hci0 acl 9 sco 8 le 0 [69713.825213] [8909] hci_sched_acl: hci0 [69713.825214] [8909] hci_sched_sco: hci0 [69713.825216] [8909] hci_sched_esco: hci0 [69713.825217] [8909] hci_sched_le: hci0 [69713.825219] [8909] hci_chan_sent: hci0 [69713.825221] [8909] hci_chan_sent: chan ffff88020d60b1c0 quote 9 [69713.825223] [8909] hci_sched_le: chan ffff88020d60b1c0 skb ffff880160b0b500 len 11 priority 0 [69713.825225] [8909] hci_send_frame: hci0 type 2 len 11 [69713.825227] [8909] hci_send_to_monitor: hdev ffff88021f0c7000 len 11 [69713.825242] [8909] hci_chan_sent: hci0 [69713.825253] [5949] hci_sock_recvmsg: sock ffff8800941a9680, sk ffff88012bf4d000 [69713.825253] [8909] hci_prio_recalculate: hci0 [69713.825292] [5949] hci_sock_recvmsg: sock ffff8800941a9680, sk ffff88012bf4d000 [69713.825768] [6450] bt_sock_poll: sock ffff88005e758500, sk ffff88010323b800 ... [69713.866902] [8909] hci_rx_work: hci0 [69713.866921] [8909] hci_send_to_monitor: hdev ffff88021f0c7000 len 7 [69713.866928] [8909] hci_rx_work: hci0 Event packet [69713.866931] [8909] hci_num_comp_pkts_evt: hci0 num_hndl 1 [69713.866937] [8909] hci_tx_work: hci0 acl 9 sco 8 le 0 [69713.866939] [5949] hci_sock_recvmsg: sock ffff8800941a9680, sk ffff88012bf4d000 [69713.866940] [8909] hci_sched_acl: hci0 ... [69713.866944] [8909] hci_sched_le: hci0 [69713.866953] [8909] hci_chan_sent: hci0 [69713.866997] [5949] hci_sock_recvmsg: sock ffff8800941a9680, sk ffff88012bf4d000 [69713.867840] [28074] hci_rx_work: hci0 [69713.867844] [28074] hci_send_to_monitor: hdev ffff88021f0c7000 len 7 [69713.867850] [28074] hci_rx_work: hci0 Event packet [69713.867853] [28074] hci_num_comp_pkts_evt: hci0 num_hndl 1 [69713.867857] [5949] hci_sock_recvmsg: sock ffff8800941a9680, sk ffff88012bf4d000 [69713.867858] [28074] hci_tx_work: hci0 acl 10 sco 8 le 0 [69713.867860] [28074] hci_sched_acl: hci0 [69713.867861] [28074] hci_sched_sco: hci0 [69713.867862] [28074] hci_sched_esco: hci0 [69713.867863] [28074] hci_sched_le: hci0 [69713.867865] [28074] hci_chan_sent: hci0 [69713.867888] [5949] hci_sock_recvmsg: sock ffff8800941a9680, sk ffff88012bf4d000 [69714.145661] [8909] hci_rx_work: hci0 [69714.145666] [8909] hci_send_to_monitor: hdev ffff88021f0c7000 len 10 [69714.145676] [8909] hci_rx_work: hci0 ACL data packet [69714.145679] [8909] hci_acldata_packet: hci0 len 6 handle 0x002d flags 0x0002 [69714.145681] [8909] hci_conn_enter_active_mode: hcon ffff88021f65a000 mode 0 [69714.145683] [8909] l2cap_recv_acldata: conn ffff880221005c00 len 6 flags 0x2 [69714.145693] [8909] l2cap_recv_frame: len 2, cid 0x0006 [69714.145696] [8909] hci_send_to_control: len 14 [69714.145710] [8909] smp_chan_destroy: [69714.145713] [8909] pairing_complete: status 3 [69714.145714] [8909] cmd_complete: sock ffff88010323ac00 [69714.145717] [8909] hci_conn_drop: hcon ffff88021f65a000 orig refcnt 3 [69714.145719] [5949] hci_sock_recvmsg: sock ffff8800941a9680, sk ffff88012bf4d000 [69714.145720] [6450] bt_sock_poll: sock ffff88005e758500, sk ffff88010323b800 [69714.145722] [6515] hci_sock_recvmsg: sock ffff88005e75a080, sk ffff88010323ac00 [69714.145724] [6450] bt_sock_poll: sock ffff8801db6b4f00, sk ffff880160351c00 ... [69714.145735] [6515] hci_sock_recvmsg: sock ffff88005e75a080, sk ffff88010323ac00 [69714.145737] [8909] hci_conn_drop: hcon ffff88021f65a000 orig refcnt 2 [69714.145739] [8909] l2cap_conn_del: hcon ffff88021f65a000 conn ffff880221005c00, err 13 [69714.145740] [6450] bt_sock_poll: sock ffff8801db6b5400, sk ffff88021e775000 [69714.145743] [6450] bt_sock_poll: sock ffff8801db6b5e00, sk ffff880160356000 [69714.145744] [8909] l2cap_chan_hold: chan ffff88005ede2c00 orig refcnt 3 [69714.145746] [6450] bt_sock_poll: sock ffff8800941ab700, sk ffff88005ede5800 [69714.145748] [8909] l2cap_chan_del: chan ffff88005ede2c00, conn ffff880221005c00, err 13 [69714.145749] [8909] l2cap_chan_put: chan ffff88005ede2c00 orig refcnt 4 [69714.145751] [8909] hci_conn_drop: hcon ffff88021f65a000 orig refcnt 1 [69714.145754] [6450] bt_sock_poll: sock ffff8800941ab700, sk ffff88005ede5800 [69714.145756] [8909] l2cap_chan_put: chan ffff88005ede2c00 orig refcnt 3 [69714.145759] [8909] hci_chan_del: hci0 hcon ffff88021f65a000 chan ffff88020d60b1c0 [69714.145766] [5949] hci_sock_recvmsg: sock ffff8800941a9680, sk ffff88012bf4d000 [69714.145787] [6515] hci_sock_release: sock ffff88005e75a080 sk ffff88010323ac00 [69714.146002] [6450] hci_sock_recvmsg: sock ffff88005e758780, sk ffff88010323d400 [69714.150795] [6450] l2cap_sock_release: sock ffff8800941ab700, sk ffff88005ede5800 [69714.150799] [6450] l2cap_sock_shutdown: sock ffff8800941ab700, sk ffff88005ede5800 [69714.150802] [6450] l2cap_chan_close: chan ffff88005ede2c00 state BT_CLOSED [69714.150805] [6450] l2cap_sock_kill: sk ffff88005ede5800 state BT_CLOSED [69714.150806] [6450] l2cap_chan_put: chan ffff88005ede2c00 orig refcnt 2 [69714.150808] [6450] l2cap_sock_destruct: sk ffff88005ede5800 [69714.150809] [6450] l2cap_chan_put: chan ffff88005ede2c00 orig refcnt 1 [69714.150811] [6450] l2cap_chan_destroy: chan ffff88005ede2c00 [69714.150970] [6450] bt_sock_poll: sock ffff88005e758500, sk ffff88010323b800 ... [69714.151991] [8909] hci_conn_drop: hcon ffff88021f65a000 orig refcnt 0 [69716.150339] [8909] hci_conn_timeout: hcon ffff88021f65a000 state BT_CONNECTED, refcnt -1 Signed-off-by: Lukasz Rymanowski Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_conn.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index ca01d1861854..a7a27bc2c0b1 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -289,10 +289,20 @@ static void hci_conn_timeout(struct work_struct *work) { struct hci_conn *conn = container_of(work, struct hci_conn, disc_work.work); + int refcnt = atomic_read(&conn->refcnt); BT_DBG("hcon %p state %s", conn, state_to_string(conn->state)); - if (atomic_read(&conn->refcnt)) + WARN_ON(refcnt < 0); + + /* FIXME: It was observed that in pairing failed scenario, refcnt + * drops below 0. Probably this is because l2cap_conn_del calls + * l2cap_chan_del for each channel, and inside l2cap_chan_del conn is + * dropped. After that loop hci_chan_del is called which also drops + * conn. For now make sure that ACL is alive if refcnt is higher then 0, + * otherwise drop it. + */ + if (refcnt > 0) return; switch (conn->state) { From 744462a91ecf5d1ec64857488bf99000ef626921 Mon Sep 17 00:00:00 2001 From: Max Stepanov Date: Tue, 10 Jun 2014 20:00:08 +0300 Subject: [PATCH 029/378] mac80211: WEP extra head/tail room in ieee80211_send_auth After skb allocation and call to ieee80211_wep_encrypt in ieee80211_send_auth the flow fails with a warning in ieee80211_wep_add_iv on verification of available head/tailroom needed for WEP_IV and WEP_ICV. Signed-off-by: Max Stepanov Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/util.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index 6886601afe1c..a6cda52ed920 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -1096,11 +1096,12 @@ void ieee80211_send_auth(struct ieee80211_sub_if_data *sdata, int err; /* 24 + 6 = header + auth_algo + auth_transaction + status_code */ - skb = dev_alloc_skb(local->hw.extra_tx_headroom + 24 + 6 + extra_len); + skb = dev_alloc_skb(local->hw.extra_tx_headroom + IEEE80211_WEP_IV_LEN + + 24 + 6 + extra_len + IEEE80211_WEP_ICV_LEN); if (!skb) return; - skb_reserve(skb, local->hw.extra_tx_headroom); + skb_reserve(skb, local->hw.extra_tx_headroom + IEEE80211_WEP_IV_LEN); mgmt = (struct ieee80211_mgmt *) skb_put(skb, 24 + 6); memset(mgmt, 0, 24 + 6); From e33e2241e272eddc38339692500bd1c7d8753a77 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Mon, 23 Jun 2014 11:06:16 +0200 Subject: [PATCH 030/378] Revert "cfg80211: Use 5MHz bandwidth by default when checking usable channels" This reverts commit 8eca1fb692cc9557f386eddce75c300a3855d11a. Felix notes that this broke regulatory, leaving channel 12 open for AP operation in the US regulatory domain where it isn't permitted. Link: http://mid.gmane.org/53A6C0FF.9090104@openwrt.org Reported-by: Felix Fietkau Signed-off-by: Johannes Berg --- net/wireless/reg.c | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/net/wireless/reg.c b/net/wireless/reg.c index 558b0e3a02d8..1afdf45db38f 100644 --- a/net/wireless/reg.c +++ b/net/wireless/reg.c @@ -935,7 +935,7 @@ freq_reg_info_regd(struct wiphy *wiphy, u32 center_freq, if (!band_rule_found) band_rule_found = freq_in_rule_band(fr, center_freq); - bw_fits = reg_does_bw_fit(fr, center_freq, MHZ_TO_KHZ(5)); + bw_fits = reg_does_bw_fit(fr, center_freq, MHZ_TO_KHZ(20)); if (band_rule_found && bw_fits) return rr; @@ -1019,10 +1019,10 @@ static void chan_reg_rule_print_dbg(const struct ieee80211_regdomain *regd, } #endif -/* Find an ieee80211_reg_rule such that a 5MHz channel with frequency - * chan->center_freq fits there. - * If there is no such reg_rule, disable the channel, otherwise set the - * flags corresponding to the bandwidths allowed in the particular reg_rule +/* + * Note that right now we assume the desired channel bandwidth + * is always 20 MHz for each individual channel (HT40 uses 20 MHz + * per channel, the primary and the extension channel). */ static void handle_channel(struct wiphy *wiphy, enum nl80211_reg_initiator initiator, @@ -1083,12 +1083,8 @@ static void handle_channel(struct wiphy *wiphy, if (reg_rule->flags & NL80211_RRF_AUTO_BW) max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule); - if (max_bandwidth_khz < MHZ_TO_KHZ(10)) - bw_flags = IEEE80211_CHAN_NO_10MHZ; - if (max_bandwidth_khz < MHZ_TO_KHZ(20)) - bw_flags |= IEEE80211_CHAN_NO_20MHZ; if (max_bandwidth_khz < MHZ_TO_KHZ(40)) - bw_flags |= IEEE80211_CHAN_NO_HT40; + bw_flags = IEEE80211_CHAN_NO_HT40; if (max_bandwidth_khz < MHZ_TO_KHZ(80)) bw_flags |= IEEE80211_CHAN_NO_80MHZ; if (max_bandwidth_khz < MHZ_TO_KHZ(160)) @@ -1522,12 +1518,8 @@ static void handle_channel_custom(struct wiphy *wiphy, if (reg_rule->flags & NL80211_RRF_AUTO_BW) max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule); - if (max_bandwidth_khz < MHZ_TO_KHZ(10)) - bw_flags = IEEE80211_CHAN_NO_10MHZ; - if (max_bandwidth_khz < MHZ_TO_KHZ(20)) - bw_flags |= IEEE80211_CHAN_NO_20MHZ; if (max_bandwidth_khz < MHZ_TO_KHZ(40)) - bw_flags |= IEEE80211_CHAN_NO_HT40; + bw_flags = IEEE80211_CHAN_NO_HT40; if (max_bandwidth_khz < MHZ_TO_KHZ(80)) bw_flags |= IEEE80211_CHAN_NO_80MHZ; if (max_bandwidth_khz < MHZ_TO_KHZ(160)) From 0ce12026d6ea4a24d52ddcde36b9643f2e26d560 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Tue, 17 Jun 2014 13:07:02 +0300 Subject: [PATCH 031/378] cfg80211: fix elapsed_jiffies calculation MAX_JIFFY_OFFSET has no meaning when calculating the elapsed jiffies, as jiffies run out until ULONG_MAX. This miscalculation results in erroneous values in case of a wrap-around. Signed-off-by: Eliad Peller Signed-off-by: Johannes Berg --- net/wireless/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/wireless/core.h b/net/wireless/core.h index e9afbf10e756..7e3a3cef7df9 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h @@ -424,7 +424,7 @@ static inline unsigned int elapsed_jiffies_msecs(unsigned long start) if (end >= start) return jiffies_to_msecs(end - start); - return jiffies_to_msecs(end + (MAX_JIFFY_OFFSET - start) + 1); + return jiffies_to_msecs(end + (ULONG_MAX - start) + 1); } void From 48439d501e3d9e8634bdc0c418e066870039599d Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Mon, 23 Jun 2014 17:42:44 +0200 Subject: [PATCH 032/378] Bluetooth: Ignore H5 non-link packets in non-active state When detecting a non-link packet, h5_reset_rx() frees the Rx skb. Not returning after that will cause the upcoming h5_rx_payload() call to dereference a now NULL Rx skb and trigger a kernel oops. Signed-off-by: Loic Poulain Signed-off-by: Marcel Holtmann Cc: stable@vger.kernel.org --- drivers/bluetooth/hci_h5.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index 04680ead9275..fede8ca7147c 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -406,6 +406,7 @@ static int h5_rx_3wire_hdr(struct hci_uart *hu, unsigned char c) H5_HDR_PKT_TYPE(hdr) != HCI_3WIRE_LINK_PKT) { BT_ERR("Non-link packet received in non-active state"); h5_reset_rx(h5); + return 0; } h5->rx_func = h5_rx_payload; From 02df00eb0019e7d15a1fcddebe4d020226c1ccda Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 10 Jun 2014 14:06:25 +0200 Subject: [PATCH 033/378] nl80211: move set_qos_map command into split state The non-split wiphy state shouldn't be increased in size so move the new set_qos_map command into the split if statement. Cc: stable@vger.kernel.org (3.14+) Fixes: fa9ffc745610 ("cfg80211: Add support for QoS mapping") Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index ba4f1723c83a..6668daf69326 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -1497,18 +1497,17 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev, } CMD(start_p2p_device, START_P2P_DEVICE); CMD(set_mcast_rate, SET_MCAST_RATE); +#ifdef CONFIG_NL80211_TESTMODE + CMD(testmode_cmd, TESTMODE); +#endif if (state->split) { CMD(crit_proto_start, CRIT_PROTOCOL_START); CMD(crit_proto_stop, CRIT_PROTOCOL_STOP); if (rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH) CMD(channel_switch, CHANNEL_SWITCH); + CMD(set_qos_map, SET_QOS_MAP); } - CMD(set_qos_map, SET_QOS_MAP); - -#ifdef CONFIG_NL80211_TESTMODE - CMD(testmode_cmd, TESTMODE); -#endif - + /* add into the if now */ #undef CMD if (rdev->ops->connect || rdev->ops->auth) { From d76744a93246eccdca1106037e8ee29debf48277 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 20 Jun 2014 11:45:25 -0700 Subject: [PATCH 034/378] mwifiex: fix Tx timeout issue https://bugzilla.kernel.org/show_bug.cgi?id=70191 https://bugzilla.kernel.org/show_bug.cgi?id=77581 It is observed that sometimes Tx packet is downloaded without adding driver's txpd header. This results in firmware parsing garbage data as packet length. Sometimes firmware is unable to read the packet if length comes out as invalid. This stops further traffic and timeout occurs. The root cause is uninitialized fields in tx_info(skb->cb) of packet used to get garbage values. In this case if MWIFIEX_BUF_FLAG_REQUEUED_PKT flag is mistakenly set, txpd header was skipped. This patch makes sure that tx_info is correctly initialized to fix the problem. Cc: Reported-by: Andrew Wiley Reported-by: Linus Gasser Reported-by: Michael Hirsch Tested-by: Xinming Hu Signed-off-by: Amitkumar Karwar Signed-off-by: Maithili Hinge Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index cbabc12fbda3..e91cd0fa5ca8 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -645,6 +645,7 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) } tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); tx_info->bss_num = priv->bss_num; tx_info->bss_type = priv->bss_type; tx_info->pkt_len = skb->len; From c6bff5449fa78e1d524bb5b67b8ab82faf835bff Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Fri, 20 Jun 2014 22:19:25 +0200 Subject: [PATCH 035/378] brcmfmac: assign chip id and rev in bus interface after brcmf_usb_dlneeded The function brcmf_usb_dlneeded() queries the device to obtain the chip id and revision. So assigning these in bus interface before the call resulted in chip id and revision being zero. This was introduced by: commit 5b8045d484d0ef77d6aa9444023220c5671fa3fe Author: Arend van Spriel Date: Tue May 27 12:56:23 2014 +0200 brcmfmac: use asynchronous firmware request in USB Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index 6db51a666f61..d06fcb05adf2 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -1184,8 +1184,6 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) bus->bus_priv.usb = bus_pub; dev_set_drvdata(dev, bus); bus->ops = &brcmf_usb_bus_ops; - bus->chip = bus_pub->devid; - bus->chiprev = bus_pub->chiprev; bus->proto_type = BRCMF_PROTO_BCDC; bus->always_use_fws_queue = true; @@ -1194,6 +1192,9 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_info *devinfo) if (ret) goto fail; } + bus->chip = bus_pub->devid; + bus->chiprev = bus_pub->chiprev; + /* request firmware here */ brcmf_fw_get_firmwares(dev, 0, brcmf_usb_get_fwname(devinfo), NULL, brcmf_usb_probe_phase2); From b0c3e138b467a5cc3b2eb44c6525227f66d1496d Mon Sep 17 00:00:00 2001 From: Adam Lee Date: Thu, 5 Jun 2014 21:47:44 +0800 Subject: [PATCH 036/378] Bluetooth: ath3k: reduce pipe setting times in ath3k_load_fwfile() Invoking usb_sndbulkpipe() on same pipe for same purpose only once is enough. Signed-off-by: Adam Lee Signed-off-by: Gustavo Padovan --- drivers/bluetooth/ath3k.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index f50dffc0374f..abe6aecbabb2 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -288,10 +288,10 @@ static int ath3k_load_fwfile(struct usb_device *udev, sent += size; count -= size; + pipe = usb_sndbulkpipe(udev, 0x02); + while (count) { size = min_t(uint, count, BULK_SIZE); - pipe = usb_sndbulkpipe(udev, 0x02); - memcpy(send_buf, firmware->data + sent, size); err = usb_bulk_msg(udev, pipe, send_buf, size, From 67f86a45bb82091a2775561a1e498010afff54ee Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 8 Jun 2014 10:05:31 +0200 Subject: [PATCH 037/378] Bluetooth: Use const for struct l2cap_ops field The struct l2cap_ops field should not allow any modifications and thus it is better declared as const. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/l2cap.h | 2 +- net/bluetooth/a2mp.c | 2 +- net/bluetooth/l2cap_sock.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 4abdcb220e3a..cf67420616e5 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -579,7 +579,7 @@ struct l2cap_chan { struct list_head global_l; void *data; - struct l2cap_ops *ops; + const struct l2cap_ops *ops; struct mutex lock; }; diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 9514cc9e850c..1996b4c4dfd5 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -704,7 +704,7 @@ static struct sk_buff *a2mp_chan_alloc_skb_cb(struct l2cap_chan *chan, return skb; } -static struct l2cap_ops a2mp_chan_ops = { +static const struct l2cap_ops a2mp_chan_ops = { .name = "L2CAP A2MP channel", .recv = a2mp_chan_recv_cb, .close = a2mp_chan_close_cb, diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index e1378693cc90..11e2207717b6 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1375,7 +1375,7 @@ static void l2cap_sock_suspend_cb(struct l2cap_chan *chan) sk->sk_state_change(sk); } -static struct l2cap_ops l2cap_chan_ops = { +static const struct l2cap_ops l2cap_chan_ops = { .name = "L2CAP Socket Interface", .new_connection = l2cap_sock_new_connection_cb, .recv = l2cap_sock_recv_cb, From 8d46321c4f63f7c2be9e3ba0bb26cb437fc5eded Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 5 Jun 2014 15:22:51 +0200 Subject: [PATCH 038/378] Bluetooth: Assign L2CAP socket priority when allocating SKB The SKB for L2CAP sockets are all allocated in a central callback in the socket support. Instead of having to pass around the socket priority all the time, assign it to skb->priority when actually allocating the SKB. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/l2cap.h | 3 +-- net/bluetooth/a2mp.c | 2 +- net/bluetooth/l2cap_core.c | 23 +++++++---------------- net/bluetooth/l2cap_sock.c | 4 +++- 4 files changed, 12 insertions(+), 20 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index cf67420616e5..18f4f27e0f74 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -872,8 +872,7 @@ struct l2cap_chan *l2cap_chan_create(void); void l2cap_chan_close(struct l2cap_chan *chan, int reason); int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, bdaddr_t *dst, u8 dst_type); -int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, - u32 priority); +int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len); void l2cap_chan_busy(struct l2cap_chan *chan, int busy); int l2cap_chan_check_security(struct l2cap_chan *chan); void l2cap_chan_set_defaults(struct l2cap_chan *chan); diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 1996b4c4dfd5..1a5b1eb96d1a 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -63,7 +63,7 @@ void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data) msg.msg_iov = (struct iovec *) &iv; msg.msg_iovlen = 1; - l2cap_chan_send(chan, &msg, total_len, 0); + l2cap_chan_send(chan, &msg, total_len); kfree(cmd); } diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 323f23cd2c37..3dca28246cbe 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2141,8 +2141,6 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan, if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) return -EFAULT; - (*frag)->priority = skb->priority; - sent += count; len -= count; @@ -2156,16 +2154,15 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan, } static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, - struct msghdr *msg, size_t len, - u32 priority) + struct msghdr *msg, size_t len) { struct l2cap_conn *conn = chan->conn; struct sk_buff *skb; int err, count, hlen = L2CAP_HDR_SIZE + L2CAP_PSMLEN_SIZE; struct l2cap_hdr *lh; - BT_DBG("chan %p psm 0x%2.2x len %zu priority %u", chan, - __le16_to_cpu(chan->psm), len, priority); + BT_DBG("chan %p psm 0x%2.2x len %zu", chan, + __le16_to_cpu(chan->psm), len); count = min_t(unsigned int, (conn->mtu - hlen), len); @@ -2174,8 +2171,6 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, if (IS_ERR(skb)) return skb; - skb->priority = priority; - /* Create L2CAP header */ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->cid = cpu_to_le16(chan->dcid); @@ -2191,8 +2186,7 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, } static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, - struct msghdr *msg, size_t len, - u32 priority) + struct msghdr *msg, size_t len) { struct l2cap_conn *conn = chan->conn; struct sk_buff *skb; @@ -2208,8 +2202,6 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, if (IS_ERR(skb)) return skb; - skb->priority = priority; - /* Create L2CAP header */ lh = (struct l2cap_hdr *) skb_put(skb, L2CAP_HDR_SIZE); lh->cid = cpu_to_le16(chan->dcid); @@ -2430,8 +2422,7 @@ static int l2cap_segment_le_sdu(struct l2cap_chan *chan, return 0; } -int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, - u32 priority) +int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) { struct sk_buff *skb; int err; @@ -2442,7 +2433,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, /* Connectionless channel */ if (chan->chan_type == L2CAP_CHAN_CONN_LESS) { - skb = l2cap_create_connless_pdu(chan, msg, len, priority); + skb = l2cap_create_connless_pdu(chan, msg, len); if (IS_ERR(skb)) return PTR_ERR(skb); @@ -2499,7 +2490,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len, return -EMSGSIZE; /* Create a basic PDU */ - skb = l2cap_create_basic_pdu(chan, msg, len, priority); + skb = l2cap_create_basic_pdu(chan, msg, len); if (IS_ERR(skb)) return PTR_ERR(skb); diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 11e2207717b6..d95964c9f91e 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -964,7 +964,7 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, return err; l2cap_chan_lock(chan); - err = l2cap_chan_send(chan, msg, len, sk->sk_priority); + err = l2cap_chan_send(chan, msg, len); l2cap_chan_unlock(chan); return err; @@ -1305,6 +1305,8 @@ static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan, if (!skb) return ERR_PTR(err); + skb->priority = sk->sk_priority; + bt_cb(skb)->chan = chan; return skb; From 0775899158d0e1436316f3bf451d78bf34b88d17 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 8 Jun 2014 10:26:57 +0200 Subject: [PATCH 039/378] Bluetooth: Shrink size of struct l2cap_ctrl fields The struct l2cap_ctrl fields are wasting an unsigned int when all the bits can fit into an __u8 field. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/bluetooth.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 904777c1cd24..373000de610d 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -260,15 +260,15 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock); /* Skb helpers */ struct l2cap_ctrl { - unsigned int sframe:1, - poll:1, - final:1, - fcs:1, - sar:2, - super:2; - __u16 reqseq; - __u16 txseq; - __u8 retries; + __u8 sframe:1, + poll:1, + final:1, + fcs:1, + sar:2, + super:2; + __u16 reqseq; + __u16 txseq; + __u8 retries; }; struct hci_dev; From d9fbd02be5c201c1659ee0d79c0820bb68d95c8c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 8 Jun 2014 11:22:28 +0200 Subject: [PATCH 040/378] Bluetooth: Use explicit header and body length for L2CAP SKB allocation When allocating the L2CAP SKB for transmission, provide the upper layers with a clear distinction on what is the header and what is the body portion of the SKB. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/l2cap.h | 1 + net/bluetooth/a2mp.c | 3 ++- net/bluetooth/l2cap_core.c | 10 +++++----- net/bluetooth/l2cap_sock.c | 3 ++- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 18f4f27e0f74..92511034d1d4 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -600,6 +600,7 @@ struct l2cap_ops { void (*set_shutdown) (struct l2cap_chan *chan); long (*get_sndtimeo) (struct l2cap_chan *chan); struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan, + unsigned long hdr_len, unsigned long len, int nb); }; diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 1a5b1eb96d1a..0fd8d1dda709 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -693,11 +693,12 @@ static void a2mp_chan_state_change_cb(struct l2cap_chan *chan, int state, } static struct sk_buff *a2mp_chan_alloc_skb_cb(struct l2cap_chan *chan, + unsigned long hdr_len, unsigned long len, int nb) { struct sk_buff *skb; - skb = bt_skb_alloc(len, GFP_KERNEL); + skb = bt_skb_alloc(hdr_len + len, GFP_KERNEL); if (!skb) return ERR_PTR(-ENOMEM); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 3dca28246cbe..ac2461442f21 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2131,7 +2131,7 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan, count = min_t(unsigned int, conn->mtu, len); - tmp = chan->ops->alloc_skb(chan, count, + tmp = chan->ops->alloc_skb(chan, 0, count, msg->msg_flags & MSG_DONTWAIT); if (IS_ERR(tmp)) return PTR_ERR(tmp); @@ -2166,7 +2166,7 @@ static struct sk_buff *l2cap_create_connless_pdu(struct l2cap_chan *chan, count = min_t(unsigned int, (conn->mtu - hlen), len); - skb = chan->ops->alloc_skb(chan, count + hlen, + skb = chan->ops->alloc_skb(chan, hlen, count, msg->msg_flags & MSG_DONTWAIT); if (IS_ERR(skb)) return skb; @@ -2197,7 +2197,7 @@ static struct sk_buff *l2cap_create_basic_pdu(struct l2cap_chan *chan, count = min_t(unsigned int, (conn->mtu - L2CAP_HDR_SIZE), len); - skb = chan->ops->alloc_skb(chan, count + L2CAP_HDR_SIZE, + skb = chan->ops->alloc_skb(chan, L2CAP_HDR_SIZE, count, msg->msg_flags & MSG_DONTWAIT); if (IS_ERR(skb)) return skb; @@ -2239,7 +2239,7 @@ static struct sk_buff *l2cap_create_iframe_pdu(struct l2cap_chan *chan, count = min_t(unsigned int, (conn->mtu - hlen), len); - skb = chan->ops->alloc_skb(chan, count + hlen, + skb = chan->ops->alloc_skb(chan, hlen, count, msg->msg_flags & MSG_DONTWAIT); if (IS_ERR(skb)) return skb; @@ -2360,7 +2360,7 @@ static struct sk_buff *l2cap_create_le_flowctl_pdu(struct l2cap_chan *chan, count = min_t(unsigned int, (conn->mtu - hlen), len); - skb = chan->ops->alloc_skb(chan, count + hlen, + skb = chan->ops->alloc_skb(chan, hlen, count, msg->msg_flags & MSG_DONTWAIT); if (IS_ERR(skb)) return skb; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index d95964c9f91e..55215ebf6547 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1292,6 +1292,7 @@ static void l2cap_sock_state_change_cb(struct l2cap_chan *chan, int state, } static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan, + unsigned long hdr_len, unsigned long len, int nb) { struct sock *sk = chan->data; @@ -1299,7 +1300,7 @@ static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan, int err; l2cap_chan_unlock(chan); - skb = bt_skb_send_alloc(sk, len, nb, &err); + skb = bt_skb_send_alloc(sk, hdr_len + len, nb, &err); l2cap_chan_lock(chan); if (!skb) From 65cc2b49db63adf1455a9783234383fbec5b8314 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 16 Jun 2014 12:30:56 +0200 Subject: [PATCH 041/378] Bluetooth: Use struct delayed_work for HCI command timeout Since the whole HCI command, event and data packet processing has been migrated to use workqueues instead of tasklets, it makes sense to use struct delayed_work instead of struct timer_list for the timeout handling. This patch converts the hdev->cmd_timer to use workqueue as well. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_core.c | 17 +++++++++-------- net/bluetooth/hci_event.c | 4 ++-- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b386bf17e6c2..de3bb22e83f9 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -273,7 +273,7 @@ struct hci_dev { struct delayed_work service_cache; - struct timer_list cmd_timer; + struct delayed_work cmd_timer; struct work_struct rx_work; struct work_struct cmd_work; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 0a43cce9a914..9e0368b02a11 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2432,7 +2432,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) hci_req_lock(hdev); if (!test_and_clear_bit(HCI_UP, &hdev->flags)) { - del_timer_sync(&hdev->cmd_timer); + cancel_delayed_work_sync(&hdev->cmd_timer); hci_req_unlock(hdev); return 0; } @@ -2488,7 +2488,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) /* Drop last sent command */ if (hdev->sent_cmd) { - del_timer_sync(&hdev->cmd_timer); + cancel_delayed_work_sync(&hdev->cmd_timer); kfree_skb(hdev->sent_cmd); hdev->sent_cmd = NULL; } @@ -3205,9 +3205,10 @@ void hci_remove_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type) } /* HCI command timer function */ -static void hci_cmd_timeout(unsigned long arg) +static void hci_cmd_timeout(struct work_struct *work) { - struct hci_dev *hdev = (void *) arg; + struct hci_dev *hdev = container_of(work, struct hci_dev, + cmd_timer.work); if (hdev->sent_cmd) { struct hci_command_hdr *sent = (void *) hdev->sent_cmd->data; @@ -3884,7 +3885,7 @@ struct hci_dev *hci_alloc_dev(void) init_waitqueue_head(&hdev->req_wait_q); - setup_timer(&hdev->cmd_timer, hci_cmd_timeout, (unsigned long) hdev); + INIT_DELAYED_WORK(&hdev->cmd_timer, hci_cmd_timeout); hci_init_sysfs(hdev); discovery_init(hdev); @@ -5287,10 +5288,10 @@ static void hci_cmd_work(struct work_struct *work) atomic_dec(&hdev->cmd_cnt); hci_send_frame(hdev, skb); if (test_bit(HCI_RESET, &hdev->flags)) - del_timer(&hdev->cmd_timer); + cancel_delayed_work(&hdev->cmd_timer); else - mod_timer(&hdev->cmd_timer, - jiffies + HCI_CMD_TIMEOUT); + schedule_delayed_work(&hdev->cmd_timer, + HCI_CMD_TIMEOUT); } else { skb_queue_head(&hdev->cmd_q, skb); queue_work(hdev->workqueue, &hdev->cmd_work); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 640c54ec1bd2..3d52be1d695d 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2709,7 +2709,7 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) } if (opcode != HCI_OP_NOP) - del_timer(&hdev->cmd_timer); + cancel_delayed_work(&hdev->cmd_timer); hci_req_cmd_complete(hdev, opcode, status); @@ -2800,7 +2800,7 @@ static void hci_cmd_status_evt(struct hci_dev *hdev, struct sk_buff *skb) } if (opcode != HCI_OP_NOP) - del_timer(&hdev->cmd_timer); + cancel_delayed_work(&hdev->cmd_timer); if (ev->status || (hdev->sent_cmd && !bt_cb(hdev->sent_cmd)->req.event)) From c29d2444170a4e0709331e357a2738a02666a633 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 16 Jun 2014 19:25:14 +0300 Subject: [PATCH 042/378] Bluetooth: Fix missing NULL check for smp_chan_create() return value The smp_chan_create function may return NULL, e.g. in the case of memory allocation failure, so we always need to check for this. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index e33a982161c1..1f4ed1e78e10 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -888,6 +888,8 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) return 0; smp = smp_chan_create(conn); + if (!smp) + return SMP_UNSPECIFIED; skb_pull(skb, sizeof(*rp)); From 7d5843b7b77cee26bd5e090bfa61780d75957648 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 16 Jun 2014 19:25:15 +0300 Subject: [PATCH 043/378] Bluetooth: Remove unnecessary SMP STK define We never store the "master" type of STKs since we request encryption directly with them so we only need one STK type (the one that's looked-up on the slave side). Simply remove the unnecessary define and rename the _SLAVE one to the shorter form. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 1 - net/bluetooth/hci_event.c | 2 +- net/bluetooth/smp.c | 6 +++++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 16587dcd6a91..98f7520d2f57 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -349,7 +349,6 @@ enum { #define HCI_LK_AUTH_COMBINATION_P256 0x08 /* The spec doesn't define types for SMP keys, the _MASTER suffix is implied */ #define HCI_SMP_STK 0x80 -#define HCI_SMP_STK_SLAVE 0x81 #define HCI_SMP_LTK 0x82 #define HCI_SMP_LTK_SLAVE 0x83 diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 3d52be1d695d..8dde40430b5f 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4241,7 +4241,7 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb) * distribute the keys. Later, security can be re-established * using a distributed LTK. */ - if (ltk->type == HCI_SMP_STK_SLAVE) { + if (ltk->type == HCI_SMP_STK) { list_del(<k->list); kfree(ltk); } diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 1f4ed1e78e10..b9cac1deb19f 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -568,8 +568,12 @@ static u8 smp_random(struct smp_chan *smp) else auth = 0; + /* Even though there's no _SLAVE suffix this is the + * slave STK we're adding for later lookup (the master + * STK never needs to be stored). + */ hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, - HCI_SMP_STK_SLAVE, auth, stk, smp->enc_key_size, + HCI_SMP_STK, auth, stk, smp->enc_key_size, ediv, rand); } From 2ceba53936d6f2071659b87748d723021937d035 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 16 Jun 2014 19:25:16 +0300 Subject: [PATCH 044/378] Bluetooth: Remove HCI prefix from SMP LTK defines The LTK type has really nothing to do with HCI so it makes more sense to have these in smp.h than hci.h. This patch moves the defines to smp.h and removes the HCI_ prefix in the same go. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 4 ---- net/bluetooth/hci_core.c | 2 +- net/bluetooth/hci_event.c | 3 ++- net/bluetooth/mgmt.c | 6 +++--- net/bluetooth/smp.c | 7 +++---- net/bluetooth/smp.h | 7 +++++++ 6 files changed, 16 insertions(+), 13 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 98f7520d2f57..6ec5b3bd1f67 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -347,10 +347,6 @@ enum { #define HCI_LK_CHANGED_COMBINATION 0x06 #define HCI_LK_UNAUTH_COMBINATION_P256 0x07 #define HCI_LK_AUTH_COMBINATION_P256 0x08 -/* The spec doesn't define types for SMP keys, the _MASTER suffix is implied */ -#define HCI_SMP_STK 0x80 -#define HCI_SMP_LTK 0x82 -#define HCI_SMP_LTK_SLAVE 0x83 /* Long Term Key types */ #define HCI_LTK_UNAUTH 0x00 diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 9e0368b02a11..6001b9293905 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2974,7 +2974,7 @@ static bool hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn, static bool ltk_type_master(u8 type) { - if (type == HCI_SMP_STK || type == HCI_SMP_LTK) + if (type == SMP_STK || type == SMP_LTK) return true; return false; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 8dde40430b5f..15a0a65bf447 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -32,6 +32,7 @@ #include "a2mp.h" #include "amp.h" +#include "smp.h" /* Handle HCI Event packets */ @@ -4241,7 +4242,7 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb) * distribute the keys. Later, security can be re-established * using a distributed LTK. */ - if (ltk->type == HCI_SMP_STK) { + if (ltk->type == SMP_STK) { list_del(<k->list); kfree(ltk); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index af8e0a6243b7..99eb845865ef 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -4550,9 +4550,9 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, addr_type = ADDR_LE_DEV_RANDOM; if (key->master) - type = HCI_SMP_LTK; + type = SMP_LTK; else - type = HCI_SMP_LTK_SLAVE; + type = SMP_LTK_SLAVE; switch (key->type) { case MGMT_LTK_UNAUTHENTICATED: @@ -5279,7 +5279,7 @@ void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent) ev.key.ediv = key->ediv; ev.key.rand = key->rand; - if (key->type == HCI_SMP_LTK) + if (key->type == SMP_LTK) ev.key.master = 1; memcpy(ev.key.val, key->val, sizeof(key->val)); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index b9cac1deb19f..78b9573f4a73 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -573,8 +573,7 @@ static u8 smp_random(struct smp_chan *smp) * STK never needs to be stored). */ hci_add_ltk(hcon->hdev, &hcon->dst, hcon->dst_type, - HCI_SMP_STK, auth, stk, smp->enc_key_size, - ediv, rand); + SMP_STK, auth, stk, smp->enc_key_size, ediv, rand); } return 0; @@ -1027,7 +1026,7 @@ static int smp_cmd_master_ident(struct l2cap_conn *conn, struct sk_buff *skb) hci_dev_lock(hdev); authenticated = (hcon->sec_level == BT_SECURITY_HIGH); - ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, HCI_SMP_LTK, + ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, SMP_LTK, authenticated, smp->tk, smp->enc_key_size, rp->ediv, rp->rand); smp->ltk = ltk; @@ -1343,7 +1342,7 @@ int smp_distribute_keys(struct l2cap_conn *conn) authenticated = hcon->sec_level == BT_SECURITY_HIGH; ltk = hci_add_ltk(hdev, &hcon->dst, hcon->dst_type, - HCI_SMP_LTK_SLAVE, authenticated, enc.ltk, + SMP_LTK_SLAVE, authenticated, enc.ltk, smp->enc_key_size, ediv, rand); smp->slave_ltk = ltk; diff --git a/net/bluetooth/smp.h b/net/bluetooth/smp.h index 5a8dc36460a1..796f4f45f92f 100644 --- a/net/bluetooth/smp.h +++ b/net/bluetooth/smp.h @@ -116,6 +116,13 @@ struct smp_cmd_security_req { #define SMP_MIN_ENC_KEY_SIZE 7 #define SMP_MAX_ENC_KEY_SIZE 16 +/* LTK types used in internal storage (struct smp_ltk) */ +enum { + SMP_STK, + SMP_LTK, + SMP_LTK_SLAVE, +}; + /* SMP Commands */ bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level); int smp_conn_security(struct hci_conn *hcon, __u8 sec_level); From ddf4ba078f80415a514e175768bf67631beee7eb Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 16 Jun 2014 19:25:17 +0300 Subject: [PATCH 045/378] Bluetooth: Remove unused LTK authentication defines These defines were probably put in to track authenticated vs unauthenticated LTKs, however since the LTK struct has a separate boolean authenticated member these were never used. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 6ec5b3bd1f67..fe584d78008d 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -348,10 +348,6 @@ enum { #define HCI_LK_UNAUTH_COMBINATION_P256 0x07 #define HCI_LK_AUTH_COMBINATION_P256 0x08 -/* Long Term Key types */ -#define HCI_LTK_UNAUTH 0x00 -#define HCI_LTK_AUTH 0x01 - /* ---- HCI Error Codes ---- */ #define HCI_ERROR_AUTH_FAILURE 0x05 #define HCI_ERROR_MEMORY_EXCEEDED 0x07 From 533e35d40130c040e38ffa3ee0401c8c84da618d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 16 Jun 2014 19:25:18 +0300 Subject: [PATCH 046/378] Bluetooth: Convert SMP flags into an enum There's no reason to have explicit values for these flags. Convert them to an enum to be consistent with other similar flags. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 78b9573f4a73..72c5aa05a489 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -35,11 +35,13 @@ #define AUTH_REQ_MASK 0x07 -#define SMP_FLAG_TK_VALID 1 -#define SMP_FLAG_CFM_PENDING 2 -#define SMP_FLAG_MITM_AUTH 3 -#define SMP_FLAG_COMPLETE 4 -#define SMP_FLAG_INITIATOR 5 +enum { + SMP_FLAG_TK_VALID, + SMP_FLAG_CFM_PENDING, + SMP_FLAG_MITM_AUTH, + SMP_FLAG_COMPLETE, + SMP_FLAG_INITIATOR, +}; struct smp_chan { struct l2cap_conn *conn; From 8a2936f44ad53ce4e734a0a0cc5bedc57711e87c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 16 Jun 2014 19:25:19 +0300 Subject: [PATCH 047/378] Bluetooth: Add flexible buffer byte order swapping function Since the SMP code needs to swap ordering of variable length buffers add a convenience function that can be used for any length buffer. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 72c5aa05a489..28f4ef48095b 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -64,18 +64,12 @@ struct smp_chan { unsigned long flags; }; -static inline void swap128(const u8 src[16], u8 dst[16]) +static inline void swap_buf(const u8 *src, u8 *dst, size_t len) { - int i; - for (i = 0; i < 16; i++) - dst[15 - i] = src[i]; -} + size_t i; -static inline void swap56(const u8 src[7], u8 dst[7]) -{ - int i; - for (i = 0; i < 7; i++) - dst[6 - i] = src[i]; + for (i = 0; i < len; i++) + dst[len - 1 - i] = src[i]; } static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r) @@ -94,7 +88,7 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r) desc.flags = 0; /* The most significant octet of key corresponds to k[0] */ - swap128(k, tmp); + swap_buf(k, tmp, 16); err = crypto_blkcipher_setkey(tfm, tmp, 16); if (err) { @@ -103,7 +97,7 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r) } /* Most significant octet of plaintextData corresponds to data[0] */ - swap128(r, data); + swap_buf(r, data, 16); sg_init_one(&sg, data, 16); @@ -112,7 +106,7 @@ static int smp_e(struct crypto_blkcipher *tfm, const u8 *k, u8 *r) BT_ERR("Encrypt data error %d", err); /* Most significant octet of encryptedData corresponds to data[0] */ - swap128(data, r); + swap_buf(data, r, 16); return err; } From 8ba8b4c05c3562d771fbfd2e7ee5648b41f04a9d Mon Sep 17 00:00:00 2001 From: Stephan Gabert Date: Mon, 16 Jun 2014 18:52:25 +0200 Subject: [PATCH 048/378] Bluetooth: Remove trailing whitespaces from Kconfig Fixed a coding style issue. Removed trailing whitespaces in drivers/bluetooth/Kconfig. Signed-off-by: Stephan Gabert Signed-off-by: Nicolas Pfeiffer Signed-off-by: Marcel Holtmann --- drivers/bluetooth/Kconfig | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index f5ce64e03fd7..fa7fd62ddffa 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -30,8 +30,8 @@ config BT_HCIUART help Bluetooth HCI UART driver. This driver is required if you want to use Bluetooth devices with - serial port interface. You will also need this driver if you have - UART based Bluetooth PCMCIA and CF devices like Xircom Credit Card + serial port interface. You will also need this driver if you have + UART based Bluetooth PCMCIA and CF devices like Xircom Credit Card adapter and BrainBoxes Bluetooth PC Card. Say Y here to compile support for Bluetooth UART devices into the @@ -41,9 +41,9 @@ config BT_HCIUART_H4 bool "UART (H4) protocol support" depends on BT_HCIUART help - UART (H4) is serial protocol for communication between Bluetooth - device and host. This protocol is required for most Bluetooth devices - with UART interface, including PCMCIA and CF cards. + UART (H4) is serial protocol for communication between Bluetooth + device and host. This protocol is required for most Bluetooth devices + with UART interface, including PCMCIA and CF cards. Say Y here to compile support for HCI UART (H4) protocol. @@ -52,7 +52,7 @@ config BT_HCIUART_BCSP depends on BT_HCIUART select BITREVERSE help - BCSP (BlueCore Serial Protocol) is serial protocol for communication + BCSP (BlueCore Serial Protocol) is serial protocol for communication between Bluetooth device and host. This protocol is required for non USB Bluetooth devices based on CSR BlueCore chip, including PCMCIA and CF cards. From 4ec86d4c86fe563482f183243f15bcd5fd3e65c5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 17 Jun 2014 15:14:48 +0300 Subject: [PATCH 049/378] Bluetooth: Fix validating IO capability values in mgmt commands The valid range of IO capabilities for the Set IO Capability and Pair Device mgmt commands is 0-4 (4 being the KeyboarDisplay capability for SMP). We should return an invalid parameters error if user space gives us a value outside of this range. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 99eb845865ef..5bf032d9bce1 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2766,6 +2766,10 @@ static int set_io_capability(struct sock *sk, struct hci_dev *hdev, void *data, BT_DBG(""); + if (cp->io_capability > SMP_IO_KEYBOARD_DISPLAY) + return cmd_complete(sk, hdev->id, MGMT_OP_SET_IO_CAPABILITY, + MGMT_STATUS_INVALID_PARAMS, NULL, 0); + hci_dev_lock(hdev); hdev->io_capability = cp->io_capability; @@ -2878,6 +2882,11 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, MGMT_STATUS_INVALID_PARAMS, &rp, sizeof(rp)); + if (cp->io_cap > SMP_IO_KEYBOARD_DISPLAY) + return cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &rp, sizeof(rp)); + hci_dev_lock(hdev); if (!hdev_is_powered(hdev)) { From d97c9fb0c82afc6042004ed3f381d85ff31fadcc Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 18 Jun 2014 14:09:40 +0300 Subject: [PATCH 050/378] Bluetooth: Fix checking for master LTKs When the rename of STK_SLAVE to simply STK happened we missed this place in the ltk_type_master function. Now, checking for master is as simple as checking whether the type is SMP_LTK. The helper function is kept around for better readability in the (right now three) callers and for simpler extension with new key types in the future. Signed-off-by: Johan Hedberg Tested-by: Lukasz Rymanowski Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_core.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 6001b9293905..c3d184fd24de 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2974,10 +2974,7 @@ static bool hci_persistent_key(struct hci_dev *hdev, struct hci_conn *conn, static bool ltk_type_master(u8 type) { - if (type == SMP_STK || type == SMP_LTK) - return true; - - return false; + return (type == SMP_LTK); } struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand, From 111902f7236ff8139c30c2b9709c999fcb931399 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 21 Jun 2014 04:53:17 +0200 Subject: [PATCH 051/378] Bluetooth: Use separate dbg_flags to special debugfs options All the special settings configured via debugfs are either developer only options or temporary solutions. To not clutter the standard flags, move them to their own dbg_flags entry. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 12 +++++++++--- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 24 ++++++++++++------------ net/bluetooth/mgmt.c | 4 ++-- 4 files changed, 24 insertions(+), 17 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index fe584d78008d..3e12e27afca1 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -104,6 +104,15 @@ enum { HCI_RESET, }; +/* BR/EDR and/or LE controller flags: the flags defined here should represent + * states configured via debugfs for debugging and testing purposes only. + */ +enum { + HCI_DUT_MODE, + HCI_FORCE_SC, + HCI_FORCE_STATIC_ADDR, +}; + /* * BR/EDR and/or LE controller flags: the flags defined here should represent * states from the controller. @@ -116,9 +125,6 @@ enum { HCI_PAIRABLE, HCI_SERVICE_CACHE, HCI_DEBUG_KEYS, - HCI_DUT_MODE, - HCI_FORCE_SC, - HCI_FORCE_STATIC_ADDR, HCI_UNREGISTER, HCI_USER_CHANNEL, diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index de3bb22e83f9..bf41e992a618 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -318,6 +318,7 @@ struct hci_dev { struct rfkill *rfkill; + unsigned long dbg_flags; unsigned long dev_flags; struct delayed_work le_scan_disable; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index c3d184fd24de..ddbb084250be 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -68,7 +68,7 @@ static ssize_t dut_mode_read(struct file *file, char __user *user_buf, struct hci_dev *hdev = file->private_data; char buf[3]; - buf[0] = test_bit(HCI_DUT_MODE, &hdev->dev_flags) ? 'Y': 'N'; + buf[0] = test_bit(HCI_DUT_MODE, &hdev->dbg_flags) ? 'Y': 'N'; buf[1] = '\n'; buf[2] = '\0'; return simple_read_from_buffer(user_buf, count, ppos, buf, 2); @@ -94,7 +94,7 @@ static ssize_t dut_mode_write(struct file *file, const char __user *user_buf, if (strtobool(buf, &enable)) return -EINVAL; - if (enable == test_bit(HCI_DUT_MODE, &hdev->dev_flags)) + if (enable == test_bit(HCI_DUT_MODE, &hdev->dbg_flags)) return -EALREADY; hci_req_lock(hdev); @@ -115,7 +115,7 @@ static ssize_t dut_mode_write(struct file *file, const char __user *user_buf, if (err < 0) return err; - change_bit(HCI_DUT_MODE, &hdev->dev_flags); + change_bit(HCI_DUT_MODE, &hdev->dbg_flags); return count; } @@ -407,7 +407,7 @@ static ssize_t force_sc_support_read(struct file *file, char __user *user_buf, struct hci_dev *hdev = file->private_data; char buf[3]; - buf[0] = test_bit(HCI_FORCE_SC, &hdev->dev_flags) ? 'Y': 'N'; + buf[0] = test_bit(HCI_FORCE_SC, &hdev->dbg_flags) ? 'Y': 'N'; buf[1] = '\n'; buf[2] = '\0'; return simple_read_from_buffer(user_buf, count, ppos, buf, 2); @@ -432,10 +432,10 @@ static ssize_t force_sc_support_write(struct file *file, if (strtobool(buf, &enable)) return -EINVAL; - if (enable == test_bit(HCI_FORCE_SC, &hdev->dev_flags)) + if (enable == test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) return -EALREADY; - change_bit(HCI_FORCE_SC, &hdev->dev_flags); + change_bit(HCI_FORCE_SC, &hdev->dbg_flags); return count; } @@ -719,7 +719,7 @@ static ssize_t force_static_address_read(struct file *file, struct hci_dev *hdev = file->private_data; char buf[3]; - buf[0] = test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags) ? 'Y': 'N'; + buf[0] = test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) ? 'Y': 'N'; buf[1] = '\n'; buf[2] = '\0'; return simple_read_from_buffer(user_buf, count, ppos, buf, 2); @@ -744,10 +744,10 @@ static ssize_t force_static_address_write(struct file *file, if (strtobool(buf, &enable)) return -EINVAL; - if (enable == test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags)) + if (enable == test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags)) return -EALREADY; - change_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags); + change_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags); return count; } @@ -1752,7 +1752,7 @@ static void hci_init4_req(struct hci_request *req, unsigned long opt) /* Enable Secure Connections if supported and configured */ if ((lmp_sc_capable(hdev) || - test_bit(HCI_FORCE_SC, &hdev->dev_flags)) && + test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) && test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) { u8 support = 0x01; hci_req_add(req, HCI_OP_WRITE_SC_SUPPORT, @@ -3782,7 +3782,7 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy, * the HCI command if the current random address is already the * static one. */ - if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags) || + if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) || !bacmp(&hdev->bdaddr, BDADDR_ANY)) { *own_addr_type = ADDR_LE_DEV_RANDOM; if (bacmp(&hdev->static_addr, &hdev->random_addr)) @@ -3811,7 +3811,7 @@ int hci_update_random_address(struct hci_request *req, bool require_privacy, void hci_copy_identity_address(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 *bdaddr_type) { - if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dev_flags) || + if (test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) || !bacmp(&hdev->bdaddr, BDADDR_ANY)) { bacpy(bdaddr, &hdev->static_addr); *bdaddr_type = ADDR_LE_DEV_RANDOM; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 5bf032d9bce1..e1651c3fc676 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -387,7 +387,7 @@ static u32 get_supported_settings(struct hci_dev *hdev) } if (lmp_sc_capable(hdev) || - test_bit(HCI_FORCE_SC, &hdev->dev_flags)) + test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) settings |= MGMT_SETTING_SECURE_CONN; } @@ -4261,7 +4261,7 @@ static int set_secure_conn(struct sock *sk, struct hci_dev *hdev, status); if (!lmp_sc_capable(hdev) && - !test_bit(HCI_FORCE_SC, &hdev->dev_flags)) + !test_bit(HCI_FORCE_SC, &hdev->dbg_flags)) return cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN, MGMT_STATUS_NOT_SUPPORTED); From 0498878b18993891f7b71c75b6adcb7c157501db Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 18 Jun 2014 16:37:07 +0300 Subject: [PATCH 052/378] Bluetooth: Provide L2CAP ops callback for memcpy_fromiovec The highly optimized TX path for L2CAP channels and its fragmentation within the HCI ACL packets requires to copy data from user provided IO vectors and also kernel provided memory buffers. This patch allows channel clients to provide a memcpy_fromiovec callback to keep this optimized behavior, but adapt it to kernel vs user memory for the TX path. For all kernel internal L2CAP channels, a default implementation is provided that can be referenced. In case of A2MP, this fixes a long-standing issue with wrongly accessing kernel memory as user memory. This patch originally by Marcel Holtmann. Signed-off-by: Jukka Rissanen Signed-off-by: Marcel Holtmann --- include/net/bluetooth/l2cap.h | 29 +++++++++++++++++++++++++++++ net/bluetooth/a2mp.c | 1 + net/bluetooth/l2cap_core.c | 6 ++++-- net/bluetooth/l2cap_sock.c | 34 +++++++++++++++++++++------------- 4 files changed, 55 insertions(+), 15 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 92511034d1d4..1ee6f00d7096 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -602,6 +602,10 @@ struct l2cap_ops { struct sk_buff *(*alloc_skb) (struct l2cap_chan *chan, unsigned long hdr_len, unsigned long len, int nb); + int (*memcpy_fromiovec) (struct l2cap_chan *chan, + unsigned char *kdata, + struct iovec *iov, + int len); }; struct l2cap_conn { @@ -857,6 +861,31 @@ static inline long l2cap_chan_no_get_sndtimeo(struct l2cap_chan *chan) return 0; } +static inline int l2cap_chan_no_memcpy_fromiovec(struct l2cap_chan *chan, + unsigned char *kdata, + struct iovec *iov, + int len) +{ + /* Following is safe since for compiler definitions of kvec and + * iovec are identical, yielding the same in-core layout and alignment + */ + struct kvec *vec = (struct kvec *)iov; + + while (len > 0) { + if (vec->iov_len) { + int copy = min_t(unsigned int, len, vec->iov_len); + memcpy(kdata, vec->iov_base, copy); + len -= copy; + kdata += copy; + vec->iov_base += copy; + vec->iov_len -= copy; + } + vec++; + } + + return 0; +} + extern bool disable_ertm; int l2cap_init_sockets(void); diff --git a/net/bluetooth/a2mp.c b/net/bluetooth/a2mp.c index 0fd8d1dda709..5dcade511fdb 100644 --- a/net/bluetooth/a2mp.c +++ b/net/bluetooth/a2mp.c @@ -720,6 +720,7 @@ static const struct l2cap_ops a2mp_chan_ops = { .resume = l2cap_chan_no_resume, .set_shutdown = l2cap_chan_no_set_shutdown, .get_sndtimeo = l2cap_chan_no_get_sndtimeo, + .memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec, }; static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index ac2461442f21..d0a5fde61a07 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -2118,7 +2118,8 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan, struct sk_buff **frag; int sent = 0; - if (memcpy_fromiovec(skb_put(skb, count), msg->msg_iov, count)) + if (chan->ops->memcpy_fromiovec(chan, skb_put(skb, count), + msg->msg_iov, count)) return -EFAULT; sent += count; @@ -2138,7 +2139,8 @@ static inline int l2cap_skbuff_fromiovec(struct l2cap_chan *chan, *frag = tmp; - if (memcpy_fromiovec(skb_put(*frag, count), msg->msg_iov, count)) + if (chan->ops->memcpy_fromiovec(chan, skb_put(*frag, count), + msg->msg_iov, count)) return -EFAULT; sent += count; diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index 55215ebf6547..bf72886de6ef 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -1313,6 +1313,13 @@ static struct sk_buff *l2cap_sock_alloc_skb_cb(struct l2cap_chan *chan, return skb; } +static int l2cap_sock_memcpy_fromiovec_cb(struct l2cap_chan *chan, + unsigned char *kdata, + struct iovec *iov, int len) +{ + return memcpy_fromiovec(kdata, iov, len); +} + static void l2cap_sock_ready_cb(struct l2cap_chan *chan) { struct sock *sk = chan->data; @@ -1379,19 +1386,20 @@ static void l2cap_sock_suspend_cb(struct l2cap_chan *chan) } static const struct l2cap_ops l2cap_chan_ops = { - .name = "L2CAP Socket Interface", - .new_connection = l2cap_sock_new_connection_cb, - .recv = l2cap_sock_recv_cb, - .close = l2cap_sock_close_cb, - .teardown = l2cap_sock_teardown_cb, - .state_change = l2cap_sock_state_change_cb, - .ready = l2cap_sock_ready_cb, - .defer = l2cap_sock_defer_cb, - .resume = l2cap_sock_resume_cb, - .suspend = l2cap_sock_suspend_cb, - .set_shutdown = l2cap_sock_set_shutdown_cb, - .get_sndtimeo = l2cap_sock_get_sndtimeo_cb, - .alloc_skb = l2cap_sock_alloc_skb_cb, + .name = "L2CAP Socket Interface", + .new_connection = l2cap_sock_new_connection_cb, + .recv = l2cap_sock_recv_cb, + .close = l2cap_sock_close_cb, + .teardown = l2cap_sock_teardown_cb, + .state_change = l2cap_sock_state_change_cb, + .ready = l2cap_sock_ready_cb, + .defer = l2cap_sock_defer_cb, + .resume = l2cap_sock_resume_cb, + .suspend = l2cap_sock_suspend_cb, + .set_shutdown = l2cap_sock_set_shutdown_cb, + .get_sndtimeo = l2cap_sock_get_sndtimeo_cb, + .alloc_skb = l2cap_sock_alloc_skb_cb, + .memcpy_fromiovec = l2cap_sock_memcpy_fromiovec_cb, }; static void l2cap_sock_destruct(struct sock *sk) From 6b8d4a6a03144c5996f98db7f8256267b0d72a3a Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 18 Jun 2014 16:37:08 +0300 Subject: [PATCH 053/378] Bluetooth: 6LoWPAN: Use connected oriented channel instead of fixed one Create a CoC dynamically instead of one fixed channel for communication to peer devices. Signed-off-by: Jukka Rissanen Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 1 - include/net/bluetooth/hci_core.h | 1 - include/net/bluetooth/l2cap.h | 1 - net/bluetooth/6lowpan.c | 787 +++++++++++++++++++++++-------- net/bluetooth/6lowpan.h | 47 -- net/bluetooth/hci_core.c | 45 -- net/bluetooth/hci_event.c | 3 - net/bluetooth/l2cap_core.c | 20 +- 8 files changed, 588 insertions(+), 317 deletions(-) delete mode 100644 net/bluetooth/6lowpan.h diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 3e12e27afca1..3f3a3f1399fb 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -145,7 +145,6 @@ enum { HCI_PERIODIC_INQ, HCI_FAST_CONNECTABLE, HCI_BREDR_ENABLED, - HCI_6LOWPAN_ENABLED, HCI_LE_SCAN_INTERRUPTED, }; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index bf41e992a618..c81de0d366df 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -521,7 +521,6 @@ enum { HCI_CONN_AES_CCM, HCI_CONN_POWER_SAVE, HCI_CONN_REMOTE_OOB, - HCI_CONN_6LOWPAN, }; static inline bool hci_conn_ssp_enabled(struct hci_conn *conn) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index 1ee6f00d7096..e0c6a9abdb62 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -137,7 +137,6 @@ struct l2cap_conninfo { #define L2CAP_FC_L2CAP 0x02 #define L2CAP_FC_CONNLESS 0x04 #define L2CAP_FC_A2MP 0x08 -#define L2CAP_FC_6LOWPAN 0x3e /* reserved and temporary value */ /* L2CAP Control Field bit masks */ #define L2CAP_CTRL_SAR 0xC000 diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 8796ffa08b43..bdb01eb3bfcc 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2013 Intel Corp. + Copyright (c) 2013-2014 Intel Corp. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 and @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -25,16 +26,20 @@ #include #include -#include "6lowpan.h" - #include /* for the compression support */ +#define VERSION "0.1" + +static struct dentry *lowpan_psm_debugfs; +static struct dentry *lowpan_control_debugfs; + #define IFACE_NAME_TEMPLATE "bt%d" #define EUI64_ADDR_LEN 8 struct skb_cb { struct in6_addr addr; - struct l2cap_conn *conn; + struct l2cap_chan *chan; + int status; }; #define lowpan_cb(skb) ((struct skb_cb *)((skb)->cb)) @@ -48,9 +53,19 @@ struct skb_cb { static LIST_HEAD(bt_6lowpan_devices); static DEFINE_RWLOCK(devices_lock); +/* If psm is set to 0 (default value), then 6lowpan is disabled. + * Other values are used to indicate a Protocol Service Multiplexer + * value for 6lowpan. + */ +static u16 psm_6lowpan; + +/* We are listening incoming connections via this channel + */ +static struct l2cap_chan *listen_chan; + struct lowpan_peer { struct list_head list; - struct l2cap_conn *conn; + struct l2cap_chan *chan; /* peer addresses in various formats */ unsigned char eui64_addr[EUI64_ADDR_LEN]; @@ -101,13 +116,26 @@ static inline struct lowpan_peer *peer_lookup_ba(struct lowpan_dev *dev, ba, type); list_for_each_entry_safe(peer, tmp, &dev->peers, list) { - BT_DBG("addr %pMR type %d", - &peer->conn->hcon->dst, peer->conn->hcon->dst_type); + BT_DBG("dst addr %pMR dst type %d", + &peer->chan->dst, peer->chan->dst_type); - if (bacmp(&peer->conn->hcon->dst, ba)) + if (bacmp(&peer->chan->dst, ba)) continue; - if (type == peer->conn->hcon->dst_type) + if (type == peer->chan->dst_type) + return peer; + } + + return NULL; +} + +static inline struct lowpan_peer *peer_lookup_chan(struct lowpan_dev *dev, + struct l2cap_chan *chan) +{ + struct lowpan_peer *peer, *tmp; + + list_for_each_entry_safe(peer, tmp, &dev->peers, list) { + if (peer->chan == chan) return peer; } @@ -120,7 +148,7 @@ static inline struct lowpan_peer *peer_lookup_conn(struct lowpan_dev *dev, struct lowpan_peer *peer, *tmp; list_for_each_entry_safe(peer, tmp, &dev->peers, list) { - if (peer->conn == conn) + if (peer->chan->conn == conn) return peer; } @@ -176,16 +204,16 @@ static int give_skb_to_upper(struct sk_buff *skb, struct net_device *dev) return -ENOMEM; ret = netif_rx(skb_cp); - - BT_DBG("receive skb %d", ret); - if (ret < 0) + if (ret < 0) { + BT_DBG("receive skb %d", ret); return NET_RX_DROP; + } return ret; } static int process_data(struct sk_buff *skb, struct net_device *netdev, - struct l2cap_conn *conn) + struct l2cap_chan *chan) { const u8 *saddr, *daddr; u8 iphc0, iphc1; @@ -196,7 +224,7 @@ static int process_data(struct sk_buff *skb, struct net_device *netdev, dev = lowpan_dev(netdev); read_lock_irqsave(&devices_lock, flags); - peer = peer_lookup_conn(dev, conn); + peer = peer_lookup_chan(dev, chan); read_unlock_irqrestore(&devices_lock, flags); if (!peer) goto drop; @@ -225,7 +253,7 @@ drop: } static int recv_pkt(struct sk_buff *skb, struct net_device *dev, - struct l2cap_conn *conn) + struct l2cap_chan *chan) { struct sk_buff *local_skb; int ret; @@ -269,7 +297,7 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev, if (!local_skb) goto drop; - ret = process_data(local_skb, dev, conn); + ret = process_data(local_skb, dev, chan); if (ret != NET_RX_SUCCESS) goto drop; @@ -286,147 +314,39 @@ static int recv_pkt(struct sk_buff *skb, struct net_device *dev, return NET_RX_SUCCESS; drop: + dev->stats.rx_dropped++; kfree_skb(skb); return NET_RX_DROP; } /* Packet from BT LE device */ -int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb) +static int chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) { struct lowpan_dev *dev; struct lowpan_peer *peer; int err; - peer = lookup_peer(conn); + peer = lookup_peer(chan->conn); if (!peer) return -ENOENT; - dev = lookup_dev(conn); + dev = lookup_dev(chan->conn); if (!dev || !dev->netdev) return -ENOENT; - err = recv_pkt(skb, dev->netdev, conn); - BT_DBG("recv pkt %d", err); + err = recv_pkt(skb, dev->netdev, chan); + if (err) { + BT_DBG("recv pkt %d", err); + err = -EAGAIN; + } return err; } -static inline int skbuff_copy(void *msg, int len, int count, int mtu, - struct sk_buff *skb, struct net_device *dev) -{ - struct sk_buff **frag; - int sent = 0; - - memcpy(skb_put(skb, count), msg, count); - - sent += count; - msg += count; - len -= count; - - dev->stats.tx_bytes += count; - dev->stats.tx_packets++; - - raw_dump_table(__func__, "Sending", skb->data, skb->len); - - /* Continuation fragments (no L2CAP header) */ - frag = &skb_shinfo(skb)->frag_list; - while (len > 0) { - struct sk_buff *tmp; - - count = min_t(unsigned int, mtu, len); - - tmp = bt_skb_alloc(count, GFP_ATOMIC); - if (!tmp) - return -ENOMEM; - - *frag = tmp; - - memcpy(skb_put(*frag, count), msg, count); - - raw_dump_table(__func__, "Sending fragment", - (*frag)->data, count); - - (*frag)->priority = skb->priority; - - sent += count; - msg += count; - len -= count; - - skb->len += (*frag)->len; - skb->data_len += (*frag)->len; - - frag = &(*frag)->next; - - dev->stats.tx_bytes += count; - dev->stats.tx_packets++; - } - - return sent; -} - -static struct sk_buff *create_pdu(struct l2cap_conn *conn, void *msg, - size_t len, u32 priority, - struct net_device *dev) -{ - struct sk_buff *skb; - int err, count; - struct l2cap_hdr *lh; - - /* FIXME: This mtu check should be not needed and atm is only used for - * testing purposes - */ - if (conn->mtu > (L2CAP_LE_MIN_MTU + L2CAP_HDR_SIZE)) - conn->mtu = L2CAP_LE_MIN_MTU + L2CAP_HDR_SIZE; - - count = min_t(unsigned int, (conn->mtu - L2CAP_HDR_SIZE), len); - - BT_DBG("conn %p len %zu mtu %d count %d", conn, len, conn->mtu, count); - - skb = bt_skb_alloc(count + L2CAP_HDR_SIZE, GFP_ATOMIC); - if (!skb) - return ERR_PTR(-ENOMEM); - - skb->priority = priority; - - lh = (struct l2cap_hdr *)skb_put(skb, L2CAP_HDR_SIZE); - lh->cid = cpu_to_le16(L2CAP_FC_6LOWPAN); - lh->len = cpu_to_le16(len); - - err = skbuff_copy(msg, len, count, conn->mtu, skb, dev); - if (unlikely(err < 0)) { - kfree_skb(skb); - BT_DBG("skbuff copy %d failed", err); - return ERR_PTR(err); - } - - return skb; -} - -static int conn_send(struct l2cap_conn *conn, - void *msg, size_t len, u32 priority, - struct net_device *dev) -{ - struct sk_buff *skb; - - skb = create_pdu(conn, msg, len, priority, dev); - if (IS_ERR(skb)) - return -EINVAL; - - BT_DBG("conn %p skb %p len %d priority %u", conn, skb, skb->len, - skb->priority); - - hci_send_acl(conn->hchan, skb, ACL_START); - - return 0; -} - static u8 get_addr_type_from_eui64(u8 byte) { - /* Is universal(0) or local(1) bit, */ - if (byte & 0x02) - return ADDR_LE_DEV_RANDOM; - - return ADDR_LE_DEV_PUBLIC; + /* Is universal(0) or local(1) bit */ + return ((byte & 0x02) ? BDADDR_LE_RANDOM : BDADDR_LE_PUBLIC); } static void copy_to_bdaddr(struct in6_addr *ip6_daddr, bdaddr_t *addr) @@ -475,7 +395,7 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev, if (ipv6_addr_is_multicast(&hdr->daddr)) { memcpy(&lowpan_cb(skb)->addr, &hdr->daddr, sizeof(struct in6_addr)); - lowpan_cb(skb)->conn = NULL; + lowpan_cb(skb)->chan = NULL; } else { unsigned long flags; @@ -484,9 +404,8 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev, */ convert_dest_bdaddr(&hdr->daddr, &addr, &addr_type); - BT_DBG("dest addr %pMR type %s IP %pI6c", &addr, - addr_type == ADDR_LE_DEV_PUBLIC ? "PUBLIC" : "RANDOM", - &hdr->daddr); + BT_DBG("dest addr %pMR type %d IP %pI6c", &addr, + addr_type, &hdr->daddr); read_lock_irqsave(&devices_lock, flags); peer = peer_lookup_ba(dev, &addr, addr_type); @@ -501,7 +420,7 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev, memcpy(&lowpan_cb(skb)->addr, &hdr->daddr, sizeof(struct in6_addr)); - lowpan_cb(skb)->conn = peer->conn; + lowpan_cb(skb)->chan = peer->chan; } saddr = dev->netdev->dev_addr; @@ -510,14 +429,42 @@ static int header_create(struct sk_buff *skb, struct net_device *netdev, } /* Packet to BT LE device */ -static int send_pkt(struct l2cap_conn *conn, const void *saddr, - const void *daddr, struct sk_buff *skb, +static int send_pkt(struct l2cap_chan *chan, struct sk_buff *skb, struct net_device *netdev) { - raw_dump_table(__func__, "raw skb data dump before fragmentation", - skb->data, skb->len); + struct msghdr msg; + struct kvec iv; + int err; - return conn_send(conn, skb->data, skb->len, 0, netdev); + /* Remember the skb so that we can send EAGAIN to the caller if + * we run out of credits. + */ + chan->data = skb; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = (struct iovec *) &iv; + msg.msg_iovlen = 1; + iv.iov_base = skb->data; + iv.iov_len = skb->len; + + err = l2cap_chan_send(chan, &msg, skb->len); + if (err > 0) { + netdev->stats.tx_bytes += err; + netdev->stats.tx_packets++; + return 0; + } + + if (!err) + err = lowpan_cb(skb)->status; + + if (err < 0) { + if (err == -EAGAIN) + netdev->stats.tx_dropped++; + else + netdev->stats.tx_errors++; + } + + return err; } static void send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev) @@ -540,8 +487,7 @@ static void send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev) list_for_each_entry_safe(pentry, ptmp, &dev->peers, list) { local_skb = skb_clone(skb, GFP_ATOMIC); - send_pkt(pentry->conn, netdev->dev_addr, - pentry->eui64_addr, local_skb, netdev); + send_pkt(pentry->chan, local_skb, netdev); kfree_skb(local_skb); } @@ -553,7 +499,6 @@ static void send_mcast_pkt(struct sk_buff *skb, struct net_device *netdev) static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev) { int err = 0; - unsigned char *eui64_addr; struct lowpan_dev *dev; struct lowpan_peer *peer; bdaddr_t addr; @@ -568,21 +513,20 @@ static netdev_tx_t bt_xmit(struct sk_buff *skb, struct net_device *netdev) unsigned long flags; convert_dest_bdaddr(&lowpan_cb(skb)->addr, &addr, &addr_type); - eui64_addr = lowpan_cb(skb)->addr.s6_addr + 8; dev = lowpan_dev(netdev); read_lock_irqsave(&devices_lock, flags); peer = peer_lookup_ba(dev, &addr, addr_type); read_unlock_irqrestore(&devices_lock, flags); - BT_DBG("xmit %s to %pMR type %s IP %pI6c peer %p", - netdev->name, &addr, - addr_type == ADDR_LE_DEV_PUBLIC ? "PUBLIC" : "RANDOM", + BT_DBG("xmit %s to %pMR type %d IP %pI6c peer %p", + netdev->name, &addr, addr_type, &lowpan_cb(skb)->addr, peer); - if (peer && peer->conn) - err = send_pkt(peer->conn, netdev->dev_addr, - eui64_addr, skb, netdev); + if (peer && peer->chan) + err = send_pkt(peer->chan, skb, netdev); + else + err = -ENOENT; } dev_kfree_skb(skb); @@ -634,7 +578,7 @@ static void set_addr(u8 *eui, u8 *addr, u8 addr_type) eui[7] = addr[0]; /* Universal/local bit set, BT 6lowpan draft ch. 3.2.1 */ - if (addr_type == ADDR_LE_DEV_PUBLIC) + if (addr_type == BDADDR_LE_PUBLIC) eui[0] &= ~0x02; else eui[0] |= 0x02; @@ -673,26 +617,64 @@ static bool is_bt_6lowpan(struct hci_conn *hcon) if (hcon->type != LE_LINK) return false; - return test_bit(HCI_CONN_6LOWPAN, &hcon->flags); + if (!psm_6lowpan) + return false; + + return true; } -static int add_peer_conn(struct l2cap_conn *conn, struct lowpan_dev *dev) +static struct l2cap_chan *chan_create(void) +{ + struct l2cap_chan *chan; + + chan = l2cap_chan_create(); + if (!chan) + return NULL; + + l2cap_chan_set_defaults(chan); + + chan->chan_type = L2CAP_CHAN_CONN_ORIENTED; + chan->mode = L2CAP_MODE_LE_FLOWCTL; + chan->omtu = 65535; + chan->imtu = chan->omtu; + + return chan; +} + +static struct l2cap_chan *chan_open(struct l2cap_chan *pchan) +{ + struct l2cap_chan *chan; + + chan = chan_create(); + if (!chan) + return NULL; + + chan->remote_mps = chan->omtu; + chan->mps = chan->omtu; + + chan->state = BT_CONNECTED; + + return chan; +} + +static struct l2cap_chan *add_peer_chan(struct l2cap_chan *chan, + struct lowpan_dev *dev) { struct lowpan_peer *peer; unsigned long flags; peer = kzalloc(sizeof(*peer), GFP_ATOMIC); if (!peer) - return -ENOMEM; + return NULL; - peer->conn = conn; + peer->chan = chan; memset(&peer->peer_addr, 0, sizeof(struct in6_addr)); /* RFC 2464 ch. 5 */ peer->peer_addr.s6_addr[0] = 0xFE; peer->peer_addr.s6_addr[1] = 0x80; - set_addr((u8 *)&peer->peer_addr.s6_addr + 8, conn->hcon->dst.b, - conn->hcon->dst_type); + set_addr((u8 *)&peer->peer_addr.s6_addr + 8, chan->dst.b, + chan->dst_type); memcpy(&peer->eui64_addr, (u8 *)&peer->peer_addr.s6_addr + 8, EUI64_ADDR_LEN); @@ -706,40 +688,24 @@ static int add_peer_conn(struct l2cap_conn *conn, struct lowpan_dev *dev) INIT_DELAYED_WORK(&dev->notify_peers, do_notify_peers); schedule_delayed_work(&dev->notify_peers, msecs_to_jiffies(100)); - return 0; + return peer->chan; } -/* This gets called when BT LE 6LoWPAN device is connected. We then - * create network device that acts as a proxy between BT LE device - * and kernel network stack. - */ -int bt_6lowpan_add_conn(struct l2cap_conn *conn) +static int setup_netdev(struct l2cap_chan *chan, struct lowpan_dev **dev) { - struct lowpan_peer *peer = NULL; - struct lowpan_dev *dev; struct net_device *netdev; int err = 0; unsigned long flags; - if (!is_bt_6lowpan(conn->hcon)) - return 0; - - peer = lookup_peer(conn); - if (peer) - return -EEXIST; - - dev = lookup_dev(conn); - if (dev) - return add_peer_conn(conn, dev); - - netdev = alloc_netdev(sizeof(*dev), IFACE_NAME_TEMPLATE, netdev_setup); + netdev = alloc_netdev(sizeof(struct lowpan_dev), IFACE_NAME_TEMPLATE, + netdev_setup); if (!netdev) return -ENOMEM; - set_dev_addr(netdev, &conn->hcon->src, conn->hcon->src_type); + set_dev_addr(netdev, &chan->src, chan->src_type); netdev->netdev_ops = &netdev_ops; - SET_NETDEV_DEV(netdev, &conn->hcon->dev); + SET_NETDEV_DEV(netdev, &chan->conn->hcon->dev); SET_NETDEV_DEVTYPE(netdev, &bt_type); err = register_netdev(netdev); @@ -749,28 +715,58 @@ int bt_6lowpan_add_conn(struct l2cap_conn *conn) goto out; } - BT_DBG("ifindex %d peer bdaddr %pMR my addr %pMR", - netdev->ifindex, &conn->hcon->dst, &conn->hcon->src); + BT_DBG("ifindex %d peer bdaddr %pMR type %d my addr %pMR type %d", + netdev->ifindex, &chan->dst, chan->dst_type, + &chan->src, chan->src_type); set_bit(__LINK_STATE_PRESENT, &netdev->state); - dev = netdev_priv(netdev); - dev->netdev = netdev; - dev->hdev = conn->hcon->hdev; - INIT_LIST_HEAD(&dev->peers); + *dev = netdev_priv(netdev); + (*dev)->netdev = netdev; + (*dev)->hdev = chan->conn->hcon->hdev; + INIT_LIST_HEAD(&(*dev)->peers); write_lock_irqsave(&devices_lock, flags); - INIT_LIST_HEAD(&dev->list); - list_add(&dev->list, &bt_6lowpan_devices); + INIT_LIST_HEAD(&(*dev)->list); + list_add(&(*dev)->list, &bt_6lowpan_devices); write_unlock_irqrestore(&devices_lock, flags); - ifup(netdev); - - return add_peer_conn(conn, dev); + return 0; out: return err; } +static inline void chan_ready_cb(struct l2cap_chan *chan) +{ + struct lowpan_dev *dev; + + dev = lookup_dev(chan->conn); + + BT_DBG("chan %p conn %p dev %p", chan, chan->conn, dev); + + if (!dev) { + if (setup_netdev(chan, &dev) < 0) { + l2cap_chan_del(chan, -ENOENT); + return; + } + } + + add_peer_chan(chan, dev); + ifup(dev->netdev); +} + +static inline struct l2cap_chan *chan_new_conn_cb(struct l2cap_chan *chan) +{ + struct l2cap_chan *pchan; + + pchan = chan_open(chan); + pchan->ops = chan->ops; + + BT_DBG("chan %p pchan %p", chan, pchan); + + return pchan; +} + static void delete_netdev(struct work_struct *work) { struct lowpan_dev *entry = container_of(work, struct lowpan_dev, @@ -781,26 +777,43 @@ static void delete_netdev(struct work_struct *work) /* The entry pointer is deleted in device_event() */ } -int bt_6lowpan_del_conn(struct l2cap_conn *conn) +static void chan_close_cb(struct l2cap_chan *chan) { struct lowpan_dev *entry, *tmp; struct lowpan_dev *dev = NULL; struct lowpan_peer *peer; int err = -ENOENT; unsigned long flags; - bool last = false; + bool last = false, removed = true; - if (!conn || !is_bt_6lowpan(conn->hcon)) - return 0; + BT_DBG("chan %p conn %p", chan, chan->conn); + + if (chan->conn && chan->conn->hcon) { + if (!is_bt_6lowpan(chan->conn->hcon)) + return; + + /* If conn is set, then the netdev is also there and we should + * not remove it. + */ + removed = false; + } write_lock_irqsave(&devices_lock, flags); list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) { dev = lowpan_dev(entry->netdev); - peer = peer_lookup_conn(dev, conn); + peer = peer_lookup_chan(dev, chan); if (peer) { last = peer_del(dev, peer); err = 0; + + BT_DBG("dev %p removing %speer %p", dev, + last ? "last " : "1 ", peer); + BT_DBG("chan %p orig refcnt %d", chan, + atomic_read(&chan->kref.refcount)); + + l2cap_chan_put(chan); + kfree(peer); break; } } @@ -810,18 +823,363 @@ int bt_6lowpan_del_conn(struct l2cap_conn *conn) cancel_delayed_work_sync(&dev->notify_peers); - /* bt_6lowpan_del_conn() is called with hci dev lock held which - * means that we must delete the netdevice in worker thread. - */ - INIT_WORK(&entry->delete_netdev, delete_netdev); - schedule_work(&entry->delete_netdev); + if (!removed) { + INIT_WORK(&entry->delete_netdev, delete_netdev); + schedule_work(&entry->delete_netdev); + } } else { write_unlock_irqrestore(&devices_lock, flags); } + return; +} + +static void chan_state_change_cb(struct l2cap_chan *chan, int state, int err) +{ + BT_DBG("chan %p conn %p state %s err %d", chan, chan->conn, + state_to_string(state), err); +} + +static struct sk_buff *chan_alloc_skb_cb(struct l2cap_chan *chan, + unsigned long hdr_len, + unsigned long len, int nb) +{ + /* Note that we must allocate using GFP_ATOMIC here as + * this function is called originally from netdev hard xmit + * function in atomic context. + */ + return bt_skb_alloc(hdr_len + len, GFP_ATOMIC); +} + +static void chan_suspend_cb(struct l2cap_chan *chan) +{ + struct sk_buff *skb = chan->data; + + BT_DBG("chan %p conn %p skb %p", chan, chan->conn, skb); + + lowpan_cb(skb)->status = -EAGAIN; +} + +static void chan_resume_cb(struct l2cap_chan *chan) +{ + struct sk_buff *skb = chan->data; + + BT_DBG("chan %p conn %p skb %p", chan, chan->conn, skb); + + lowpan_cb(skb)->status = 0; +} + +static long chan_get_sndtimeo_cb(struct l2cap_chan *chan) +{ + return msecs_to_jiffies(1000); +} + +static const struct l2cap_ops bt_6lowpan_chan_ops = { + .name = "L2CAP 6LoWPAN channel", + .new_connection = chan_new_conn_cb, + .recv = chan_recv_cb, + .close = chan_close_cb, + .state_change = chan_state_change_cb, + .ready = chan_ready_cb, + .resume = chan_resume_cb, + .suspend = chan_suspend_cb, + .get_sndtimeo = chan_get_sndtimeo_cb, + .alloc_skb = chan_alloc_skb_cb, + .memcpy_fromiovec = l2cap_chan_no_memcpy_fromiovec, + + .teardown = l2cap_chan_no_teardown, + .defer = l2cap_chan_no_defer, + .set_shutdown = l2cap_chan_no_set_shutdown, +}; + +static inline __u8 bdaddr_type(__u8 type) +{ + if (type == ADDR_LE_DEV_PUBLIC) + return BDADDR_LE_PUBLIC; + else + return BDADDR_LE_RANDOM; +} + +static struct l2cap_chan *chan_get(void) +{ + struct l2cap_chan *pchan; + + pchan = chan_create(); + if (!pchan) + return NULL; + + pchan->ops = &bt_6lowpan_chan_ops; + + return pchan; +} + +static int bt_6lowpan_connect(bdaddr_t *addr, u8 dst_type) +{ + struct l2cap_chan *pchan; + int err; + + pchan = chan_get(); + if (!pchan) + return -EINVAL; + + err = l2cap_chan_connect(pchan, cpu_to_le16(psm_6lowpan), 0, + addr, dst_type); + + BT_DBG("chan %p err %d", pchan, err); + if (err < 0) + l2cap_chan_put(pchan); + return err; } +static int bt_6lowpan_disconnect(struct l2cap_conn *conn, u8 dst_type) +{ + struct lowpan_peer *peer; + + BT_DBG("conn %p dst type %d", conn, dst_type); + + peer = lookup_peer(conn); + if (!peer) + return -ENOENT; + + BT_DBG("peer %p chan %p", peer, peer->chan); + + l2cap_chan_close(peer->chan, ENOENT); + + return 0; +} + +static struct l2cap_chan *bt_6lowpan_listen(void) +{ + bdaddr_t *addr = BDADDR_ANY; + struct l2cap_chan *pchan; + int err; + + if (psm_6lowpan == 0) + return NULL; + + pchan = chan_get(); + if (!pchan) + return NULL; + + pchan->state = BT_LISTEN; + pchan->src_type = BDADDR_LE_PUBLIC; + + BT_DBG("psm 0x%04x chan %p src type %d", psm_6lowpan, pchan, + pchan->src_type); + + err = l2cap_add_psm(pchan, addr, cpu_to_le16(psm_6lowpan)); + if (err) { + l2cap_chan_put(pchan); + BT_ERR("psm cannot be added err %d", err); + return NULL; + } + + return pchan; +} + +static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type, + struct l2cap_conn **conn) +{ + struct hci_conn *hcon; + struct hci_dev *hdev; + bdaddr_t *src = BDADDR_ANY; + int n; + + n = sscanf(buf, "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhu", + &addr->b[5], &addr->b[4], &addr->b[3], + &addr->b[2], &addr->b[1], &addr->b[0], + addr_type); + + if (n < 7) + return -EINVAL; + + hdev = hci_get_route(addr, src); + if (!hdev) + return -ENOENT; + + hci_dev_lock(hdev); + hcon = hci_conn_hash_lookup_ba(hdev, LE_LINK, addr); + hci_dev_unlock(hdev); + + if (!hcon) + return -ENOENT; + + *conn = (struct l2cap_conn *)hcon->l2cap_data; + + BT_DBG("conn %p dst %pMR type %d", *conn, &hcon->dst, hcon->dst_type); + + return 0; +} + +static void disconnect_all_peers(void) +{ + struct lowpan_dev *entry, *tmp_dev; + struct lowpan_peer *peer, *tmp_peer, *new_peer; + struct list_head peers; + unsigned long flags; + + INIT_LIST_HEAD(&peers); + + /* We make a separate list of peers as the close_cb() will + * modify the device peers list so it is better not to mess + * with the same list at the same time. + */ + + read_lock_irqsave(&devices_lock, flags); + + list_for_each_entry_safe(entry, tmp_dev, &bt_6lowpan_devices, list) { + list_for_each_entry_safe(peer, tmp_peer, &entry->peers, list) { + new_peer = kmalloc(sizeof(*new_peer), GFP_ATOMIC); + if (!new_peer) + break; + + new_peer->chan = peer->chan; + INIT_LIST_HEAD(&new_peer->list); + + list_add(&new_peer->list, &peers); + } + } + + read_unlock_irqrestore(&devices_lock, flags); + + list_for_each_entry_safe(peer, tmp_peer, &peers, list) { + l2cap_chan_close(peer->chan, ENOENT); + kfree(peer); + } +} + +static int lowpan_psm_set(void *data, u64 val) +{ + u16 psm; + + psm = val; + if (psm == 0 || psm_6lowpan != psm) + /* Disconnect existing connections if 6lowpan is + * disabled (psm = 0), or if psm changes. + */ + disconnect_all_peers(); + + psm_6lowpan = psm; + + if (listen_chan) { + l2cap_chan_close(listen_chan, 0); + l2cap_chan_put(listen_chan); + } + + listen_chan = bt_6lowpan_listen(); + + return 0; +} + +static int lowpan_psm_get(void *data, u64 *val) +{ + *val = psm_6lowpan; + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(lowpan_psm_fops, lowpan_psm_get, + lowpan_psm_set, "%llu\n"); + +static ssize_t lowpan_control_write(struct file *fp, + const char __user *user_buffer, + size_t count, + loff_t *position) +{ + char buf[32]; + size_t buf_size = min(count, sizeof(buf) - 1); + int ret; + bdaddr_t addr; + u8 addr_type; + struct l2cap_conn *conn = NULL; + + if (copy_from_user(buf, user_buffer, buf_size)) + return -EFAULT; + + buf[buf_size] = '\0'; + + if (memcmp(buf, "connect ", 8) == 0) { + ret = get_l2cap_conn(&buf[8], &addr, &addr_type, &conn); + if (ret == -EINVAL) + return ret; + + if (listen_chan) { + l2cap_chan_close(listen_chan, 0); + l2cap_chan_put(listen_chan); + listen_chan = NULL; + } + + if (conn) { + struct lowpan_peer *peer; + + if (!is_bt_6lowpan(conn->hcon)) + return -EINVAL; + + peer = lookup_peer(conn); + if (peer) { + BT_DBG("6LoWPAN connection already exists"); + return -EALREADY; + } + + BT_DBG("conn %p dst %pMR type %d user %d", conn, + &conn->hcon->dst, conn->hcon->dst_type, + addr_type); + } + + ret = bt_6lowpan_connect(&addr, addr_type); + if (ret < 0) + return ret; + + return count; + } + + if (memcmp(buf, "disconnect ", 11) == 0) { + ret = get_l2cap_conn(&buf[11], &addr, &addr_type, &conn); + if (ret < 0) + return ret; + + ret = bt_6lowpan_disconnect(conn, addr_type); + if (ret < 0) + return ret; + + return count; + } + + return count; +} + +static int lowpan_control_show(struct seq_file *f, void *ptr) +{ + struct lowpan_dev *entry, *tmp_dev; + struct lowpan_peer *peer, *tmp_peer; + unsigned long flags; + + read_lock_irqsave(&devices_lock, flags); + + list_for_each_entry_safe(entry, tmp_dev, &bt_6lowpan_devices, list) { + list_for_each_entry_safe(peer, tmp_peer, &entry->peers, list) + seq_printf(f, "%pMR (type %u)\n", + &peer->chan->dst, peer->chan->dst_type); + } + + read_unlock_irqrestore(&devices_lock, flags); + + return 0; +} + +static int lowpan_control_open(struct inode *inode, struct file *file) +{ + return single_open(file, lowpan_control_show, inode->i_private); +} + +static const struct file_operations lowpan_control_fops = { + .open = lowpan_control_open, + .read = seq_read, + .write = lowpan_control_write, + .llseek = seq_lseek, + .release = single_release, +}; + static int device_event(struct notifier_block *unused, unsigned long event, void *ptr) { @@ -856,10 +1214,25 @@ static struct notifier_block bt_6lowpan_dev_notifier = { int bt_6lowpan_init(void) { + lowpan_psm_debugfs = debugfs_create_file("6lowpan_psm", 0644, + bt_debugfs, NULL, + &lowpan_psm_fops); + lowpan_control_debugfs = debugfs_create_file("6lowpan_control", 0644, + bt_debugfs, NULL, + &lowpan_control_fops); + return register_netdevice_notifier(&bt_6lowpan_dev_notifier); } -void bt_6lowpan_cleanup(void) +void bt_6lowpan_exit(void) { + debugfs_remove(lowpan_psm_debugfs); + debugfs_remove(lowpan_control_debugfs); + + if (listen_chan) { + l2cap_chan_close(listen_chan, 0); + l2cap_chan_put(listen_chan); + } + unregister_netdevice_notifier(&bt_6lowpan_dev_notifier); } diff --git a/net/bluetooth/6lowpan.h b/net/bluetooth/6lowpan.h deleted file mode 100644 index 5d281f1eaf55..000000000000 --- a/net/bluetooth/6lowpan.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - Copyright (c) 2013 Intel Corp. - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License version 2 and - only version 2 as published by the Free Software Foundation. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. -*/ - -#ifndef __6LOWPAN_H -#define __6LOWPAN_H - -#include -#include -#include - -#if IS_ENABLED(CONFIG_BT_6LOWPAN) -int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb); -int bt_6lowpan_add_conn(struct l2cap_conn *conn); -int bt_6lowpan_del_conn(struct l2cap_conn *conn); -int bt_6lowpan_init(void); -void bt_6lowpan_cleanup(void); -#else -static int bt_6lowpan_recv(struct l2cap_conn *conn, struct sk_buff *skb) -{ - return -EOPNOTSUPP; -} -static int bt_6lowpan_add_conn(struct l2cap_conn *conn) -{ - return -EOPNOTSUPP; -} -int bt_6lowpan_del_conn(struct l2cap_conn *conn) -{ - return -EOPNOTSUPP; -} -static int bt_6lowpan_init(void) -{ - return -EOPNOTSUPP; -} -static void bt_6lowpan_cleanup(void) { } -#endif - -#endif /* __6LOWPAN_H */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index ddbb084250be..bbfc5455acec 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -928,49 +928,6 @@ static int adv_channel_map_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(adv_channel_map_fops, adv_channel_map_get, adv_channel_map_set, "%llu\n"); -static ssize_t lowpan_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct hci_dev *hdev = file->private_data; - char buf[3]; - - buf[0] = test_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags) ? 'Y' : 'N'; - buf[1] = '\n'; - buf[2] = '\0'; - return simple_read_from_buffer(user_buf, count, ppos, buf, 2); -} - -static ssize_t lowpan_write(struct file *fp, const char __user *user_buffer, - size_t count, loff_t *position) -{ - struct hci_dev *hdev = fp->private_data; - bool enable; - char buf[32]; - size_t buf_size = min(count, (sizeof(buf)-1)); - - if (copy_from_user(buf, user_buffer, buf_size)) - return -EFAULT; - - buf[buf_size] = '\0'; - - if (strtobool(buf, &enable) < 0) - return -EINVAL; - - if (enable == test_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags)) - return -EALREADY; - - change_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags); - - return count; -} - -static const struct file_operations lowpan_debugfs_fops = { - .open = simple_open, - .read = lowpan_read, - .write = lowpan_write, - .llseek = default_llseek, -}; - static int le_auto_conn_show(struct seq_file *sf, void *ptr) { struct hci_dev *hdev = sf->private; @@ -1881,8 +1838,6 @@ static int __hci_init(struct hci_dev *hdev) hdev, &conn_max_interval_fops); debugfs_create_file("adv_channel_map", 0644, hdev->debugfs, hdev, &adv_channel_map_fops); - debugfs_create_file("6lowpan", 0644, hdev->debugfs, hdev, - &lowpan_debugfs_fops); debugfs_create_file("le_auto_conn", 0644, hdev->debugfs, hdev, &le_auto_conn_fops); debugfs_create_u16("discov_interleaved_timeout", 0644, diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 15a0a65bf447..e5b430113cfb 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4056,9 +4056,6 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) conn->handle = __le16_to_cpu(ev->handle); conn->state = BT_CONNECTED; - if (test_bit(HCI_6LOWPAN_ENABLED, &hdev->dev_flags)) - set_bit(HCI_CONN_6LOWPAN, &conn->flags); - hci_conn_add_sysfs(conn); hci_proto_connect_cfm(conn, ev->status); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index d0a5fde61a07..cbf6b9d1c404 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -40,7 +40,6 @@ #include "smp.h" #include "a2mp.h" #include "amp.h" -#include "6lowpan.h" #define LE_FLOWCTL_MAX_CREDITS 65535 @@ -205,6 +204,7 @@ done: write_unlock(&chan_list_lock); return err; } +EXPORT_SYMBOL_GPL(l2cap_add_psm); int l2cap_add_scid(struct l2cap_chan *chan, __u16 scid) { @@ -437,6 +437,7 @@ struct l2cap_chan *l2cap_chan_create(void) return chan; } +EXPORT_SYMBOL_GPL(l2cap_chan_create); static void l2cap_chan_destroy(struct kref *kref) { @@ -464,6 +465,7 @@ void l2cap_chan_put(struct l2cap_chan *c) kref_put(&c->kref, l2cap_chan_destroy); } +EXPORT_SYMBOL_GPL(l2cap_chan_put); void l2cap_chan_set_defaults(struct l2cap_chan *chan) { @@ -482,6 +484,7 @@ void l2cap_chan_set_defaults(struct l2cap_chan *chan) set_bit(FLAG_FORCE_ACTIVE, &chan->flags); } +EXPORT_SYMBOL_GPL(l2cap_chan_set_defaults); static void l2cap_le_flowctl_init(struct l2cap_chan *chan) { @@ -614,6 +617,7 @@ void l2cap_chan_del(struct l2cap_chan *chan, int err) return; } +EXPORT_SYMBOL_GPL(l2cap_chan_del); void l2cap_conn_update_id_addr(struct hci_conn *hcon) { @@ -717,6 +721,7 @@ void l2cap_chan_close(struct l2cap_chan *chan, int reason) break; } } +EXPORT_SYMBOL(l2cap_chan_close); static inline u8 l2cap_get_auth_type(struct l2cap_chan *chan) { @@ -1460,8 +1465,6 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) BT_DBG(""); - bt_6lowpan_add_conn(conn); - /* Check if we have socket listening on cid */ pchan = l2cap_global_chan_by_scid(BT_LISTEN, L2CAP_CID_ATT, &hcon->src, &hcon->dst); @@ -2555,6 +2558,7 @@ int l2cap_chan_send(struct l2cap_chan *chan, struct msghdr *msg, size_t len) return err; } +EXPORT_SYMBOL_GPL(l2cap_chan_send); static void l2cap_send_srej(struct l2cap_chan *chan, u16 txseq) { @@ -6933,10 +6937,6 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) l2cap_conn_del(conn->hcon, EACCES); break; - case L2CAP_FC_6LOWPAN: - bt_6lowpan_recv(conn, skb); - break; - default: l2cap_data_channel(conn, cid, skb); break; @@ -7183,6 +7183,7 @@ done: hci_dev_put(hdev); return err; } +EXPORT_SYMBOL_GPL(l2cap_chan_connect); /* ---- L2CAP interface with lower layer (HCI) ---- */ @@ -7245,8 +7246,6 @@ void l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason) { BT_DBG("hcon %p reason %d", hcon, reason); - bt_6lowpan_del_conn(hcon->l2cap_data); - l2cap_conn_del(hcon, bt_to_errno(reason)); } @@ -7529,14 +7528,11 @@ int __init l2cap_init(void) debugfs_create_u16("l2cap_le_default_mps", 0644, bt_debugfs, &le_default_mps); - bt_6lowpan_init(); - return 0; } void l2cap_exit(void) { - bt_6lowpan_cleanup(); debugfs_remove(l2cap_debugfs); l2cap_cleanup_sockets(); } From 5547e48c09d51ad21948c8090a34339939651450 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 18 Jun 2014 16:37:09 +0300 Subject: [PATCH 054/378] Bluetooth: 6LoWPAN: Create a kernel module Instead of adding the 6LoWPAN functionality to Bluetooth module, we create a separate kernel module for it. Usage: In the slave side do this: $ modprobe bluetooth_6lowpan $ echo 62 > /sys/kernel/debug/bluetooth/6lowpan_psm $ hciconfig hci0 leadv In the master side do this: $ modprobe bluetooth_6lowpan $ echo 62 > /sys/kernel/debug/bluetooth/6lowpan_psm $ echo 'connect E0:06:E6:B7:2A:73 1' > \ /sys/kernel/debug/bluetooth/6lowpan_control The 6LoWPAN functionality can be controlled by psm value. If it is left to 0, then the module is disabled and all the 6LoWPAN connections are dropped if there were any. In the above example, the psm value is just an example and not a real value for 6LoWPAN service. The real psm value is yet to be defined in Bluetooth specification. The 6lowpan controlling interface is a temporary solution until the specifications are ready. Signed-off-by: Jukka Rissanen Signed-off-by: Marcel Holtmann --- net/bluetooth/6lowpan.c | 13 +++++++++++-- net/bluetooth/Kconfig | 6 +++--- net/bluetooth/Makefile | 4 +++- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index bdb01eb3bfcc..ceffe20fcbaa 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -1212,7 +1213,7 @@ static struct notifier_block bt_6lowpan_dev_notifier = { .notifier_call = device_event, }; -int bt_6lowpan_init(void) +static int __init bt_6lowpan_init(void) { lowpan_psm_debugfs = debugfs_create_file("6lowpan_psm", 0644, bt_debugfs, NULL, @@ -1224,7 +1225,7 @@ int bt_6lowpan_init(void) return register_netdevice_notifier(&bt_6lowpan_dev_notifier); } -void bt_6lowpan_exit(void) +static void __exit bt_6lowpan_exit(void) { debugfs_remove(lowpan_psm_debugfs); debugfs_remove(lowpan_control_debugfs); @@ -1236,3 +1237,11 @@ void bt_6lowpan_exit(void) unregister_netdevice_notifier(&bt_6lowpan_dev_notifier); } + +module_init(bt_6lowpan_init); +module_exit(bt_6lowpan_exit); + +MODULE_AUTHOR("Jukka Rissanen "); +MODULE_DESCRIPTION("Bluetooth 6LoWPAN"); +MODULE_VERSION(VERSION); +MODULE_LICENSE("GPL"); diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index 06ec14499ca1..f5afaa22f6ec 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -6,7 +6,6 @@ menuconfig BT tristate "Bluetooth subsystem support" depends on NET && !S390 depends on RFKILL || !RFKILL - select 6LOWPAN_IPHC if BT_6LOWPAN select CRC16 select CRYPTO select CRYPTO_BLKCIPHER @@ -41,10 +40,11 @@ menuconfig BT more information, see . config BT_6LOWPAN - bool "Bluetooth 6LoWPAN support" + tristate "Bluetooth 6LoWPAN support" depends on BT && IPV6 + select 6LOWPAN_IPHC if BT_6LOWPAN help - IPv6 compression over Bluetooth. + IPv6 compression over Bluetooth Low Energy. source "net/bluetooth/rfcomm/Kconfig" diff --git a/net/bluetooth/Makefile b/net/bluetooth/Makefile index ca51246b1016..886e9aa3ecf1 100644 --- a/net/bluetooth/Makefile +++ b/net/bluetooth/Makefile @@ -7,10 +7,12 @@ obj-$(CONFIG_BT_RFCOMM) += rfcomm/ obj-$(CONFIG_BT_BNEP) += bnep/ obj-$(CONFIG_BT_CMTP) += cmtp/ obj-$(CONFIG_BT_HIDP) += hidp/ +obj-$(CONFIG_BT_6LOWPAN) += bluetooth_6lowpan.o + +bluetooth_6lowpan-y := 6lowpan.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 \ a2mp.o amp.o -bluetooth-$(CONFIG_BT_6LOWPAN) += 6lowpan.o subdir-ccflags-y += -D__CHECK_ENDIAN__ From 18d93c176641cf7a3c0c452a6f03f46b5373d370 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 18 Jun 2014 16:37:10 +0300 Subject: [PATCH 055/378] Bluetooth: 6LoWPAN: Count module usage Count how many 6LoWPAN connections there exists so that we do not unload the module if there are still connections alive. Signed-off-by: Jukka Rissanen Signed-off-by: Marcel Holtmann --- net/bluetooth/6lowpan.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index ceffe20fcbaa..ba6c64163685 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -100,6 +100,8 @@ static inline bool peer_del(struct lowpan_dev *dev, struct lowpan_peer *peer) { list_del(&peer->list); + module_put(THIS_MODULE); + if (atomic_dec_and_test(&dev->peer_count)) { BT_DBG("last peer"); return true; @@ -752,6 +754,9 @@ static inline void chan_ready_cb(struct l2cap_chan *chan) } } + if (!try_module_get(THIS_MODULE)) + return; + add_peer_chan(chan, dev); ifup(dev->netdev); } From 7f118253820fc3ad38659485adb3ebdfe64820e1 Mon Sep 17 00:00:00 2001 From: Jukka Rissanen Date: Wed, 18 Jun 2014 16:37:11 +0300 Subject: [PATCH 056/378] Bluetooth: 6LoWPAN: Remove network devices when unloading When the module is unloaded, unregister the network device so that the system does not try to access non-existing device. Signed-off-by: Jukka Rissanen Signed-off-by: Marcel Holtmann --- net/bluetooth/6lowpan.c | 54 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index ba6c64163685..5a7f81df603c 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -607,6 +607,17 @@ static void ifup(struct net_device *netdev) rtnl_unlock(); } +static void ifdown(struct net_device *netdev) +{ + int err; + + rtnl_lock(); + err = dev_close(netdev); + if (err < 0) + BT_INFO("iface %s cannot be closed (%d)", netdev->name, err); + rtnl_unlock(); +} + static void do_notify_peers(struct work_struct *work) { struct lowpan_dev *dev = container_of(work, struct lowpan_dev, @@ -829,6 +840,8 @@ static void chan_close_cb(struct l2cap_chan *chan) cancel_delayed_work_sync(&dev->notify_peers); + ifdown(dev->netdev); + if (!removed) { INIT_WORK(&entry->delete_netdev, delete_netdev); schedule_work(&entry->delete_netdev); @@ -1186,6 +1199,43 @@ static const struct file_operations lowpan_control_fops = { .release = single_release, }; +static void disconnect_devices(void) +{ + struct lowpan_dev *entry, *tmp, *new_dev; + struct list_head devices; + unsigned long flags; + + INIT_LIST_HEAD(&devices); + + /* We make a separate list of devices because the unregister_netdev() + * will call device_event() which will also want to modify the same + * devices list. + */ + + read_lock_irqsave(&devices_lock, flags); + + list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) { + new_dev = kmalloc(sizeof(*new_dev), GFP_ATOMIC); + if (!new_dev) + break; + + new_dev->netdev = entry->netdev; + INIT_LIST_HEAD(&new_dev->list); + + list_add(&new_dev->list, &devices); + } + + read_unlock_irqrestore(&devices_lock, flags); + + list_for_each_entry_safe(entry, tmp, &devices, list) { + ifdown(entry->netdev); + BT_DBG("Unregistering netdev %s %p", + entry->netdev->name, entry->netdev); + unregister_netdev(entry->netdev); + kfree(entry); + } +} + static int device_event(struct notifier_block *unused, unsigned long event, void *ptr) { @@ -1202,6 +1252,8 @@ static int device_event(struct notifier_block *unused, list_for_each_entry_safe(entry, tmp, &bt_6lowpan_devices, list) { if (entry->netdev == netdev) { + BT_DBG("Unregistered netdev %s %p", + netdev->name, netdev); list_del(&entry->list); kfree(entry); break; @@ -1240,6 +1292,8 @@ static void __exit bt_6lowpan_exit(void) l2cap_chan_put(listen_chan); } + disconnect_devices(); + unregister_netdevice_notifier(&bt_6lowpan_dev_notifier); } From e04fde60efabe27afdbe041e3e5a09ec752ec9d2 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 23 Jun 2014 11:40:04 +0200 Subject: [PATCH 057/378] Bluetooth: Store current LE connection parameters in hci_conn struct The LE connection parameters are needed later on to be able to decide if it is required to trigger connection update procedures. So when the connection has been established successfully, store the current used parameters in hci_conn struct. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 3 +++ net/bluetooth/hci_event.c | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index c81de0d366df..cd73a82cc713 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -381,6 +381,9 @@ struct hci_conn { __u16 setting; __u16 le_conn_min_interval; __u16 le_conn_max_interval; + __u16 le_conn_interval; + __u16 le_conn_latency; + __u16 le_supv_timeout; __s8 rssi; __s8 tx_power; __s8 max_tx_power; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index e5b430113cfb..3d4741d789d3 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4056,6 +4056,10 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) conn->handle = __le16_to_cpu(ev->handle); conn->state = BT_CONNECTED; + conn->le_conn_interval = le16_to_cpu(ev->interval); + conn->le_conn_latency = le16_to_cpu(ev->latency); + conn->le_supv_timeout = le16_to_cpu(ev->supervision_timeout); + hci_conn_add_sysfs(conn); hci_proto_connect_cfm(conn, ev->status); From 1855d92dce0dc0ed81a78eacae710529600513f4 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 23 Jun 2014 11:40:05 +0200 Subject: [PATCH 058/378] Bluetooth: Track LE connection parameter update event When the LE controller changes its connection parameters, it will send a connection parameter update event. Make sure that the new set of parameters are stored in hci_conn struct and thus will properly update the previous values retrieved from the connection complete event. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 9 +++++++++ net/bluetooth/hci_event.c | 27 +++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 3f3a3f1399fb..6b8371d73d3d 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1666,6 +1666,15 @@ struct hci_ev_le_conn_complete { __u8 clk_accurancy; } __packed; +#define HCI_EV_LE_CONN_UPDATE_COMPLETE 0x03 +struct hci_ev_le_conn_update_complete { + __u8 status; + __le16 handle; + __le16 interval; + __le16 latency; + __le16 supervision_timeout; +} __packed; + #define HCI_EV_LE_LTK_REQ 0x05 struct hci_ev_le_ltk_req { __le16 handle; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 3d4741d789d3..e239435312dc 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4070,6 +4070,29 @@ unlock: hci_dev_unlock(hdev); } +static void hci_le_conn_update_complete_evt(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_ev_le_conn_update_complete *ev = (void *) skb->data; + struct hci_conn *conn; + + BT_DBG("%s status 0x%2.2x", hdev->name, ev->status); + + if (ev->status) + return; + + hci_dev_lock(hdev); + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); + if (conn) { + conn->le_conn_interval = le16_to_cpu(ev->interval); + conn->le_conn_latency = le16_to_cpu(ev->latency); + conn->le_supv_timeout = le16_to_cpu(ev->supervision_timeout); + } + + hci_dev_unlock(hdev); +} + /* This function requires the caller holds hdev->lock */ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) @@ -4269,6 +4292,10 @@ static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_le_conn_complete_evt(hdev, skb); break; + case HCI_EV_LE_CONN_UPDATE_COMPLETE: + hci_le_conn_update_complete_evt(hdev, skb); + break; + case HCI_EV_LE_ADVERTISING_REPORT: hci_le_adv_report_evt(hdev, skb); break; From a720d7351e2571bf7498681970b076e366a7d221 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 23 Jun 2014 12:14:51 +0200 Subject: [PATCH 059/378] Bluetooth: Set default min/max connection interval for LE slaves For all incoming LE connections, the minimum and maximum connection interval is a value that should be copied from the controller default values. This allows to properly check if the resulting connection interval of a newly established connection is in the range we are expecting. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_event.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index e239435312dc..e1e1ecb4db10 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4026,6 +4026,14 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) conn->init_addr_type = ev->bdaddr_type; bacpy(&conn->init_addr, &ev->bdaddr); + + /* For incoming connections, set the default minimum + * and maximum connection interval. They will be used + * to check if the parameters are in range and if not + * trigger the connection update procedure. + */ + conn->le_conn_min_interval = hdev->le_conn_min_interval; + conn->le_conn_max_interval = hdev->le_conn_max_interval; } /* Lookup the identity address from the stored connection From 80afeb6cec3739d3167e1fef6214224f0f588f13 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 23 Jun 2014 12:18:51 +0200 Subject: [PATCH 060/378] Bluetooth: Add support LE slave connection update procedure When the current LE connection parameters of a slave connection do not match up with the controller defined values, then trigger the connection update procedure to allow adjusting them. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/l2cap_core.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index cbf6b9d1c404..565afb78296a 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1481,6 +1481,25 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, dst_type)) return; + /* For LE slave connections, make sure the connection interval + * is in the range of the minium and maximum interval that has + * been configured for this connection. If not, then trigger + * the connection update procedure. + */ + if (!(hcon->link_mode & HCI_LM_MASTER) && + (hcon->le_conn_interval < hcon->le_conn_min_interval || + hcon->le_conn_interval > hcon->le_conn_max_interval)) { + struct l2cap_conn_param_update_req req; + + req.min = cpu_to_le16(hcon->le_conn_min_interval); + req.max = cpu_to_le16(hcon->le_conn_max_interval); + req.latency = cpu_to_le16(hcon->le_conn_latency); + req.to_multiplier = cpu_to_le16(hcon->le_supv_timeout); + + l2cap_send_cmd(conn, l2cap_get_ident(conn), + L2CAP_CONN_PARAM_UPDATE_REQ, sizeof(req), &req); + } + l2cap_chan_lock(pchan); chan = pchan->ops->new_connection(pchan); From 37f468cd171c7d76d560b6bd5c09dc506ab9dbec Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Mon, 23 Jun 2014 15:49:46 +0200 Subject: [PATCH 061/378] Bluetooth: Remove redundant calls to h5_reset_rx h5_reset_rx is unconditionally called at the end of h5_complete_rx_pkt, no need to call it anymore after that. Signed-off-by: Loic Poulain Signed-off-by: Marcel Holtmann --- drivers/bluetooth/hci_h5.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/bluetooth/hci_h5.c b/drivers/bluetooth/hci_h5.c index fede8ca7147c..caacb422995d 100644 --- a/drivers/bluetooth/hci_h5.c +++ b/drivers/bluetooth/hci_h5.c @@ -355,10 +355,7 @@ static void h5_complete_rx_pkt(struct hci_uart *hu) static int h5_rx_crc(struct hci_uart *hu, unsigned char c) { - struct h5 *h5 = hu->priv; - h5_complete_rx_pkt(hu); - h5_reset_rx(h5); return 0; } @@ -373,7 +370,6 @@ static int h5_rx_payload(struct hci_uart *hu, unsigned char c) h5->rx_pending = 2; } else { h5_complete_rx_pkt(hu); - h5_reset_rx(h5); } return 0; From 567fa2aa3dfad9848c25a226927a4ca5f94229ac Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 24 Jun 2014 13:15:48 +0300 Subject: [PATCH 062/378] Bluetooth: Update hci_add_link_key() to return pointer to key By returning the added (or updated) key we pave the way for further refactoring (in subsequent patches) that allows moving the mgmt event sending out from this function (and thereby removal of the awkward new_key parameter). Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 5 +++-- net/bluetooth/hci_core.c | 11 ++++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index cd73a82cc713..7f81791a865d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -859,8 +859,9 @@ void hci_uuids_clear(struct hci_dev *hdev); void hci_link_keys_clear(struct hci_dev *hdev); struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); -int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, - bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len); +struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, + int new_key, bdaddr_t *bdaddr, u8 *val, + u8 type, u8 pin_len); struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand, bool master); struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index bbfc5455acec..ee42788aed2c 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3001,8 +3001,9 @@ struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, return NULL; } -int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, - bdaddr_t *bdaddr, u8 *val, u8 type, u8 pin_len) +struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, + int new_key, bdaddr_t *bdaddr, u8 *val, + u8 type, u8 pin_len) { struct link_key *key, *old_key; u8 old_key_type; @@ -3016,7 +3017,7 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, old_key_type = conn ? conn->key_type : 0xff; key = kzalloc(sizeof(*key), GFP_KERNEL); if (!key) - return -ENOMEM; + return NULL; list_add(&key->list, &hdev->link_keys); } @@ -3042,7 +3043,7 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, key->type = type; if (!new_key) - return 0; + return key; persistent = hci_persistent_key(hdev, conn, type, old_key_type); @@ -3051,7 +3052,7 @@ int hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, int new_key, if (conn) conn->flush_key = !persistent; - return 0; + return key; } struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, From 7652ff6aeaf0eeaec1f2e7e2f3ce0e588447dbd1 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 24 Jun 2014 13:15:49 +0300 Subject: [PATCH 063/378] Bluetooth: Move mgmt event sending out from hci_add_link_key() There are two callers of hci_add_link_key(). The first one is the HCI Link Key Notification event and the second one the mgmt code that receives a list of link keys from user space. Previously we've had the hci_add_link_key() function being responsible for also emitting a mgmt signal but for the latter use case this should not happen. Because of this a rather awkward new_key paramter has been passed to the function. This patch moves the mgmt event sending out from the hci_add_link_key() function, thereby making the code a bit more understandable. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 4 ++-- net/bluetooth/hci_core.c | 17 +++++------------ net/bluetooth/hci_event.c | 18 +++++++++++++++--- net/bluetooth/mgmt.c | 4 ++-- 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 7f81791a865d..888911d205dc 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -860,8 +860,8 @@ void hci_uuids_clear(struct hci_dev *hdev); void hci_link_keys_clear(struct hci_dev *hdev); struct link_key *hci_find_link_key(struct hci_dev *hdev, bdaddr_t *bdaddr); struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, - int new_key, bdaddr_t *bdaddr, u8 *val, - u8 type, u8 pin_len); + bdaddr_t *bdaddr, u8 *val, u8 type, + u8 pin_len, bool *persistent); struct smp_ltk *hci_find_ltk(struct hci_dev *hdev, __le16 ediv, __le64 rand, bool master); struct smp_ltk *hci_add_ltk(struct hci_dev *hdev, bdaddr_t *bdaddr, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index ee42788aed2c..159783e746c0 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3002,12 +3002,11 @@ struct smp_irk *hci_find_irk_by_addr(struct hci_dev *hdev, bdaddr_t *bdaddr, } struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, - int new_key, bdaddr_t *bdaddr, u8 *val, - u8 type, u8 pin_len) + bdaddr_t *bdaddr, u8 *val, u8 type, + u8 pin_len, bool *persistent) { struct link_key *key, *old_key; u8 old_key_type; - bool persistent; old_key = hci_find_link_key(hdev, bdaddr); if (old_key) { @@ -3042,15 +3041,9 @@ struct link_key *hci_add_link_key(struct hci_dev *hdev, struct hci_conn *conn, else key->type = type; - if (!new_key) - return key; - - persistent = hci_persistent_key(hdev, conn, type, old_key_type); - - mgmt_new_link_key(hdev, key, persistent); - - if (conn) - conn->flush_key = !persistent; + if (persistent) + *persistent = hci_persistent_key(hdev, conn, type, + old_key_type); return key; } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index e1e1ecb4db10..47bd48a597bd 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3111,6 +3111,8 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_link_key_notify *ev = (void *) skb->data; struct hci_conn *conn; + struct link_key *key; + bool persistent; u8 pin_len = 0; BT_DBG("%s", hdev->name); @@ -3129,10 +3131,20 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_conn_drop(conn); } - if (test_bit(HCI_MGMT, &hdev->dev_flags)) - hci_add_link_key(hdev, conn, 1, &ev->bdaddr, ev->link_key, - ev->key_type, pin_len); + if (!test_bit(HCI_MGMT, &hdev->dev_flags)) + goto unlock; + key = hci_add_link_key(hdev, conn, &ev->bdaddr, ev->link_key, + ev->key_type, pin_len, &persistent); + if (!key) + goto unlock; + + mgmt_new_link_key(hdev, key, persistent); + + if (conn) + conn->flush_key = !persistent; + +unlock: hci_dev_unlock(hdev); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index e1651c3fc676..bb5b04191253 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2424,8 +2424,8 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, for (i = 0; i < key_count; i++) { struct mgmt_link_key_info *key = &cp->keys[i]; - hci_add_link_key(hdev, NULL, 0, &key->addr.bdaddr, key->val, - key->type, key->pin_len); + hci_add_link_key(hdev, NULL, &key->addr.bdaddr, key->val, + key->type, key->pin_len, NULL); } cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, 0, NULL, 0); From 0663b297f1953e5d84928722e44f71272f5ff058 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 24 Jun 2014 13:15:50 +0300 Subject: [PATCH 064/378] Bluetooth: Rename HCI_DEBUG_KEYS to HCI_KEEP_DEBUG_KEYS We're planning to add a flag to actively use debug keys in addition to simply just accepting them, which makes the current generically named DEBUG_KEYS flag a bit confusing. Since the flag in practice affects whether the kernel keeps debug keys around or not rename it to HCI_KEEP_DEBUG_KEYS. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 2 +- net/bluetooth/hci_event.c | 2 +- net/bluetooth/mgmt.c | 14 +++++++++----- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 6b8371d73d3d..ffb489e3946d 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -124,7 +124,7 @@ enum { HCI_MGMT, HCI_PAIRABLE, HCI_SERVICE_CACHE, - HCI_DEBUG_KEYS, + HCI_KEEP_DEBUG_KEYS, HCI_UNREGISTER, HCI_USER_CHANNEL, diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 47bd48a597bd..b0fdeec2f2ca 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3066,7 +3066,7 @@ static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s found key type %u for %pMR", hdev->name, key->type, &ev->bdaddr); - if (!test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags) && + if (!test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags) && key->type == HCI_LK_DEBUG_COMBINATION) { BT_DBG("%s ignoring debug key", hdev->name); goto not_found; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index bb5b04191253..634b44ddc9f9 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -440,7 +440,7 @@ static u32 get_current_settings(struct hci_dev *hdev) if (test_bit(HCI_SC_ENABLED, &hdev->dev_flags)) settings |= MGMT_SETTING_SECURE_CONN; - if (test_bit(HCI_DEBUG_KEYS, &hdev->dev_flags)) + if (test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags)) settings |= MGMT_SETTING_DEBUG_KEYS; if (test_bit(HCI_PRIVACY, &hdev->dev_flags)) @@ -2414,9 +2414,11 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, hci_link_keys_clear(hdev); if (cp->debug_keys) - changed = !test_and_set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags); + changed = !test_and_set_bit(HCI_KEEP_DEBUG_KEYS, + &hdev->dev_flags); else - changed = test_and_clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags); + changed = test_and_clear_bit(HCI_KEEP_DEBUG_KEYS, + &hdev->dev_flags); if (changed) new_settings(hdev, NULL); @@ -4349,9 +4351,11 @@ static int set_debug_keys(struct sock *sk, struct hci_dev *hdev, hci_dev_lock(hdev); if (cp->val) - changed = !test_and_set_bit(HCI_DEBUG_KEYS, &hdev->dev_flags); + changed = !test_and_set_bit(HCI_KEEP_DEBUG_KEYS, + &hdev->dev_flags); else - changed = test_and_clear_bit(HCI_DEBUG_KEYS, &hdev->dev_flags); + changed = test_and_clear_bit(HCI_KEEP_DEBUG_KEYS, + &hdev->dev_flags); err = send_settings_rsp(sk, MGMT_OP_SET_DEBUG_KEYS, hdev); if (err < 0) From 6d5650c4e519794fcc441635ea54f47d68140c93 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 24 Jun 2014 13:15:51 +0300 Subject: [PATCH 065/378] Bluetooth: Don't store debug keys if flag for them is not set Instead of waiting for a disconnection to occur to remove a debug key simply never store it in the list to begin with. This means we can also remove the debug keys check when looking up keys in hci_link_key_request_evt(). Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_event.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index b0fdeec2f2ca..c92c5a020fe7 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3066,12 +3066,6 @@ static void hci_link_key_request_evt(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s found key type %u for %pMR", hdev->name, key->type, &ev->bdaddr); - if (!test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags) && - key->type == HCI_LK_DEBUG_COMBINATION) { - BT_DBG("%s ignoring debug key", hdev->name); - goto not_found; - } - conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &ev->bdaddr); if (conn) { if ((key->type == HCI_LK_UNAUTH_COMBINATION_P192 || @@ -3141,8 +3135,18 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb) mgmt_new_link_key(hdev, key, persistent); - if (conn) + /* Keep debug keys around only if the HCI_KEEP_DEBUG_KEYS flag + * is set. If it's not set simply remove the key from the kernel + * list (we've still notified user space about it but with + * store_hint being 0). + */ + if (key->type == HCI_LK_DEBUG_COMBINATION && + !test_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags)) { + list_del(&key->list); + kfree(key); + } else if (conn) { conn->flush_key = !persistent; + } unlock: hci_dev_unlock(hdev); From af6a9c321384400efab1726636e8189737bbbd09 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 24 Jun 2014 13:15:53 +0300 Subject: [PATCH 066/378] Bluetooth: Convert hcon->flush_key to a proper flag There's no point in having boolean variables in the hci_conn struct since it already has a flags member. This patch converts the flush_key member into a proper flag. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_event.c | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 888911d205dc..4f70605e0399 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -394,7 +394,6 @@ struct hci_conn { __u8 remote_cap; __u8 remote_auth; __u8 remote_id; - bool flush_key; unsigned int sent; @@ -524,6 +523,7 @@ enum { HCI_CONN_AES_CCM, HCI_CONN_POWER_SAVE, HCI_CONN_REMOTE_OOB, + HCI_CONN_FLUSH_KEY, }; static inline bool hci_conn_ssp_enabled(struct hci_conn *conn) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index c92c5a020fe7..90cba6a8293b 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2159,7 +2159,8 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) mgmt_device_disconnected(hdev, &conn->dst, conn->type, conn->dst_type, reason, mgmt_connected); - if (conn->type == ACL_LINK && conn->flush_key) + if (conn->type == ACL_LINK && + test_bit(HCI_CONN_FLUSH_KEY, &conn->flags)) hci_remove_link_key(hdev, &conn->dst); params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); @@ -3145,7 +3146,10 @@ static void hci_link_key_notify_evt(struct hci_dev *hdev, struct sk_buff *skb) list_del(&key->list); kfree(key); } else if (conn) { - conn->flush_key = !persistent; + if (persistent) + clear_bit(HCI_CONN_FLUSH_KEY, &conn->flags); + else + set_bit(HCI_CONN_FLUSH_KEY, &conn->flags); } unlock: From 58e9293c4e18b9b5f52822e7cbce589c70920721 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 24 Jun 2014 14:00:26 +0300 Subject: [PATCH 067/378] Bluetooth: Fix ignoring debug keys in mgmt_load_link_keys We should never allow user space to feed back debug keys to the kernel. If the user desires to use debug keys require setting the appropriate debug keys mode and performing a new pairing. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 634b44ddc9f9..747746b0d2c4 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2426,6 +2426,12 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, for (i = 0; i < key_count; i++) { struct mgmt_link_key_info *key = &cp->keys[i]; + /* Always ignore debug keys and require a new pairing if + * the user wants to use them. + */ + if (key->type == HCI_LK_DEBUG_COMBINATION) + continue; + hci_add_link_key(hdev, NULL, &key->addr.bdaddr, key->val, key->type, key->pin_len, NULL); } From 3769972badcd542913c460ca2834312cdff9f16c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 24 Jun 2014 14:00:27 +0300 Subject: [PATCH 068/378] Bluetooth: Add a new HCI_USE_DEBUG_KEYS flag To pave the way for actively using debug keys for pairing this patch adds a new HCI_USE_DEBUG_KEYS flag for the purpose. When the flag is set we issue a HCI_Write_SSP_Debug mode whenever HCI_Write_SSP_Mode(0x01) has been issued as well as before issuing a HCI_Write_SSP_Mode(0x00) command. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 1 + net/bluetooth/mgmt.c | 12 ++++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index ffb489e3946d..cc2e88dd20ea 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -125,6 +125,7 @@ enum { HCI_PAIRABLE, HCI_SERVICE_CACHE, HCI_KEEP_DEBUG_KEYS, + HCI_USE_DEBUG_KEYS, HCI_UNREGISTER, HCI_USER_CHANNEL, diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 747746b0d2c4..69afbb2df133 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1877,6 +1877,10 @@ static int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) goto failed; } + if (!cp->val && test_bit(HCI_USE_DEBUG_KEYS, &hdev->dev_flags)) + hci_send_cmd(hdev, HCI_OP_WRITE_SSP_DEBUG_MODE, + sizeof(cp->val), &cp->val); + err = hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, 1, &cp->val); if (err < 0) { mgmt_pending_remove(cmd); @@ -5784,10 +5788,14 @@ void mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) hci_req_init(&req, hdev); - if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) + if (test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { + if (test_bit(HCI_USE_DEBUG_KEYS, &hdev->dev_flags)) + hci_req_add(&req, HCI_OP_WRITE_SSP_DEBUG_MODE, + sizeof(enable), &enable); update_eir(&req); - else + } else { clear_eir(&req); + } hci_req_run(&req, NULL); } From b97109790c1fcbe6b5da21c441ba336cf1ab9a3c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 24 Jun 2014 14:00:28 +0300 Subject: [PATCH 069/378] Bluetooth: Add support for mode 0x02 for mgmt_set_debug_keys This patch adds a new valid mode 0x02 for the mgmt_set_debug_keys command. The 0x02 mode sets the HCI_USE_DEBUG_KEYS flag which makes us always use debug keys for pairing. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 69afbb2df133..f75a090cd7e4 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -4349,12 +4349,12 @@ static int set_debug_keys(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_mode *cp = data; - bool changed; + bool changed, use_changed; int err; BT_DBG("request for %s", hdev->name); - if (cp->val != 0x00 && cp->val != 0x01) + if (cp->val != 0x00 && cp->val != 0x01 && cp->val != 0x02) return cmd_status(sk, hdev->id, MGMT_OP_SET_DEBUG_KEYS, MGMT_STATUS_INVALID_PARAMS); @@ -4367,6 +4367,20 @@ static int set_debug_keys(struct sock *sk, struct hci_dev *hdev, changed = test_and_clear_bit(HCI_KEEP_DEBUG_KEYS, &hdev->dev_flags); + if (cp->val == 0x02) + use_changed = !test_and_set_bit(HCI_USE_DEBUG_KEYS, + &hdev->dev_flags); + else + use_changed = test_and_clear_bit(HCI_USE_DEBUG_KEYS, + &hdev->dev_flags); + + if (hdev_is_powered(hdev) && use_changed && + test_bit(HCI_SSP_ENABLED, &hdev->dev_flags)) { + u8 mode = (cp->val == 0x02) ? 0x01 : 0x00; + hci_send_cmd(hdev, HCI_OP_WRITE_SSP_DEBUG_MODE, + sizeof(mode), &mode); + } + err = send_settings_rsp(sk, MGMT_OP_SET_DEBUG_KEYS, hdev); if (err < 0) goto unlock; From 985d904902681d736924afe3f4dae212c0e5f6a4 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 24 Jun 2014 13:13:04 +0200 Subject: [PATCH 070/378] Bluetooth: Remove ssp_debug_mode debugfs option The ssp_debug_mode debugfs option for developers is no longer needed. Support for using Secure Simple Pairing (SSP) debug mode is exposed by the management interface now. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 51 ---------------------------------------- 1 file changed, 51 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 159783e746c0..9852449ac104 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -352,55 +352,6 @@ static int auto_accept_delay_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get, auto_accept_delay_set, "%llu\n"); -static int ssp_debug_mode_set(void *data, u64 val) -{ - struct hci_dev *hdev = data; - struct sk_buff *skb; - __u8 mode; - int err; - - if (val != 0 && val != 1) - return -EINVAL; - - if (!test_bit(HCI_UP, &hdev->flags)) - return -ENETDOWN; - - hci_req_lock(hdev); - mode = val; - skb = __hci_cmd_sync(hdev, HCI_OP_WRITE_SSP_DEBUG_MODE, sizeof(mode), - &mode, HCI_CMD_TIMEOUT); - hci_req_unlock(hdev); - - if (IS_ERR(skb)) - return PTR_ERR(skb); - - err = -bt_to_errno(skb->data[0]); - kfree_skb(skb); - - if (err < 0) - return err; - - hci_dev_lock(hdev); - hdev->ssp_debug_mode = val; - hci_dev_unlock(hdev); - - return 0; -} - -static int ssp_debug_mode_get(void *data, u64 *val) -{ - struct hci_dev *hdev = data; - - hci_dev_lock(hdev); - *val = hdev->ssp_debug_mode; - hci_dev_unlock(hdev); - - return 0; -} - -DEFINE_SIMPLE_ATTRIBUTE(ssp_debug_mode_fops, ssp_debug_mode_get, - ssp_debug_mode_set, "%llu\n"); - static ssize_t force_sc_support_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { @@ -1787,8 +1738,6 @@ static int __hci_init(struct hci_dev *hdev) if (lmp_ssp_capable(hdev)) { debugfs_create_file("auto_accept_delay", 0644, hdev->debugfs, hdev, &auto_accept_delay_fops); - debugfs_create_file("ssp_debug_mode", 0644, hdev->debugfs, - hdev, &ssp_debug_mode_fops); debugfs_create_file("force_sc_support", 0644, hdev->debugfs, hdev, &force_sc_support_fops); debugfs_create_file("sc_only_mode", 0444, hdev->debugfs, From 4dae27983eaaee15c6867561eb2c8d7b2d28d6cc Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 24 Jun 2014 17:03:50 +0300 Subject: [PATCH 071/378] Bluetooth: Convert hci_conn->link_mode into flags Since the link_mode member of the hci_conn struct is a bit field and we already have a flags member as well it makes sense to merge these two together. This patch moves all used link_mode bits into corresponding flags. To keep backwards compatibility with user space we still need to provide a get_link_mode() helper function for the ioctl's that expect a link_mode style value. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 10 +++++--- net/bluetooth/hci_conn.c | 43 ++++++++++++++++++++++++-------- net/bluetooth/hci_event.c | 28 ++++++++++----------- net/bluetooth/l2cap_core.c | 4 +-- net/bluetooth/smp.c | 12 ++++----- 5 files changed, 62 insertions(+), 35 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 4f70605e0399..0b0597d81827 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -367,7 +367,6 @@ struct hci_conn { __u8 features[HCI_MAX_PAGES][8]; __u16 pkt_type; __u16 link_policy; - __u32 link_mode; __u8 key_type; __u8 auth_type; __u8 sec_level; @@ -524,6 +523,11 @@ enum { HCI_CONN_POWER_SAVE, HCI_CONN_REMOTE_OOB, HCI_CONN_FLUSH_KEY, + HCI_CONN_MASTER, + HCI_CONN_ENCRYPT, + HCI_CONN_AUTH, + HCI_CONN_SECURE, + HCI_CONN_FIPS, }; static inline bool hci_conn_ssp_enabled(struct hci_conn *conn) @@ -1025,7 +1029,7 @@ static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status) if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) return; - encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00; + encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00; l2cap_security_cfm(conn, status, encrypt); if (conn->security_cfm_cb) @@ -1066,7 +1070,7 @@ static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status) if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags)) return; - encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00; + encrypt = test_bit(HCI_CONN_ENCRYPT, &conn->flags) ? 0x01 : 0x00; read_lock(&hci_cb_list_lock); list_for_each_entry(cb, &hci_cb_list, list) { diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index a7a27bc2c0b1..626160c37103 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -67,7 +67,7 @@ static void hci_acl_create_connection(struct hci_conn *conn) conn->state = BT_CONNECT; conn->out = true; - conn->link_mode = HCI_LM_MASTER; + set_bit(HCI_CONN_MASTER, &conn->flags); conn->attempt++; @@ -743,7 +743,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, } conn->out = true; - conn->link_mode |= HCI_LM_MASTER; + set_bit(HCI_CONN_MASTER, &conn->flags); params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); if (params) { @@ -865,7 +865,8 @@ int hci_conn_check_link_mode(struct hci_conn *conn) return 0; } - if (hci_conn_ssp_enabled(conn) && !(conn->link_mode & HCI_LM_ENCRYPT)) + if (hci_conn_ssp_enabled(conn) && + !test_bit(HCI_CONN_ENCRYPT, &conn->flags)) return 0; return 1; @@ -881,7 +882,7 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) if (sec_level > conn->sec_level) conn->pending_sec_level = sec_level; - else if (conn->link_mode & HCI_LM_AUTH) + else if (test_bit(HCI_CONN_AUTH, &conn->flags)) return 1; /* Make sure we preserve an existing MITM requirement*/ @@ -899,7 +900,7 @@ static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) /* If we're already encrypted set the REAUTH_PEND flag, * otherwise set the ENCRYPT_PEND. */ - if (conn->link_mode & HCI_LM_ENCRYPT) + if (test_bit(HCI_CONN_ENCRYPT, &conn->flags)) set_bit(HCI_CONN_REAUTH_PEND, &conn->flags); else set_bit(HCI_CONN_ENCRYPT_PEND, &conn->flags); @@ -940,7 +941,7 @@ int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type) return 1; /* For other security levels we need the link key. */ - if (!(conn->link_mode & HCI_LM_AUTH)) + if (!test_bit(HCI_CONN_AUTH, &conn->flags)) goto auth; /* An authenticated FIPS approved combination key has sufficient @@ -980,7 +981,7 @@ auth: return 0; encrypt: - if (conn->link_mode & HCI_LM_ENCRYPT) + if (test_bit(HCI_CONN_ENCRYPT, &conn->flags)) return 1; hci_conn_encrypt(conn); @@ -1027,7 +1028,7 @@ int hci_conn_switch_role(struct hci_conn *conn, __u8 role) { BT_DBG("hcon %p", conn); - if (!role && conn->link_mode & HCI_LM_MASTER) + if (!role && test_bit(HCI_CONN_MASTER, &conn->flags)) return 1; if (!test_and_set_bit(HCI_CONN_RSWITCH_PEND, &conn->flags)) { @@ -1101,6 +1102,28 @@ void hci_conn_check_pending(struct hci_dev *hdev) hci_dev_unlock(hdev); } +static u32 get_link_mode(struct hci_conn *conn) +{ + u32 link_mode = 0; + + if (test_bit(HCI_CONN_MASTER, &conn->flags)) + link_mode |= HCI_LM_MASTER; + + if (test_bit(HCI_CONN_ENCRYPT, &conn->flags)) + link_mode |= HCI_LM_ENCRYPT; + + if (test_bit(HCI_CONN_AUTH, &conn->flags)) + link_mode |= HCI_LM_AUTH; + + if (test_bit(HCI_CONN_SECURE, &conn->flags)) + link_mode |= HCI_LM_SECURE; + + if (test_bit(HCI_CONN_FIPS, &conn->flags)) + link_mode |= HCI_LM_FIPS; + + return link_mode; +} + int hci_get_conn_list(void __user *arg) { struct hci_conn *c; @@ -1136,7 +1159,7 @@ int hci_get_conn_list(void __user *arg) (ci + n)->type = c->type; (ci + n)->out = c->out; (ci + n)->state = c->state; - (ci + n)->link_mode = c->link_mode; + (ci + n)->link_mode = get_link_mode(c); if (++n >= req.conn_num) break; } @@ -1172,7 +1195,7 @@ int hci_get_conn_info(struct hci_dev *hdev, void __user *arg) ci.type = conn->type; ci.out = conn->out; ci.state = conn->state; - ci.link_mode = conn->link_mode; + ci.link_mode = get_link_mode(conn); } hci_dev_unlock(hdev); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 90cba6a8293b..7a23324eac39 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -103,9 +103,9 @@ static void hci_cc_role_discovery(struct hci_dev *hdev, struct sk_buff *skb) conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); if (conn) { if (rp->role) - conn->link_mode &= ~HCI_LM_MASTER; + clear_bit(HCI_CONN_MASTER, &conn->flags); else - conn->link_mode |= HCI_LM_MASTER; + set_bit(HCI_CONN_MASTER, &conn->flags); } hci_dev_unlock(hdev); @@ -1346,7 +1346,7 @@ static void hci_cs_create_conn(struct hci_dev *hdev, __u8 status) conn = hci_conn_add(hdev, ACL_LINK, &cp->bdaddr); if (conn) { conn->out = true; - conn->link_mode |= HCI_LM_MASTER; + set_bit(HCI_CONN_MASTER, &conn->flags); } else BT_ERR("No memory for new connection"); } @@ -1989,10 +1989,10 @@ static void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_conn_add_sysfs(conn); if (test_bit(HCI_AUTH, &hdev->flags)) - conn->link_mode |= HCI_LM_AUTH; + set_bit(HCI_CONN_AUTH, &conn->flags); if (test_bit(HCI_ENCRYPT, &hdev->flags)) - conn->link_mode |= HCI_LM_ENCRYPT; + set_bit(HCI_CONN_ENCRYPT, &conn->flags); /* Get remote features */ if (conn->type == ACL_LINK) { @@ -2220,7 +2220,7 @@ static void hci_auth_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) test_bit(HCI_CONN_REAUTH_PEND, &conn->flags)) { BT_INFO("re-auth of legacy device is not possible."); } else { - conn->link_mode |= HCI_LM_AUTH; + set_bit(HCI_CONN_AUTH, &conn->flags); conn->sec_level = conn->pending_sec_level; } } else { @@ -2323,19 +2323,19 @@ static void hci_encrypt_change_evt(struct hci_dev *hdev, struct sk_buff *skb) if (!ev->status) { if (ev->encrypt) { /* Encryption implies authentication */ - conn->link_mode |= HCI_LM_AUTH; - conn->link_mode |= HCI_LM_ENCRYPT; + set_bit(HCI_CONN_AUTH, &conn->flags); + set_bit(HCI_CONN_ENCRYPT, &conn->flags); conn->sec_level = conn->pending_sec_level; /* P-256 authentication key implies FIPS */ if (conn->key_type == HCI_LK_AUTH_COMBINATION_P256) - conn->link_mode |= HCI_LM_FIPS; + set_bit(HCI_CONN_FIPS, &conn->flags); if ((conn->type == ACL_LINK && ev->encrypt == 0x02) || conn->type == LE_LINK) set_bit(HCI_CONN_AES_CCM, &conn->flags); } else { - conn->link_mode &= ~HCI_LM_ENCRYPT; + clear_bit(HCI_CONN_ENCRYPT, &conn->flags); clear_bit(HCI_CONN_AES_CCM, &conn->flags); } } @@ -2386,7 +2386,7 @@ static void hci_change_link_key_complete_evt(struct hci_dev *hdev, conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(ev->handle)); if (conn) { if (!ev->status) - conn->link_mode |= HCI_LM_SECURE; + set_bit(HCI_CONN_SECURE, &conn->flags); clear_bit(HCI_CONN_AUTH_PEND, &conn->flags); @@ -2828,9 +2828,9 @@ static void hci_role_change_evt(struct hci_dev *hdev, struct sk_buff *skb) if (conn) { if (!ev->status) { if (ev->role) - conn->link_mode &= ~HCI_LM_MASTER; + clear_bit(HCI_CONN_MASTER, &conn->flags); else - conn->link_mode |= HCI_LM_MASTER; + set_bit(HCI_CONN_MASTER, &conn->flags); } clear_bit(HCI_CONN_RSWITCH_PEND, &conn->flags); @@ -4007,7 +4007,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) if (ev->role == LE_CONN_ROLE_MASTER) { conn->out = true; - conn->link_mode |= HCI_LM_MASTER; + set_bit(HCI_CONN_MASTER, &conn->flags); } /* If we didn't have a hci_conn object previously diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 565afb78296a..d015aa190fdc 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1486,7 +1486,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) * been configured for this connection. If not, then trigger * the connection update procedure. */ - if (!(hcon->link_mode & HCI_LM_MASTER) && + if (!test_bit(HCI_CONN_MASTER, &hcon->flags) && (hcon->le_conn_interval < hcon->le_conn_min_interval || hcon->le_conn_interval > hcon->le_conn_max_interval)) { struct l2cap_conn_param_update_req req; @@ -5244,7 +5244,7 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, u16 min, max, latency, to_multiplier; int err; - if (!(hcon->link_mode & HCI_LM_MASTER)) + if (!test_bit(HCI_CONN_MASTER, &hcon->flags)) return -EINVAL; if (cmd_len != sizeof(struct l2cap_conn_param_update_req)) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 28f4ef48095b..976fce2315fd 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -435,7 +435,7 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, * Confirms and the slave Enters the passkey. */ if (method == OVERLAP) { - if (hcon->link_mode & HCI_LM_MASTER) + if (test_bit(HCI_CONN_MASTER, &hcon->flags)) method = CFM_PASSKEY; else method = REQ_PASSKEY; @@ -683,7 +683,7 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) if (skb->len < sizeof(*req)) return SMP_INVALID_PARAMS; - if (conn->hcon->link_mode & HCI_LM_MASTER) + if (test_bit(HCI_CONN_MASTER, &conn->hcon->flags)) return SMP_CMD_NOTSUPP; if (!test_and_set_bit(HCI_CONN_LE_SMP_PEND, &conn->hcon->flags)) @@ -750,7 +750,7 @@ static u8 smp_cmd_pairing_rsp(struct l2cap_conn *conn, struct sk_buff *skb) if (skb->len < sizeof(*rsp)) return SMP_INVALID_PARAMS; - if (!(conn->hcon->link_mode & HCI_LM_MASTER)) + if (!test_bit(HCI_CONN_MASTER, &conn->hcon->flags)) return SMP_CMD_NOTSUPP; skb_pull(skb, sizeof(*rsp)); @@ -873,7 +873,7 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) if (skb->len < sizeof(*rp)) return SMP_INVALID_PARAMS; - if (!(conn->hcon->link_mode & HCI_LM_MASTER)) + if (!test_bit(HCI_CONN_MASTER, &conn->hcon->flags)) return SMP_CMD_NOTSUPP; sec_level = authreq_to_seclevel(rp->auth_req); @@ -937,7 +937,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) if (sec_level > hcon->pending_sec_level) hcon->pending_sec_level = sec_level; - if (hcon->link_mode & HCI_LM_MASTER) + if (test_bit(HCI_CONN_MASTER, &hcon->flags)) if (smp_ltk_encrypt(conn, hcon->pending_sec_level)) return 0; @@ -957,7 +957,7 @@ int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) hcon->pending_sec_level > BT_SECURITY_MEDIUM) authreq |= SMP_AUTH_MITM; - if (hcon->link_mode & HCI_LM_MASTER) { + if (test_bit(HCI_CONN_MASTER, &hcon->flags)) { struct smp_cmd_pairing cp; build_pairing_cmd(conn, &cp, NULL, authreq); From dbbfa2ab7a2a508fd6ad2ce08f7f34c3041f51ef Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Wed, 25 Jun 2014 16:44:45 -0300 Subject: [PATCH 072/378] Bluetooth: Use macro instead of hard-coded value This patch replaces the hard-coded value in hci_bdaddr_is_rpa() helper by the corresponding macro ADDR_LE_DEV_RANDOM. Signed-off-by: Andre Guedes Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 0b0597d81827..dda7f00c07c5 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1155,7 +1155,7 @@ static inline bool eir_has_data_type(u8 *data, size_t data_len, u8 type) static inline bool hci_bdaddr_is_rpa(bdaddr_t *bdaddr, u8 addr_type) { - if (addr_type != 0x01) + if (addr_type != ADDR_LE_DEV_RANDOM) return false; if ((bdaddr->b[5] & 0xc0) == 0x40) From 31dd624e1cf937655a06fa4eeec06f4bafa34ab7 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 27 Jun 2014 14:23:02 +0300 Subject: [PATCH 073/378] Bluetooth: Fix missing hdev locking in smp_cmd_ident_addr_info The hdev lock must be held before calling into smp_distribute_keys. Also things such as hci_add_irk() require the lock. This patch fixes the issue by adding the necessary locking into the smp_cmd_ident_addr_info function. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 976fce2315fd..a38941593e8b 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -1076,6 +1076,8 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn, skb_pull(skb, sizeof(*info)); + hci_dev_lock(hcon->hdev); + /* Strictly speaking the Core Specification (4.1) allows sending * an empty address which would force us to rely on just the IRK * as "identity information". However, since such @@ -1085,8 +1087,7 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn, */ if (!bacmp(&info->bdaddr, BDADDR_ANY)) { BT_ERR("Ignoring IRK with no identity address"); - smp_distribute_keys(conn); - return 0; + goto distribute; } bacpy(&smp->id_addr, &info->bdaddr); @@ -1100,8 +1101,11 @@ static int smp_cmd_ident_addr_info(struct l2cap_conn *conn, smp->remote_irk = hci_add_irk(conn->hcon->hdev, &smp->id_addr, smp->id_addr_type, smp->irk, &rpa); +distribute: smp_distribute_keys(conn); + hci_dev_unlock(hcon->hdev); + return 0; } From 6a7bd103c8a4286ef6f7134bfe6f104f32f2c4d4 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 27 Jun 2014 14:23:03 +0300 Subject: [PATCH 074/378] Bluetooth: Add dedicated AES instance for each SMP context Many places have to be extra careful to not hold the hdev lock when calling into the SMP code. This is because the SMP crypto functions use the crypto handle that's part of the hci_dev struct. Giving the SMP context its own handle helps simplifying the locking logic and removes the risk for deadlocks. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index a38941593e8b..39ca9616d2de 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -62,6 +62,8 @@ struct smp_chan { struct smp_ltk *slave_ltk; struct smp_irk *remote_irk; unsigned long flags; + + struct crypto_blkcipher *tfm_aes; }; static inline void swap_buf(const u8 *src, u8 *dst, size_t len) @@ -583,6 +585,13 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) if (!smp) return NULL; + smp->tfm_aes = crypto_alloc_blkcipher("ecb(aes)", 0, CRYPTO_ALG_ASYNC); + if (IS_ERR(smp->tfm_aes)) { + BT_ERR("Unable to create ECB crypto context"); + kfree(smp); + return NULL; + } + smp->conn = conn; conn->smp_chan = smp; conn->hcon->smp_conn = conn; @@ -605,6 +614,8 @@ void smp_chan_destroy(struct l2cap_conn *conn) kfree(smp->csrk); kfree(smp->slave_csrk); + crypto_free_blkcipher(smp->tfm_aes); + /* If pairing failed clean up any keys we might have */ if (!complete) { if (smp->ltk) { From ec70f36f8b17dd21c0d64af4481aa3c898c1cec7 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 27 Jun 2014 14:23:04 +0300 Subject: [PATCH 075/378] Bluetooth: Update SMP crypto functions to take the SMP context Passing the full SMP context instead of just the crypto context lets us use the crypto handle from the context which in turn removes the need to lock the hci_dev. Passing the SMP context instead of just the crypto handle allows a bit more detailed logging which is helpful in multi-adapter scenarios. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 48 ++++++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 29 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 39ca9616d2de..2566a3e43bb5 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -172,13 +172,16 @@ int smp_generate_rpa(struct crypto_blkcipher *tfm, u8 irk[16], bdaddr_t *rpa) return 0; } -static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16], - u8 preq[7], u8 pres[7], u8 _iat, bdaddr_t *ia, - u8 _rat, bdaddr_t *ra, u8 res[16]) +static int smp_c1(struct smp_chan *smp, u8 k[16], u8 r[16], u8 preq[7], + u8 pres[7], u8 _iat, bdaddr_t *ia, u8 _rat, bdaddr_t *ra, + u8 res[16]) { + struct hci_dev *hdev = smp->conn->hcon->hdev; u8 p1[16], p2[16]; int err; + BT_DBG("%s", hdev->name); + memset(p1, 0, 16); /* p1 = pres || preq || _rat || _iat */ @@ -196,7 +199,7 @@ static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16], u128_xor((u128 *) res, (u128 *) r, (u128 *) p1); /* res = e(k, res) */ - err = smp_e(tfm, k, res); + err = smp_e(smp->tfm_aes, k, res); if (err) { BT_ERR("Encrypt data error"); return err; @@ -206,23 +209,26 @@ static int smp_c1(struct crypto_blkcipher *tfm, u8 k[16], u8 r[16], u128_xor((u128 *) res, (u128 *) res, (u128 *) p2); /* res = e(k, res) */ - err = smp_e(tfm, k, res); + err = smp_e(smp->tfm_aes, k, res); if (err) BT_ERR("Encrypt data error"); return err; } -static int smp_s1(struct crypto_blkcipher *tfm, u8 k[16], u8 r1[16], - u8 r2[16], u8 _r[16]) +static int smp_s1(struct smp_chan *smp, u8 k[16], u8 r1[16], u8 r2[16], + u8 _r[16]) { + struct hci_dev *hdev = smp->conn->hcon->hdev; int err; + BT_DBG("%s", hdev->name); + /* Just least significant octets from r1 and r2 are considered */ memcpy(_r, r2, 8); memcpy(_r + 8, r1, 8); - err = smp_e(tfm, k, _r); + err = smp_e(smp->tfm_aes, k, _r); if (err) BT_ERR("Encrypt data error"); @@ -475,23 +481,15 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, static u8 smp_confirm(struct smp_chan *smp) { struct l2cap_conn *conn = smp->conn; - struct hci_dev *hdev = conn->hcon->hdev; - struct crypto_blkcipher *tfm = hdev->tfm_aes; struct smp_cmd_pairing_confirm cp; int ret; BT_DBG("conn %p", conn); - /* Prevent mutual access to hdev->tfm_aes */ - hci_dev_lock(hdev); - - ret = smp_c1(tfm, smp->tk, smp->prnd, smp->preq, smp->prsp, + ret = smp_c1(smp, smp->tk, smp->prnd, smp->preq, smp->prsp, conn->hcon->init_addr_type, &conn->hcon->init_addr, conn->hcon->resp_addr_type, &conn->hcon->resp_addr, cp.confirm_val); - - hci_dev_unlock(hdev); - if (ret) return SMP_UNSPECIFIED; @@ -506,25 +504,17 @@ static u8 smp_random(struct smp_chan *smp) { struct l2cap_conn *conn = smp->conn; struct hci_conn *hcon = conn->hcon; - struct hci_dev *hdev = hcon->hdev; - struct crypto_blkcipher *tfm = hdev->tfm_aes; u8 confirm[16]; int ret; - if (IS_ERR_OR_NULL(tfm)) + if (IS_ERR_OR_NULL(smp->tfm_aes)) return SMP_UNSPECIFIED; BT_DBG("conn %p %s", conn, conn->hcon->out ? "master" : "slave"); - /* Prevent mutual access to hdev->tfm_aes */ - hci_dev_lock(hdev); - - ret = smp_c1(tfm, smp->tk, smp->rrnd, smp->preq, smp->prsp, + ret = smp_c1(smp, smp->tk, smp->rrnd, smp->preq, smp->prsp, hcon->init_addr_type, &hcon->init_addr, hcon->resp_addr_type, &hcon->resp_addr, confirm); - - hci_dev_unlock(hdev); - if (ret) return SMP_UNSPECIFIED; @@ -538,7 +528,7 @@ static u8 smp_random(struct smp_chan *smp) __le64 rand = 0; __le16 ediv = 0; - smp_s1(tfm, smp->tk, smp->rrnd, smp->prnd, stk); + smp_s1(smp, smp->tk, smp->rrnd, smp->prnd, stk); memset(stk + smp->enc_key_size, 0, SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); @@ -556,7 +546,7 @@ static u8 smp_random(struct smp_chan *smp) smp_send_cmd(conn, SMP_CMD_PAIRING_RANDOM, sizeof(smp->prnd), smp->prnd); - smp_s1(tfm, smp->tk, smp->prnd, smp->rrnd, stk); + smp_s1(smp, smp->tk, smp->prnd, smp->rrnd, stk); memset(stk + smp->enc_key_size, 0, SMP_MAX_ENC_KEY_SIZE - smp->enc_key_size); From a9999348e70ebaf5ceaad8f23611d40a6db1e3bc Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 27 Jun 2014 14:23:05 +0300 Subject: [PATCH 076/378] Bluetooth: Remove unnecessary hci_dev_unlock for smp_user_confirm_reply Now that the SMP context has it's own crypto handle it doesn't need to lock the hci_dev anymore for most operations. This means that it is safe to call smp_user_confirm_reply with the lock already held. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f75a090cd7e4..d542f8af6a5d 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3052,14 +3052,7 @@ static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev, } if (addr->type == BDADDR_LE_PUBLIC || addr->type == BDADDR_LE_RANDOM) { - /* Continue with pairing via SMP. The hdev lock must be - * released as SMP may try to recquire it for crypto - * purposes. - */ - hci_dev_unlock(hdev); err = smp_user_confirm_reply(conn, mgmt_op, passkey); - hci_dev_lock(hdev); - if (!err) err = cmd_complete(sk, hdev->id, mgmt_op, MGMT_STATUS_SUCCESS, addr, From 642ac7745a45904d2a7c2463a3a3e60dc097be04 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 27 Jun 2014 14:23:06 +0300 Subject: [PATCH 077/378] Bluetooth: Fix missing check for SMP session in smp_user_confirm_reply The smp_user_confirm_reply() function is called whenever user space sends a user confirmation reply mgmt command. In case of a misbehaving user space, or if the SMP session was removed by the time the command comes it is important that we return an appropriate error and do not try to access the non-existent SMP context. This patch adds the appropriate check for the HCI_CONN_LE_SMP_PEND flag before proceeding further. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 2566a3e43bb5..641ce8b69d2a 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -638,7 +638,7 @@ int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey) BT_DBG(""); - if (!conn) + if (!conn || !test_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) return -ENOTCONN; smp = conn->smp_chan; From b10e8017bd9d02a3c7975c06d8fa2fc39df1731c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 27 Jun 2014 14:23:07 +0300 Subject: [PATCH 078/378] Bluetooth: Remove unnecessary hcon->smp_conn variable The smp_conn member of struct hci_conn was simply a pointer to the l2cap_conn object. Since we already have hcon->l2cap_data that points to the same thing there's no need to have this second variable. This patch removes it and changes the single place that was using it to use hcon->l2cap_data instead. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 - net/bluetooth/smp.c | 4 +--- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index dda7f00c07c5..ca2a99807615 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -409,7 +409,6 @@ struct hci_conn { struct hci_dev *hdev; void *l2cap_data; void *sco_data; - void *smp_conn; struct amp_mgr *amp_mgr; struct hci_conn *link; diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 641ce8b69d2a..414c5151aa46 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -584,7 +584,6 @@ static struct smp_chan *smp_chan_create(struct l2cap_conn *conn) smp->conn = conn; conn->smp_chan = smp; - conn->hcon->smp_conn = conn; hci_conn_hold(conn->hcon); @@ -626,13 +625,12 @@ void smp_chan_destroy(struct l2cap_conn *conn) kfree(smp); conn->smp_chan = NULL; - conn->hcon->smp_conn = NULL; hci_conn_drop(conn->hcon); } int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey) { - struct l2cap_conn *conn = hcon->smp_conn; + struct l2cap_conn *conn = hcon->l2cap_data; struct smp_chan *smp; u32 value; From a2b1976b8e0184635a1119f8511fc3e68902e429 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 27 Jun 2014 13:45:08 +0200 Subject: [PATCH 079/378] Bluetooth: Remove reason parameter from hci_amp_disconn function The hci_amp_disconn function is a local function and there is no need for a reason parameter. That one can be retrieved from the hci_conn object easily. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_conn.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 626160c37103..cc64fbe952fa 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -136,7 +136,7 @@ void hci_disconnect(struct hci_conn *conn, __u8 reason) hci_send_cmd(conn->hdev, HCI_OP_DISCONNECT, sizeof(cp), &cp); } -static void hci_amp_disconn(struct hci_conn *conn, __u8 reason) +static void hci_amp_disconn(struct hci_conn *conn) { struct hci_cp_disconn_phy_link cp; @@ -145,7 +145,7 @@ static void hci_amp_disconn(struct hci_conn *conn, __u8 reason) conn->state = BT_DISCONN; cp.phy_handle = HCI_PHY_HANDLE(conn->handle); - cp.reason = reason; + cp.reason = hci_proto_disconn_ind(conn); hci_send_cmd(conn->hdev, HCI_OP_DISCONN_PHY_LINK, sizeof(cp), &cp); } @@ -273,13 +273,14 @@ void hci_sco_setup(struct hci_conn *conn, __u8 status) static void hci_conn_disconnect(struct hci_conn *conn) { - __u8 reason = hci_proto_disconn_ind(conn); + __u8 reason; switch (conn->type) { case AMP_LINK: - hci_amp_disconn(conn, reason); + hci_amp_disconn(conn); break; default: + reason = hci_proto_disconn_ind(conn); hci_disconnect(conn, reason); break; } From 40051e4686d6fa8743a38933727604f75bef05cf Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 27 Jun 2014 13:45:09 +0200 Subject: [PATCH 080/378] Bluetooth: Remove unneeded hci_conn_disconnect abstraction The abstraction of disconnect operation via hci_conn_disconnect is not needed and it does not add any readability. Handle the difference of AMP physical channels and BR/EDR/LE connection in the timeout callback. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_conn.c | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index cc64fbe952fa..6d0fe3df2fa5 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -271,21 +271,6 @@ void hci_sco_setup(struct hci_conn *conn, __u8 status) } } -static void hci_conn_disconnect(struct hci_conn *conn) -{ - __u8 reason; - - switch (conn->type) { - case AMP_LINK: - hci_amp_disconn(conn); - break; - default: - reason = hci_proto_disconn_ind(conn); - hci_disconnect(conn, reason); - break; - } -} - static void hci_conn_timeout(struct work_struct *work) { struct hci_conn *conn = container_of(work, struct hci_conn, @@ -320,7 +305,12 @@ static void hci_conn_timeout(struct work_struct *work) break; case BT_CONFIG: case BT_CONNECTED: - hci_conn_disconnect(conn); + if (conn->type == AMP_LINK) { + hci_amp_disconn(conn); + } else { + __u8 reason = hci_proto_disconn_ind(conn); + hci_disconnect(conn, reason); + } break; default: conn->state = BT_CLOSED; From df935429be40b02568d4bcf9ebfacf8011b85a85 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 27 Jun 2014 14:32:16 +0200 Subject: [PATCH 081/378] Bluetooth: Send HCI_Read_Clock_Offset before disconnecting When the connection is in master role and it is going to be disconnected based on the disconnection timeout, then send the HCI_Read_Clock_Offset command in an attempt to update the clock offset value in the inquiry cache. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 5 +++++ net/bluetooth/hci_conn.c | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index cc2e88dd20ea..6933766e7215 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -533,6 +533,11 @@ struct hci_cp_read_remote_version { __le16 handle; } __packed; +#define HCI_OP_READ_CLOCK_OFFSET 0x041f +struct hci_cp_read_clock_offset { + __le16 handle; +} __packed; + #define HCI_OP_SETUP_SYNC_CONN 0x0428 struct hci_cp_setup_sync_conn { __le16 handle; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 6d0fe3df2fa5..8a0c7a0ac1b6 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -309,6 +309,25 @@ static void hci_conn_timeout(struct work_struct *work) hci_amp_disconn(conn); } else { __u8 reason = hci_proto_disconn_ind(conn); + + /* When we are master of an established connection + * and it enters the disconnect timeout, then go + * ahead and try to read the current clock offset. + * + * Processing of the result is done within the + * event handling and hci_clock_offset_evt function. + */ + if (conn->type == ACL_LINK && + test_bit(HCI_CONN_MASTER, &conn->flags)) { + struct hci_dev *hdev = conn->hdev; + struct hci_cp_read_clock_offset cp; + + cp.handle = cpu_to_le16(conn->handle); + + hci_send_cmd(hdev, HCI_OP_READ_CLOCK_OFFSET, + sizeof(cp), &cp); + } + hci_disconnect(conn, reason); } break; From 730f091b056524df2cb1c5f345f2d24e44236c19 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 28 Jun 2014 12:36:10 +0200 Subject: [PATCH 082/378] Bluetooth: Increment management interface revision This patch increments the management interface revision due to the changes with the debug key command and other fixes. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/mgmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d542f8af6a5d..d42c07d2a817 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -35,7 +35,7 @@ #include "smp.h" #define MGMT_VERSION 1 -#define MGMT_REVISION 6 +#define MGMT_REVISION 7 static const u16 mgmt_commands[] = { MGMT_OP_READ_INDEX_LIST, From fca20018e7b86a8716511c7681115baa47aca8e4 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 28 Jun 2014 17:54:05 +0300 Subject: [PATCH 083/378] Bluetooth: Use kzalloc instead of kmalloc for pending mgmt commands By using kzalloc we ensure that there are no struct members, such as the user_data pointer, left uninitialized. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d42c07d2a817..727ae15f9c36 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -984,7 +984,7 @@ static struct pending_cmd *mgmt_pending_add(struct sock *sk, u16 opcode, { struct pending_cmd *cmd; - cmd = kmalloc(sizeof(*cmd), GFP_KERNEL); + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); if (!cmd) return NULL; From 33f35721030185a2c5a1bb8afd4c3744709745b5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 28 Jun 2014 17:54:06 +0300 Subject: [PATCH 084/378] Bluetooth: Add tracking of local and piconet clock values This patch adds support for storing the local and piconet clock values from the HCI_Read_Clock command response to the hci_dev and hci_conn structs. This will be later used in another patch to implement support for the Get Clock Info mgmt command. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 12 ++++++++++ include/net/bluetooth/hci_core.h | 4 ++++ net/bluetooth/hci_event.c | 39 ++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 6933766e7215..58c5930dea60 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -1087,6 +1087,18 @@ struct hci_rp_read_rssi { __s8 rssi; } __packed; +#define HCI_OP_READ_CLOCK 0x1407 +struct hci_cp_read_clock { + __le16 handle; + __u8 which; +} __packed; +struct hci_rp_read_clock { + __u8 status; + __le16 handle; + __le32 clock; + __le16 accuracy; +} __packed; + #define HCI_OP_READ_LOCAL_AMP_INFO 0x1409 struct hci_rp_read_local_amp_info { __u8 status; diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ca2a99807615..0906990dedd8 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -207,6 +207,7 @@ struct hci_dev { __u16 conn_info_min_age; __u16 conn_info_max_age; __u8 ssp_debug_mode; + __u32 clock; __u16 devid_source; __u16 devid_vendor; @@ -388,6 +389,9 @@ struct hci_conn { __s8 max_tx_power; unsigned long flags; + __u32 clock; + __u16 clock_accuracy; + unsigned long conn_info_timestamp; __u8 remote_cap; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 7a23324eac39..315d615ca3f9 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -721,6 +721,41 @@ static void hci_cc_read_data_block_size(struct hci_dev *hdev, hdev->block_cnt, hdev->block_len); } +static void hci_cc_read_clock(struct hci_dev *hdev, struct sk_buff *skb) +{ + struct hci_rp_read_clock *rp = (void *) skb->data; + struct hci_cp_read_clock *cp; + struct hci_conn *conn; + + BT_DBG("%s", hdev->name); + + if (skb->len < sizeof(*rp)) + return; + + if (rp->status) + return; + + hci_dev_lock(hdev); + + cp = hci_sent_cmd_data(hdev, HCI_OP_READ_CLOCK); + if (!cp) + goto unlock; + + if (cp->which == 0x00) { + hdev->clock = le32_to_cpu(rp->clock); + goto unlock; + } + + conn = hci_conn_hash_lookup_handle(hdev, __le16_to_cpu(rp->handle)); + if (conn) { + conn->clock = le32_to_cpu(rp->clock); + conn->clock_accuracy = le16_to_cpu(rp->accuracy); + } + +unlock: + hci_dev_unlock(hdev); +} + static void hci_cc_read_local_amp_info(struct hci_dev *hdev, struct sk_buff *skb) { @@ -2597,6 +2632,10 @@ static void hci_cmd_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_cc_read_local_amp_info(hdev, skb); break; + case HCI_OP_READ_CLOCK: + hci_cc_read_clock(hdev, skb); + break; + case HCI_OP_READ_LOCAL_AMP_ASSOC: hci_cc_read_local_amp_assoc(hdev, skb); break; From 958684263d3efbc721fb2b86f94876893eb638d2 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sat, 28 Jun 2014 17:54:07 +0300 Subject: [PATCH 085/378] Bluetooth: Add support for Get Clock Info mgmt command This patch implements support for the Get Clock Information mgmt command. This is done by performing one or two HCI_Read_Clock commands and creating the response from the stored values in the hci_dev and hci_conn structs. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 12 +++ net/bluetooth/mgmt.c | 144 +++++++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index bcffc9ae0c89..3109dec13409 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -424,6 +424,18 @@ struct mgmt_rp_get_conn_info { __s8 max_tx_power; } __packed; +#define MGMT_OP_GET_CLOCK_INFO 0x0032 +struct mgmt_cp_get_clock_info { + struct mgmt_addr_info addr; +} __packed; +#define MGMT_GET_CLOCK_INFO_SIZE MGMT_ADDR_INFO_SIZE +struct mgmt_rp_get_clock_info { + struct mgmt_addr_info addr; + __le32 local_clock; + __le32 piconet_clock; + __le16 accuracy; +} __packed; + #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 727ae15f9c36..6faa4616cbfe 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -85,6 +85,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_SET_PRIVACY, MGMT_OP_LOAD_IRKS, MGMT_OP_GET_CONN_INFO, + MGMT_OP_GET_CLOCK_INFO, }; static const u16 mgmt_events[] = { @@ -571,6 +572,22 @@ static struct pending_cmd *mgmt_pending_find(u16 opcode, struct hci_dev *hdev) return NULL; } +static struct pending_cmd *mgmt_pending_find_data(u16 opcode, + struct hci_dev *hdev, + const void *data) +{ + struct 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; +} + static u8 create_scan_rsp_data(struct hci_dev *hdev, u8 *ptr) { u8 ad_len = 0; @@ -4820,6 +4837,132 @@ unlock: return err; } +static void get_clock_info_complete(struct hci_dev *hdev, u8 status) +{ + struct mgmt_cp_get_clock_info *cp; + struct mgmt_rp_get_clock_info rp; + struct hci_cp_read_clock *hci_cp; + struct pending_cmd *cmd; + struct hci_conn *conn; + + BT_DBG("%s status %u", hdev->name, status); + + hci_dev_lock(hdev); + + hci_cp = hci_sent_cmd_data(hdev, HCI_OP_READ_CLOCK); + if (!hci_cp) + goto unlock; + + if (hci_cp->which) { + u16 handle = __le16_to_cpu(hci_cp->handle); + conn = hci_conn_hash_lookup_handle(hdev, handle); + } else { + conn = NULL; + } + + cmd = mgmt_pending_find_data(MGMT_OP_GET_CLOCK_INFO, hdev, conn); + if (!cmd) + goto unlock; + + cp = cmd->param; + + memset(&rp, 0, sizeof(rp)); + memcpy(&rp.addr, &cp->addr, sizeof(rp.addr)); + + if (status) + goto send_rsp; + + rp.local_clock = cpu_to_le32(hdev->clock); + + if (conn) { + rp.piconet_clock = cpu_to_le32(conn->clock); + rp.accuracy = cpu_to_le16(conn->clock_accuracy); + } + +send_rsp: + cmd_complete(cmd->sk, cmd->index, cmd->opcode, mgmt_status(status), + &rp, sizeof(rp)); + mgmt_pending_remove(cmd); + if (conn) + hci_conn_drop(conn); + +unlock: + hci_dev_unlock(hdev); +} + +static int get_clock_info(struct sock *sk, struct hci_dev *hdev, void *data, + u16 len) +{ + struct mgmt_cp_get_clock_info *cp = data; + struct mgmt_rp_get_clock_info rp; + struct hci_cp_read_clock hci_cp; + struct pending_cmd *cmd; + struct hci_request req; + struct hci_conn *conn; + int err; + + BT_DBG("%s", hdev->name); + + memset(&rp, 0, sizeof(rp)); + bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); + rp.addr.type = cp->addr.type; + + if (cp->addr.type != BDADDR_BREDR) + return cmd_complete(sk, hdev->id, MGMT_OP_GET_CLOCK_INFO, + MGMT_STATUS_INVALID_PARAMS, + &rp, sizeof(rp)); + + hci_dev_lock(hdev); + + if (!hdev_is_powered(hdev)) { + err = cmd_complete(sk, hdev->id, MGMT_OP_GET_CLOCK_INFO, + MGMT_STATUS_NOT_POWERED, &rp, sizeof(rp)); + goto unlock; + } + + if (bacmp(&cp->addr.bdaddr, BDADDR_ANY)) { + conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, + &cp->addr.bdaddr); + if (!conn || conn->state != BT_CONNECTED) { + err = cmd_complete(sk, hdev->id, + MGMT_OP_GET_CLOCK_INFO, + MGMT_STATUS_NOT_CONNECTED, + &rp, sizeof(rp)); + goto unlock; + } + } else { + conn = NULL; + } + + cmd = mgmt_pending_add(sk, MGMT_OP_GET_CLOCK_INFO, hdev, data, len); + if (!cmd) { + err = -ENOMEM; + goto unlock; + } + + hci_req_init(&req, hdev); + + memset(&hci_cp, 0, sizeof(hci_cp)); + hci_req_add(&req, HCI_OP_READ_CLOCK, sizeof(hci_cp), &hci_cp); + + if (conn) { + hci_conn_hold(conn); + cmd->user_data = conn; + + hci_cp.handle = cpu_to_le16(conn->handle); + hci_cp.which = 0x01; /* Piconet clock */ + hci_req_add(&req, HCI_OP_READ_CLOCK, sizeof(hci_cp), &hci_cp); + } + + err = hci_req_run(&req, get_clock_info_complete); + if (err < 0) + mgmt_pending_remove(cmd); + +unlock: + hci_dev_unlock(hdev); + return err; +} + static const struct mgmt_handler { int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len); @@ -4876,6 +5019,7 @@ static const struct mgmt_handler { { set_privacy, false, MGMT_SET_PRIVACY_SIZE }, { load_irks, true, MGMT_LOAD_IRKS_SIZE }, { get_conn_info, false, MGMT_GET_CONN_INFO_SIZE }, + { get_clock_info, false, MGMT_GET_CLOCK_INFO_SIZE }, }; From fee746b0babf128a50ece050ee6e63003ebb5ae1 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 29 Jun 2014 12:13:05 +0200 Subject: [PATCH 086/378] Bluetooth: Restrict access for raw-only controllers Bluetooth controllers that are marked for raw-only usage can only be used with user channel access. Any other operation should be rejected. This simplifies the whole raw-only support since it now depends on the fact that the controller is marked with HCI_QUIRK_RAW_DEVICE and runtime raw access is restricted to user channel operation. The kernel internal processing of HCI commands and events is designed around the case that either the kernel has full control over the device or that the device is driven from userspace. This now makes a clear distinction between these two possible operation modes. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_conn.c | 7 ---- net/bluetooth/hci_core.c | 74 +++++++++++++++++++++++++++++++--------- net/bluetooth/hci_sock.c | 14 +++++--- net/bluetooth/mgmt.c | 6 +++- 4 files changed, 72 insertions(+), 29 deletions(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 8a0c7a0ac1b6..25ee27ddc882 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -346,9 +346,6 @@ static void hci_conn_idle(struct work_struct *work) BT_DBG("hcon %p mode %d", conn, conn->mode); - if (test_bit(HCI_RAW, &hdev->flags)) - return; - if (!lmp_sniff_capable(hdev) || !lmp_sniff_capable(conn)) return; @@ -539,7 +536,6 @@ struct hci_dev *hci_get_route(bdaddr_t *dst, bdaddr_t *src) list_for_each_entry(d, &hci_dev_list, list) { if (!test_bit(HCI_UP, &d->flags) || - test_bit(HCI_RAW, &d->flags) || test_bit(HCI_USER_CHANNEL, &d->dev_flags) || d->dev_type != HCI_BREDR) continue; @@ -1059,9 +1055,6 @@ void hci_conn_enter_active_mode(struct hci_conn *conn, __u8 force_active) BT_DBG("hcon %p mode %d", conn, conn->mode); - if (test_bit(HCI_RAW, &hdev->flags)) - return; - if (conn->mode != HCI_CM_SNIFF) goto timer; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 9852449ac104..50db0201213c 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2117,6 +2117,11 @@ int hci_inquiry(void __user *arg) goto done; } + if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) { + err = -EOPNOTSUPP; + goto done; + } + if (hdev->dev_type != HCI_BREDR) { err = -EOPNOTSUPP; goto done; @@ -2246,10 +2251,7 @@ static int hci_dev_do_open(struct hci_dev *hdev) ret = hdev->setup(hdev); if (!ret) { - if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) - set_bit(HCI_RAW, &hdev->flags); - - if (!test_bit(HCI_RAW, &hdev->flags) && + if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks) && !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) ret = __hci_init(hdev); } @@ -2286,7 +2288,7 @@ static int hci_dev_do_open(struct hci_dev *hdev) } hdev->close(hdev); - hdev->flags = 0; + hdev->flags &= BIT(HCI_RAW); } done: @@ -2305,6 +2307,21 @@ int hci_dev_open(__u16 dev) if (!hdev) return -ENODEV; + /* Devices that are marked for raw-only usage can only be powered + * up as user channel. Trying to bring them up as normal devices + * will result into a failure. Only user channel operation is + * possible. + * + * When this function is called for a user channel, the flag + * HCI_USER_CHANNEL will be set first before attempting to + * open the device. + */ + if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks) && + !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { + err = -EOPNOTSUPP; + goto done; + } + /* We need to ensure that no other power on/off work is pending * before proceeding to call hci_dev_do_open. This is * particularly important if the setup procedure has not yet @@ -2321,8 +2338,8 @@ int hci_dev_open(__u16 dev) err = hci_dev_do_open(hdev); +done: hci_dev_put(hdev); - return err; } @@ -2374,7 +2391,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) /* Reset device */ skb_queue_purge(&hdev->cmd_q); atomic_set(&hdev->cmd_cnt, 1); - if (!test_bit(HCI_RAW, &hdev->flags) && + if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks) && !test_bit(HCI_AUTO_OFF, &hdev->dev_flags) && test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks)) { set_bit(HCI_INIT, &hdev->flags); @@ -2405,7 +2422,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) hdev->close(hdev); /* Clear flags */ - hdev->flags = 0; + hdev->flags &= BIT(HCI_RAW); hdev->dev_flags &= ~HCI_PERSISTENT_MASK; if (!test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) { @@ -2474,6 +2491,11 @@ int hci_dev_reset(__u16 dev) goto done; } + if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) { + ret = -EOPNOTSUPP; + goto done; + } + /* Drop queues */ skb_queue_purge(&hdev->rx_q); skb_queue_purge(&hdev->cmd_q); @@ -2489,8 +2511,7 @@ int hci_dev_reset(__u16 dev) atomic_set(&hdev->cmd_cnt, 1); hdev->acl_cnt = 0; hdev->sco_cnt = 0; hdev->le_cnt = 0; - if (!test_bit(HCI_RAW, &hdev->flags)) - ret = __hci_req_sync(hdev, hci_reset_req, 0, HCI_INIT_TIMEOUT); + ret = __hci_req_sync(hdev, hci_reset_req, 0, HCI_INIT_TIMEOUT); done: hci_req_unlock(hdev); @@ -2512,6 +2533,11 @@ int hci_dev_reset_stat(__u16 dev) goto done; } + if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) { + ret = -EOPNOTSUPP; + goto done; + } + memset(&hdev->stat, 0, sizeof(struct hci_dev_stats)); done: @@ -2537,6 +2563,11 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) goto done; } + if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) { + err = -EOPNOTSUPP; + goto done; + } + if (hdev->dev_type != HCI_BREDR) { err = -EOPNOTSUPP; goto done; @@ -2760,8 +2791,10 @@ static void hci_power_on(struct work_struct *work) HCI_AUTO_OFF_TIMEOUT); } - if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags)) - mgmt_index_added(hdev); + if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags)) { + if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) + mgmt_index_added(hdev); + } } static void hci_power_off(struct work_struct *work) @@ -3887,6 +3920,13 @@ int hci_register_dev(struct hci_dev *hdev) list_add(&hdev->list, &hci_dev_list); write_unlock(&hci_dev_list_lock); + /* Devices that are marked for raw-only usage need to set + * the HCI_RAW flag to indicate that only user channel is + * supported. + */ + if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) + set_bit(HCI_RAW, &hdev->flags); + hci_notify(hdev, HCI_DEV_REG); hci_dev_hold(hdev); @@ -3929,7 +3969,8 @@ void hci_unregister_dev(struct hci_dev *hdev) cancel_work_sync(&hdev->power_on); if (!test_bit(HCI_INIT, &hdev->flags) && - !test_bit(HCI_SETUP, &hdev->dev_flags)) { + !test_bit(HCI_SETUP, &hdev->dev_flags) && + !test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) { hci_dev_lock(hdev); mgmt_index_removed(hdev); hci_dev_unlock(hdev); @@ -4694,7 +4735,7 @@ static inline int __get_blocks(struct hci_dev *hdev, struct sk_buff *skb) static void __check_timeout(struct hci_dev *hdev, unsigned int cnt) { - if (!test_bit(HCI_RAW, &hdev->flags)) { + if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) { /* ACL tx timeout must be longer than maximum * link supervision timeout (40.9 seconds) */ if (!cnt && time_after(jiffies, hdev->acl_last_tx + @@ -4877,7 +4918,7 @@ static void hci_sched_le(struct hci_dev *hdev) if (!hci_conn_num(hdev, LE_LINK)) return; - if (!test_bit(HCI_RAW, &hdev->flags)) { + if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) { /* LE tx timeout must be longer than maximum * link supervision timeout (40.9 seconds) */ if (!hdev->le_cnt && hdev->le_pkts && @@ -5122,8 +5163,7 @@ static void hci_rx_work(struct work_struct *work) hci_send_to_sock(hdev, skb); } - if (test_bit(HCI_RAW, &hdev->flags) || - test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { + if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { kfree_skb(skb); continue; } diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 80d25c150a65..54e4e8fd5d97 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -453,7 +453,8 @@ static int hci_sock_release(struct socket *sock) if (hdev) { if (hci_pi(sk)->channel == HCI_CHANNEL_USER) { - mgmt_index_added(hdev); + if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) + mgmt_index_added(hdev); clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags); hci_dev_close(hdev->id); } @@ -517,6 +518,9 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) return -EBUSY; + if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) + return -EOPNOTSUPP; + if (hdev->dev_type != HCI_BREDR) return -EOPNOTSUPP; @@ -702,12 +706,14 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, goto done; } - mgmt_index_removed(hdev); + if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) + mgmt_index_removed(hdev); err = hci_dev_open(hdev->id); if (err) { clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags); - mgmt_index_added(hdev); + if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) + mgmt_index_added(hdev); hci_dev_put(hdev); goto done; } @@ -960,7 +966,7 @@ static int hci_sock_sendmsg(struct kiocb *iocb, struct socket *sock, goto drop; } - if (test_bit(HCI_RAW, &hdev->flags) || (ogf == 0x3f)) { + if (ogf == 0x3f) { skb_queue_tail(&hdev->raw_q, skb); queue_work(hdev->workqueue, &hdev->tx_work); } else { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 6faa4616cbfe..41b1aec0c5dc 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -347,6 +347,9 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, if (test_bit(HCI_USER_CHANNEL, &d->dev_flags)) continue; + if (test_bit(HCI_QUIRK_RAW_DEVICE, &d->quirks)) + continue; + if (d->dev_type == HCI_BREDR) { rp->index[count++] = cpu_to_le16(d->id); BT_DBG("Added hci%u", d->id); @@ -5066,7 +5069,8 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) } if (test_bit(HCI_SETUP, &hdev->dev_flags) || - test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { + test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) || + test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) { err = cmd_status(sk, index, opcode, MGMT_STATUS_INVALID_INDEX); goto done; From 6ab535a777d76a2b1e5ad03119cd0c1e5a366b06 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 29 Jun 2014 12:20:15 +0200 Subject: [PATCH 087/378] Bluetooth: Don't use non-resolvable private address for passive scanning The usage of non-resovlable private addresses for passive scanning is a bad idea. Passive scanning will not send any SCAN_REQ and thus using your identity address for passive scanning is not a privacy issue. It is important to use the identity address during passive scanning since that is the only way devices using direct advertising will be reported correctly by the controller. This is overlooked detail in the Bluetooth specification that current controllers are not able to report direct advertising events for other than their current address. When remote peers are using direct advertising and scanning is done with non-resolvable private address these devices will not be found. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 50db0201213c..3ee2885dd9bc 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -5250,12 +5250,13 @@ void hci_req_add_le_passive_scan(struct hci_request *req) struct hci_dev *hdev = req->hdev; u8 own_addr_type; - /* Set require_privacy to true to avoid identification from - * unknown peer devices. Since this is passive scanning, no - * SCAN_REQ using the local identity should be sent. Mandating - * privacy is just an extra precaution. + /* Set require_privacy to false since no SCAN_REQ are send + * during passive scanning. Not using an unresolvable address + * here is important so that peer devices using direct + * advertising with our address will be correctly reported + * by the controller. */ - if (hci_update_random_address(req, true, &own_addr_type)) + if (hci_update_random_address(req, false, &own_addr_type)) return; memset(¶m_cp, 0, sizeof(param_cp)); From 4b10966f0f204d80f087f955344cbf6074a5cf86 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 29 Jun 2014 13:41:49 +0200 Subject: [PATCH 088/378] Bluetooth: Move hci_pend_le_conn_* functions to different location The hci_pend_le_conn_* function should be placed before their actual users. So move them before hci_conn_params_* functions. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 144 +++++++++++++++++++-------------------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 3ee2885dd9bc..ffee5f547506 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3414,6 +3414,78 @@ static bool is_identity_address(bdaddr_t *addr, u8 addr_type) return false; } +/* This function requires the caller holds hdev->lock */ +struct bdaddr_list *hci_pend_le_conn_lookup(struct hci_dev *hdev, + bdaddr_t *addr, u8 addr_type) +{ + struct bdaddr_list *entry; + + list_for_each_entry(entry, &hdev->pend_le_conns, list) { + if (bacmp(&entry->bdaddr, addr) == 0 && + entry->bdaddr_type == addr_type) + return entry; + } + + return NULL; +} + +/* This function requires the caller holds hdev->lock */ +void hci_pend_le_conn_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) +{ + struct bdaddr_list *entry; + + entry = hci_pend_le_conn_lookup(hdev, addr, addr_type); + if (entry) + goto done; + + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + if (!entry) { + BT_ERR("Out of memory"); + return; + } + + bacpy(&entry->bdaddr, addr); + entry->bdaddr_type = addr_type; + + list_add(&entry->list, &hdev->pend_le_conns); + + BT_DBG("addr %pMR (type %u)", addr, addr_type); + +done: + hci_update_background_scan(hdev); +} + +/* This function requires the caller holds hdev->lock */ +void hci_pend_le_conn_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) +{ + struct bdaddr_list *entry; + + entry = hci_pend_le_conn_lookup(hdev, addr, addr_type); + if (!entry) + goto done; + + list_del(&entry->list); + kfree(entry); + + BT_DBG("addr %pMR (type %u)", addr, addr_type); + +done: + hci_update_background_scan(hdev); +} + +/* This function requires the caller holds hdev->lock */ +void hci_pend_le_conns_clear(struct hci_dev *hdev) +{ + struct bdaddr_list *entry, *tmp; + + list_for_each_entry_safe(entry, tmp, &hdev->pend_le_conns, list) { + list_del(&entry->list); + kfree(entry); + } + + BT_DBG("All LE pending connections cleared"); +} + /* This function requires the caller holds hdev->lock */ int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, u8 auto_connect, u16 conn_min_interval, @@ -3492,78 +3564,6 @@ void hci_conn_params_clear(struct hci_dev *hdev) BT_DBG("All LE connection parameters were removed"); } -/* This function requires the caller holds hdev->lock */ -struct bdaddr_list *hci_pend_le_conn_lookup(struct hci_dev *hdev, - bdaddr_t *addr, u8 addr_type) -{ - struct bdaddr_list *entry; - - list_for_each_entry(entry, &hdev->pend_le_conns, list) { - if (bacmp(&entry->bdaddr, addr) == 0 && - entry->bdaddr_type == addr_type) - return entry; - } - - return NULL; -} - -/* This function requires the caller holds hdev->lock */ -void hci_pend_le_conn_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) -{ - struct bdaddr_list *entry; - - entry = hci_pend_le_conn_lookup(hdev, addr, addr_type); - if (entry) - goto done; - - entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) { - BT_ERR("Out of memory"); - return; - } - - bacpy(&entry->bdaddr, addr); - entry->bdaddr_type = addr_type; - - list_add(&entry->list, &hdev->pend_le_conns); - - BT_DBG("addr %pMR (type %u)", addr, addr_type); - -done: - hci_update_background_scan(hdev); -} - -/* This function requires the caller holds hdev->lock */ -void hci_pend_le_conn_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) -{ - struct bdaddr_list *entry; - - entry = hci_pend_le_conn_lookup(hdev, addr, addr_type); - if (!entry) - goto done; - - list_del(&entry->list); - kfree(entry); - - BT_DBG("addr %pMR (type %u)", addr, addr_type); - -done: - hci_update_background_scan(hdev); -} - -/* This function requires the caller holds hdev->lock */ -void hci_pend_le_conns_clear(struct hci_dev *hdev) -{ - struct bdaddr_list *entry, *tmp; - - list_for_each_entry_safe(entry, tmp, &hdev->pend_le_conns, list) { - list_del(&entry->list); - kfree(entry); - } - - BT_DBG("All LE pending connections cleared"); -} - static void inquiry_complete(struct hci_dev *hdev, u8 status) { if (status) { From 1089b67d8eb7fcdfae837a91aae9af94e329361c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 29 Jun 2014 13:41:50 +0200 Subject: [PATCH 089/378] Bluetooth: Clear pending connections from hci_conn_params_clear When hci_conn_params_clear is called, it is always followed by a call to hci_pend_le_conns_clear. So instead of making this explicit just make sure it is always called. This makes this function similar on how hci_conn_params_add and hci_conn_params_del work. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index ffee5f547506..8b206d0942aa 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -959,7 +959,6 @@ static ssize_t le_auto_conn_write(struct file *file, const char __user *data, } else if (memcmp(buf, "clr", 3) == 0) { hci_dev_lock(hdev); hci_conn_params_clear(hdev); - hci_pend_le_conns_clear(hdev); hci_update_background_scan(hdev); hci_dev_unlock(hdev); } else { @@ -3561,6 +3560,8 @@ void hci_conn_params_clear(struct hci_dev *hdev) kfree(params); } + hci_pend_le_conns_clear(hdev); + BT_DBG("All LE connection parameters were removed"); } @@ -4006,7 +4007,6 @@ void hci_unregister_dev(struct hci_dev *hdev) hci_remote_oob_data_clear(hdev); hci_white_list_clear(hdev); hci_conn_params_clear(hdev); - hci_pend_le_conns_clear(hdev); hci_dev_unlock(hdev); hci_dev_put(hdev); From 1c1697c0cad41fa468e3cb6061ce74debb4f3733 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 29 Jun 2014 13:41:51 +0200 Subject: [PATCH 090/378] Bluetooth: Update background scanning from hci_conn_params_clear When calling hci_conn_params_clear function, it should update the background scanning properly and not require a separate call to update it. For the case when the function is used during unregister of a controller, an extra safe guard is but in place. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 8b206d0942aa..c566b57610c9 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -959,7 +959,6 @@ static ssize_t le_auto_conn_write(struct file *file, const char __user *data, } else if (memcmp(buf, "clr", 3) == 0) { hci_dev_lock(hdev); hci_conn_params_clear(hdev); - hci_update_background_scan(hdev); hci_dev_unlock(hdev); } else { err = -EINVAL; @@ -3483,6 +3482,8 @@ void hci_pend_le_conns_clear(struct hci_dev *hdev) } BT_DBG("All LE pending connections cleared"); + + hci_update_background_scan(hdev); } /* This function requires the caller holds hdev->lock */ @@ -5293,6 +5294,9 @@ void hci_update_background_scan(struct hci_dev *hdev) struct hci_conn *conn; int err; + if (test_bit(HCI_UNREGISTER, &hdev->dev_flags)) + return; + hci_req_init(&req, hdev); if (list_empty(&hdev->pend_le_conns)) { From f044eb0524a02ea7f921c9234fbdba43290da1e2 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 29 Jun 2014 16:43:26 +0200 Subject: [PATCH 091/378] Bluetooth: Store latency and supervision timeout in connection params When the slave updates the connection parameters, store also the connection latency and supervision timeout information in the internal list of connection parameters for known devices. Having these values available allowes the auto-connection procedure to use the correct values from the beginning without having to request an update on every connection establishment. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_conn.c | 16 ++++++++++++++-- net/bluetooth/hci_core.c | 2 ++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 0906990dedd8..182044824495 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -439,6 +439,8 @@ struct hci_conn_params { u16 conn_min_interval; u16 conn_max_interval; + u16 conn_latency; + u16 supervision_timeout; enum { HCI_AUTO_CONN_DISABLED, diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 25ee27ddc882..adb413d77637 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -216,11 +216,23 @@ bool hci_setup_sync(struct hci_conn *conn, __u16 handle) void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, u16 to_multiplier) { - struct hci_cp_le_conn_update cp; struct hci_dev *hdev = conn->hdev; + struct hci_conn_params *params; + struct hci_cp_le_conn_update cp; + + hci_dev_lock(hdev); + + params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); + if (params) { + params->conn_min_interval = min; + params->conn_max_interval = max; + params->conn_latency = latency; + params->supervision_timeout = to_multiplier; + } + + hci_dev_unlock(hdev); memset(&cp, 0, sizeof(cp)); - cp.handle = cpu_to_le16(conn->handle); cp.conn_interval_min = cpu_to_le16(min); cp.conn_interval_max = cpu_to_le16(max); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index c566b57610c9..97a6453bee30 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3514,6 +3514,8 @@ int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, update: params->conn_min_interval = conn_min_interval; params->conn_max_interval = conn_max_interval; + params->conn_latency = 0x0000; + params->supervision_timeout = 0x002a; params->auto_connect = auto_connect; switch (auto_connect) { From 037fc415bce5ac7604ac52216ec03d011f81e5b0 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 29 Jun 2014 16:43:27 +0200 Subject: [PATCH 092/378] Bluetooth: Use LE connection parameters if known When the LE connection parameters for connection latency and supervision timeout are known, then use then. If they are not know fallback to defaults. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_conn.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index adb413d77637..e7ee7267f846 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -645,7 +645,8 @@ static void hci_req_add_le_create_conn(struct hci_request *req, cp.own_address_type = own_addr_type; cp.conn_interval_min = cpu_to_le16(conn->le_conn_min_interval); cp.conn_interval_max = cpu_to_le16(conn->le_conn_max_interval); - cp.supervision_timeout = cpu_to_le16(0x002a); + cp.conn_latency = cpu_to_le16(conn->le_conn_latency); + cp.supervision_timeout = cpu_to_le16(conn->le_supv_timeout); cp.min_ce_len = cpu_to_le16(0x0000); cp.max_ce_len = cpu_to_le16(0x0000); @@ -767,9 +768,13 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, if (params) { conn->le_conn_min_interval = params->conn_min_interval; conn->le_conn_max_interval = params->conn_max_interval; + conn->le_conn_latency = params->conn_latency; + conn->le_supv_timeout = params->supervision_timeout; } else { conn->le_conn_min_interval = hdev->le_conn_min_interval; conn->le_conn_max_interval = hdev->le_conn_max_interval; + conn->le_conn_latency = 0x0000; + conn->le_supv_timeout = 0x002a; } /* If controller is scanning, we stop it since some controllers are From 2faade53e65f276cf1c30a885fb64808a083714e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 29 Jun 2014 19:44:03 +0200 Subject: [PATCH 093/378] Bluetooth: Add support for Add/Remove Device management commands This allows adding or removing devices from the background scanning list the kernel maintains. Device flagged for auto-connection will be automatically connected if they are found. The passive scanning required for auto-connection will be started and stopped on demand. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 13 +++++ net/bluetooth/mgmt.c | 99 +++++++++++++++++++++++++++++++++++- 2 files changed, 111 insertions(+), 1 deletion(-) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 3109dec13409..bc9b105f2b50 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -436,6 +436,19 @@ struct mgmt_rp_get_clock_info { __le16 accuracy; } __packed; +#define MGMT_OP_ADD_DEVICE 0x0033 +struct mgmt_cp_add_device { + struct mgmt_addr_info addr; + __u8 action; +} __packed; +#define MGMT_ADD_DEVICE_SIZE (MGMT_ADDR_INFO_SIZE + 1) + +#define MGMT_OP_REMOVE_DEVICE 0x0034 +struct mgmt_cp_remove_device { + struct mgmt_addr_info addr; +} __packed; +#define MGMT_REMOVE_DEVICE_SIZE MGMT_ADDR_INFO_SIZE + #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 41b1aec0c5dc..64b55c7881ee 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -86,6 +86,8 @@ static const u16 mgmt_commands[] = { MGMT_OP_LOAD_IRKS, MGMT_OP_GET_CONN_INFO, MGMT_OP_GET_CLOCK_INFO, + MGMT_OP_ADD_DEVICE, + MGMT_OP_REMOVE_DEVICE, }; static const u16 mgmt_events[] = { @@ -4966,6 +4968,100 @@ unlock: return err; } +static int add_device(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_cp_add_device *cp = data; + u8 auto_conn, addr_type; + int err; + + BT_DBG("%s", hdev->name); + + if (!bdaddr_type_is_le(cp->addr.type) || + !bacmp(&cp->addr.bdaddr, BDADDR_ANY)) + return cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &cp->addr, sizeof(cp->addr)); + + if (cp->action != 0x00 && cp->action != 0x01) + return cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &cp->addr, sizeof(cp->addr)); + + hci_dev_lock(hdev); + + if (cp->addr.type == BDADDR_LE_PUBLIC) + addr_type = ADDR_LE_DEV_PUBLIC; + else + addr_type = ADDR_LE_DEV_RANDOM; + + if (cp->action) + auto_conn = HCI_AUTO_CONN_ALWAYS; + else + auto_conn = HCI_AUTO_CONN_DISABLED; + + if (hci_conn_params_add(hdev, &cp->addr.bdaddr, addr_type, auto_conn, + hdev->le_conn_min_interval, + hdev->le_conn_max_interval) < 0) { + err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, + MGMT_STATUS_FAILED, + &cp->addr, sizeof(cp->addr)); + goto unlock; + } + + err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, + MGMT_STATUS_SUCCESS, &cp->addr, sizeof(cp->addr)); + +unlock: + hci_dev_unlock(hdev); + return err; +} + +static int remove_device(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_cp_remove_device *cp = data; + int err; + + BT_DBG("%s", hdev->name); + + hci_dev_lock(hdev); + + if (bacmp(&cp->addr.bdaddr, BDADDR_ANY)) { + u8 addr_type; + + if (!bdaddr_type_is_le(cp->addr.type)) { + err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &cp->addr, sizeof(cp->addr)); + goto unlock; + } + + if (cp->addr.type == BDADDR_LE_PUBLIC) + addr_type = ADDR_LE_DEV_PUBLIC; + else + addr_type = ADDR_LE_DEV_RANDOM; + + hci_conn_params_del(hdev, &cp->addr.bdaddr, addr_type); + } else { + if (cp->addr.type) { + err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &cp->addr, sizeof(cp->addr)); + goto unlock; + } + + hci_conn_params_clear(hdev); + } + + err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, + MGMT_STATUS_SUCCESS, &cp->addr, sizeof(cp->addr)); + +unlock: + hci_dev_unlock(hdev); + return err; +} + static const struct mgmt_handler { int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len); @@ -5023,9 +5119,10 @@ static const struct mgmt_handler { { load_irks, true, MGMT_LOAD_IRKS_SIZE }, { get_conn_info, false, MGMT_GET_CONN_INFO_SIZE }, { get_clock_info, false, MGMT_GET_CLOCK_INFO_SIZE }, + { add_device, false, MGMT_ADD_DEVICE_SIZE }, + { remove_device, false, MGMT_REMOVE_DEVICE_SIZE }, }; - int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) { void *buf; From 0b3c7d372b6a74531f1927a3962e41029e26d2d8 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 29 Jun 2014 16:15:49 +0200 Subject: [PATCH 094/378] Bluetooth: Replace le_auto_conn debugfs with device_list entry Since the auto-connection handling has gained offical management command support, remove the le_auto_conn debugfs entry. For debugging purposes replace it a simple device_list debugfs entry that allows listing of the current internal auto-connection list used for passive scanning. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 93 ++++------------------------------------ 1 file changed, 9 insertions(+), 84 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 97a6453bee30..8d972023196b 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -879,104 +879,29 @@ static int adv_channel_map_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(adv_channel_map_fops, adv_channel_map_get, adv_channel_map_set, "%llu\n"); -static int le_auto_conn_show(struct seq_file *sf, void *ptr) +static int device_list_show(struct seq_file *f, void *ptr) { - struct hci_dev *hdev = sf->private; + struct hci_dev *hdev = f->private; struct hci_conn_params *p; hci_dev_lock(hdev); - list_for_each_entry(p, &hdev->le_conn_params, list) { - seq_printf(sf, "%pMR %u %u\n", &p->addr, p->addr_type, + seq_printf(f, "%pMR %u %u\n", &p->addr, p->addr_type, p->auto_connect); } - hci_dev_unlock(hdev); return 0; } -static int le_auto_conn_open(struct inode *inode, struct file *file) +static int device_list_open(struct inode *inode, struct file *file) { - return single_open(file, le_auto_conn_show, inode->i_private); + return single_open(file, device_list_show, inode->i_private); } -static ssize_t le_auto_conn_write(struct file *file, const char __user *data, - size_t count, loff_t *offset) -{ - struct seq_file *sf = file->private_data; - struct hci_dev *hdev = sf->private; - u8 auto_connect = 0; - bdaddr_t addr; - u8 addr_type; - char *buf; - int err = 0; - int n; - - /* Don't allow partial write */ - if (*offset != 0) - return -EINVAL; - - if (count < 3) - return -EINVAL; - - buf = memdup_user(data, count); - if (IS_ERR(buf)) - return PTR_ERR(buf); - - if (memcmp(buf, "add", 3) == 0) { - n = sscanf(&buf[4], "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhu %hhu", - &addr.b[5], &addr.b[4], &addr.b[3], &addr.b[2], - &addr.b[1], &addr.b[0], &addr_type, - &auto_connect); - - if (n < 7) { - err = -EINVAL; - goto done; - } - - hci_dev_lock(hdev); - err = hci_conn_params_add(hdev, &addr, addr_type, auto_connect, - hdev->le_conn_min_interval, - hdev->le_conn_max_interval); - hci_dev_unlock(hdev); - - if (err) - goto done; - } else if (memcmp(buf, "del", 3) == 0) { - n = sscanf(&buf[4], "%hhx:%hhx:%hhx:%hhx:%hhx:%hhx %hhu", - &addr.b[5], &addr.b[4], &addr.b[3], &addr.b[2], - &addr.b[1], &addr.b[0], &addr_type); - - if (n < 7) { - err = -EINVAL; - goto done; - } - - hci_dev_lock(hdev); - hci_conn_params_del(hdev, &addr, addr_type); - hci_dev_unlock(hdev); - } else if (memcmp(buf, "clr", 3) == 0) { - hci_dev_lock(hdev); - hci_conn_params_clear(hdev); - hci_dev_unlock(hdev); - } else { - err = -EINVAL; - } - -done: - kfree(buf); - - if (err) - return err; - else - return count; -} - -static const struct file_operations le_auto_conn_fops = { - .open = le_auto_conn_open, +static const struct file_operations device_list_fops = { + .open = device_list_open, .read = seq_read, - .write = le_auto_conn_write, .llseek = seq_lseek, .release = single_release, }; @@ -1785,8 +1710,8 @@ static int __hci_init(struct hci_dev *hdev) hdev, &conn_max_interval_fops); debugfs_create_file("adv_channel_map", 0644, hdev->debugfs, hdev, &adv_channel_map_fops); - debugfs_create_file("le_auto_conn", 0644, hdev->debugfs, hdev, - &le_auto_conn_fops); + debugfs_create_file("device_list", 0444, hdev->debugfs, hdev, + &device_list_fops); debugfs_create_u16("discov_interleaved_timeout", 0644, hdev->debugfs, &hdev->discov_interleaved_timeout); From 8afef092a192cb946393bb11cc95b59739c1e57b Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 29 Jun 2014 22:28:34 +0200 Subject: [PATCH 095/378] Bluetooth: Add Device Added and Device Removed management events When devices are added or removed, then make sure that events are send out to all other clients so that the list of devices can be easily tracked. This is especially important when external clients are adding or removing devices within the auto-connection list. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 11 +++++++++++ net/bluetooth/mgmt.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index bc9b105f2b50..1f95ad4fce02 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -603,3 +603,14 @@ struct mgmt_ev_new_csrk { __u8 store_hint; struct mgmt_csrk_info key; } __packed; + +#define MGMT_EV_DEVICE_ADDED 0x001a +struct mgmt_ev_device_added { + struct mgmt_addr_info addr; + __u8 action; +} __packed; + +#define MGMT_EV_DEVICE_REMOVED 0x001b +struct mgmt_ev_device_removed { + struct mgmt_addr_info addr; +} __packed; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 64b55c7881ee..d83197f9e727 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -114,6 +114,8 @@ static const u16 mgmt_events[] = { MGMT_EV_PASSKEY_NOTIFY, MGMT_EV_NEW_IRK, MGMT_EV_NEW_CSRK, + MGMT_EV_DEVICE_ADDED, + MGMT_EV_DEVICE_REMOVED, }; #define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000) @@ -4968,6 +4970,18 @@ unlock: return err; } +static void device_added(struct sock *sk, struct hci_dev *hdev, + bdaddr_t *bdaddr, u8 type, u8 action) +{ + struct mgmt_ev_device_added ev; + + bacpy(&ev.addr.bdaddr, bdaddr); + ev.addr.type = type; + ev.action = action; + + mgmt_event(MGMT_EV_DEVICE_ADDED, hdev, &ev, sizeof(ev), sk); +} + static int add_device(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { @@ -5009,6 +5023,8 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, goto unlock; } + device_added(sk, hdev, &cp->addr.bdaddr, cp->addr.type, cp->action); + err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, MGMT_STATUS_SUCCESS, &cp->addr, sizeof(cp->addr)); @@ -5017,6 +5033,17 @@ unlock: return err; } +static void device_removed(struct sock *sk, struct hci_dev *hdev, + bdaddr_t *bdaddr, u8 type) +{ + struct mgmt_ev_device_removed ev; + + bacpy(&ev.addr.bdaddr, bdaddr); + ev.addr.type = type; + + mgmt_event(MGMT_EV_DEVICE_REMOVED, hdev, &ev, sizeof(ev), sk); +} + static int remove_device(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { @@ -5043,6 +5070,8 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, addr_type = ADDR_LE_DEV_RANDOM; hci_conn_params_del(hdev, &cp->addr.bdaddr, addr_type); + + device_removed(sk, hdev, &cp->addr.bdaddr, cp->addr.type); } else { if (cp->addr.type) { err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, From 04fb7d9066dd9173ef0d4ccea8fe3bb59bd94605 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 30 Jun 2014 12:34:36 +0200 Subject: [PATCH 096/378] Bluetooth: Provide defaults for LE connection latency and timeout Store the connection latency and supervision timeout default values with all the other controller defaults. And when needed use them for new connections. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_conn.c | 4 ++-- net/bluetooth/hci_core.c | 6 ++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 182044824495..ed842c7e5cf1 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -203,6 +203,8 @@ struct hci_dev { __u16 le_scan_window; __u16 le_conn_min_interval; __u16 le_conn_max_interval; + __u16 le_conn_latency; + __u16 le_supv_timeout; __u16 discov_interleaved_timeout; __u16 conn_info_min_age; __u16 conn_info_max_age; diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index e7ee7267f846..d00aaf976efc 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -773,8 +773,8 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, } else { conn->le_conn_min_interval = hdev->le_conn_min_interval; conn->le_conn_max_interval = hdev->le_conn_max_interval; - conn->le_conn_latency = 0x0000; - conn->le_supv_timeout = 0x002a; + conn->le_conn_latency = hdev->le_conn_latency; + conn->le_supv_timeout = hdev->le_supv_timeout; } /* If controller is scanning, we stop it since some controllers are diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 8d972023196b..94551c33c4c6 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3439,8 +3439,8 @@ int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, update: params->conn_min_interval = conn_min_interval; params->conn_max_interval = conn_max_interval; - params->conn_latency = 0x0000; - params->supervision_timeout = 0x002a; + params->conn_latency = hdev->le_conn_latency; + params->supervision_timeout = hdev->le_supv_timeout; params->auto_connect = auto_connect; switch (auto_connect) { @@ -3706,6 +3706,8 @@ struct hci_dev *hci_alloc_dev(void) hdev->le_scan_window = 0x0030; hdev->le_conn_min_interval = 0x0028; hdev->le_conn_max_interval = 0x0038; + hdev->le_conn_latency = 0x0000; + hdev->le_supv_timeout = 0x002a; hdev->rpa_timeout = HCI_DEFAULT_RPA_TIMEOUT; hdev->discov_interleaved_timeout = DISCOV_INTERLEAVED_TIMEOUT; From 816a93d10a2809f09ce81e9fef638380d68d2c6d Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 30 Jun 2014 12:34:37 +0200 Subject: [PATCH 097/378] Bluetooth: Expose default connection latency setting via debugfs The controller has a default value for the connection latency. Expose this via debugfs for testing purposes. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 94551c33c4c6..79d292cfb867 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -851,6 +851,34 @@ static int conn_max_interval_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(conn_max_interval_fops, conn_max_interval_get, conn_max_interval_set, "%llu\n"); +static int conn_latency_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val > 0x01f3) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->le_conn_latency = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int conn_latency_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->le_conn_latency; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(conn_latency_fops, conn_latency_get, + conn_latency_set, "%llu\n"); + static int adv_channel_map_set(void *data, u64 val) { struct hci_dev *hdev = data; @@ -1708,6 +1736,8 @@ static int __hci_init(struct hci_dev *hdev) hdev, &conn_min_interval_fops); debugfs_create_file("conn_max_interval", 0644, hdev->debugfs, hdev, &conn_max_interval_fops); + debugfs_create_file("conn_latency", 0644, hdev->debugfs, + hdev, &conn_latency_fops); debugfs_create_file("adv_channel_map", 0644, hdev->debugfs, hdev, &adv_channel_map_fops); debugfs_create_file("device_list", 0444, hdev->debugfs, hdev, From f1649577a6c20410335dbb4765e74e51fd5df585 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 30 Jun 2014 12:34:38 +0200 Subject: [PATCH 098/378] Bluetooth: Expose default supervision timeout setting via debugfs The controller has a default value for the supervision timeout. Expose this via debugfs for testing purposes. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 79d292cfb867..6c28687c9286 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -879,6 +879,34 @@ static int conn_latency_get(void *data, u64 *val) DEFINE_SIMPLE_ATTRIBUTE(conn_latency_fops, conn_latency_get, conn_latency_set, "%llu\n"); +static int supervision_timeout_set(void *data, u64 val) +{ + struct hci_dev *hdev = data; + + if (val < 0x000a || val > 0x0c80) + return -EINVAL; + + hci_dev_lock(hdev); + hdev->le_supv_timeout = val; + hci_dev_unlock(hdev); + + return 0; +} + +static int supervision_timeout_get(void *data, u64 *val) +{ + struct hci_dev *hdev = data; + + hci_dev_lock(hdev); + *val = hdev->le_supv_timeout; + hci_dev_unlock(hdev); + + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(supervision_timeout_fops, supervision_timeout_get, + supervision_timeout_set, "%llu\n"); + static int adv_channel_map_set(void *data, u64 val) { struct hci_dev *hdev = data; @@ -1738,6 +1766,8 @@ static int __hci_init(struct hci_dev *hdev) hdev, &conn_max_interval_fops); debugfs_create_file("conn_latency", 0644, hdev->debugfs, hdev, &conn_latency_fops); + debugfs_create_file("supervision_timeout", 0644, hdev->debugfs, + hdev, &supervision_timeout_fops); debugfs_create_file("adv_channel_map", 0644, hdev->debugfs, hdev, &adv_channel_map_fops); debugfs_create_file("device_list", 0444, hdev->debugfs, hdev, From bf5b3c8be07905c242bb7f751dcb890b94c22d93 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 30 Jun 2014 12:34:39 +0200 Subject: [PATCH 099/378] Bluetooth: Provide function to create and set connection parameters In some cases it is useful to not overwrite connection parametes and instead just create default ones if they don't exist. This function does exactly that. hci_conn_params_add will allow to create new default connection parameters. hci_conn_params_set will set the values and also create new parameters if they don't exist. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 3 ++- net/bluetooth/hci_core.c | 36 +++++++++++++++++++++++++++++++- net/bluetooth/mgmt.c | 5 ++++- 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ed842c7e5cf1..c0d2506e2019 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -852,7 +852,8 @@ int hci_white_list_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); -int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, +int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); +int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, u8 auto_connect, u16 conn_min_interval, u16 conn_max_interval); void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 6c28687c9286..adea7de95633 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3472,7 +3472,41 @@ void hci_pend_le_conns_clear(struct hci_dev *hdev) } /* This function requires the caller holds hdev->lock */ -int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, +int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) +{ + struct hci_conn_params *params; + + if (!is_identity_address(addr, addr_type)) + return -EINVAL; + + params = hci_conn_params_lookup(hdev, addr, addr_type); + if (params) + return 0; + + params = kzalloc(sizeof(*params), GFP_KERNEL); + if (!params) { + BT_ERR("Out of memory"); + return -ENOMEM; + } + + bacpy(¶ms->addr, addr); + params->addr_type = addr_type; + + list_add(¶ms->list, &hdev->le_conn_params); + + params->conn_min_interval = hdev->le_conn_min_interval; + params->conn_max_interval = hdev->le_conn_max_interval; + params->conn_latency = hdev->le_conn_latency; + params->supervision_timeout = hdev->le_supv_timeout; + params->auto_connect = HCI_AUTO_CONN_DISABLED; + + BT_DBG("addr %pMR (type %u)", addr, addr_type); + + return 0; +} + +/* This function requires the caller holds hdev->lock */ +int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, u8 auto_connect, u16 conn_min_interval, u16 conn_max_interval) { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d83197f9e727..e30d0ebb5018 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5014,7 +5014,10 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, else auto_conn = HCI_AUTO_CONN_DISABLED; - if (hci_conn_params_add(hdev, &cp->addr.bdaddr, addr_type, auto_conn, + /* If the connection parameters don't exist for this device, + * they will be created and configured with defaults. + */ + if (hci_conn_params_set(hdev, &cp->addr.bdaddr, addr_type, auto_conn, hdev->le_conn_min_interval, hdev->le_conn_max_interval) < 0) { err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, From 7c264b10006f3c10f7a9ef314a213f9784d9ca1f Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 30 Jun 2014 12:34:40 +0200 Subject: [PATCH 100/378] Bluetooth: Add default connection parameters before pairing When trying to pair a new Bluetooth Low Energy device, then make sure that the default connections parameters are in place before trying to establish the first connection to that device. With the connection parameters structure allocated, the slave preferred values can now easily be tracked and all future connections will use the correct values from that start decreasing connection establishment time. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/mgmt.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index e30d0ebb5018..6baba309f9e2 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2947,6 +2947,17 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, else addr_type = ADDR_LE_DEV_RANDOM; + /* When pairing a new device, it is expected to remember + * this device for future connections. Adding the connection + * parameter information ahead of time allows tracking + * of the slave preferred values and will speed up any + * further connection establishment. + * + * If connection parameters already exist, then they + * will be kept and this function does nothing. + */ + hci_conn_params_add(hdev, &cp->addr.bdaddr, addr_type); + conn = hci_connect_le(hdev, &cp->addr.bdaddr, addr_type, sec_level, auth_type); } From c20c02d5c8d76f39be461c25bf5de3a1dc96885e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 30 Jun 2014 16:04:12 +0200 Subject: [PATCH 101/378] Bluetooth: Start background scanning only when controller is ready When the controller is not active or in init/setup phase, do not try to start or stop background scanning. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index adea7de95633..2312e77582b0 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -5317,7 +5317,10 @@ void hci_update_background_scan(struct hci_dev *hdev) struct hci_conn *conn; int err; - if (test_bit(HCI_UNREGISTER, &hdev->dev_flags)) + if (!test_bit(HCI_UP, &hdev->flags) || + test_bit(HCI_INIT, &hdev->flags) || + test_bit(HCI_SETUP, &hdev->dev_flags) || + test_bit(HCI_UNREGISTER, &hdev->dev_flags)) return; hci_req_init(&req, hdev); From d4905f2453bd228d8ffc57f4e25bba0a8f52c805 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Wed, 25 Jun 2014 21:52:52 -0300 Subject: [PATCH 102/378] Bluetooth: Connection parameters check helper This patch renames l2cap_check_conn_param() to hci_check_conn_params() and moves it to hci_core.h so it can reused in others files. This helper will be reused in the next patch. Signed-off-by: Andre Guedes Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 21 +++++++++++++++++++++ net/bluetooth/l2cap_core.c | 23 +---------------------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index c0d2506e2019..ec830871b9b8 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1181,6 +1181,27 @@ static inline struct smp_irk *hci_get_irk(struct hci_dev *hdev, return hci_find_irk_by_rpa(hdev, bdaddr); } +static inline int hci_check_conn_params(u16 min, u16 max, u16 latency, + u16 to_multiplier) +{ + u16 max_latency; + + if (min > max || min < 6 || max > 3200) + return -EINVAL; + + if (to_multiplier < 10 || to_multiplier > 3200) + return -EINVAL; + + if (max >= to_multiplier * 8) + return -EINVAL; + + max_latency = (to_multiplier * 8 / max) - 1; + if (latency > 499 || latency > max_latency) + return -EINVAL; + + return 0; +} + int hci_register_cb(struct hci_cb *hcb); int hci_unregister_cb(struct hci_cb *hcb); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index d015aa190fdc..e203a5fdf874 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -5213,27 +5213,6 @@ static inline int l2cap_move_channel_confirm_rsp(struct l2cap_conn *conn, return 0; } -static inline int l2cap_check_conn_param(u16 min, u16 max, u16 latency, - u16 to_multiplier) -{ - u16 max_latency; - - if (min > max || min < 6 || max > 3200) - return -EINVAL; - - if (to_multiplier < 10 || to_multiplier > 3200) - return -EINVAL; - - if (max >= to_multiplier * 8) - return -EINVAL; - - max_latency = (to_multiplier * 8 / max) - 1; - if (latency > 499 || latency > max_latency) - return -EINVAL; - - return 0; -} - static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, struct l2cap_cmd_hdr *cmd, u16 cmd_len, u8 *data) @@ -5261,7 +5240,7 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, memset(&rsp, 0, sizeof(rsp)); - err = l2cap_check_conn_param(min, max, latency, to_multiplier); + err = hci_check_conn_params(min, max, latency, to_multiplier); if (err) rsp.result = cpu_to_le16(L2CAP_CONN_PARAM_REJECTED); else From 26b0f4e2f9d022193039a72db09c048b9ae93459 Mon Sep 17 00:00:00 2001 From: Fabian Frederick Date: Mon, 30 Jun 2014 19:26:23 +0200 Subject: [PATCH 103/378] Bluetooth: constify seq_operations bt_seq_ops is only used with __seq_open_private as const struct seq_operations * Signed-off-by: Fabian Frederick Signed-off-by: Marcel Holtmann --- net/bluetooth/af_bluetooth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c index 2021c481cdb6..4dca0299ed96 100644 --- a/net/bluetooth/af_bluetooth.c +++ b/net/bluetooth/af_bluetooth.c @@ -639,7 +639,7 @@ static int bt_seq_show(struct seq_file *seq, void *v) return 0; } -static struct seq_operations bt_seq_ops = { +static const struct seq_operations bt_seq_ops = { .start = bt_seq_start, .next = bt_seq_next, .stop = bt_seq_stop, From bf19d51b768ceeccab12f932cac73b60b1d20bab Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 1 Jul 2014 12:07:23 +0300 Subject: [PATCH 104/378] Bluetooth: Allow L2CAP getpeername() for BT_CONFIG state We have all the necessary remote information for getpeername() when we are in the BT_CONFIG state so this should be allowed. This is particularly important for LE sockets where changing the security level will temporarily move the socket into BT_CONFIG state. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap_sock.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/l2cap_sock.c b/net/bluetooth/l2cap_sock.c index bf72886de6ef..9bb4d1b3a483 100644 --- a/net/bluetooth/l2cap_sock.c +++ b/net/bluetooth/l2cap_sock.c @@ -361,7 +361,8 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, BT_DBG("sock %p, sk %p", sock, sk); if (peer && sk->sk_state != BT_CONNECTED && - sk->sk_state != BT_CONNECT && sk->sk_state != BT_CONNECT2) + sk->sk_state != BT_CONNECT && sk->sk_state != BT_CONNECT2 && + sk->sk_state != BT_CONFIG) return -ENOTCONN; memset(la, 0, sizeof(struct sockaddr_l2)); From 81218d2099e85542a57e266336db5e0585c9f2cc Mon Sep 17 00:00:00 2001 From: Nick Pelly Date: Mon, 30 Jun 2014 11:25:01 +0530 Subject: [PATCH 105/378] Bluetooth: Fallback to SCO on error code 0x10 (Connection Accept Timeout) This is to support the Motorola HF850 carkit which reports the error code 0x10 for an eSCO attempt, even though it advertises eSCO support. With this patch we will retry with a SCO connection, which succeeds. Signed-off-by: Nick Pelly Signed-off-by: Kiran Kumar Raparthy Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_event.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 315d615ca3f9..fe7a54b65e55 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3408,6 +3408,7 @@ static void hci_sync_conn_complete_evt(struct hci_dev *hdev, hci_conn_add_sysfs(conn); break; + case 0x10: /* Connection Accept Timeout */ case 0x0d: /* Connection Rejected due to Limited Resources */ case 0x11: /* Unsupported Feature or Parameter Value */ case 0x1c: /* SCO interval rejected */ From f81cd823a844c60349c40b53a1b84b5968113596 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 1 Jul 2014 10:59:24 +0200 Subject: [PATCH 106/378] Bluetooth: Use bool for smp_ltk_encrypt return value The return value of smp_ltk_encrypt is simple boolean, so just use bool and make the code a bit more readable. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/smp.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 414c5151aa46..35f57090ddd0 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -837,7 +837,7 @@ static u8 smp_cmd_pairing_random(struct l2cap_conn *conn, struct sk_buff *skb) return smp_random(smp); } -static u8 smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level) +static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level) { struct smp_ltk *key; struct hci_conn *hcon = conn->hcon; @@ -845,18 +845,18 @@ static u8 smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level) key = hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type, hcon->out); if (!key) - return 0; + return false; if (sec_level > BT_SECURITY_MEDIUM && !key->authenticated) - return 0; + return false; if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &hcon->flags)) - return 1; + return true; hci_le_start_enc(hcon, key->ediv, key->rand, key->val); hcon->enc_key_size = key->enc_size; - return 1; + return true; } static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) From 51d167c0972ef1496f2e6ab08aab602644d1f9bb Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 1 Jul 2014 12:11:04 +0200 Subject: [PATCH 107/378] Bluetooth: Change hci_conn_params_add to return the parameter struct When adding new connection parameters, it is useful to return either the existing struct or the newly created one. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 3 ++- net/bluetooth/hci_core.c | 11 ++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ec830871b9b8..5a83621672bf 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -852,7 +852,8 @@ int hci_white_list_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); -int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); +struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, + bdaddr_t *addr, u8 addr_type); int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, u8 auto_connect, u16 conn_min_interval, u16 conn_max_interval); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 2312e77582b0..883ddd52344d 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3472,21 +3472,22 @@ void hci_pend_le_conns_clear(struct hci_dev *hdev) } /* This function requires the caller holds hdev->lock */ -int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) +struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, + bdaddr_t *addr, u8 addr_type) { struct hci_conn_params *params; if (!is_identity_address(addr, addr_type)) - return -EINVAL; + return NULL; params = hci_conn_params_lookup(hdev, addr, addr_type); if (params) - return 0; + return params; params = kzalloc(sizeof(*params), GFP_KERNEL); if (!params) { BT_ERR("Out of memory"); - return -ENOMEM; + return NULL; } bacpy(¶ms->addr, addr); @@ -3502,7 +3503,7 @@ int hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) BT_DBG("addr %pMR (type %u)", addr, addr_type); - return 0; + return params; } /* This function requires the caller holds hdev->lock */ From 8c87aae1fa2ffa89e5e840b8e928fa0eb5c13157 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 1 Jul 2014 12:11:05 +0200 Subject: [PATCH 108/378] Bluetooth: Use hci_conn_params_add within hci_conn_params_set The hci_conn_params_add function provides the default allocation of connection parameters. To avoid code duplication, use that code from hci_conn_params_set to allocate or lookup parameter struct. As a benefit the connection latency and supervision timeout parameters are no longer reset to default when calling hci_conn_params_set. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 883ddd52344d..9ae945d8ad7e 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3513,29 +3513,12 @@ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, { struct hci_conn_params *params; - if (!is_identity_address(addr, addr_type)) - return -EINVAL; + params = hci_conn_params_add(hdev, addr, addr_type); + if (!params) + return -EIO; - params = hci_conn_params_lookup(hdev, addr, addr_type); - if (params) - goto update; - - params = kzalloc(sizeof(*params), GFP_KERNEL); - if (!params) { - BT_ERR("Out of memory"); - return -ENOMEM; - } - - bacpy(¶ms->addr, addr); - params->addr_type = addr_type; - - list_add(¶ms->list, &hdev->le_conn_params); - -update: params->conn_min_interval = conn_min_interval; params->conn_max_interval = conn_max_interval; - params->conn_latency = hdev->le_conn_latency; - params->supervision_timeout = hdev->le_supv_timeout; params->auto_connect = auto_connect; switch (auto_connect) { From d06b50ce14119acb04773a9808ccff5d1767b7e4 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 1 Jul 2014 12:11:06 +0200 Subject: [PATCH 109/378] Bluetooth: Remove connection interval parameters from hci_conn_params_set The connection interval parameter of hci_conn_params_set are always used with the controller defaults. So just let hci_conn_params_add set the controller default and not bother resetting them to controller defaults every time the hci_conn_params_set is called. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 3 +-- net/bluetooth/hci_core.c | 10 +++------- net/bluetooth/mgmt.c | 5 ++--- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 5a83621672bf..61d4d265f42d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -855,8 +855,7 @@ struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev, struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, - u8 auto_connect, u16 conn_min_interval, - u16 conn_max_interval); + u8 auto_connect); void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); void hci_conn_params_clear(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 9ae945d8ad7e..237963d5473c 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3508,8 +3508,7 @@ struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, /* This function requires the caller holds hdev->lock */ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, - u8 auto_connect, u16 conn_min_interval, - u16 conn_max_interval) + u8 auto_connect) { struct hci_conn_params *params; @@ -3517,8 +3516,6 @@ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, if (!params) return -EIO; - params->conn_min_interval = conn_min_interval; - params->conn_max_interval = conn_max_interval; params->auto_connect = auto_connect; switch (auto_connect) { @@ -3532,9 +3529,8 @@ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, break; } - BT_DBG("addr %pMR (type %u) auto_connect %u conn_min_interval 0x%.4x " - "conn_max_interval 0x%.4x", addr, addr_type, auto_connect, - conn_min_interval, conn_max_interval); + BT_DBG("addr %pMR (type %u) auto_connect %u", addr, addr_type, + auto_connect); return 0; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 6baba309f9e2..c6e9b551242b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5028,9 +5028,8 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, /* If the connection parameters don't exist for this device, * they will be created and configured with defaults. */ - if (hci_conn_params_set(hdev, &cp->addr.bdaddr, addr_type, auto_conn, - hdev->le_conn_min_interval, - hdev->le_conn_max_interval) < 0) { + if (hci_conn_params_set(hdev, &cp->addr.bdaddr, addr_type, + auto_conn) < 0) { err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, MGMT_STATUS_FAILED, &cp->addr, sizeof(cp->addr)); From 42bd6a56ed1ab4b2cb50f4d4e674874da9b47f46 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 1 Jul 2014 14:11:19 +0200 Subject: [PATCH 110/378] Bluetooth: Fix merge of advertising data and scan response data The advertising data and scan response data are merged in the wrong order. It should be advertsing data first and then scan response data and not the other way around. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg Cc: stable@vger.kernel.org # 3.16 --- net/bluetooth/hci_event.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index fe7a54b65e55..ea155183c1d6 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4270,8 +4270,8 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, * sending a merged device found event. */ mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK, - d->last_adv_addr_type, NULL, rssi, 0, 1, data, len, - d->last_adv_data, d->last_adv_data_len); + d->last_adv_addr_type, NULL, rssi, 0, 1, + d->last_adv_data, d->last_adv_data_len, data, len); clear_pending_adv_report(hdev); } From af58925ca6175695e502fa792f43a946f7474765 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 1 Jul 2014 14:11:20 +0200 Subject: [PATCH 111/378] Bluetooth: Provide flags parameter direct to mgmt_device_found Providing the flags parameter directly to mgmt_device_found function makes the core simpler and more readable. With this it becomes a lot easier to add new flags in the future. This also changes hci_inquiry_cache_update to just return that flags needed for mgmt_device_found since that is its only use for the two return parameters anyway. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 9 +++--- include/net/bluetooth/mgmt.h | 1 + net/bluetooth/hci_core.c | 24 ++++++++++------ net/bluetooth/hci_event.c | 48 ++++++++++++++++++-------------- net/bluetooth/mgmt.c | 10 ++----- 5 files changed, 50 insertions(+), 42 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 61d4d265f42d..ab3d4dda071c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -510,8 +510,8 @@ struct inquiry_entry *hci_inquiry_cache_lookup_resolve(struct hci_dev *hdev, int state); void hci_inquiry_cache_update_resolve(struct hci_dev *hdev, struct inquiry_entry *ie); -bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, - bool name_known, bool *ssp); +u32 hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, + bool name_known); void hci_inquiry_cache_flush(struct hci_dev *hdev); /* ----- HCI Connections ----- */ @@ -1318,9 +1318,8 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192, u8 *randomizer192, u8 *hash256, u8 *randomizer256, u8 status); void 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, u8 *scan_rsp, - u8 scan_rsp_len); + u8 addr_type, u8 *dev_class, s8 rssi, u32 flags, + u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len); void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, s8 rssi, u8 *name, u8 name_len); void mgmt_discovering(struct hci_dev *hdev, u8 discovering); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 1f95ad4fce02..2d88f361a016 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -547,6 +547,7 @@ struct mgmt_ev_auth_failed { #define MGMT_DEV_FOUND_CONFIRM_NAME 0x01 #define MGMT_DEV_FOUND_LEGACY_PAIRING 0x02 +#define MGMT_DEV_FOUND_NOT_CONNECTABLE 0x04 #define MGMT_EV_DEVICE_FOUND 0x0012 struct mgmt_ev_device_found { diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 237963d5473c..0aa392406629 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -35,6 +35,7 @@ #include #include #include +#include #include "smp.h" @@ -1970,22 +1971,24 @@ void hci_inquiry_cache_update_resolve(struct hci_dev *hdev, list_add(&ie->list, pos); } -bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, - bool name_known, bool *ssp) +u32 hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, + bool name_known) { struct discovery_state *cache = &hdev->discovery; struct inquiry_entry *ie; + u32 flags = 0; BT_DBG("cache %p, %pMR", cache, &data->bdaddr); hci_remove_remote_oob_data(hdev, &data->bdaddr); - *ssp = data->ssp_mode; + if (!data->ssp_mode) + flags |= MGMT_DEV_FOUND_LEGACY_PAIRING; ie = hci_inquiry_cache_lookup(hdev, &data->bdaddr); if (ie) { - if (ie->data.ssp_mode) - *ssp = true; + if (!ie->data.ssp_mode) + flags |= MGMT_DEV_FOUND_LEGACY_PAIRING; if (ie->name_state == NAME_NEEDED && data->rssi != ie->data.rssi) { @@ -1998,8 +2001,10 @@ bool hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, /* Entry not in the cache. Add new one. */ ie = kzalloc(sizeof(struct inquiry_entry), GFP_ATOMIC); - if (!ie) - return false; + if (!ie) { + flags |= MGMT_DEV_FOUND_CONFIRM_NAME; + goto done; + } list_add(&ie->all, &cache->all); @@ -2022,9 +2027,10 @@ update: cache->timestamp = jiffies; if (ie->name_state == NAME_NOT_KNOWN) - return false; + flags |= MGMT_DEV_FOUND_CONFIRM_NAME; - return true; +done: + return flags; } static int inquiry_cache_dump(struct hci_dev *hdev, int num, __u8 *buf) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index ea155183c1d6..a4854a5d9298 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1132,7 +1132,7 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK, d->last_adv_addr_type, NULL, - d->last_adv_rssi, 0, 1, + d->last_adv_rssi, 0, d->last_adv_data, d->last_adv_data_len, NULL, 0); } @@ -1965,7 +1965,7 @@ static void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); for (; num_rsp; num_rsp--, info++) { - bool name_known, ssp; + u32 flags; bacpy(&data.bdaddr, &info->bdaddr); data.pscan_rep_mode = info->pscan_rep_mode; @@ -1976,10 +1976,10 @@ static void hci_inquiry_result_evt(struct hci_dev *hdev, struct sk_buff *skb) data.rssi = 0x00; data.ssp_mode = 0x00; - name_known = hci_inquiry_cache_update(hdev, &data, false, &ssp); + flags = hci_inquiry_cache_update(hdev, &data, false); + mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, - info->dev_class, 0, !name_known, ssp, NULL, - 0, NULL, 0); + info->dev_class, 0, flags, NULL, 0, NULL, 0); } hci_dev_unlock(hdev); @@ -3257,7 +3257,6 @@ static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, { struct inquiry_data data; int num_rsp = *((__u8 *) skb->data); - bool name_known, ssp; BT_DBG("%s num_rsp %d", hdev->name, num_rsp); @@ -3274,6 +3273,8 @@ static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, info = (void *) (skb->data + 1); for (; num_rsp; num_rsp--, info++) { + u32 flags; + bacpy(&data.bdaddr, &info->bdaddr); data.pscan_rep_mode = info->pscan_rep_mode; data.pscan_period_mode = info->pscan_period_mode; @@ -3283,16 +3284,18 @@ static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, data.rssi = info->rssi; data.ssp_mode = 0x00; - name_known = hci_inquiry_cache_update(hdev, &data, - false, &ssp); + flags = hci_inquiry_cache_update(hdev, &data, false); + mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, info->rssi, - !name_known, ssp, NULL, 0, NULL, 0); + flags, NULL, 0, NULL, 0); } } else { struct inquiry_info_with_rssi *info = (void *) (skb->data + 1); for (; num_rsp; num_rsp--, info++) { + u32 flags; + bacpy(&data.bdaddr, &info->bdaddr); data.pscan_rep_mode = info->pscan_rep_mode; data.pscan_period_mode = info->pscan_period_mode; @@ -3301,11 +3304,12 @@ static void hci_inquiry_result_with_rssi_evt(struct hci_dev *hdev, data.clock_offset = info->clock_offset; data.rssi = info->rssi; data.ssp_mode = 0x00; - name_known = hci_inquiry_cache_update(hdev, &data, - false, &ssp); + + flags = hci_inquiry_cache_update(hdev, &data, false); + mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, info->dev_class, info->rssi, - !name_known, ssp, NULL, 0, NULL, 0); + flags, NULL, 0, NULL, 0); } } @@ -3472,7 +3476,8 @@ static void hci_extended_inquiry_result_evt(struct hci_dev *hdev, hci_dev_lock(hdev); for (; num_rsp; num_rsp--, info++) { - bool name_known, ssp; + u32 flags; + bool name_known; bacpy(&data.bdaddr, &info->bdaddr); data.pscan_rep_mode = info->pscan_rep_mode; @@ -3490,12 +3495,13 @@ static void hci_extended_inquiry_result_evt(struct hci_dev *hdev, else name_known = true; - name_known = hci_inquiry_cache_update(hdev, &data, name_known, - &ssp); + flags = hci_inquiry_cache_update(hdev, &data, name_known); + eir_len = eir_get_length(info->data, sizeof(info->data)); + mgmt_device_found(hdev, &info->bdaddr, ACL_LINK, 0x00, - info->dev_class, info->rssi, !name_known, - ssp, info->data, eir_len, NULL, 0); + info->dev_class, info->rssi, + flags, info->data, eir_len, NULL, 0); } hci_dev_unlock(hdev); @@ -4226,7 +4232,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, } mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL, - rssi, 0, 1, data, len, NULL, 0); + rssi, 0, data, len, NULL, 0); return; } @@ -4243,7 +4249,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, if (!match) mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK, d->last_adv_addr_type, NULL, - d->last_adv_rssi, 0, 1, + d->last_adv_rssi, 0, d->last_adv_data, d->last_adv_data_len, NULL, 0); @@ -4261,7 +4267,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, */ clear_pending_adv_report(hdev); mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL, - rssi, 0, 1, data, len, NULL, 0); + rssi, 0, data, len, NULL, 0); return; } @@ -4270,7 +4276,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, * sending a merged device found event. */ mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK, - d->last_adv_addr_type, NULL, rssi, 0, 1, + d->last_adv_addr_type, NULL, rssi, 0, d->last_adv_data, d->last_adv_data_len, data, len); clear_pending_adv_report(hdev); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index c6e9b551242b..336a2311bdca 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6233,9 +6233,8 @@ void mgmt_read_local_oob_data_complete(struct hci_dev *hdev, u8 *hash192, } void 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, u8 *scan_rsp, - u8 scan_rsp_len) + u8 addr_type, u8 *dev_class, s8 rssi, u32 flags, + u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len) { char buf[512]; struct mgmt_ev_device_found *ev = (void *) buf; @@ -6263,10 +6262,7 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, } ev->rssi = rssi; - if (cfm_name) - ev->flags |= cpu_to_le32(MGMT_DEV_FOUND_CONFIRM_NAME); - if (!ssp) - ev->flags |= cpu_to_le32(MGMT_DEV_FOUND_LEGACY_PAIRING); + ev->flags = cpu_to_le32(flags); if (eir_len > 0) memcpy(ev->eir, eir, eir_len); From c70a7e4cc8d22cb1ce684637ef8a4bb3a80d15b7 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 1 Jul 2014 14:11:21 +0200 Subject: [PATCH 112/378] Bluetooth: Add support for Not Connectable flag for Device Found events The Device Found events of the management interface should indicate if it is possible to connect to a remote device or if it is broadcaster only advertising. To allow this differentation the Not Connectable flag is introduced that will be set when it is known that a device can not be connected. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 40 +++++++++++++++++++++++++------- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ab3d4dda071c..eb0add396595 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -71,6 +71,7 @@ struct discovery_state { bdaddr_t last_adv_addr; u8 last_adv_addr_type; s8 last_adv_rssi; + u32 last_adv_flags; u8 last_adv_data[HCI_MAX_AD_LENGTH]; u8 last_adv_data_len; }; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a4854a5d9298..8097559ebb48 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1089,13 +1089,15 @@ static void clear_pending_adv_report(struct hci_dev *hdev) } static void store_pending_adv_report(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 bdaddr_type, s8 rssi, u8 *data, u8 len) + u8 bdaddr_type, s8 rssi, u32 flags, + u8 *data, u8 len) { struct discovery_state *d = &hdev->discovery; bacpy(&d->last_adv_addr, bdaddr); d->last_adv_addr_type = bdaddr_type; d->last_adv_rssi = rssi; + d->last_adv_flags = flags; memcpy(d->last_adv_data, data, len); d->last_adv_data_len = len; } @@ -1132,7 +1134,7 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK, d->last_adv_addr_type, NULL, - d->last_adv_rssi, 0, + d->last_adv_rssi, d->last_adv_flags, d->last_adv_data, d->last_adv_data_len, NULL, 0); } @@ -4209,6 +4211,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, { struct discovery_state *d = &hdev->discovery; bool match; + u32 flags; /* Passive scanning shouldn't trigger any device found events */ if (hdev->le_scan_type == LE_SCAN_PASSIVE) { @@ -4217,6 +4220,27 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, return; } + /* When receiving non-connectable or scannable undirected + * advertising reports, this means that the remote device is + * not connectable and then clearly indicate this in the + * device found event. + * + * When receiving a scan response, then there is no way to + * know if the remote device is connectable or not. However + * since scan responses are merged with a previously seen + * advertising report, the flags field from that report + * will be used. + * + * In the really unlikely case that a controller get confused + * and just sends a scan response event, then it is marked as + * not connectable as well. + */ + if (type == LE_ADV_NONCONN_IND || type == LE_ADV_SCAN_IND || + type == LE_ADV_SCAN_RSP) + flags = MGMT_DEV_FOUND_NOT_CONNECTABLE; + else + flags = 0; + /* If there's nothing pending either store the data from this * event or send an immediate device found event if the data * should not be stored for later. @@ -4227,12 +4251,12 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, */ if (type == LE_ADV_IND || type == LE_ADV_SCAN_IND) { store_pending_adv_report(hdev, bdaddr, bdaddr_type, - rssi, data, len); + rssi, flags, data, len); return; } mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL, - rssi, 0, data, len, NULL, 0); + rssi, flags, data, len, NULL, 0); return; } @@ -4249,7 +4273,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, if (!match) mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK, d->last_adv_addr_type, NULL, - d->last_adv_rssi, 0, + d->last_adv_rssi, d->last_adv_flags, d->last_adv_data, d->last_adv_data_len, NULL, 0); @@ -4258,7 +4282,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, */ if (type == LE_ADV_IND || type == LE_ADV_SCAN_IND) { store_pending_adv_report(hdev, bdaddr, bdaddr_type, - rssi, data, len); + rssi, flags, data, len); return; } @@ -4267,7 +4291,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, */ clear_pending_adv_report(hdev); mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL, - rssi, 0, data, len, NULL, 0); + rssi, flags, data, len, NULL, 0); return; } @@ -4276,7 +4300,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, * sending a merged device found event. */ mgmt_device_found(hdev, &d->last_adv_addr, LE_LINK, - d->last_adv_addr_type, NULL, rssi, 0, + d->last_adv_addr_type, NULL, rssi, d->last_adv_flags, d->last_adv_data, d->last_adv_data_len, data, len); clear_pending_adv_report(hdev); } From 854f47278fb36f4904649b994acf559e13920232 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 1 Jul 2014 18:40:20 +0300 Subject: [PATCH 113/378] Bluetooth: Fix redundant encryption when receiving Security Request If we're already encrypted with a good enough LTK we should just ignore an incoming SMP Security Request. The code was already taking care of this in the smp_conn_security function before calling smp_ltk_encrypt but failed to do the same in smp_cmd_security_req. This patch fixes the issue by moving up the smp_sufficient_security function and using it in the Security Request handler before trying to request encryption. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 35f57090ddd0..6ce7785a2708 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -859,6 +859,17 @@ static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level) return true; } +bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level) +{ + if (sec_level == BT_SECURITY_LOW) + return true; + + if (hcon->sec_level >= sec_level) + return true; + + return false; +} + static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_security_req *rp = (void *) skb->data; @@ -876,6 +887,9 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) return SMP_CMD_NOTSUPP; sec_level = authreq_to_seclevel(rp->auth_req); + if (smp_sufficient_security(hcon, sec_level)) + return 0; + if (sec_level > hcon->pending_sec_level) hcon->pending_sec_level = sec_level; @@ -904,17 +918,6 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) return 0; } -bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level) -{ - if (sec_level == BT_SECURITY_LOW) - return true; - - if (hcon->sec_level >= sec_level) - return true; - - return false; -} - int smp_conn_security(struct hci_conn *hcon, __u8 sec_level) { struct l2cap_conn *conn = hcon->l2cap_data; From fe59a05f941dbeb14316449be42d059761bed62c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 1 Jul 2014 19:14:12 +0300 Subject: [PATCH 114/378] Bluetooth: Add flag to track STK encryption There are certain subtle differences in behavior when we're encrypted with the STK, such as allowing re-encryption even though the security level stays the same. Because of this, add a flag to track whether we're encrypted with an STK or not. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 3 +++ net/bluetooth/smp.c | 4 ++++ 3 files changed, 8 insertions(+) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index eb0add396595..9078da681f16 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -536,6 +536,7 @@ enum { HCI_CONN_AUTH, HCI_CONN_SECURE, HCI_CONN_FIPS, + HCI_CONN_STK_ENCRYPT, }; static inline bool hci_conn_ssp_enabled(struct hci_conn *conn) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 8097559ebb48..b0b760dd66a3 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4365,8 +4365,11 @@ static void hci_le_ltk_request_evt(struct hci_dev *hdev, struct sk_buff *skb) * using a distributed LTK. */ if (ltk->type == SMP_STK) { + set_bit(HCI_CONN_STK_ENCRYPT, &conn->flags); list_del(<k->list); kfree(ltk); + } else { + clear_bit(HCI_CONN_STK_ENCRYPT, &conn->flags); } hci_dev_unlock(hdev); diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 6ce7785a2708..68e6f245581c 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -538,6 +538,7 @@ static u8 smp_random(struct smp_chan *smp) hci_le_start_enc(hcon, ediv, rand, stk); hcon->enc_key_size = smp->enc_key_size; + set_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags); } else { u8 stk[16], auth; __le64 rand = 0; @@ -856,6 +857,9 @@ static bool smp_ltk_encrypt(struct l2cap_conn *conn, u8 sec_level) hci_le_start_enc(hcon, key->ediv, key->rand, key->val); hcon->enc_key_size = key->enc_size; + /* We never store STKs for master role, so clear this flag */ + clear_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags); + return true; } From 9ab65d60c212c23f1605f35aea229f4c94df2334 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 1 Jul 2014 19:14:13 +0300 Subject: [PATCH 115/378] Bluetooth: Allow re-encryption with LTK when STK is in use If we're encrypted with the STK we should allow re-encryption with an LTK even though the achieved security level is the same. This patch adds the necessary logic to the smp_sufficient_security function which is used to determine whether to proceed with encryption or not. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 68e6f245581c..55c41de2f5a0 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -868,6 +868,14 @@ bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level) if (sec_level == BT_SECURITY_LOW) return true; + /* If we're encrypted with an STK always claim insufficient + * security. This way we allow the connection to be re-encrypted + * with an LTK, even if the LTK provides the same level of + * security. + */ + if (test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags)) + return false; + if (hcon->sec_level >= sec_level) return true; From b8221770c984bcd185c93cb3a31acab27b67c94a Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 1 Jul 2014 19:28:23 +0200 Subject: [PATCH 116/378] Bluetooth: Do not trigger background scanning when HCI_AUTO_OFF is set When a new controller is initialized, but not powered from userspace at the moment, the HCI_AUTO_OFF flag is still set. During this period, userspace might program device for auto-connection, but never power on the controller. In this case do not try to start background scanning and leave it for later to be started. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 0aa392406629..25ed6d3de410 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -5306,6 +5306,7 @@ void hci_update_background_scan(struct hci_dev *hdev) if (!test_bit(HCI_UP, &hdev->flags) || test_bit(HCI_INIT, &hdev->flags) || test_bit(HCI_SETUP, &hdev->dev_flags) || + test_bit(HCI_AUTO_OFF, &hdev->dev_flags) || test_bit(HCI_UNREGISTER, &hdev->dev_flags)) return; From c83ed19d23909db10a4402d4c6c0164bab9a9cf0 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Tue, 1 Jul 2014 19:28:24 +0200 Subject: [PATCH 117/378] Bluetooth: Ensure that background scanning gets enabled on power on The background scanning normally gets enabled during power on by adding devices to the pending connection list. However devices might be already on that list and the list of devices is empty, then it is better to trigger the background manually. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/mgmt.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 336a2311bdca..408468c07a8a 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5283,11 +5283,27 @@ void mgmt_index_removed(struct hci_dev *hdev) static void restart_le_auto_conns(struct hci_dev *hdev) { struct hci_conn_params *p; + bool added = false; list_for_each_entry(p, &hdev->le_conn_params, list) { - if (p->auto_connect == HCI_AUTO_CONN_ALWAYS) + if (p->auto_connect == HCI_AUTO_CONN_ALWAYS) { hci_pend_le_conn_add(hdev, &p->addr, p->addr_type); + added = true; + } } + + /* Calling hci_pend_le_conn_add will actually already trigger + * background scanning when needed. So no need to trigger it + * just another time. + * + * This check is here to avoid an unneeded restart of the + * passive scanning. Since this is during the controller + * power up phase the duplicate filtering is not an issue. + */ + if (added) + return; + + hci_update_background_scan(hdev); } static void powered_complete(struct hci_dev *hdev, u8 status) From 2a8357f2393d89a34b5a77051d29951af9646406 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 1 Jul 2014 22:09:47 +0300 Subject: [PATCH 118/378] Bluetooth: Fix redundant device (un)blocked events For the Block/Unblock Device mgmt commands we should only emit the Blocked/Unblocked events on any socket except for the one which received the command. The code was previously incorrectly trying to look up a non-existent pending command and thereby ending up not skipping the command socket for the event. We can simplify the code a lot by simply sending the event directly from the command handler functions. We have the reference to the command socket available there which makes it easy to pass to the mgmt_event function for skipping. The only notable side-effect of this is that the old blacklisting ioctl's no-longer cause mgmt events to be emitted, however as user space versions using these ioctl's are not mgmt-aware this is acceptable. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 -- net/bluetooth/hci_core.c | 4 +-- net/bluetooth/mgmt.c | 50 ++++++++++---------------------- 3 files changed, 18 insertions(+), 38 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 9078da681f16..3404f9bd2da0 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1325,8 +1325,6 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, u8 addr_type, s8 rssi, u8 *name, u8 name_len); void mgmt_discovering(struct hci_dev *hdev, u8 discovering); -int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); -int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent); void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk); void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 25ed6d3de410..72eb41424d04 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3275,7 +3275,7 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) list_add(&entry->list, &hdev->blacklist); - return mgmt_device_blocked(hdev, bdaddr, type); + return 0; } int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) @@ -3294,7 +3294,7 @@ int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) list_del(&entry->list); kfree(entry); - return mgmt_device_unblocked(hdev, bdaddr, type); + return 0; } struct bdaddr_list *hci_white_list_lookup(struct hci_dev *hdev, diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 408468c07a8a..ba5e215a7561 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3773,11 +3773,16 @@ static int block_device(struct sock *sk, struct hci_dev *hdev, void *data, hci_dev_lock(hdev); err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type); - if (err < 0) + if (err < 0) { status = MGMT_STATUS_FAILED; - else - status = MGMT_STATUS_SUCCESS; + goto done; + } + mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &cp->addr, sizeof(cp->addr), + sk); + status = MGMT_STATUS_SUCCESS; + +done: err = cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, status, &cp->addr, sizeof(cp->addr)); @@ -3803,11 +3808,16 @@ static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data, hci_dev_lock(hdev); err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type); - if (err < 0) + if (err < 0) { status = MGMT_STATUS_INVALID_PARAMS; - else - status = MGMT_STATUS_SUCCESS; + goto done; + } + mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &cp->addr, sizeof(cp->addr), + sk); + status = MGMT_STATUS_SUCCESS; + +done: err = cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, status, &cp->addr, sizeof(cp->addr)); @@ -6346,34 +6356,6 @@ void mgmt_discovering(struct hci_dev *hdev, u8 discovering) mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL); } -int mgmt_device_blocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) -{ - struct pending_cmd *cmd; - struct mgmt_ev_device_blocked ev; - - cmd = mgmt_pending_find(MGMT_OP_BLOCK_DEVICE, hdev); - - bacpy(&ev.addr.bdaddr, bdaddr); - ev.addr.type = type; - - return mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &ev, sizeof(ev), - cmd ? cmd->sk : NULL); -} - -int mgmt_device_unblocked(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) -{ - struct pending_cmd *cmd; - struct mgmt_ev_device_unblocked ev; - - cmd = mgmt_pending_find(MGMT_OP_UNBLOCK_DEVICE, hdev); - - bacpy(&ev.addr.bdaddr, bdaddr); - ev.addr.type = type; - - return mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &ev, sizeof(ev), - cmd ? cmd->sk : NULL); -} - static void adv_enable_complete(struct hci_dev *hdev, u8 status) { BT_DBG("%s status %u", hdev->name, status); From 4df82b5911c0e380d8b308958f158c3e7b365467 Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Mon, 31 Mar 2014 14:41:44 -0700 Subject: [PATCH 119/378] Bluetooth: btmrvl: indicate pscan scheduling instant in a debug event A vendor specific command is sent to firmware during initialization to enable this feature. This command is for SD8897 only. Signed-off-by: Bing Zhao Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btmrvl_drv.h | 1 + drivers/bluetooth/btmrvl_main.c | 19 +++++++++++++++++++ drivers/bluetooth/btmrvl_sdio.c | 5 +++++ drivers/bluetooth/btmrvl_sdio.h | 2 ++ 4 files changed, 27 insertions(+) diff --git a/drivers/bluetooth/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h index dc79f88f8717..4c313e73df83 100644 --- a/drivers/bluetooth/btmrvl_drv.h +++ b/drivers/bluetooth/btmrvl_drv.h @@ -89,6 +89,7 @@ struct btmrvl_private { #define MRVL_VENDOR_PKT 0xFE /* Vendor specific Bluetooth commands */ +#define BT_CMD_PSCAN_WIN_REPORT_ENABLE 0xFC03 #define BT_CMD_AUTO_SLEEP_MODE 0xFC23 #define BT_CMD_HOST_SLEEP_CONFIG 0xFC59 #define BT_CMD_HOST_SLEEP_ENABLE 0xFC5A diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index e9dbddb0b8f1..d35f2e189a6d 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -214,6 +214,23 @@ int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, u8 subcmd) } EXPORT_SYMBOL_GPL(btmrvl_send_module_cfg_cmd); +int btmrvl_pscan_window_reporting(struct btmrvl_private *priv, u8 subcmd) +{ + struct btmrvl_sdio_card *card = priv->btmrvl_dev.card; + int ret; + + if (!card->support_pscan_win_report) + return 0; + + ret = btmrvl_send_sync_cmd(priv, BT_CMD_PSCAN_WIN_REPORT_ENABLE, + &subcmd, 1); + if (ret) + BT_ERR("PSCAN_WIN_REPORT_ENABLE command failed: %#x", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(btmrvl_pscan_window_reporting); + int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv) { int ret; @@ -489,6 +506,8 @@ static int btmrvl_setup(struct hci_dev *hdev) btmrvl_cal_data_dt(priv); + btmrvl_pscan_window_reporting(priv, 0x01); + priv->btmrvl_dev.psmode = 1; btmrvl_enable_ps(priv); diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index 9dedca516ff5..efff06438b02 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -108,6 +108,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8688 = { .helper = "mrvl/sd8688_helper.bin", .firmware = "mrvl/sd8688.bin", .reg = &btmrvl_reg_8688, + .support_pscan_win_report = false, .sd_blksz_fw_dl = 64, }; @@ -115,6 +116,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8787 = { .helper = NULL, .firmware = "mrvl/sd8787_uapsta.bin", .reg = &btmrvl_reg_87xx, + .support_pscan_win_report = false, .sd_blksz_fw_dl = 256, }; @@ -122,6 +124,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8797 = { .helper = NULL, .firmware = "mrvl/sd8797_uapsta.bin", .reg = &btmrvl_reg_87xx, + .support_pscan_win_report = false, .sd_blksz_fw_dl = 256, }; @@ -129,6 +132,7 @@ static const struct btmrvl_sdio_device btmrvl_sdio_sd8897 = { .helper = NULL, .firmware = "mrvl/sd8897_uapsta.bin", .reg = &btmrvl_reg_88xx, + .support_pscan_win_report = true, .sd_blksz_fw_dl = 256, }; @@ -1067,6 +1071,7 @@ static int btmrvl_sdio_probe(struct sdio_func *func, card->firmware = data->firmware; card->reg = data->reg; card->sd_blksz_fw_dl = data->sd_blksz_fw_dl; + card->support_pscan_win_report = data->support_pscan_win_report; } if (btmrvl_sdio_register_dev(card) < 0) { diff --git a/drivers/bluetooth/btmrvl_sdio.h b/drivers/bluetooth/btmrvl_sdio.h index d4dd3b0fa53d..453559f98a75 100644 --- a/drivers/bluetooth/btmrvl_sdio.h +++ b/drivers/bluetooth/btmrvl_sdio.h @@ -89,6 +89,7 @@ struct btmrvl_sdio_card { const char *helper; const char *firmware; const struct btmrvl_sdio_card_reg *reg; + bool support_pscan_win_report; u16 sd_blksz_fw_dl; u8 rx_unit; struct btmrvl_private *priv; @@ -98,6 +99,7 @@ struct btmrvl_sdio_device { const char *helper; const char *firmware; const struct btmrvl_sdio_card_reg *reg; + const bool support_pscan_win_report; u16 sd_blksz_fw_dl; }; From 396e04f4bb9afefb0744715dc76d9abe18ee5fb0 Mon Sep 17 00:00:00 2001 From: Chin-Ran Lo Date: Tue, 1 Jul 2014 14:00:14 -0700 Subject: [PATCH 120/378] Bluetooth: btmrvl: wait for HOST_SLEEP_ENABLE event in suspend After BT_CMD_HOST_SLEEP_ENABLE command finishes, driver should wait until getting BT_EVENT_HOST_SLEEP_ENABLE event to complete suspend procedure. Without this patch the suspend handler would return success earlier. By the time when the BT_EVENT_HOST_SLEEP_ENABLE event comes in the controller driver could have already turned off the bus clock. This causes kernel crash or system reboot eventually. Cc: # 3.13+ Signed-off-by: Chin-Ran Lo Signed-off-by: Jeff CF Chen Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btmrvl_drv.h | 1 + drivers/bluetooth/btmrvl_main.c | 25 ++++++++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h index 4c313e73df83..a8a9d3380e59 100644 --- a/drivers/bluetooth/btmrvl_drv.h +++ b/drivers/bluetooth/btmrvl_drv.h @@ -68,6 +68,7 @@ struct btmrvl_adapter { u8 hs_state; u8 wakeup_tries; wait_queue_head_t cmd_wait_q; + wait_queue_head_t event_hs_wait_q; u8 cmd_complete; bool is_suspended; }; diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index d35f2e189a6d..cc65fd2fe856 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -114,6 +114,7 @@ int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb) adapter->hs_state = HS_ACTIVATED; if (adapter->psmode) adapter->ps_state = PS_SLEEP; + wake_up_interruptible(&adapter->event_hs_wait_q); BT_DBG("HS ACTIVATED!"); } else { BT_DBG("HS Enable failed"); @@ -270,11 +271,31 @@ EXPORT_SYMBOL_GPL(btmrvl_enable_ps); int btmrvl_enable_hs(struct btmrvl_private *priv) { + struct btmrvl_adapter *adapter = priv->adapter; int ret; ret = btmrvl_send_sync_cmd(priv, BT_CMD_HOST_SLEEP_ENABLE, NULL, 0); - if (ret) + if (ret) { BT_ERR("Host sleep enable command failed\n"); + return ret; + } + + ret = wait_event_interruptible_timeout(adapter->event_hs_wait_q, + adapter->hs_state, + msecs_to_jiffies(WAIT_UNTIL_HS_STATE_CHANGED)); + if (ret < 0) { + BT_ERR("event_hs_wait_q terminated (%d): %d,%d,%d", + ret, adapter->hs_state, adapter->ps_state, + adapter->wakeup_tries); + } else if (!ret) { + BT_ERR("hs_enable timeout: %d,%d,%d", adapter->hs_state, + adapter->ps_state, adapter->wakeup_tries); + ret = -ETIMEDOUT; + } else { + BT_DBG("host sleep enabled: %d,%d,%d", adapter->hs_state, + adapter->ps_state, adapter->wakeup_tries); + ret = 0; + } return ret; } @@ -375,6 +396,7 @@ static void btmrvl_init_adapter(struct btmrvl_private *priv) } init_waitqueue_head(&priv->adapter->cmd_wait_q); + init_waitqueue_head(&priv->adapter->event_hs_wait_q); } static void btmrvl_free_adapter(struct btmrvl_private *priv) @@ -685,6 +707,7 @@ int btmrvl_remove_card(struct btmrvl_private *priv) hdev = priv->btmrvl_dev.hcidev; wake_up_interruptible(&priv->adapter->cmd_wait_q); + wake_up_interruptible(&priv->adapter->event_hs_wait_q); kthread_stop(priv->main_thread.task); From 8e75b46a4f5d2c71b2f3ea632df1b15502514948 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Tue, 1 Jul 2014 18:10:08 -0300 Subject: [PATCH 121/378] Bluetooth: Connection Parameter Update Procedure This patch adds support for LE Connection Parameters Request Link Layer control procedure introduced in Core spec 4.1. This procedure allows a Peripheral or Central to update the Link Layer connection parameters of an established connection. Regarding the acceptance of connection parameters, the LL procedure follows the same approach of L2CAP procedure (see l2cap_conn_param_ update_req function). We accept any connection parameters values as long as they are within the valid range. Signed-off-by: Andre Guedes Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 28 +++++++++++++++++++++ net/bluetooth/hci_event.c | 50 +++++++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 58c5930dea60..95b754785a7d 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -355,6 +355,7 @@ enum { #define HCI_LK_AUTH_COMBINATION_P256 0x08 /* ---- HCI Error Codes ---- */ +#define HCI_ERROR_UNKNOWN_CONN_ID 0x02 #define HCI_ERROR_AUTH_FAILURE 0x05 #define HCI_ERROR_MEMORY_EXCEEDED 0x07 #define HCI_ERROR_CONNECTION_TIMEOUT 0x08 @@ -364,6 +365,7 @@ enum { #define HCI_ERROR_REMOTE_POWER_OFF 0x15 #define HCI_ERROR_LOCAL_HOST_TERM 0x16 #define HCI_ERROR_PAIRING_NOT_ALLOWED 0x18 +#define HCI_ERROR_INVALID_LL_PARAMS 0x1E #define HCI_ERROR_ADVERTISING_TIMEOUT 0x3c /* Flow control modes */ @@ -1305,6 +1307,23 @@ struct hci_rp_le_read_supported_states { __u8 le_states[8]; } __packed; +#define HCI_OP_LE_CONN_PARAM_REQ_REPLY 0x2020 +struct hci_cp_le_conn_param_req_reply { + __le16 handle; + __le16 interval_min; + __le16 interval_max; + __le16 latency; + __le16 timeout; + __le16 min_ce_len; + __le16 max_ce_len; +} __packed; + +#define HCI_OP_LE_CONN_PARAM_REQ_NEG_REPLY 0x2021 +struct hci_cp_le_conn_param_req_neg_reply { + __le16 handle; + __u8 reason; +} __packed; + /* ---- HCI Events ---- */ #define HCI_EV_INQUIRY_COMPLETE 0x01 @@ -1700,6 +1719,15 @@ struct hci_ev_le_ltk_req { __le16 ediv; } __packed; +#define HCI_EV_LE_REMOTE_CONN_PARAM_REQ 0x06 +struct hci_ev_le_remote_conn_param_req { + __le16 handle; + __le16 interval_min; + __le16 interval_max; + __le16 latency; + __le16 timeout; +} __packed; + /* Advertising report event types */ #define LE_ADV_IND 0x00 #define LE_ADV_DIRECT_IND 0x01 diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index b0b760dd66a3..544e2ef85d82 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4382,6 +4382,52 @@ not_found: hci_dev_unlock(hdev); } +static void send_conn_param_neg_reply(struct hci_dev *hdev, u16 handle, + u8 reason) +{ + struct hci_cp_le_conn_param_req_neg_reply cp; + + cp.handle = cpu_to_le16(handle); + cp.reason = reason; + + hci_send_cmd(hdev, HCI_OP_LE_CONN_PARAM_REQ_NEG_REPLY, sizeof(cp), + &cp); +} + +static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, + struct sk_buff *skb) +{ + struct hci_ev_le_remote_conn_param_req *ev = (void *) skb->data; + struct hci_cp_le_conn_param_req_reply cp; + struct hci_conn *hcon; + u16 handle, min, max, latency, timeout; + + handle = le16_to_cpu(ev->handle); + min = le16_to_cpu(ev->interval_min); + max = le16_to_cpu(ev->interval_max); + latency = le16_to_cpu(ev->latency); + timeout = le16_to_cpu(ev->timeout); + + hcon = hci_conn_hash_lookup_handle(hdev, handle); + if (!hcon || hcon->state != BT_CONNECTED) + return send_conn_param_neg_reply(hdev, handle, + HCI_ERROR_UNKNOWN_CONN_ID); + + if (hci_check_conn_params(min, max, latency, timeout)) + return send_conn_param_neg_reply(hdev, handle, + HCI_ERROR_INVALID_LL_PARAMS); + + cp.handle = ev->handle; + cp.interval_min = ev->interval_min; + cp.interval_max = ev->interval_max; + cp.latency = ev->latency; + cp.timeout = ev->timeout; + cp.min_ce_len = 0; + cp.max_ce_len = 0; + + hci_send_cmd(hdev, HCI_OP_LE_CONN_PARAM_REQ_REPLY, sizeof(cp), &cp); +} + static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_le_meta *le_ev = (void *) skb->data; @@ -4405,6 +4451,10 @@ static void hci_le_meta_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_le_ltk_request_evt(hdev, skb); break; + case HCI_EV_LE_REMOTE_CONN_PARAM_REQ: + hci_le_remote_conn_param_req_evt(hdev, skb); + break; + default: break; } From 9193c6e884ae1b403f53ca1370a4b7f8185c547b Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Tue, 1 Jul 2014 18:10:09 -0300 Subject: [PATCH 122/378] Bluetooth: Move LE event mask setting into init3 phase During init2 phase, the LE local features have not be read yet so we aren't able to rely on hdev->le_features to determine if the controller supports the Connection Parameters Request Procedure. For that reason, this patch moves LE event mask setting from init2 into init3 initialization phase. The hdev->le_features mask will be checked by the next patch in order to know if "LE Remote Connection Parameter Request Event" should be enabled. Signed-off-by: Andre Guedes Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_core.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 72eb41424d04..5788e031b869 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1437,13 +1437,6 @@ static void hci_setup_event_mask(struct hci_request *req) events[7] |= 0x20; /* LE Meta-Event */ hci_req_add(req, HCI_OP_SET_EVENT_MASK, sizeof(events), events); - - if (lmp_le_capable(hdev)) { - memset(events, 0, sizeof(events)); - events[0] = 0x1f; - hci_req_add(req, HCI_OP_LE_SET_EVENT_MASK, - sizeof(events), events); - } } static void hci_init2_req(struct hci_request *req, unsigned long opt) @@ -1613,8 +1606,16 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt) if (hdev->commands[5] & 0x10) hci_setup_link_policy(req); - if (lmp_le_capable(hdev)) + if (lmp_le_capable(hdev)) { + u8 events[8]; + + memset(events, 0, sizeof(events)); + events[0] = 0x1f; + hci_req_add(req, HCI_OP_LE_SET_EVENT_MASK, sizeof(events), + events); + hci_set_le_support(req); + } /* Read features beyond page 1 if available */ for (p = 2; p < HCI_MAX_PAGES && p <= hdev->max_page; p++) { From 662bc2e63de765bb701e3d3eca6af9fe553d72ac Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Tue, 1 Jul 2014 18:10:10 -0300 Subject: [PATCH 123/378] Bluetooth: Enable new LE meta event The Bluetooth 4.1 introduces a new LE meta event called "LE Remote Connection Parameter Request" event. In order to the controller sends this event to host, we should enable it during controller initialization. Signed-off-by: Andre Guedes Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 3 +++ net/bluetooth/hci_core.c | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 95b754785a7d..66358af6b1fc 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -307,6 +307,9 @@ enum { #define LMP_HOST_LE_BREDR 0x04 #define LMP_HOST_SC 0x08 +/* LE features */ +#define HCI_LE_CONN_PARAM_REQ_PROC 0x02 + /* Connection modes */ #define HCI_CM_ACTIVE 0x0000 #define HCI_CM_HOLD 0x0001 diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 5788e031b869..615d0cf5e511 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1611,6 +1611,15 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt) memset(events, 0, sizeof(events)); events[0] = 0x1f; + + /* If controller supports the Connection Parameters Request + * Link Layer Procedure, enable the corresponding event. + */ + if (hdev->le_features[0] & HCI_LE_CONN_PARAM_REQ_PROC) + events[0] |= 0x20; /* LE Remote Connection + * Parameter Request + */ + hci_req_add(req, HCI_OP_LE_SET_EVENT_MASK, sizeof(events), events); From ffb5a827d5ca5aef3f3fe5d64e42f3cf7fed4fc8 Mon Sep 17 00:00:00 2001 From: Andre Guedes Date: Tue, 1 Jul 2014 18:10:11 -0300 Subject: [PATCH 124/378] Bluetooth: Introduce "New Connection Parameter" Event This patch introduces a new Mgmt event called "New Connection Parameter". This event indicates to userspace the connection parameters values the remote device requested. The user may store these values and load them into kernel. This way, next time a connection is established to that device, the kernel will use those parameters values instead of the default ones. This event is sent when the remote device requests new connection parameters through connection parameter update procedure. This event is not sent for slave connections. Signed-off-by: Andre Guedes Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 3 +++ include/net/bluetooth/mgmt.h | 10 ++++++++++ net/bluetooth/hci_event.c | 4 ++++ net/bluetooth/l2cap_core.c | 6 +++++- net/bluetooth/mgmt.c | 19 +++++++++++++++++++ 5 files changed, 41 insertions(+), 1 deletion(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3404f9bd2da0..01fbbe20defb 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1329,6 +1329,9 @@ void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent); void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk); void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk, bool persistent); +void mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 bdaddr_type, u16 min_interval, u16 max_interval, + u16 latency, u16 timeout); void mgmt_reenable_advertising(struct hci_dev *hdev); void mgmt_smp_complete(struct hci_conn *conn, bool complete); diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 2d88f361a016..3c0f29614d1b 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -615,3 +615,13 @@ struct mgmt_ev_device_added { struct mgmt_ev_device_removed { struct mgmt_addr_info addr; } __packed; + +#define MGMT_EV_NEW_CONN_PARAM 0x001c +struct mgmt_ev_new_conn_param { + struct mgmt_addr_info addr; + __u8 store_hint; + __le16 min_interval; + __le16 max_interval; + __le16 latency; + __le16 timeout; +} __packed; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 544e2ef85d82..b9d16e0ed661 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4417,6 +4417,10 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, return send_conn_param_neg_reply(hdev, handle, HCI_ERROR_INVALID_LL_PARAMS); + if (test_bit(HCI_CONN_MASTER, &hcon->flags)) + mgmt_new_conn_param(hdev, &hcon->dst, hcon->dst_type, min, max, + latency, timeout); + cp.handle = ev->handle; cp.interval_min = ev->interval_min; cp.interval_max = ev->interval_max; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index e203a5fdf874..058b3b2b59b5 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -5249,8 +5249,12 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, l2cap_send_cmd(conn, cmd->ident, L2CAP_CONN_PARAM_UPDATE_RSP, sizeof(rsp), &rsp); - if (!err) + if (!err) { + mgmt_new_conn_param(hcon->hdev, &hcon->dst, hcon->dst_type, + min, max, latency, to_multiplier); + hci_le_conn_update(hcon, min, max, latency, to_multiplier); + } return 0; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ba5e215a7561..93cfefa260d5 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -116,6 +116,7 @@ static const u16 mgmt_events[] = { MGMT_EV_NEW_CSRK, MGMT_EV_DEVICE_ADDED, MGMT_EV_DEVICE_REMOVED, + MGMT_EV_NEW_CONN_PARAM, }; #define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000) @@ -5690,6 +5691,24 @@ void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk, mgmt_event(MGMT_EV_NEW_CSRK, hdev, &ev, sizeof(ev), NULL); } +void mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr, + u8 bdaddr_type, u16 min_interval, u16 max_interval, + u16 latency, u16 timeout) +{ + struct mgmt_ev_new_conn_param ev; + + memset(&ev, 0, sizeof(ev)); + bacpy(&ev.addr.bdaddr, bdaddr); + ev.addr.type = link_to_bdaddr(LE_LINK, bdaddr_type); + ev.store_hint = 0x00; + ev.min_interval = cpu_to_le16(min_interval); + ev.max_interval = cpu_to_le16(max_interval); + ev.latency = cpu_to_le16(latency); + ev.timeout = cpu_to_le16(timeout); + + mgmt_event(MGMT_EV_NEW_CONN_PARAM, hdev, &ev, sizeof(ev), NULL); +} + static inline u16 eir_append_data(u8 *eir, u16 eir_len, u8 type, u8 *data, u8 data_len) { From 24c457e27076beb2a85b4213642a6388eb88f240 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 2 Jul 2014 00:53:47 +0200 Subject: [PATCH 125/378] Bluetooth: Add support for hdev->set_bdaddr callback handling Some embedded controllers allow the programming of a public address and this adds vendor support for supporting OEM confguration of such addresses. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_core.c | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 01fbbe20defb..ee480a86e558 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -171,6 +171,7 @@ struct hci_dev { __u8 bus; __u8 dev_type; bdaddr_t bdaddr; + bdaddr_t public_addr; bdaddr_t random_addr; bdaddr_t static_addr; __u8 adv_addr_type; @@ -344,6 +345,7 @@ struct hci_dev { int (*setup)(struct hci_dev *hdev); int (*send)(struct hci_dev *hdev, struct sk_buff *skb); void (*notify)(struct hci_dev *hdev, unsigned int evt); + int (*set_bdaddr)(struct hci_dev *hdev, const bdaddr_t *bdaddr); }; #define HCI_PHY_HANDLE(handle) (handle & 0xff) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 615d0cf5e511..63197d70d8eb 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2249,6 +2249,17 @@ static int hci_dev_do_open(struct hci_dev *hdev) if (hdev->setup && test_bit(HCI_SETUP, &hdev->dev_flags)) ret = hdev->setup(hdev); + /* If public address change is configured, ensure that the + * address gets programmed. If the driver does not support + * changing the public address, fail the power on procedure. + */ + if (!ret && bacmp(&hdev->public_addr, BDADDR_ANY)) { + if (hdev->set_bdaddr) + ret = hdev->set_bdaddr(hdev, &hdev->public_addr); + else + ret = -EADDRNOTAVAIL; + } + if (!ret) { if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks) && !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) From abbaf50e3bccbb6c254c530ff1478acb56a6fed7 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 2 Jul 2014 00:53:48 +0200 Subject: [PATCH 126/378] Bluetooth: Add public address configration for Broadcom USB devices For the Broadcom based USB devices add support for configuration of the public device address. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/btusb.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 6250fc2fb93a..e0e39cc1af31 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -1530,6 +1530,23 @@ done: return ret; } +static int btusb_set_bdaddr_bcm(struct hci_dev *hdev, const bdaddr_t *bdaddr) +{ + struct sk_buff *skb; + long ret; + + skb = __hci_cmd_sync(hdev, 0xfc01, 6, bdaddr, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + ret = PTR_ERR(skb); + BT_ERR("%s: BCM: Change address command failed (%ld)", + hdev->name, ret); + return ret; + } + kfree_skb(skb); + + return 0; +} + static int btusb_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1635,8 +1652,10 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info & BTUSB_BCM92035) hdev->setup = btusb_setup_bcm92035; - if (id->driver_info & BTUSB_BCM_PATCHRAM) + if (id->driver_info & BTUSB_BCM_PATCHRAM) { hdev->setup = btusb_setup_bcm_patchram; + hdev->set_bdaddr = btusb_set_bdaddr_bcm; + } if (id->driver_info & BTUSB_INTEL) hdev->setup = btusb_setup_intel; From e493150e363917bf7e86e8fa4316e915fc2cf40b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 09:36:21 +0300 Subject: [PATCH 127/378] Bluetooth: Centralize looking up blocked devices to l2cap_recv_frame The ATT channel isn't the only one that we should ensure doesn't receive data from blocked devices. SMP is another, and in general we don't want data packets going to any of the various handlers. Therefore, add a single check to the l2cap_recv_frame function. The patch fixes at the same time the use of a correct address type. The blacklist stores the values with the user space facing triple type wheras hci_conn->dst_type uses the HCI address type (0x00 or 0x01). Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap_core.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 058b3b2b59b5..a6e276204ae9 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -6878,9 +6878,6 @@ static void l2cap_att_channel(struct l2cap_conn *conn, BT_DBG("chan %p, len %d", chan, skb->len); - if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, hcon->dst_type)) - goto drop; - if (chan->imtu < skb->len) goto drop; @@ -6913,6 +6910,12 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) return; } + if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, + bdaddr_type(hcon, hcon->dst_type))) { + kfree_skb(skb); + return; + } + BT_DBG("len %d, cid 0x%4.4x", len, cid); switch (cid) { From 837d502efc3fe9a088b943aa1a7279cee4d0e118 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 09:36:22 +0300 Subject: [PATCH 128/378] Bluetooth: Drop LE connections for blocked devices Unlike BR/EDR we cannot reject LE connections of blocked devices but have to do it as soon as we get a LE Connection Complete event. The patch adds a blacklist check to the hci_le_conn_complete_evt function and drops all connections for blocked devices. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_event.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index b9d16e0ed661..ed49a065dd67 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4038,6 +4038,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) struct hci_ev_le_conn_complete *ev = (void *) skb->data; struct hci_conn *conn; struct smp_irk *irk; + u8 addr_type; BT_DBG("%s status 0x%2.2x", hdev->name, ev->status); @@ -4119,6 +4120,17 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) conn->dst_type = irk->addr_type; } + if (conn->dst_type == ADDR_LE_DEV_PUBLIC) + addr_type = BDADDR_LE_PUBLIC; + else + addr_type = BDADDR_LE_RANDOM; + + /* Drop the connection if he device is blocked */ + if (hci_blacklist_lookup(hdev, &conn->dst, addr_type)) { + hci_conn_drop(conn); + goto unlock; + } + if (ev->status) { hci_le_conn_failed(conn, ev->status); goto unlock; From cb8d65973b57edeec53cd219ddbcb646cfd73452 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 2 Jul 2014 11:25:25 +0200 Subject: [PATCH 129/378] Bluetooth: Add public address configration for Intel USB devices For the Intel based USB devices add support for configuration of the public device address. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/btusb.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index e0e39cc1af31..66966b9c4244 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -1382,6 +1382,23 @@ exit_mfg_deactivate: return 0; } +static int btusb_set_bdaddr_intel(struct hci_dev *hdev, const bdaddr_t *bdaddr) +{ + struct sk_buff *skb; + long ret; + + skb = __hci_cmd_sync(hdev, 0xfc31, 6, bdaddr, HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + ret = PTR_ERR(skb); + BT_ERR("%s: changing Intel device address failed (%ld)", + hdev->name, ret); + return ret; + } + kfree_skb(skb); + + return 0; +} + static int btusb_setup_bcm_patchram(struct hci_dev *hdev) { struct btusb_data *data = hci_get_drvdata(hdev); @@ -1657,8 +1674,10 @@ static int btusb_probe(struct usb_interface *intf, hdev->set_bdaddr = btusb_set_bdaddr_bcm; } - if (id->driver_info & BTUSB_INTEL) + if (id->driver_info & BTUSB_INTEL) { hdev->setup = btusb_setup_intel; + hdev->set_bdaddr = btusb_set_bdaddr_intel; + } /* Interface numbers are hardcoded in the specification */ data->isoc = usb_ifnum_to_if(data->udev, 1); From 96c2103a57c5c6498138f38df926af6b86ea4e86 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 2 Jul 2014 11:30:51 +0200 Subject: [PATCH 130/378] Bluetooth: Default to internal use manufacturer identifier When allocating a new controller structure, then default to the internal use value 0xffff first. Default to 0x0000 is a bad idea since that is the manufacturer identifier of Ericsson Technology Licensing. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 63197d70d8eb..21a4c8dfb874 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3796,6 +3796,7 @@ struct hci_dev *hci_alloc_dev(void) hdev->link_mode = (HCI_LM_ACCEPT); hdev->num_iac = 0x01; /* One IAC support is mandatory */ hdev->io_capability = 0x03; /* No Input No Output */ + hdev->manufacturer = 0xffff; /* Default to internal use */ hdev->inq_tx_power = HCI_TX_POWER_INVALID; hdev->adv_tx_power = HCI_TX_POWER_INVALID; From 40cb0984150e118141a1b1a42f67f6d921ea8e09 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 2 Jul 2014 12:06:45 +0200 Subject: [PATCH 131/378] Bluetooth: Check for default address of Intel USB controllers Some Intel Bluetooth controllers come with a default address. If this address is found, print an error to warn the user about it. The controller is fully operational, but the danger of duplicate Bluetooth addresses might causes issues. At least with a clear error it becomes easier to debug these cases. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/btusb.c | 49 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 66966b9c4244..32aabea731f2 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -1182,6 +1182,49 @@ static int btusb_setup_intel_patching(struct hci_dev *hdev, return 0; } +#define BDADDR_INTEL (&(bdaddr_t) {{0x00, 0x8b, 0x9e, 0x19, 0x03, 0x00}}) + +static int btusb_check_bdaddr_intel(struct hci_dev *hdev) +{ + struct sk_buff *skb; + struct hci_rp_read_bd_addr *rp; + + skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + BT_ERR("%s reading Intel device address failed (%ld)", + hdev->name, PTR_ERR(skb)); + return PTR_ERR(skb); + } + + if (skb->len != sizeof(*rp)) { + BT_ERR("%s Intel device address length mismatch", hdev->name); + kfree_skb(skb); + return -EIO; + } + + rp = (struct hci_rp_read_bd_addr *) skb->data; + if (rp->status) { + BT_ERR("%s Intel device address result failed (%02x)", + hdev->name, rp->status); + kfree_skb(skb); + return -bt_to_errno(rp->status); + } + + /* For some Intel based controllers, the default Bluetooth device + * address 00:03:19:9E:8B:00 can be found. These controllers are + * fully operational, but have the danger of duplicate addresses + * and that in turn can cause problems with Bluetooth operation. + */ + if (!bacmp(&rp->bdaddr, BDADDR_INTEL)) + BT_ERR("%s found Intel default device address (%pMR)", + hdev->name, &rp->bdaddr); + + kfree_skb(skb); + + return 0; +} + static int btusb_setup_intel(struct hci_dev *hdev) { struct sk_buff *skb; @@ -1254,6 +1297,7 @@ static int btusb_setup_intel(struct hci_dev *hdev) BT_INFO("%s: Intel device is already patched. patch num: %02x", hdev->name, ver->fw_patch_num); kfree_skb(skb); + btusb_check_bdaddr_intel(hdev); return 0; } @@ -1266,6 +1310,7 @@ static int btusb_setup_intel(struct hci_dev *hdev) fw = btusb_setup_intel_get_fw(hdev, ver); if (!fw) { kfree_skb(skb); + btusb_check_bdaddr_intel(hdev); return 0; } fw_ptr = fw->data; @@ -1345,6 +1390,7 @@ static int btusb_setup_intel(struct hci_dev *hdev) BT_INFO("%s: Intel Bluetooth firmware patch completed and activated", hdev->name); + btusb_check_bdaddr_intel(hdev); return 0; exit_mfg_disable: @@ -1359,6 +1405,8 @@ exit_mfg_disable: kfree_skb(skb); BT_INFO("%s: Intel Bluetooth firmware patch completed", hdev->name); + + btusb_check_bdaddr_intel(hdev); return 0; exit_mfg_deactivate: @@ -1379,6 +1427,7 @@ exit_mfg_deactivate: BT_INFO("%s: Intel Bluetooth firmware patch completed and deactivated", hdev->name); + btusb_check_bdaddr_intel(hdev); return 0; } From c8abb73fb82b30ee736ada0f1ae50c9b4382713e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 2 Jul 2014 12:38:22 +0200 Subject: [PATCH 132/378] Bluetooth: Check for default address of Broadcom BCM20702A0 controllers The Broadcom BCM20702A0 USB controllers might come with the default address 00:20:70:02:A0:00 when booting up. If this happens, then warn about such address being used. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/btusb.c | 42 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 32aabea731f2..3244e311ca29 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -1448,6 +1448,8 @@ static int btusb_set_bdaddr_intel(struct hci_dev *hdev, const bdaddr_t *bdaddr) return 0; } +#define BDADDR_BCM20702A0 (&(bdaddr_t) {{0x00, 0xa0, 0x02, 0x70, 0x20, 0x00}}) + static int btusb_setup_bcm_patchram(struct hci_dev *hdev) { struct btusb_data *data = hci_get_drvdata(hdev); @@ -1461,6 +1463,7 @@ static int btusb_setup_bcm_patchram(struct hci_dev *hdev) u16 opcode; struct sk_buff *skb; struct hci_rp_read_local_version *ver; + struct hci_rp_read_bd_addr *bda; long ret; snprintf(fw_name, sizeof(fw_name), "brcm/%s-%04x-%04x.hcd", @@ -1470,8 +1473,7 @@ static int btusb_setup_bcm_patchram(struct hci_dev *hdev) ret = request_firmware(&fw, fw_name, &hdev->dev); if (ret < 0) { - BT_INFO("%s: BCM: patch %s not found", hdev->name, - fw_name); + BT_INFO("%s: BCM: patch %s not found", hdev->name, fw_name); return 0; } @@ -1590,6 +1592,42 @@ reset_fw: ver->lmp_ver, ver->lmp_subver); kfree_skb(skb); + /* Read BD Address */ + skb = __hci_cmd_sync(hdev, HCI_OP_READ_BD_ADDR, 0, NULL, + HCI_INIT_TIMEOUT); + if (IS_ERR(skb)) { + ret = PTR_ERR(skb); + BT_ERR("%s: HCI_OP_READ_BD_ADDR failed (%ld)", + hdev->name, ret); + goto done; + } + + if (skb->len != sizeof(*bda)) { + BT_ERR("%s: HCI_OP_READ_BD_ADDR event length mismatch", + hdev->name); + kfree_skb(skb); + ret = -EIO; + goto done; + } + + bda = (struct hci_rp_read_bd_addr *) skb->data; + if (bda->status) { + BT_ERR("%s: HCI_OP_READ_BD_ADDR error status (%02x)", + hdev->name, bda->status); + kfree_skb(skb); + ret = -bt_to_errno(bda->status); + goto done; + } + + /* The address 00:20:70:02:A0:00 indicates a BCM20702A0 controller + * with no configured address. + */ + if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0)) + BT_INFO("%s: BCM: using default device address (%pMR)", + hdev->name, &bda->bdaddr); + + kfree_skb(skb); + done: release_firmware(fw); From af96324312ed5b3d095d54b6d56b93f693f54c9f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 16:45:39 +0300 Subject: [PATCH 133/378] Bluetooth: Fix sparse warning with btmrvl driver This patch fixes the following sparse warning caused by a missing declaration in the header file: drivers/bluetooth/btmrvl_main.c:218:5: warning: symbol 'btmrvl_pscan_window_reporting' was not declared. Should it be static? Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- drivers/bluetooth/btmrvl_drv.h | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/bluetooth/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h index a8a9d3380e59..caf684119a4e 100644 --- a/drivers/bluetooth/btmrvl_drv.h +++ b/drivers/bluetooth/btmrvl_drv.h @@ -145,6 +145,7 @@ bool btmrvl_check_evtpkt(struct btmrvl_private *priv, struct sk_buff *skb); int btmrvl_process_event(struct btmrvl_private *priv, struct sk_buff *skb); int btmrvl_send_module_cfg_cmd(struct btmrvl_private *priv, u8 subcmd); +int btmrvl_pscan_window_reporting(struct btmrvl_private *priv, u8 subcmd); int btmrvl_send_hscfg_cmd(struct btmrvl_private *priv); int btmrvl_enable_ps(struct btmrvl_private *priv); int btmrvl_prepare_command(struct btmrvl_private *priv); From 373110c5d30b0944b47cddbe586069b7457f8845 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 17:37:25 +0300 Subject: [PATCH 134/378] Bluetooth: Rename hci_conn_params_clear to hci_conn_params_clear_all We'll soon have specific clear functions for clearing enabled or disabled entries, so rename the function that removes everything to clear_all(). Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_core.c | 4 ++-- net/bluetooth/mgmt.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index ee480a86e558..091934bcfd84 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -861,7 +861,7 @@ struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, u8 auto_connect); void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); -void hci_conn_params_clear(struct hci_dev *hdev); +void hci_conn_params_clear_all(struct hci_dev *hdev); struct bdaddr_list *hci_pend_le_conn_lookup(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 21a4c8dfb874..e6e169007fd2 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3580,7 +3580,7 @@ void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) } /* This function requires the caller holds hdev->lock */ -void hci_conn_params_clear(struct hci_dev *hdev) +void hci_conn_params_clear_all(struct hci_dev *hdev) { struct hci_conn_params *params, *tmp; @@ -4038,7 +4038,7 @@ void hci_unregister_dev(struct hci_dev *hdev) hci_smp_irks_clear(hdev); hci_remote_oob_data_clear(hdev); hci_white_list_clear(hdev); - hci_conn_params_clear(hdev); + hci_conn_params_clear_all(hdev); hci_dev_unlock(hdev); hci_dev_put(hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 93cfefa260d5..29850e76ea3c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5104,7 +5104,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, goto unlock; } - hci_conn_params_clear(hdev); + hci_conn_params_clear_all(hdev); } err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, From 55af49a8fe85278ea244e72d2d264cf5e0941c61 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 17:37:26 +0300 Subject: [PATCH 135/378] Bluetooth: Add specific connection parameter clear functions In some circumstances we'll need to either clear only the enabled parameters or only the disabled ones. This patch adds convenience functions for this purpose. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 ++ net/bluetooth/hci_core.c | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 091934bcfd84..2091e0013b8c 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -862,6 +862,8 @@ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, u8 auto_connect); void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); void hci_conn_params_clear_all(struct hci_dev *hdev); +void hci_conn_params_clear_disabled(struct hci_dev *hdev); +void hci_conn_params_clear_enabled(struct hci_dev *hdev); struct bdaddr_list *hci_pend_le_conn_lookup(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index e6e169007fd2..7e46a7c6092f 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3579,6 +3579,38 @@ void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) BT_DBG("addr %pMR (type %u)", addr, addr_type); } +/* This function requires the caller holds hdev->lock */ +void hci_conn_params_clear_disabled(struct hci_dev *hdev) +{ + struct hci_conn_params *params, *tmp; + + list_for_each_entry_safe(params, tmp, &hdev->le_conn_params, list) { + if (params->auto_connect != HCI_AUTO_CONN_DISABLED) + continue; + list_del(¶ms->list); + kfree(params); + } + + BT_DBG("All LE disabled connection parameters were removed"); +} + +/* This function requires the caller holds hdev->lock */ +void hci_conn_params_clear_enabled(struct hci_dev *hdev) +{ + struct hci_conn_params *params, *tmp; + + list_for_each_entry_safe(params, tmp, &hdev->le_conn_params, list) { + if (params->auto_connect == HCI_AUTO_CONN_DISABLED) + continue; + list_del(¶ms->list); + kfree(params); + } + + hci_pend_le_conns_clear(hdev); + + BT_DBG("All enabled LE connection parameters were removed"); +} + /* This function requires the caller holds hdev->lock */ void hci_conn_params_clear_all(struct hci_dev *hdev) { From a3451d279f839d987cbcf25b0f3be666aef99d0b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 17:37:27 +0300 Subject: [PATCH 136/378] Bluetooth: Add new auto_conn value matching mgmt action 0x00 The 0x00 action value of mgmt means "scan and report" but do not connect. This is different from HCI_AUTO_CONN_DISABLED so we need a new value for it. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 1 + net/bluetooth/mgmt.c | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 2091e0013b8c..f4a2f50f30b5 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -449,6 +449,7 @@ struct hci_conn_params { enum { HCI_AUTO_CONN_DISABLED, + HCI_AUTO_CONN_REPORT, HCI_AUTO_CONN_ALWAYS, HCI_AUTO_CONN_LINK_LOSS, } auto_connect; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 7e46a7c6092f..a3cd0bbd3518 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3547,6 +3547,7 @@ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, switch (auto_connect) { case HCI_AUTO_CONN_DISABLED: + case HCI_AUTO_CONN_REPORT: case HCI_AUTO_CONN_LINK_LOSS: hci_pend_le_conn_del(hdev, addr, addr_type); break; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 29850e76ea3c..f7217f9eda03 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5034,7 +5034,7 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, if (cp->action) auto_conn = HCI_AUTO_CONN_ALWAYS; else - auto_conn = HCI_AUTO_CONN_DISABLED; + auto_conn = HCI_AUTO_CONN_REPORT; /* If the connection parameters don't exist for this device, * they will be created and configured with defaults. From c71593dd34ae1fd46777662a522a32cfde86f073 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 17:37:28 +0300 Subject: [PATCH 137/378] Bluetooth: Remove only enabled entries with Remove Device command The Remove Device mgmt command is supposed to undo what the Add Device command does. An entry added by Add Device cannot have the HCI_AUTO_CONN_DISABLED auto_connect value, so we should treat this as an invalid entry to remove. This patch adds the necessary pieces to the Remove Device command handler so that it only removes entries which were added by Add Device. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f7217f9eda03..574dd9f7c39e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5079,6 +5079,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, hci_dev_lock(hdev); if (bacmp(&cp->addr.bdaddr, BDADDR_ANY)) { + struct hci_conn_params *params; u8 addr_type; if (!bdaddr_type_is_le(cp->addr.type)) { @@ -5093,7 +5094,25 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, else addr_type = ADDR_LE_DEV_RANDOM; - hci_conn_params_del(hdev, &cp->addr.bdaddr, addr_type); + params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr, + addr_type); + if (!params) { + err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &cp->addr, sizeof(cp->addr)); + goto unlock; + } + + if (params->auto_connect == HCI_AUTO_CONN_DISABLED) { + err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &cp->addr, sizeof(cp->addr)); + goto unlock; + } + + hci_pend_le_conn_del(hdev, &cp->addr.bdaddr, addr_type); + list_del(¶ms->list); + kfree(params); device_removed(sk, hdev, &cp->addr.bdaddr, cp->addr.type); } else { @@ -5104,7 +5123,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, goto unlock; } - hci_conn_params_clear_all(hdev); + hci_conn_params_clear_enabled(hdev); } err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, From a26f3dcff2cf5890f33d883c98d90cdfa51ed460 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 17:37:29 +0300 Subject: [PATCH 138/378] Bluetooth: Add Load Connection Parameters command This patch implements the new Load Connection Parameters mgmt command that's intended to load the desired connection parameters for LE devices. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/mgmt.h | 15 +++++++ net/bluetooth/mgmt.c | 79 ++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 3c0f29614d1b..5b3e8009eddd 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -449,6 +449,21 @@ struct mgmt_cp_remove_device { } __packed; #define MGMT_REMOVE_DEVICE_SIZE MGMT_ADDR_INFO_SIZE +struct mgmt_conn_param { + struct mgmt_addr_info addr; + __le16 min_interval; + __le16 max_interval; + __le16 latency; + __le16 timeout; +} __packed; + +#define MGMT_OP_LOAD_CONN_PARAM 0x0035 +struct mgmt_cp_load_conn_param { + __le16 param_count; + struct mgmt_conn_param params[0]; +} __packed; +#define MGMT_LOAD_CONN_PARAM_SIZE 2 + #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 574dd9f7c39e..59bf1ac41429 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -88,6 +88,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_GET_CLOCK_INFO, MGMT_OP_ADD_DEVICE, MGMT_OP_REMOVE_DEVICE, + MGMT_OP_LOAD_CONN_PARAM, }; static const u16 mgmt_events[] = { @@ -5134,6 +5135,83 @@ unlock: return err; } +static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data, + u16 len) +{ + struct mgmt_cp_load_conn_param *cp = data; + u16 param_count, expected_len; + int i; + + if (!lmp_le_capable(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM, + MGMT_STATUS_NOT_SUPPORTED); + + param_count = __le16_to_cpu(cp->param_count); + + expected_len = sizeof(*cp) + param_count * + sizeof(struct mgmt_conn_param); + if (expected_len != len) { + BT_ERR("load_conn_param: expected %u bytes, got %u bytes", + expected_len, len); + return cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM, + MGMT_STATUS_INVALID_PARAMS); + } + + BT_DBG("%s param_count %u", hdev->name, param_count); + + hci_dev_lock(hdev); + + hci_conn_params_clear_disabled(hdev); + + for (i = 0; i < param_count; i++) { + struct mgmt_conn_param *param = &cp->params[i]; + struct hci_conn_params *hci_param; + u16 min, max, latency, timeout; + u8 addr_type; + + BT_DBG("Adding %pMR (type %u)", ¶m->addr.bdaddr, + param->addr.type); + + if (param->addr.type == BDADDR_LE_PUBLIC) { + addr_type = ADDR_LE_DEV_PUBLIC; + } else if (param->addr.type == BDADDR_LE_RANDOM) { + addr_type = ADDR_LE_DEV_RANDOM; + } else { + BT_ERR("Ignoring invalid connection parameters"); + continue; + } + + min = le16_to_cpu(param->min_interval); + max = le16_to_cpu(param->max_interval); + latency = le16_to_cpu(param->latency); + timeout = le16_to_cpu(param->timeout); + + BT_DBG("min 0x%04x max 0x%04x latency 0x%04x timeout 0x%04x", + min, max, latency, timeout); + + if (hci_check_conn_params(min, max, latency, timeout) < 0) { + BT_ERR("Ignoring invalid connection parameters"); + continue; + } + + hci_param = hci_conn_params_add(hdev, ¶m->addr.bdaddr, + addr_type); + if (!hci_param) { + BT_ERR("Failed to add connection parameters"); + continue; + } + + hci_param->conn_min_interval = min; + hci_param->conn_max_interval = max; + hci_param->conn_latency = latency; + hci_param->supervision_timeout = timeout; + } + + hci_dev_unlock(hdev); + + return cmd_complete(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM, 0, NULL, 0); +} + static const struct mgmt_handler { int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len); @@ -5193,6 +5271,7 @@ static const struct mgmt_handler { { get_clock_info, false, MGMT_GET_CLOCK_INFO_SIZE }, { add_device, false, MGMT_ADD_DEVICE_SIZE }, { remove_device, false, MGMT_REMOVE_DEVICE_SIZE }, + { load_conn_param, true, MGMT_LOAD_CONN_PARAM_SIZE }, }; int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) From 348d50b8e96c2c4630801e6e720c7c722ade83e8 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 17:37:30 +0300 Subject: [PATCH 139/378] Bluetooth: Fix missing update of conn params We should update any stored connection parameters when we receive the LE Remote Connection Parameter Request HCI event. This patch adds the necessary code to the function that handles the event. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_event.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index ed49a065dd67..3ad2576fd3f1 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4429,9 +4429,25 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, return send_conn_param_neg_reply(hdev, handle, HCI_ERROR_INVALID_LL_PARAMS); - if (test_bit(HCI_CONN_MASTER, &hcon->flags)) + if (test_bit(HCI_CONN_MASTER, &hcon->flags)) { + struct hci_conn_params *params; + + hci_dev_lock(hdev); + + params = hci_conn_params_lookup(hdev, &hcon->dst, + hcon->dst_type); + if (params) { + params->conn_min_interval = min; + params->conn_max_interval = max; + params->conn_latency = latency; + params->supervision_timeout = timeout; + } + + hci_dev_unlock(hdev); + mgmt_new_conn_param(hdev, &hcon->dst, hcon->dst_type, min, max, latency, timeout); + } cp.handle = ev->handle; cp.interval_min = ev->interval_min; From 7d6ca6939cb2f701204317cbab15af1b98f7f501 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 17:37:31 +0300 Subject: [PATCH 140/378] Bluetooth: Make hci_le_conn_update return the store hint The caller of hci_le_conn_update is directly interested in knowing what the best value is for the store_hint parameter of the corresponding mgmt event. Since hci_le_conn_update knows whether there were stored parameters that were updated or not we can have it return an initial store_hint value to the caller. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 4 ++-- net/bluetooth/hci_conn.c | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index f4a2f50f30b5..32c8e51f6b26 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1368,8 +1368,8 @@ struct hci_sec_filter { #define hci_req_lock(d) mutex_lock(&d->req_lock) #define hci_req_unlock(d) mutex_unlock(&d->req_lock) -void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, - u16 latency, u16 to_multiplier); +u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, + u16 to_multiplier); void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand, __u8 ltk[16]); diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index d00aaf976efc..0d579d036833 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -213,8 +213,8 @@ bool hci_setup_sync(struct hci_conn *conn, __u16 handle) return true; } -void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, - u16 latency, u16 to_multiplier) +u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, + u16 to_multiplier) { struct hci_dev *hdev = conn->hdev; struct hci_conn_params *params; @@ -242,6 +242,11 @@ void hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, cp.max_ce_len = cpu_to_le16(0x0000); hci_send_cmd(hdev, HCI_OP_LE_CONN_UPDATE, sizeof(cp), &cp); + + if (params) + return 0x01; + + return 0x00; } void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand, From f4869e2adb7ab9d09a9335b4e26a63ec413f2c6f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 17:37:32 +0300 Subject: [PATCH 141/378] Bluetooth: Pass store hint to mgmt_new_conn_param The calling functions of mgmt_new_conn_param have more information about the parameters, such as whether the kernel is tracking them or not. It makes therefore sense to have them pass an initial store_hint value to the mgmt_new_conn_param function. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 4 ++-- net/bluetooth/hci_event.c | 8 ++++++-- net/bluetooth/l2cap_core.c | 10 +++++++--- net/bluetooth/mgmt.c | 6 +++--- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 32c8e51f6b26..d0bca13843ce 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1335,8 +1335,8 @@ void mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk); void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk, bool persistent); void mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 bdaddr_type, u16 min_interval, u16 max_interval, - u16 latency, u16 timeout); + u8 bdaddr_type, u8 store_hint, u16 min_interval, + u16 max_interval, u16 latency, u16 timeout); void mgmt_reenable_advertising(struct hci_dev *hdev); void mgmt_smp_complete(struct hci_conn *conn, bool complete); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 3ad2576fd3f1..846f6a6af881 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4431,6 +4431,7 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, if (test_bit(HCI_CONN_MASTER, &hcon->flags)) { struct hci_conn_params *params; + u8 store_hint; hci_dev_lock(hdev); @@ -4441,12 +4442,15 @@ static void hci_le_remote_conn_param_req_evt(struct hci_dev *hdev, params->conn_max_interval = max; params->conn_latency = latency; params->supervision_timeout = timeout; + store_hint = 0x01; + } else{ + store_hint = 0x00; } hci_dev_unlock(hdev); - mgmt_new_conn_param(hdev, &hcon->dst, hcon->dst_type, min, max, - latency, timeout); + mgmt_new_conn_param(hdev, &hcon->dst, hcon->dst_type, + store_hint, min, max, latency, timeout); } cp.handle = ev->handle; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index a6e276204ae9..dbef22d644e2 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -5250,10 +5250,14 @@ static inline int l2cap_conn_param_update_req(struct l2cap_conn *conn, sizeof(rsp), &rsp); if (!err) { - mgmt_new_conn_param(hcon->hdev, &hcon->dst, hcon->dst_type, - min, max, latency, to_multiplier); + u8 store_hint; + + store_hint = hci_le_conn_update(hcon, min, max, latency, + to_multiplier); + mgmt_new_conn_param(hcon->hdev, &hcon->dst, hcon->dst_type, + store_hint, min, max, latency, + to_multiplier); - hci_le_conn_update(hcon, min, max, latency, to_multiplier); } return 0; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 59bf1ac41429..fb1aa0cac137 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5790,15 +5790,15 @@ void mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk, } void mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr, - u8 bdaddr_type, u16 min_interval, u16 max_interval, - u16 latency, u16 timeout) + u8 bdaddr_type, u8 store_hint, u16 min_interval, + u16 max_interval, u16 latency, u16 timeout) { struct mgmt_ev_new_conn_param ev; memset(&ev, 0, sizeof(ev)); bacpy(&ev.addr.bdaddr, bdaddr); ev.addr.type = link_to_bdaddr(LE_LINK, bdaddr_type); - ev.store_hint = 0x00; + ev.store_hint = store_hint; ev.min_interval = cpu_to_le16(min_interval); ev.max_interval = cpu_to_le16(max_interval); ev.latency = cpu_to_le16(latency); From c46245b3efce80884acf65c01443582aec1f31ed Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 17:37:33 +0300 Subject: [PATCH 142/378] Bluetooth: Make is_identity_address a global function There are more places that can take advantage of is_identity_address() besides hci_core.c. This patch moves the function to hci_core.h and gives it the appropriate hci_ prefix. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 12 ++++++++++++ net/bluetooth/hci_core.c | 14 +------------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index d0bca13843ce..92f1bad6e22d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1179,6 +1179,18 @@ static inline bool hci_bdaddr_is_rpa(bdaddr_t *bdaddr, u8 addr_type) return false; } +static inline bool hci_is_identity_address(bdaddr_t *addr, u8 addr_type) +{ + if (addr_type == ADDR_LE_DEV_PUBLIC) + return true; + + /* Check for Random Static address type */ + if ((addr->b[5] & 0xc0) == 0xc0) + return true; + + return false; +} + static inline struct smp_irk *hci_get_irk(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 addr_type) { diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index a3cd0bbd3518..1fff3d890f41 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3412,18 +3412,6 @@ static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type) return true; } -static bool is_identity_address(bdaddr_t *addr, u8 addr_type) -{ - if (addr_type == ADDR_LE_DEV_PUBLIC) - return true; - - /* Check for Random Static address type */ - if ((addr->b[5] & 0xc0) == 0xc0) - return true; - - return false; -} - /* This function requires the caller holds hdev->lock */ struct bdaddr_list *hci_pend_le_conn_lookup(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) @@ -3504,7 +3492,7 @@ struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, { struct hci_conn_params *params; - if (!is_identity_address(addr, addr_type)) + if (!hci_is_identity_address(addr, addr_type)) return NULL; params = hci_conn_params_lookup(hdev, addr, addr_type); From c103aea6f709c68916160eca4ed20224934e62d7 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 17:37:34 +0300 Subject: [PATCH 143/378] Bluetooth: Don't send connection parameters without identity address If we don't have an identity address for connection parameters it doesn't really make sense to send them to user space. Instead just ignore them for now. Later we can add support for sending them when we eventually get the identity through pairing. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index fb1aa0cac137..50a0a3ec50b0 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5795,6 +5795,9 @@ void mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr, { struct mgmt_ev_new_conn_param ev; + if (!hci_is_identity_address(bdaddr, bdaddr_type)) + return; + memset(&ev, 0, sizeof(ev)); bacpy(&ev.addr.bdaddr, bdaddr); ev.addr.type = link_to_bdaddr(LE_LINK, bdaddr_type); From 4a964404c08fed64d1afd8b0af1e7f2b8f7ae90e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 2 Jul 2014 19:10:33 +0200 Subject: [PATCH 144/378] Bluetooth: Introduce unconfigured controller state With the new unconfigured controller state it is possible to provide a fully functional HCI transport, but disable the higher level operations that would normally happen. This way userspace can try to configure the controller before releases the unconfigured state. The internal state is represented by HCI_UNCONFIGURED. This replaces the HCI_QUIRK_RAW_DEVICE quirk as internal state representation. This is now a real state and drivers can use the quirk to actually trigger this state. In the future this will allow a more fine grained switching from unconfigured state to configured state for controller inititialization. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_core.c | 43 ++++++++++++++++++++++--------------- net/bluetooth/hci_sock.c | 8 +++---- net/bluetooth/mgmt.c | 6 +++--- 4 files changed, 34 insertions(+), 24 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 66358af6b1fc..606a9b1466a3 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -127,6 +127,7 @@ enum { HCI_KEEP_DEBUG_KEYS, HCI_USE_DEBUG_KEYS, HCI_UNREGISTER, + HCI_UNCONFIGURED, HCI_USER_CHANNEL, HCI_LE_SCAN, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 1fff3d890f41..395b014ad0e8 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2116,7 +2116,7 @@ int hci_inquiry(void __user *arg) goto done; } - if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) { + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { err = -EOPNOTSUPP; goto done; } @@ -2261,7 +2261,7 @@ static int hci_dev_do_open(struct hci_dev *hdev) } if (!ret) { - if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks) && + if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) && !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) ret = __hci_init(hdev); } @@ -2274,6 +2274,7 @@ static int hci_dev_do_open(struct hci_dev *hdev) set_bit(HCI_UP, &hdev->flags); hci_notify(hdev, HCI_DEV_UP); if (!test_bit(HCI_SETUP, &hdev->dev_flags) && + !test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) && !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) && hdev->dev_type == HCI_BREDR) { hci_dev_lock(hdev); @@ -2317,7 +2318,7 @@ int hci_dev_open(__u16 dev) if (!hdev) return -ENODEV; - /* Devices that are marked for raw-only usage can only be powered + /* Devices that are marked as unconfigured can only be powered * up as user channel. Trying to bring them up as normal devices * will result into a failure. Only user channel operation is * possible. @@ -2326,7 +2327,7 @@ int hci_dev_open(__u16 dev) * HCI_USER_CHANNEL will be set first before attempting to * open the device. */ - if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks) && + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) && !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { err = -EOPNOTSUPP; goto done; @@ -2401,8 +2402,8 @@ static int hci_dev_do_close(struct hci_dev *hdev) /* Reset device */ skb_queue_purge(&hdev->cmd_q); atomic_set(&hdev->cmd_cnt, 1); - if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks) && - !test_bit(HCI_AUTO_OFF, &hdev->dev_flags) && + if (!test_bit(HCI_AUTO_OFF, &hdev->dev_flags) && + !test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) && test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks)) { set_bit(HCI_INIT, &hdev->flags); __hci_req_sync(hdev, hci_reset_req, 0, HCI_CMD_TIMEOUT); @@ -2501,7 +2502,7 @@ int hci_dev_reset(__u16 dev) goto done; } - if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) { + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { ret = -EOPNOTSUPP; goto done; } @@ -2543,7 +2544,7 @@ int hci_dev_reset_stat(__u16 dev) goto done; } - if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) { + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { ret = -EOPNOTSUPP; goto done; } @@ -2573,7 +2574,7 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) goto done; } - if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) { + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { err = -EOPNOTSUPP; goto done; } @@ -2791,6 +2792,7 @@ static void hci_power_on(struct work_struct *work) * valid, it is important to turn the device back off. */ if (test_bit(HCI_RFKILLED, &hdev->dev_flags) || + test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) || (hdev->dev_type == HCI_BREDR && !bacmp(&hdev->bdaddr, BDADDR_ANY) && !bacmp(&hdev->static_addr, BDADDR_ANY))) { @@ -2802,7 +2804,15 @@ static void hci_power_on(struct work_struct *work) } if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags)) { - if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) + /* For unconfigured devices, set the HCI_RAW flag + * so that userspace can easily identify them. + * + * If the device is fully configured and ready for + * operation, announce it via management interface. + */ + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) + set_bit(HCI_RAW, &hdev->flags); + else mgmt_index_added(hdev); } } @@ -3974,12 +3984,11 @@ int hci_register_dev(struct hci_dev *hdev) list_add(&hdev->list, &hci_dev_list); write_unlock(&hci_dev_list_lock); - /* Devices that are marked for raw-only usage need to set - * the HCI_RAW flag to indicate that only user channel is - * supported. + /* Devices that are marked for raw-only usage are unconfigured + * and should not be included in normal operation. */ if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) - set_bit(HCI_RAW, &hdev->flags); + set_bit(HCI_UNCONFIGURED, &hdev->dev_flags); hci_notify(hdev, HCI_DEV_REG); hci_dev_hold(hdev); @@ -4024,7 +4033,7 @@ void hci_unregister_dev(struct hci_dev *hdev) if (!test_bit(HCI_INIT, &hdev->flags) && !test_bit(HCI_SETUP, &hdev->dev_flags) && - !test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) { + !test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { hci_dev_lock(hdev); mgmt_index_removed(hdev); hci_dev_unlock(hdev); @@ -4788,7 +4797,7 @@ static inline int __get_blocks(struct hci_dev *hdev, struct sk_buff *skb) static void __check_timeout(struct hci_dev *hdev, unsigned int cnt) { - if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) { + if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { /* ACL tx timeout must be longer than maximum * link supervision timeout (40.9 seconds) */ if (!cnt && time_after(jiffies, hdev->acl_last_tx + @@ -4971,7 +4980,7 @@ static void hci_sched_le(struct hci_dev *hdev) if (!hci_conn_num(hdev, LE_LINK)) return; - if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) { + if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { /* LE tx timeout must be longer than maximum * link supervision timeout (40.9 seconds) */ if (!hdev->le_cnt && hdev->le_pkts && diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 54e4e8fd5d97..db9610323d4d 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -453,7 +453,7 @@ static int hci_sock_release(struct socket *sock) if (hdev) { if (hci_pi(sk)->channel == HCI_CHANNEL_USER) { - if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) + if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) mgmt_index_added(hdev); clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags); hci_dev_close(hdev->id); @@ -518,7 +518,7 @@ static int hci_sock_bound_ioctl(struct sock *sk, unsigned int cmd, if (test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) return -EBUSY; - if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) return -EOPNOTSUPP; if (hdev->dev_type != HCI_BREDR) @@ -706,13 +706,13 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, goto done; } - if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) + if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) mgmt_index_removed(hdev); err = hci_dev_open(hdev->id); if (err) { clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags); - if (!test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) + if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) mgmt_index_added(hdev); hci_dev_put(hdev); goto done; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 50a0a3ec50b0..1ab98980054c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -353,7 +353,7 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, if (test_bit(HCI_USER_CHANNEL, &d->dev_flags)) continue; - if (test_bit(HCI_QUIRK_RAW_DEVICE, &d->quirks)) + if (test_bit(HCI_UNCONFIGURED, &d->dev_flags)) continue; if (d->dev_type == HCI_BREDR) { @@ -5317,8 +5317,8 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) } if (test_bit(HCI_SETUP, &hdev->dev_flags) || - test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) || - test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) { + test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) || + test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { err = cmd_status(sk, index, opcode, MGMT_STATUS_INVALID_INDEX); goto done; From 0602a8adc3ce3f592d03df426c92d1f36229403c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 2 Jul 2014 21:30:54 +0200 Subject: [PATCH 145/378] Bluetooth: Add support for Unconfigured Index Added events When a controller is in unconfigured state it is currently hidden from the management interface. This change now announces the new controller with an Unconfigured Index Added event and allows clients to easily detect the controller. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 2 ++ net/bluetooth/hci_core.c | 17 ++++++++++------- net/bluetooth/hci_sock.c | 9 +++------ net/bluetooth/mgmt.c | 12 +++++++++++- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 5b3e8009eddd..7da29fd748d8 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -640,3 +640,5 @@ struct mgmt_ev_new_conn_param { __le16 latency; __le16 timeout; } __packed; + +#define MGMT_EV_UNCONF_INDEX_ADDED 0x001d diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 395b014ad0e8..df25a8329ecc 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2806,14 +2806,18 @@ static void hci_power_on(struct work_struct *work) if (test_and_clear_bit(HCI_SETUP, &hdev->dev_flags)) { /* For unconfigured devices, set the HCI_RAW flag * so that userspace can easily identify them. - * - * If the device is fully configured and ready for - * operation, announce it via management interface. */ if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) set_bit(HCI_RAW, &hdev->flags); - else - mgmt_index_added(hdev); + + /* For fully configured devices, this will send + * the Index Added event. For unconfigured devices, + * it will send Unconfigued Index Added event. + * + * Devices with HCI_QUIRK_RAW_DEVICE are ignored + * and no event will be send. + */ + mgmt_index_added(hdev); } } @@ -4032,8 +4036,7 @@ void hci_unregister_dev(struct hci_dev *hdev) cancel_work_sync(&hdev->power_on); if (!test_bit(HCI_INIT, &hdev->flags) && - !test_bit(HCI_SETUP, &hdev->dev_flags) && - !test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { + !test_bit(HCI_SETUP, &hdev->dev_flags)) { hci_dev_lock(hdev); mgmt_index_removed(hdev); hci_dev_unlock(hdev); diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index db9610323d4d..ba13ad8e25c6 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -453,8 +453,7 @@ static int hci_sock_release(struct socket *sock) if (hdev) { if (hci_pi(sk)->channel == HCI_CHANNEL_USER) { - if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) - mgmt_index_added(hdev); + mgmt_index_added(hdev); clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags); hci_dev_close(hdev->id); } @@ -706,14 +705,12 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, goto done; } - if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) - mgmt_index_removed(hdev); + mgmt_index_removed(hdev); err = hci_dev_open(hdev->id); if (err) { clear_bit(HCI_USER_CHANNEL, &hdev->dev_flags); - if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) - mgmt_index_added(hdev); + mgmt_index_added(hdev); hci_dev_put(hdev); goto done; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1ab98980054c..ab70d5858db9 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -118,6 +118,7 @@ static const u16 mgmt_events[] = { MGMT_EV_DEVICE_ADDED, MGMT_EV_DEVICE_REMOVED, MGMT_EV_NEW_CONN_PARAM, + MGMT_EV_UNCONF_INDEX_ADDED, }; #define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000) @@ -5373,7 +5374,13 @@ void mgmt_index_added(struct hci_dev *hdev) if (hdev->dev_type != HCI_BREDR) return; - mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL); + if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) + return; + + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) + mgmt_event(MGMT_EV_UNCONF_INDEX_ADDED, hdev, NULL, 0, NULL); + else + mgmt_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, NULL); } void mgmt_index_removed(struct hci_dev *hdev) @@ -5383,6 +5390,9 @@ void mgmt_index_removed(struct hci_dev *hdev) if (hdev->dev_type != HCI_BREDR) return; + if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) + return; + mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL); From edd3896bc41059fc064c4ec76da004a57203d88e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 2 Jul 2014 21:30:55 +0200 Subject: [PATCH 146/378] Bluetooth: Add support for Unconfigured Index Removed events When a controller in an unconfigured state gets removed, then send Unconfigured Index Removed events. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 2 ++ net/bluetooth/mgmt.c | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 7da29fd748d8..651993213bd9 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -642,3 +642,5 @@ struct mgmt_ev_new_conn_param { } __packed; #define MGMT_EV_UNCONF_INDEX_ADDED 0x001d + +#define MGMT_EV_UNCONF_INDEX_REMOVED 0x001e diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ab70d5858db9..1a78d26b0049 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -119,6 +119,7 @@ static const u16 mgmt_events[] = { MGMT_EV_DEVICE_REMOVED, MGMT_EV_NEW_CONN_PARAM, MGMT_EV_UNCONF_INDEX_ADDED, + MGMT_EV_UNCONF_INDEX_REMOVED, }; #define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000) @@ -5395,7 +5396,10 @@ void mgmt_index_removed(struct hci_dev *hdev) mgmt_pending_foreach(0, hdev, cmd_status_rsp, &status); - mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL); + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) + mgmt_event(MGMT_EV_UNCONF_INDEX_REMOVED, hdev, NULL, 0, NULL); + else + mgmt_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, NULL); } /* This function requires the caller holds hdev->lock */ From 73d1df2a7a1036a1f000e5f0ece6ade3e082b854 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 2 Jul 2014 22:10:52 +0200 Subject: [PATCH 147/378] Bluetooth: Add support for Read Unconfigured Index List command This command allows to get the list of currently known controller that are in unconfigured state. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 7 +++ net/bluetooth/mgmt.c | 91 ++++++++++++++++++++++++++++++++---- 2 files changed, 88 insertions(+), 10 deletions(-) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 651993213bd9..e0786cfa5490 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -464,6 +464,13 @@ struct mgmt_cp_load_conn_param { } __packed; #define MGMT_LOAD_CONN_PARAM_SIZE 2 +#define MGMT_OP_READ_UNCONF_INDEX_LIST 0x0036 +#define MGMT_READ_UNCONF_INDEX_LIST_SIZE 0 +struct mgmt_rp_read_unconf_index_list { + __le16 num_controllers; + __le16 index[0]; +} __packed; + #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 1a78d26b0049..325bb8136d2c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -89,6 +89,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_ADD_DEVICE, MGMT_OP_REMOVE_DEVICE, MGMT_OP_LOAD_CONN_PARAM, + MGMT_OP_READ_UNCONF_INDEX_LIST, }; static const u16 mgmt_events[] = { @@ -336,7 +337,8 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, count = 0; list_for_each_entry(d, &hci_dev_list, list) { - if (d->dev_type == HCI_BREDR) + if (d->dev_type == HCI_BREDR && + !test_bit(HCI_UNCONFIGURED, &d->dev_flags)) count++; } @@ -349,16 +351,18 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, count = 0; list_for_each_entry(d, &hci_dev_list, list) { - if (test_bit(HCI_SETUP, &d->dev_flags)) + if (test_bit(HCI_SETUP, &d->dev_flags) || + test_bit(HCI_USER_CHANNEL, &d->dev_flags)) continue; - if (test_bit(HCI_USER_CHANNEL, &d->dev_flags)) + /* Devices marked as raw-only are neither configured + * nor unconfigured controllers. + */ + if (test_bit(HCI_QUIRK_RAW_DEVICE, &d->quirks)) continue; - if (test_bit(HCI_UNCONFIGURED, &d->dev_flags)) - continue; - - if (d->dev_type == HCI_BREDR) { + if (d->dev_type == HCI_BREDR && + !test_bit(HCI_UNCONFIGURED, &d->dev_flags)) { rp->index[count++] = cpu_to_le16(d->id); BT_DBG("Added hci%u", d->id); } @@ -377,6 +381,65 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, return err; } +static int read_unconf_index_list(struct sock *sk, struct hci_dev *hdev, + void *data, u16 data_len) +{ + struct mgmt_rp_read_unconf_index_list *rp; + struct hci_dev *d; + size_t rp_len; + u16 count; + int err; + + BT_DBG("sock %p", sk); + + read_lock(&hci_dev_list_lock); + + count = 0; + list_for_each_entry(d, &hci_dev_list, list) { + if (d->dev_type == HCI_BREDR && + test_bit(HCI_UNCONFIGURED, &d->dev_flags)) + count++; + } + + rp_len = sizeof(*rp) + (2 * count); + rp = kmalloc(rp_len, GFP_ATOMIC); + if (!rp) { + read_unlock(&hci_dev_list_lock); + return -ENOMEM; + } + + count = 0; + list_for_each_entry(d, &hci_dev_list, list) { + if (test_bit(HCI_SETUP, &d->dev_flags) || + test_bit(HCI_USER_CHANNEL, &d->dev_flags)) + continue; + + /* Devices marked as raw-only are neither configured + * nor unconfigured controllers. + */ + if (test_bit(HCI_QUIRK_RAW_DEVICE, &d->quirks)) + continue; + + if (d->dev_type == HCI_BREDR && + test_bit(HCI_UNCONFIGURED, &d->dev_flags)) { + rp->index[count++] = cpu_to_le16(d->id); + BT_DBG("Added hci%u", d->id); + } + } + + rp->num_controllers = cpu_to_le16(count); + rp_len = sizeof(*rp) + (2 * count); + + read_unlock(&hci_dev_list_lock); + + err = cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_UNCONF_INDEX_LIST, + 0, rp, rp_len); + + kfree(rp); + + return err; +} + static u32 get_supported_settings(struct hci_dev *hdev) { u32 settings = 0; @@ -5273,7 +5336,8 @@ static const struct mgmt_handler { { get_clock_info, false, MGMT_GET_CLOCK_INFO_SIZE }, { add_device, false, MGMT_ADD_DEVICE_SIZE }, { remove_device, false, MGMT_REMOVE_DEVICE_SIZE }, - { load_conn_param, true, MGMT_LOAD_CONN_PARAM_SIZE }, + { load_conn_param, true, MGMT_LOAD_CONN_PARAM_SIZE }, + { read_unconf_index_list, false, MGMT_READ_UNCONF_INDEX_LIST_SIZE }, }; int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) @@ -5335,8 +5399,15 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) goto done; } - if ((hdev && opcode < MGMT_OP_READ_INFO) || - (!hdev && opcode >= MGMT_OP_READ_INFO)) { + if (hdev && (opcode <= MGMT_OP_READ_INDEX_LIST || + opcode == MGMT_OP_READ_UNCONF_INDEX_LIST)) { + err = cmd_status(sk, index, opcode, + MGMT_STATUS_INVALID_INDEX); + goto done; + } + + if (!hdev && (opcode > MGMT_OP_READ_INDEX_LIST && + opcode != MGMT_OP_READ_UNCONF_INDEX_LIST)) { err = cmd_status(sk, index, opcode, MGMT_STATUS_INVALID_INDEX); goto done; From 82a30cfcb85538b677817815939306d20d5b8e61 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 3 Jul 2014 01:35:10 +0200 Subject: [PATCH 148/378] Bluetooth: Support HCI_QUIRK_RAW_DEVICE for hci_vhci driver This adds support for configuring the hci_vhci virtual controllers as a raw-only device using HCI_QUIRK_RAW_DEVICE. This is useful for testing the kernel internal infrastructure. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/hci_vhci.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index add1c6a72063..aa446c7eb7e5 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -40,7 +40,7 @@ #include #include -#define VERSION "1.4" +#define VERSION "1.5" static bool amp; @@ -95,10 +95,21 @@ static int vhci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) return 0; } -static int vhci_create_device(struct vhci_data *data, __u8 dev_type) +static int vhci_create_device(struct vhci_data *data, __u8 opcode) { struct hci_dev *hdev; struct sk_buff *skb; + __u8 dev_type; + + /* bits 0-1 are dev_type (BR/EDR or AMP) */ + dev_type = opcode & 0x03; + + if (dev_type != HCI_BREDR && dev_type != HCI_AMP) + return -EINVAL; + + /* bits 2-6 are reserved (must be zero) */ + if (opcode & 0x7c) + return -EINVAL; skb = bt_skb_alloc(4, GFP_KERNEL); if (!skb) @@ -121,6 +132,10 @@ static int vhci_create_device(struct vhci_data *data, __u8 dev_type) hdev->flush = vhci_flush; hdev->send = vhci_send_frame; + /* bit 7 is for raw device */ + if (opcode & 0x80) + set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); + if (hci_register_dev(hdev) < 0) { BT_ERR("Can't register HCI device"); hci_free_dev(hdev); @@ -132,7 +147,7 @@ static int vhci_create_device(struct vhci_data *data, __u8 dev_type) bt_cb(skb)->pkt_type = HCI_VENDOR_PKT; *skb_put(skb, 1) = 0xff; - *skb_put(skb, 1) = dev_type; + *skb_put(skb, 1) = opcode; put_unaligned_le16(hdev->id, skb_put(skb, 2)); skb_queue_tail(&data->readq, skb); @@ -146,7 +161,7 @@ static inline ssize_t vhci_get_user(struct vhci_data *data, { size_t len = iov_length(iov, count); struct sk_buff *skb; - __u8 pkt_type, dev_type; + __u8 pkt_type, opcode; unsigned long i; int ret; @@ -190,7 +205,7 @@ static inline ssize_t vhci_get_user(struct vhci_data *data, cancel_delayed_work_sync(&data->open_timeout); - dev_type = *((__u8 *) skb->data); + opcode = *((__u8 *) skb->data); skb_pull(skb, 1); if (skb->len > 0) { @@ -200,10 +215,7 @@ static inline ssize_t vhci_get_user(struct vhci_data *data, kfree_skb(skb); - if (dev_type != HCI_BREDR && dev_type != HCI_AMP) - return -EINVAL; - - ret = vhci_create_device(data, dev_type); + ret = vhci_create_device(data, opcode); break; default: From 851efca8387e10a25ca259f7efcc47819b19bff9 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 22:42:00 +0300 Subject: [PATCH 149/378] Bluetooth: Track number of added devices with HCI_AUTO_CONN_REPORT To be able to make the right choice of whether to start passive scanning or to send out a mgmt_device_found event we need to know if there are any devices in the le_conn_params list with the auto_connect value set to HCI_AUTO_CONN_REPORT. This patch adds a counter for this kind of devices. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 92f1bad6e22d..b5f4405b41c2 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -312,6 +312,7 @@ struct hci_dev { struct list_head le_white_list; struct list_head le_conn_params; struct list_head pend_le_conns; + unsigned int pend_le_reports; struct hci_dev_stats stat; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index df25a8329ecc..8e0061f72dd1 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3545,20 +3545,28 @@ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, if (!params) return -EIO; - params->auto_connect = auto_connect; + if (params->auto_connect == HCI_AUTO_CONN_REPORT && + auto_connect != HCI_AUTO_CONN_REPORT) + hdev->pend_le_reports--; switch (auto_connect) { case HCI_AUTO_CONN_DISABLED: - case HCI_AUTO_CONN_REPORT: case HCI_AUTO_CONN_LINK_LOSS: hci_pend_le_conn_del(hdev, addr, addr_type); break; + case HCI_AUTO_CONN_REPORT: + if (params->auto_connect != HCI_AUTO_CONN_REPORT) + hdev->pend_le_reports++; + hci_pend_le_conn_del(hdev, addr, addr_type); + break; case HCI_AUTO_CONN_ALWAYS: if (!is_connected(hdev, addr, addr_type)) hci_pend_le_conn_add(hdev, addr, addr_type); break; } + params->auto_connect = auto_connect; + BT_DBG("addr %pMR (type %u) auto_connect %u", addr, addr_type, auto_connect); @@ -3574,6 +3582,9 @@ void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) if (!params) return; + if (params->auto_connect == HCI_AUTO_CONN_REPORT) + hdev->pend_le_reports--; + hci_pend_le_conn_del(hdev, addr, addr_type); list_del(¶ms->list); @@ -3605,6 +3616,8 @@ void hci_conn_params_clear_enabled(struct hci_dev *hdev) list_for_each_entry_safe(params, tmp, &hdev->le_conn_params, list) { if (params->auto_connect == HCI_AUTO_CONN_DISABLED) continue; + if (params->auto_connect == HCI_AUTO_CONN_REPORT) + hdev->pend_le_reports--; list_del(¶ms->list); kfree(params); } From 75ce208cc44938195f57c55f81c8e4447dd492fb Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 22:42:01 +0300 Subject: [PATCH 150/378] Bluetooth: Allow mgmt_device_found events for kernel-side scanning When the kernel is doing LE scanning because of one or more devices added with action 0x00 through the Add Device command we do want to let mgmt_device_found() to proceed with sending an event. This kind of devices are tracked with hdev->pend_le_reports, so check this value before bailing out from the function. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 325bb8136d2c..0a82f08cd191 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6472,8 +6472,16 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, struct smp_irk *irk; size_t ev_size; - if (!hci_discovery_active(hdev)) - return; + /* Don't send events for a non-kernel initiated discovery. With + * LE one exception is if we have pend_le_reports > 0 in which + * case we're doing passive scanning and want these events. + */ + if (!hci_discovery_active(hdev)) { + if (link_type == ACL_LINK) + return; + if (link_type == LE_LINK && !hdev->pend_le_reports) + return; + } /* Make sure that the buffer is big enough. The 5 extra bytes * are for the potential CoD field. From 0d2bf13462732d3b2e11d8efb0545c1ed272298b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 22:42:02 +0300 Subject: [PATCH 151/378] Bluetooth: Add support for background LE scanning If we have one or more devices with HCI_AUTO_CONN_REPORT we should do background scanning and emit mgmt_device_found events. This patch modifies the hci_update_background_scan() function to extend the conditions needed to trigger scanning, and adds the necessary code to process_adv_report() to emit mgmt_device_found events. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_core.c | 7 ++++--- net/bluetooth/hci_event.c | 25 +++++++++++++++++++++++-- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 8e0061f72dd1..41cc64429ea1 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -5380,9 +5380,10 @@ void hci_update_background_scan(struct hci_dev *hdev) hci_req_init(&req, hdev); - if (list_empty(&hdev->pend_le_conns)) { - /* If there is no pending LE connections, we should stop - * the background scanning. + if (list_empty(&hdev->pend_le_conns) && !hdev->pend_le_reports) { + /* If there is no pending LE connections or devices + * to be scanned for, we should stop the background + * scanning. */ /* If controller is not scanning we are done. */ diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 846f6a6af881..87a704bd7eb7 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4225,11 +4225,32 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, bool match; u32 flags; - /* Passive scanning shouldn't trigger any device found events */ + /* Passive scanning shouldn't trigger any device found events, + * except for devices marked as CONN_REPORT for which we do send + * device found events. + */ if (hdev->le_scan_type == LE_SCAN_PASSIVE) { + struct hci_conn_params *param; + if (type == LE_ADV_IND || type == LE_ADV_DIRECT_IND) check_pending_le_conn(hdev, bdaddr, bdaddr_type); - return; + + if (!hdev->pend_le_reports) + return; + + if (type == LE_ADV_DIRECT_IND) + return; + + param = hci_conn_params_lookup(hdev, bdaddr, bdaddr_type); + if (!param || param->auto_connect != HCI_AUTO_CONN_REPORT) + return; + + if (type == LE_ADV_NONCONN_IND || type == LE_ADV_SCAN_IND) + flags = MGMT_DEV_FOUND_NOT_CONNECTABLE; + else + flags = 0; + mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL, + rssi, flags, data, len, NULL, 0); } /* When receiving non-connectable or scannable undirected From 079446c8a254d65da0378a45c2106dbf1ff6a769 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 2 Jul 2014 23:09:24 +0300 Subject: [PATCH 152/378] Bluetooth: Support scanning for devices using RPA When we're scanning for specific devices that use an RPA we need to convert the RPA to the identity address before looking up the entry in the connection parameters. This patch adds the necessary code to do this in the process_adv_report() function. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_event.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 87a704bd7eb7..dd70e3a4fea5 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4231,6 +4231,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, */ if (hdev->le_scan_type == LE_SCAN_PASSIVE) { struct hci_conn_params *param; + struct smp_irk *irk; if (type == LE_ADV_IND || type == LE_ADV_DIRECT_IND) check_pending_le_conn(hdev, bdaddr, bdaddr_type); @@ -4241,6 +4242,17 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, if (type == LE_ADV_DIRECT_IND) return; + /* Check if we need to convert to identity address */ + irk = hci_get_irk(hdev, bdaddr, bdaddr_type); + if (irk) { + bdaddr = &irk->bdaddr; + bdaddr_type = irk->addr_type; + } + + /* The conn params list only contains identity addresses */ + if (!hci_is_identity_address(bdaddr, bdaddr_type)) + return; + param = hci_conn_params_lookup(hdev, bdaddr, bdaddr_type); if (!param || param->auto_connect != HCI_AUTO_CONN_REPORT) return; From ba1d6936f6f83927f17a28ecc9cbb989fa0a7e34 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 3 Jul 2014 13:52:27 +0300 Subject: [PATCH 153/378] Bluetooth: Fix buffer overflow with variable length commands The handler for variable length commands were trying to calculate the expected length of the command based on the given parameter count, and then comparing that with the received data. However, the expected count was stored in a u16 which can easily overflow. With a carefully crafted command this can then be made to match the given data even though the parameter count is actually way too big, resulting in a buffer overflow when parsing the parameters. This patch fixes the issue by calculating a per-command maximum parameter count and returns INVALID_PARAMS if it is exceeded. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 0a82f08cd191..c01cc5e37a6f 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2468,6 +2468,8 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_load_link_keys *cp = data; + const u16 max_key_count = ((U16_MAX - sizeof(*cp)) / + sizeof(struct mgmt_link_key_info)); u16 key_count, expected_len; bool changed; int i; @@ -2479,6 +2481,12 @@ static int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, MGMT_STATUS_NOT_SUPPORTED); key_count = __le16_to_cpu(cp->key_count); + if (key_count > max_key_count) { + BT_ERR("load_link_keys: too big key_count value %u", + key_count); + return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, + MGMT_STATUS_INVALID_PARAMS); + } expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_link_key_info); @@ -4568,6 +4576,8 @@ static int load_irks(struct sock *sk, struct hci_dev *hdev, void *cp_data, u16 len) { struct mgmt_cp_load_irks *cp = cp_data; + const u16 max_irk_count = ((U16_MAX - sizeof(*cp)) / + sizeof(struct mgmt_irk_info)); u16 irk_count, expected_len; int i, err; @@ -4578,6 +4588,11 @@ static int load_irks(struct sock *sk, struct hci_dev *hdev, void *cp_data, MGMT_STATUS_NOT_SUPPORTED); irk_count = __le16_to_cpu(cp->irk_count); + if (irk_count > max_irk_count) { + BT_ERR("load_irks: too big irk_count value %u", irk_count); + return cmd_status(sk, hdev->id, MGMT_OP_LOAD_IRKS, + MGMT_STATUS_INVALID_PARAMS); + } expected_len = sizeof(*cp) + irk_count * sizeof(struct mgmt_irk_info); if (expected_len != len) { @@ -4647,6 +4662,8 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, void *cp_data, u16 len) { struct mgmt_cp_load_long_term_keys *cp = cp_data; + const u16 max_key_count = ((U16_MAX - sizeof(*cp)) / + sizeof(struct mgmt_ltk_info)); u16 key_count, expected_len; int i, err; @@ -4657,6 +4674,11 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, MGMT_STATUS_NOT_SUPPORTED); key_count = __le16_to_cpu(cp->key_count); + if (key_count > max_key_count) { + BT_ERR("load_ltks: too big key_count value %u", key_count); + return cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, + MGMT_STATUS_INVALID_PARAMS); + } expected_len = sizeof(*cp) + key_count * sizeof(struct mgmt_ltk_info); @@ -5204,6 +5226,8 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) { struct mgmt_cp_load_conn_param *cp = data; + const u16 max_param_count = ((U16_MAX - sizeof(*cp)) / + sizeof(struct mgmt_conn_param)); u16 param_count, expected_len; int i; @@ -5212,6 +5236,12 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data, MGMT_STATUS_NOT_SUPPORTED); param_count = __le16_to_cpu(cp->param_count); + if (param_count > max_param_count) { + BT_ERR("load_conn_param: too big param_count value %u", + param_count); + return cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM, + MGMT_STATUS_INVALID_PARAMS); + } expected_len = sizeof(*cp) + param_count * sizeof(struct mgmt_conn_param); From 617ca1bf11de84c23d1c83724fa89cfdc83b023a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 3 Jul 2014 19:33:47 +0300 Subject: [PATCH 154/378] Bluetooth: Fix missing update of pend_le_reports When calling Remove Device for an entry using HCI_AUTO_CONN_REPORT we need to decrement the pend_le_reports value correspondingly. This patch fixes one such missing action in the Remove Device command handler. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index c01cc5e37a6f..02a4d31fee30 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5198,6 +5198,9 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, goto unlock; } + if (params->auto_connect == HCI_AUTO_CONN_REPORT) + hdev->pend_le_reports--; + hci_pend_le_conn_del(hdev, &cp->addr.bdaddr, addr_type); list_del(¶ms->list); kfree(params); From 435a13d839abe8c8b9ebe1be635d1ab8f7352f56 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 3 Jul 2014 19:33:48 +0300 Subject: [PATCH 155/378] Bluetooth: Remove redundant IRK lookup When processing passive scanning results we need the resolved identity address both in check_pending_le_conn() as well as later in process_adv_report(). Since process_adv_report() calls check_pending_le_conn() we can simply resolve the IRK earlier in the function and thereby eliminate a second lookup. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_event.c | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index dd70e3a4fea5..643c5a8d4050 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4186,16 +4186,6 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) { struct hci_conn *conn; - struct smp_irk *irk; - - /* If this is a resolvable address, we should resolve it and then - * update address and address type variables. - */ - irk = hci_get_irk(hdev, addr, addr_type); - if (irk) { - addr = &irk->bdaddr; - addr_type = irk->addr_type; - } if (!hci_pend_le_conn_lookup(hdev, addr, addr_type)) return; @@ -4233,6 +4223,13 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, struct hci_conn_params *param; struct smp_irk *irk; + /* Check if we need to convert to identity address */ + irk = hci_get_irk(hdev, bdaddr, bdaddr_type); + if (irk) { + bdaddr = &irk->bdaddr; + bdaddr_type = irk->addr_type; + } + if (type == LE_ADV_IND || type == LE_ADV_DIRECT_IND) check_pending_le_conn(hdev, bdaddr, bdaddr_type); @@ -4242,13 +4239,6 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, if (type == LE_ADV_DIRECT_IND) return; - /* Check if we need to convert to identity address */ - irk = hci_get_irk(hdev, bdaddr, bdaddr_type); - if (irk) { - bdaddr = &irk->bdaddr; - bdaddr_type = irk->addr_type; - } - /* The conn params list only contains identity addresses */ if (!hci_is_identity_address(bdaddr, bdaddr_type)) return; From 912b42ef05a1e9f72a82c21d678a29c5055045d5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 3 Jul 2014 19:33:49 +0300 Subject: [PATCH 156/378] Bluetooth: Use hci_conn_params in pend_le_conns Since the connection parameters are always a basis for adding entries to hdev->pend_le_conns (so far of type bdaddr_list) it's simpler and more efficient to have the parameters themselves be the entries in the pend_le_conns list. We do this by adding another list_head to the hci_conn_params struct. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 9 +++-- net/bluetooth/hci_core.c | 68 ++++++++++---------------------- net/bluetooth/hci_event.c | 7 +++- net/bluetooth/mgmt.c | 4 +- 4 files changed, 33 insertions(+), 55 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b5f4405b41c2..09f9fb85d8fd 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -439,6 +439,7 @@ struct hci_chan { struct hci_conn_params { struct list_head list; + struct list_head pend_le_conn; bdaddr_t addr; u8 addr_type; @@ -867,10 +868,10 @@ void hci_conn_params_clear_all(struct hci_dev *hdev); void hci_conn_params_clear_disabled(struct hci_dev *hdev); void hci_conn_params_clear_enabled(struct hci_dev *hdev); -struct bdaddr_list *hci_pend_le_conn_lookup(struct hci_dev *hdev, - bdaddr_t *addr, u8 addr_type); -void hci_pend_le_conn_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); -void hci_pend_le_conn_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); +struct hci_conn_params *hci_pend_le_conn_lookup(struct hci_dev *hdev, + bdaddr_t *addr, u8 addr_type); +void hci_pend_le_conn_add(struct hci_dev *hdev, struct hci_conn_params *params); +void hci_pend_le_conn_del(struct hci_dev *hdev, struct hci_conn_params *params); void hci_pend_le_conns_clear(struct hci_dev *hdev); void hci_update_background_scan(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 41cc64429ea1..8882a6cd2876 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3427,73 +3427,46 @@ static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type) } /* This function requires the caller holds hdev->lock */ -struct bdaddr_list *hci_pend_le_conn_lookup(struct hci_dev *hdev, - bdaddr_t *addr, u8 addr_type) +struct hci_conn_params *hci_pend_le_conn_lookup(struct hci_dev *hdev, + bdaddr_t *addr, u8 addr_type) { - struct bdaddr_list *entry; + struct hci_conn_params *param; - list_for_each_entry(entry, &hdev->pend_le_conns, list) { - if (bacmp(&entry->bdaddr, addr) == 0 && - entry->bdaddr_type == addr_type) - return entry; + list_for_each_entry(param, &hdev->pend_le_conns, pend_le_conn) { + if (bacmp(¶m->addr, addr) == 0 && + param->addr_type == addr_type) + return param; } return NULL; } /* This function requires the caller holds hdev->lock */ -void hci_pend_le_conn_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) +void hci_pend_le_conn_add(struct hci_dev *hdev, struct hci_conn_params *params) { - struct bdaddr_list *entry; + list_del_init(¶ms->pend_le_conn); + list_add(¶ms->pend_le_conn, &hdev->pend_le_conns); - entry = hci_pend_le_conn_lookup(hdev, addr, addr_type); - if (entry) - goto done; + BT_DBG("addr %pMR (type %u)", ¶ms->addr, params->addr_type); - entry = kzalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) { - BT_ERR("Out of memory"); - return; - } - - bacpy(&entry->bdaddr, addr); - entry->bdaddr_type = addr_type; - - list_add(&entry->list, &hdev->pend_le_conns); - - BT_DBG("addr %pMR (type %u)", addr, addr_type); - -done: hci_update_background_scan(hdev); } /* This function requires the caller holds hdev->lock */ -void hci_pend_le_conn_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) +void hci_pend_le_conn_del(struct hci_dev *hdev, struct hci_conn_params *params) { - struct bdaddr_list *entry; + list_del_init(¶ms->pend_le_conn); - entry = hci_pend_le_conn_lookup(hdev, addr, addr_type); - if (!entry) - goto done; + BT_DBG("addr %pMR (type %u)", ¶ms->addr, params->addr_type); - list_del(&entry->list); - kfree(entry); - - BT_DBG("addr %pMR (type %u)", addr, addr_type); - -done: hci_update_background_scan(hdev); } /* This function requires the caller holds hdev->lock */ void hci_pend_le_conns_clear(struct hci_dev *hdev) { - struct bdaddr_list *entry, *tmp; - - list_for_each_entry_safe(entry, tmp, &hdev->pend_le_conns, list) { - list_del(&entry->list); - kfree(entry); - } + while (!list_empty(&hdev->pend_le_conns)) + list_del_init(hdev->pend_le_conns.next); BT_DBG("All LE pending connections cleared"); @@ -3523,6 +3496,7 @@ struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, params->addr_type = addr_type; list_add(¶ms->list, &hdev->le_conn_params); + INIT_LIST_HEAD(¶ms->pend_le_conn); params->conn_min_interval = hdev->le_conn_min_interval; params->conn_max_interval = hdev->le_conn_max_interval; @@ -3552,16 +3526,16 @@ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, switch (auto_connect) { case HCI_AUTO_CONN_DISABLED: case HCI_AUTO_CONN_LINK_LOSS: - hci_pend_le_conn_del(hdev, addr, addr_type); + hci_pend_le_conn_del(hdev, params); break; case HCI_AUTO_CONN_REPORT: if (params->auto_connect != HCI_AUTO_CONN_REPORT) hdev->pend_le_reports++; - hci_pend_le_conn_del(hdev, addr, addr_type); + hci_pend_le_conn_del(hdev, params); break; case HCI_AUTO_CONN_ALWAYS: if (!is_connected(hdev, addr, addr_type)) - hci_pend_le_conn_add(hdev, addr, addr_type); + hci_pend_le_conn_add(hdev, params); break; } @@ -3585,7 +3559,7 @@ void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) if (params->auto_connect == HCI_AUTO_CONN_REPORT) hdev->pend_le_reports--; - hci_pend_le_conn_del(hdev, addr, addr_type); + hci_pend_le_conn_del(hdev, params); list_del(¶ms->list); kfree(params); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 643c5a8d4050..4ebfcedd9ae7 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2209,7 +2209,7 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) /* Fall through */ case HCI_AUTO_CONN_ALWAYS: - hci_pend_le_conn_add(hdev, &conn->dst, conn->dst_type); + hci_pend_le_conn_add(hdev, params); break; default: @@ -4036,6 +4036,7 @@ static void hci_disconn_phylink_complete_evt(struct hci_dev *hdev, static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_le_conn_complete *ev = (void *) skb->data; + struct hci_conn_params *params; struct hci_conn *conn; struct smp_irk *irk; u8 addr_type; @@ -4152,7 +4153,9 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_proto_connect_cfm(conn, ev->status); - hci_pend_le_conn_del(hdev, &conn->dst, conn->dst_type); + params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); + if (params) + hci_pend_le_conn_del(hdev, params); unlock: hci_dev_unlock(hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 02a4d31fee30..59ca4057955c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5201,7 +5201,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, if (params->auto_connect == HCI_AUTO_CONN_REPORT) hdev->pend_le_reports--; - hci_pend_le_conn_del(hdev, &cp->addr.bdaddr, addr_type); + hci_pend_le_conn_del(hdev, params); list_del(¶ms->list); kfree(params); @@ -5514,7 +5514,7 @@ static void restart_le_auto_conns(struct hci_dev *hdev) list_for_each_entry(p, &hdev->le_conn_params, list) { if (p->auto_connect == HCI_AUTO_CONN_ALWAYS) { - hci_pend_le_conn_add(hdev, &p->addr, p->addr_type); + hci_pend_le_conn_add(hdev, p); added = true; } } From bb5ce4d018f896403d7a394ec56a550e7890b563 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 3 Jul 2014 19:33:50 +0300 Subject: [PATCH 157/378] Bluetooth: Remove unnecessary checks for auto-connected devices If a device is in the pend_le_conns list it cannot at the same time also have the need to be notified through mgmt_device_found. By making check_pending_le_conn return whether it found an entry or not we can avoid unnecessary checks in process_adv_report(). Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_event.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 4ebfcedd9ae7..e0b439d4f958 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4185,18 +4185,18 @@ static void hci_le_conn_update_complete_evt(struct hci_dev *hdev, } /* This function requires the caller holds hdev->lock */ -static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, +static bool check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) { struct hci_conn *conn; if (!hci_pend_le_conn_lookup(hdev, addr, addr_type)) - return; + return false; conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW, HCI_AT_NO_BONDING); if (!IS_ERR(conn)) - return; + return true; switch (PTR_ERR(conn)) { case -EBUSY: @@ -4209,6 +4209,8 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, default: BT_DBG("Failed to connect: err %ld", PTR_ERR(conn)); } + + return true; } static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, @@ -4233,8 +4235,10 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, bdaddr_type = irk->addr_type; } - if (type == LE_ADV_IND || type == LE_ADV_DIRECT_IND) - check_pending_le_conn(hdev, bdaddr, bdaddr_type); + if (type == LE_ADV_IND || type == LE_ADV_DIRECT_IND) { + if (check_pending_le_conn(hdev, bdaddr, bdaddr_type)) + return; + } if (!hdev->pend_le_reports) return; From 738f61859d08771e12b552d043b48c8fc13708d1 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 3 Jul 2014 19:33:51 +0300 Subject: [PATCH 158/378] Bluetooth: Add identity address check in param lookup functions Since we only store entries with identity addresses in the le_conn_params and pend_le_conns lists we can avoid unnecessary lookups by checking for an identity address before diving into the lists themselves. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_core.c | 8 ++++++++ net/bluetooth/hci_event.c | 4 ---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 8882a6cd2876..750f969df8db 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3399,6 +3399,10 @@ struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev, { struct hci_conn_params *params; + /* The conn params list only contains identity addresses */ + if (!hci_is_identity_address(addr, addr_type)) + return NULL; + list_for_each_entry(params, &hdev->le_conn_params, list) { if (bacmp(¶ms->addr, addr) == 0 && params->addr_type == addr_type) { @@ -3432,6 +3436,10 @@ struct hci_conn_params *hci_pend_le_conn_lookup(struct hci_dev *hdev, { struct hci_conn_params *param; + /* The list only contains identity addresses */ + if (!hci_is_identity_address(addr, addr_type)) + return NULL; + list_for_each_entry(param, &hdev->pend_le_conns, pend_le_conn) { if (bacmp(¶m->addr, addr) == 0 && param->addr_type == addr_type) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index e0b439d4f958..20317e516e74 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4246,10 +4246,6 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, if (type == LE_ADV_DIRECT_IND) return; - /* The conn params list only contains identity addresses */ - if (!hci_is_identity_address(bdaddr, bdaddr_type)) - return; - param = hci_conn_params_lookup(hdev, bdaddr, bdaddr_type); if (!param || param->auto_connect != HCI_AUTO_CONN_REPORT) return; From 701a9e619347ac06d59481db14bbc4df99763270 Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Tue, 1 Jul 2014 14:39:04 -0700 Subject: [PATCH 159/378] mwifiex: initialize Tx/Rx info of a packet correctly There are few places at the begining of Tx/Rx paths where tx_info/rx_info is not correctly initialized. This patch takes care of it. Signed-off-by: Amitkumar Karwar Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/11n_aggr.c | 1 + drivers/net/wireless/mwifiex/cfg80211.c | 1 + drivers/net/wireless/mwifiex/cmdevt.c | 1 + drivers/net/wireless/mwifiex/sta_tx.c | 1 + drivers/net/wireless/mwifiex/tdls.c | 2 ++ drivers/net/wireless/mwifiex/txrx.c | 1 + drivers/net/wireless/mwifiex/uap_txrx.c | 1 + 7 files changed, 8 insertions(+) diff --git a/drivers/net/wireless/mwifiex/11n_aggr.c b/drivers/net/wireless/mwifiex/11n_aggr.c index 5b32106182f8..fe0f66f73507 100644 --- a/drivers/net/wireless/mwifiex/11n_aggr.c +++ b/drivers/net/wireless/mwifiex/11n_aggr.c @@ -185,6 +185,7 @@ mwifiex_11n_aggregate_pkt(struct mwifiex_private *priv, skb_reserve(skb_aggr, headroom + sizeof(struct txpd)); tx_info_aggr = MWIFIEX_SKB_TXCB(skb_aggr); + memset(tx_info_aggr, 0, sizeof(*tx_info_aggr)); tx_info_aggr->bss_type = tx_info_src->bss_type; tx_info_aggr->bss_num = tx_info_src->bss_num; diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index e95dec91a561..b511613bba2d 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -220,6 +220,7 @@ mwifiex_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, } tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); tx_info->bss_num = priv->bss_num; tx_info->bss_type = priv->bss_type; tx_info->pkt_len = pkt_len; diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index 8dee6c86f4f1..c161141f6c39 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -453,6 +453,7 @@ int mwifiex_process_event(struct mwifiex_adapter *adapter) if (skb) { rx_info = MWIFIEX_SKB_RXCB(skb); + memset(rx_info, 0, sizeof(*rx_info)); rx_info->bss_num = priv->bss_num; rx_info->bss_type = priv->bss_type; } diff --git a/drivers/net/wireless/mwifiex/sta_tx.c b/drivers/net/wireless/mwifiex/sta_tx.c index 5fce7e78a36e..70eb863c7249 100644 --- a/drivers/net/wireless/mwifiex/sta_tx.c +++ b/drivers/net/wireless/mwifiex/sta_tx.c @@ -150,6 +150,7 @@ int mwifiex_send_null_packet(struct mwifiex_private *priv, u8 flags) return -1; tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); tx_info->bss_num = priv->bss_num; tx_info->bss_type = priv->bss_type; tx_info->pkt_len = data_len - (sizeof(struct txpd) + INTF_HEADER_LEN); diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c index e73034fbbde9..0e88364e0c67 100644 --- a/drivers/net/wireless/mwifiex/tdls.c +++ b/drivers/net/wireless/mwifiex/tdls.c @@ -605,6 +605,7 @@ int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, } tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); tx_info->bss_num = priv->bss_num; tx_info->bss_type = priv->bss_type; @@ -760,6 +761,7 @@ int mwifiex_send_tdls_action_frame(struct mwifiex_private *priv, const u8 *peer, skb->priority = MWIFIEX_PRIO_VI; tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); tx_info->bss_num = priv->bss_num; tx_info->bss_type = priv->bss_type; tx_info->flags |= MWIFIEX_BUF_FLAG_TDLS_PKT; diff --git a/drivers/net/wireless/mwifiex/txrx.c b/drivers/net/wireless/mwifiex/txrx.c index 37f26afd4314..fd7e5b9b4581 100644 --- a/drivers/net/wireless/mwifiex/txrx.c +++ b/drivers/net/wireless/mwifiex/txrx.c @@ -55,6 +55,7 @@ int mwifiex_handle_rx_packet(struct mwifiex_adapter *adapter, return -1; } + memset(rx_info, 0, sizeof(*rx_info)); rx_info->bss_num = priv->bss_num; rx_info->bss_type = priv->bss_type; diff --git a/drivers/net/wireless/mwifiex/uap_txrx.c b/drivers/net/wireless/mwifiex/uap_txrx.c index 9a56bc61cb1d..b0601b91cc4f 100644 --- a/drivers/net/wireless/mwifiex/uap_txrx.c +++ b/drivers/net/wireless/mwifiex/uap_txrx.c @@ -175,6 +175,7 @@ static void mwifiex_uap_queue_bridged_pkt(struct mwifiex_private *priv, } tx_info = MWIFIEX_SKB_TXCB(skb); + memset(tx_info, 0, sizeof(*tx_info)); tx_info->bss_num = priv->bss_num; tx_info->bss_type = priv->bss_type; tx_info->flags |= MWIFIEX_BUF_FLAG_BRIDGED_PKT; From 9fc3bfb681bdf59999f56072fff4632a5abea897 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 4 Jul 2014 00:46:56 +0200 Subject: [PATCH 160/378] Bluetooth: Add support for controller configuration info command The Read Controller Configuration Information command allows retrieving details about possible configurations option. The supported options are returned and also the missing options (if any). Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 11 +++++++++++ net/bluetooth/mgmt.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index e0786cfa5490..0579eb3952e3 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -97,6 +97,7 @@ struct mgmt_rp_read_index_list { #define MGMT_SETTING_SECURE_CONN 0x00000800 #define MGMT_SETTING_DEBUG_KEYS 0x00001000 #define MGMT_SETTING_PRIVACY 0x00002000 +#define MGMT_SETTING_CONFIGURATION 0x00004000 #define MGMT_OP_READ_INFO 0x0004 #define MGMT_READ_INFO_SIZE 0 @@ -471,6 +472,16 @@ struct mgmt_rp_read_unconf_index_list { __le16 index[0]; } __packed; +#define MGMT_OPTION_PUBLIC_ADDRESS 0x00000001 + +#define MGMT_OP_READ_CONFIG_INFO 0x0037 +#define MGMT_READ_CONFIG_INFO_SIZE 0 +struct mgmt_rp_read_config_info { + __le16 manufacturer; + __le32 supported_options; + __le32 missing_options; +} __packed; + #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 59ca4057955c..474b6dcdf665 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -90,6 +90,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_REMOVE_DEVICE, MGMT_OP_LOAD_CONN_PARAM, MGMT_OP_READ_UNCONF_INDEX_LIST, + MGMT_OP_READ_CONFIG_INFO, }; static const u16 mgmt_events[] = { @@ -440,6 +441,29 @@ static int read_unconf_index_list(struct sock *sk, struct hci_dev *hdev, return err; } +static int read_config_info(struct sock *sk, struct hci_dev *hdev, + void *data, u16 data_len) +{ + struct mgmt_rp_read_config_info rp; + + BT_DBG("sock %p %s", sk, hdev->name); + + hci_dev_lock(hdev); + + memset(&rp, 0, sizeof(rp)); + rp.manufacturer = cpu_to_le16(hdev->manufacturer); + if (hdev->set_bdaddr) + rp.supported_options = cpu_to_le32(MGMT_OPTION_PUBLIC_ADDRESS); + else + rp.supported_options = cpu_to_le32(0); + rp.missing_options = cpu_to_le32(0); + + hci_dev_unlock(hdev); + + return cmd_complete(sk, hdev->id, MGMT_OP_READ_CONFIG_INFO, 0, &rp, + sizeof(rp)); +} + static u32 get_supported_settings(struct hci_dev *hdev) { u32 settings = 0; @@ -472,6 +496,9 @@ static u32 get_supported_settings(struct hci_dev *hdev) settings |= MGMT_SETTING_PRIVACY; } + if (hdev->set_bdaddr) + settings |= MGMT_SETTING_CONFIGURATION; + return settings; } @@ -5371,6 +5398,7 @@ static const struct mgmt_handler { { remove_device, false, MGMT_REMOVE_DEVICE_SIZE }, { load_conn_param, true, MGMT_LOAD_CONN_PARAM_SIZE }, { read_unconf_index_list, false, MGMT_READ_UNCONF_INDEX_LIST_SIZE }, + { read_config_info, false, MGMT_READ_CONFIG_INFO_SIZE }, }; int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) From 97bf2e99934bdfd3f91914e6c935271b62567470 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 4 Jul 2014 12:37:16 +0300 Subject: [PATCH 161/378] Bluetooth: Fix missing return statement in process_adv_report If we're doing passive scanning we shouldn't proceed with any of the code that deals with active scanning (pending reports, etc.). This patch fixes a missing return statement for the passive scanning section in the process_adv_report() function. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_event.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 20317e516e74..e0407e674061 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4256,6 +4256,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, flags = 0; mgmt_device_found(hdev, bdaddr, LE_LINK, bdaddr_type, NULL, rssi, flags, data, len, NULL, 0); + return; } /* When receiving non-connectable or scannable undirected From 93450c75448e370659ce7ca9c192298596fb055e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 4 Jul 2014 12:37:17 +0300 Subject: [PATCH 162/378] Bluetooth: Convert pend_le_conn list to a generic action list In preparation to store also HCI_AUTO_CONN_REPORT entries in a list it makes sense to convert the existing pend_le_conn list head of hci_conn_params into a more generically named "action". This makes sense because a parameter entry will never participate in more than one action list. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_core.c | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 09f9fb85d8fd..3b1143caa380 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -439,7 +439,7 @@ struct hci_chan { struct hci_conn_params { struct list_head list; - struct list_head pend_le_conn; + struct list_head action; bdaddr_t addr; u8 addr_type; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 750f969df8db..fed2a0648d3d 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3440,7 +3440,7 @@ struct hci_conn_params *hci_pend_le_conn_lookup(struct hci_dev *hdev, if (!hci_is_identity_address(addr, addr_type)) return NULL; - list_for_each_entry(param, &hdev->pend_le_conns, pend_le_conn) { + list_for_each_entry(param, &hdev->pend_le_conns, action) { if (bacmp(¶m->addr, addr) == 0 && param->addr_type == addr_type) return param; @@ -3452,8 +3452,8 @@ struct hci_conn_params *hci_pend_le_conn_lookup(struct hci_dev *hdev, /* This function requires the caller holds hdev->lock */ void hci_pend_le_conn_add(struct hci_dev *hdev, struct hci_conn_params *params) { - list_del_init(¶ms->pend_le_conn); - list_add(¶ms->pend_le_conn, &hdev->pend_le_conns); + list_del_init(¶ms->action); + list_add(¶ms->action, &hdev->pend_le_conns); BT_DBG("addr %pMR (type %u)", ¶ms->addr, params->addr_type); @@ -3463,7 +3463,7 @@ void hci_pend_le_conn_add(struct hci_dev *hdev, struct hci_conn_params *params) /* This function requires the caller holds hdev->lock */ void hci_pend_le_conn_del(struct hci_dev *hdev, struct hci_conn_params *params) { - list_del_init(¶ms->pend_le_conn); + list_del_init(¶ms->action); BT_DBG("addr %pMR (type %u)", ¶ms->addr, params->addr_type); @@ -3504,7 +3504,7 @@ struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, params->addr_type = addr_type; list_add(¶ms->list, &hdev->le_conn_params); - INIT_LIST_HEAD(¶ms->pend_le_conn); + INIT_LIST_HEAD(¶ms->action); params->conn_min_interval = hdev->le_conn_min_interval; params->conn_max_interval = hdev->le_conn_max_interval; From 66f8455aeac3427110d451534567eb1b9aea6929 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 4 Jul 2014 12:37:18 +0300 Subject: [PATCH 163/378] Bluetooth: Convert pend_le_reports into a list To simplify manipulation and lookup of hci_conn_params entries of the type HCI_AUTO_CONN_REPORT it makes sense to store them in their own list. The new action list_head in hci_conn_params is used for this purpose. Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_core.c | 17 +++++++++++------ net/bluetooth/hci_event.c | 2 +- net/bluetooth/mgmt.c | 4 ++-- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3b1143caa380..9670f4c66ee3 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -312,7 +312,7 @@ struct hci_dev { struct list_head le_white_list; struct list_head le_conn_params; struct list_head pend_le_conns; - unsigned int pend_le_reports; + struct list_head pend_le_reports; struct hci_dev_stats stat; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index fed2a0648d3d..296f44748636 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3529,7 +3529,7 @@ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, if (params->auto_connect == HCI_AUTO_CONN_REPORT && auto_connect != HCI_AUTO_CONN_REPORT) - hdev->pend_le_reports--; + list_del_init(¶ms->action); switch (auto_connect) { case HCI_AUTO_CONN_DISABLED: @@ -3537,8 +3537,11 @@ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, hci_pend_le_conn_del(hdev, params); break; case HCI_AUTO_CONN_REPORT: - if (params->auto_connect != HCI_AUTO_CONN_REPORT) - hdev->pend_le_reports++; + if (params->auto_connect != HCI_AUTO_CONN_REPORT) { + list_del_init(¶ms->action); + list_add(¶ms->action, + &hdev->pend_le_reports); + } hci_pend_le_conn_del(hdev, params); break; case HCI_AUTO_CONN_ALWAYS: @@ -3565,7 +3568,7 @@ void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) return; if (params->auto_connect == HCI_AUTO_CONN_REPORT) - hdev->pend_le_reports--; + list_del_init(¶ms->action); hci_pend_le_conn_del(hdev, params); @@ -3599,7 +3602,7 @@ void hci_conn_params_clear_enabled(struct hci_dev *hdev) if (params->auto_connect == HCI_AUTO_CONN_DISABLED) continue; if (params->auto_connect == HCI_AUTO_CONN_REPORT) - hdev->pend_le_reports--; + list_del_init(¶ms->action); list_del(¶ms->list); kfree(params); } @@ -3859,6 +3862,7 @@ struct hci_dev *hci_alloc_dev(void) INIT_LIST_HEAD(&hdev->le_white_list); INIT_LIST_HEAD(&hdev->le_conn_params); INIT_LIST_HEAD(&hdev->pend_le_conns); + INIT_LIST_HEAD(&hdev->pend_le_reports); INIT_LIST_HEAD(&hdev->conn_hash.list); INIT_WORK(&hdev->rx_work, hci_rx_work); @@ -5362,7 +5366,8 @@ void hci_update_background_scan(struct hci_dev *hdev) hci_req_init(&req, hdev); - if (list_empty(&hdev->pend_le_conns) && !hdev->pend_le_reports) { + if (list_empty(&hdev->pend_le_conns) && + list_empty(&hdev->pend_le_reports)) { /* If there is no pending LE connections or devices * to be scanned for, we should stop the background * scanning. diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index e0407e674061..2a2ef698e851 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4240,7 +4240,7 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, return; } - if (!hdev->pend_le_reports) + if (list_empty(&hdev->pend_le_reports)) return; if (type == LE_ADV_DIRECT_IND) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 474b6dcdf665..a823cccf81f1 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5226,7 +5226,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, } if (params->auto_connect == HCI_AUTO_CONN_REPORT) - hdev->pend_le_reports--; + list_del_init(¶ms->action); hci_pend_le_conn_del(hdev, params); list_del(¶ms->list); @@ -6540,7 +6540,7 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, if (!hci_discovery_active(hdev)) { if (link_type == ACL_LINK) return; - if (link_type == LE_LINK && !hdev->pend_le_reports) + if (link_type == LE_LINK && list_empty(&hdev->pend_le_reports)) return; } From a2f41a8f370a940629a0e42258ab9e941b1dbc83 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 4 Jul 2014 12:37:19 +0300 Subject: [PATCH 164/378] Bluetooth: Simplify use of hci_pend_le_conns_clear() Now that pend_le_connections is a list of hci_conn_params entries we can simply remove items from that list as we iterate through the global list of le_conn_params. This also moves the responsibility of calling hci_update_background_scan() to the functions that were previously calling hci_pend_le_conns_clear(). The only user that's left for hci_pend_le_conns_clear() is hci_dev_do_close() which anyway does not need to call hci_update_background_scan(). Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_core.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 296f44748636..dd8aa5f86810 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3477,8 +3477,6 @@ void hci_pend_le_conns_clear(struct hci_dev *hdev) list_del_init(hdev->pend_le_conns.next); BT_DBG("All LE pending connections cleared"); - - hci_update_background_scan(hdev); } /* This function requires the caller holds hdev->lock */ @@ -3601,13 +3599,12 @@ void hci_conn_params_clear_enabled(struct hci_dev *hdev) list_for_each_entry_safe(params, tmp, &hdev->le_conn_params, list) { if (params->auto_connect == HCI_AUTO_CONN_DISABLED) continue; - if (params->auto_connect == HCI_AUTO_CONN_REPORT) - list_del_init(¶ms->action); + list_del(¶ms->action); list_del(¶ms->list); kfree(params); } - hci_pend_le_conns_clear(hdev); + hci_update_background_scan(hdev); BT_DBG("All enabled LE connection parameters were removed"); } @@ -3618,11 +3615,12 @@ void hci_conn_params_clear_all(struct hci_dev *hdev) struct hci_conn_params *params, *tmp; list_for_each_entry_safe(params, tmp, &hdev->le_conn_params, list) { + list_del(¶ms->action); list_del(¶ms->list); kfree(params); } - hci_pend_le_conns_clear(hdev); + hci_update_background_scan(hdev); BT_DBG("All LE connection parameters were removed"); } From 42ce26de67e13c50885e7856ff91aaeedf07a81b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 4 Jul 2014 12:37:20 +0300 Subject: [PATCH 165/378] Bluetooth: Don't bother doing anything if auto_connect doesn't change When hci_conn_params_set() is called if the new auto_connect value is the same as the old one we don't need to take any action. Simply return success from the function in this case. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index dd8aa5f86810..0601fcbd21eb 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3525,6 +3525,9 @@ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, if (!params) return -EIO; + if (params->auto_connect == auto_connect) + return 0; + if (params->auto_connect == HCI_AUTO_CONN_REPORT && auto_connect != HCI_AUTO_CONN_REPORT) list_del_init(¶ms->action); From 95305baa779223060c7129100424da6c0d01045b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 4 Jul 2014 12:37:21 +0300 Subject: [PATCH 166/378] Bluetooth: Simplify hci_conn_params->action list usage Since params->action is used for both the pend_le_conns and pend_le_reports lists we can simplify the adding and deleting of the lists considerably. For example, when deleting entries in most situations we no-longer need to check the auto_connect value but can directly proceed with calling list_del_init on param->action (which is safe even if the entry is not part of any list). Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_core.c | 28 +++++++++++----------------- net/bluetooth/hci_event.c | 6 ++++-- net/bluetooth/mgmt.c | 6 ++---- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 0601fcbd21eb..3b0da6eeb449 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3528,26 +3528,22 @@ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, if (params->auto_connect == auto_connect) return 0; - if (params->auto_connect == HCI_AUTO_CONN_REPORT && - auto_connect != HCI_AUTO_CONN_REPORT) - list_del_init(¶ms->action); + list_del_init(¶ms->action); switch (auto_connect) { case HCI_AUTO_CONN_DISABLED: case HCI_AUTO_CONN_LINK_LOSS: - hci_pend_le_conn_del(hdev, params); + hci_update_background_scan(hdev); break; case HCI_AUTO_CONN_REPORT: - if (params->auto_connect != HCI_AUTO_CONN_REPORT) { - list_del_init(¶ms->action); - list_add(¶ms->action, - &hdev->pend_le_reports); - } - hci_pend_le_conn_del(hdev, params); + list_add(¶ms->action, &hdev->pend_le_reports); + hci_update_background_scan(hdev); break; case HCI_AUTO_CONN_ALWAYS: - if (!is_connected(hdev, addr, addr_type)) - hci_pend_le_conn_add(hdev, params); + if (!is_connected(hdev, addr, addr_type)) { + list_add(¶ms->action, &hdev->pend_le_conns); + hci_update_background_scan(hdev); + } break; } @@ -3568,14 +3564,12 @@ void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) if (!params) return; - if (params->auto_connect == HCI_AUTO_CONN_REPORT) - list_del_init(¶ms->action); - - hci_pend_le_conn_del(hdev, params); - + list_del(¶ms->action); list_del(¶ms->list); kfree(params); + hci_update_background_scan(hdev); + BT_DBG("addr %pMR (type %u)", addr, addr_type); } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 2a2ef698e851..0ccdf9df2188 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4154,8 +4154,10 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_proto_connect_cfm(conn, ev->status); params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); - if (params) - hci_pend_le_conn_del(hdev, params); + if (params) { + list_del_init(¶ms->action); + hci_update_background_scan(hdev); + } unlock: hci_dev_unlock(hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index a823cccf81f1..77c64b8cb7e2 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5225,12 +5225,10 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, goto unlock; } - if (params->auto_connect == HCI_AUTO_CONN_REPORT) - list_del_init(¶ms->action); - - hci_pend_le_conn_del(hdev, params); + list_del_init(¶ms->action); list_del(¶ms->list); kfree(params); + hci_update_background_scan(hdev); device_removed(sk, hdev, &cp->addr.bdaddr, cp->addr.type); } else { From ae44e5d19e870385d1e73ce248c19ea4761bb40c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 4 Jul 2014 12:37:22 +0300 Subject: [PATCH 167/378] Bluetooth: Remove unused hci_pend_le_conn_del() function Now that there are no-longer any users of the hci_pend_le_conn_del() function we can simply go ahead and remove it. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 - net/bluetooth/hci_core.c | 10 ---------- 2 files changed, 11 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 9670f4c66ee3..758146c513d3 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -871,7 +871,6 @@ void hci_conn_params_clear_enabled(struct hci_dev *hdev); struct hci_conn_params *hci_pend_le_conn_lookup(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); void hci_pend_le_conn_add(struct hci_dev *hdev, struct hci_conn_params *params); -void hci_pend_le_conn_del(struct hci_dev *hdev, struct hci_conn_params *params); void hci_pend_le_conns_clear(struct hci_dev *hdev); void hci_update_background_scan(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 3b0da6eeb449..6eadde820333 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3460,16 +3460,6 @@ void hci_pend_le_conn_add(struct hci_dev *hdev, struct hci_conn_params *params) hci_update_background_scan(hdev); } -/* This function requires the caller holds hdev->lock */ -void hci_pend_le_conn_del(struct hci_dev *hdev, struct hci_conn_params *params) -{ - list_del_init(¶ms->action); - - BT_DBG("addr %pMR (type %u)", ¶ms->addr, params->addr_type); - - hci_update_background_scan(hdev); -} - /* This function requires the caller holds hdev->lock */ void hci_pend_le_conns_clear(struct hci_dev *hdev) { From d7347f3cc2b63be0ea35b3239faf4b32fde2fb44 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 4 Jul 2014 12:37:23 +0300 Subject: [PATCH 168/378] Bluetooth: Fix clearing and restarting all LE actions on power cycle When powering off (hci_dev_do_close) we should clear both the pend_le_reports and pend_le_conns types of entries. When powering on respectively we should populate both lists. This patch converts the hci_pend_le_conns_clear() function into hci_pend_le_actions_clear() (which can now be static) and converts the restart_le_auto_conns() function into restart_le_actions(). Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 - net/bluetooth/hci_core.c | 23 +++++++++++----------- net/bluetooth/mgmt.c | 33 ++++++++++++++++---------------- 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 758146c513d3..9208b18cb1e9 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -871,7 +871,6 @@ void hci_conn_params_clear_enabled(struct hci_dev *hdev); struct hci_conn_params *hci_pend_le_conn_lookup(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); void hci_pend_le_conn_add(struct hci_dev *hdev, struct hci_conn_params *params); -void hci_pend_le_conns_clear(struct hci_dev *hdev); void hci_update_background_scan(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 6eadde820333..3a1a25dcb2bc 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2354,6 +2354,17 @@ done: return err; } +/* This function requires the caller holds hdev->lock */ +static void hci_pend_le_actions_clear(struct hci_dev *hdev) +{ + struct hci_conn_params *p; + + list_for_each_entry(p, &hdev->le_conn_params, list) + list_del_init(&p->action); + + BT_DBG("All LE pending actions cleared"); +} + static int hci_dev_do_close(struct hci_dev *hdev) { BT_DBG("%s %p", hdev->name, hdev); @@ -2391,7 +2402,7 @@ static int hci_dev_do_close(struct hci_dev *hdev) hci_dev_lock(hdev); hci_inquiry_cache_flush(hdev); hci_conn_hash_flush(hdev); - hci_pend_le_conns_clear(hdev); + hci_pend_le_actions_clear(hdev); hci_dev_unlock(hdev); hci_notify(hdev, HCI_DEV_DOWN); @@ -3460,16 +3471,6 @@ void hci_pend_le_conn_add(struct hci_dev *hdev, struct hci_conn_params *params) hci_update_background_scan(hdev); } -/* This function requires the caller holds hdev->lock */ -void hci_pend_le_conns_clear(struct hci_dev *hdev) -{ - while (!list_empty(&hdev->pend_le_conns)) - list_del_init(hdev->pend_le_conns.next); - - BT_DBG("All LE pending connections cleared"); -} - -/* This function requires the caller holds hdev->lock */ struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 77c64b8cb7e2..f1672b15c0f3 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5533,29 +5533,28 @@ void mgmt_index_removed(struct hci_dev *hdev) } /* This function requires the caller holds hdev->lock */ -static void restart_le_auto_conns(struct hci_dev *hdev) +static void restart_le_actions(struct hci_dev *hdev) { struct hci_conn_params *p; - bool added = false; list_for_each_entry(p, &hdev->le_conn_params, list) { - if (p->auto_connect == HCI_AUTO_CONN_ALWAYS) { - hci_pend_le_conn_add(hdev, p); - added = true; + /* Needed for AUTO_OFF case where might not "really" + * have been powered off. + */ + list_del_init(&p->action); + + switch (p->auto_connect) { + case HCI_AUTO_CONN_ALWAYS: + list_add(&p->action, &hdev->pend_le_conns); + break; + case HCI_AUTO_CONN_REPORT: + list_add(&p->action, &hdev->pend_le_reports); + break; + default: + break; } } - /* Calling hci_pend_le_conn_add will actually already trigger - * background scanning when needed. So no need to trigger it - * just another time. - * - * This check is here to avoid an unneeded restart of the - * passive scanning. Since this is during the controller - * power up phase the duplicate filtering is not an issue. - */ - if (added) - return; - hci_update_background_scan(hdev); } @@ -5567,7 +5566,7 @@ static void powered_complete(struct hci_dev *hdev, u8 status) hci_dev_lock(hdev); - restart_le_auto_conns(hdev); + restart_le_actions(hdev); mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); From 418025d1c390b3979f0fd3bb639f0a15026d0530 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 4 Jul 2014 12:37:24 +0300 Subject: [PATCH 169/378] Bluetooth: Remove unnecessary usage of hci_pend_le_conn_add This is the last place using hci_pend_le_conn_add() and we can more just as simply manipulate the list directly here. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_event.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 0ccdf9df2188..9e0b2e888bba 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2209,7 +2209,9 @@ static void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) /* Fall through */ case HCI_AUTO_CONN_ALWAYS: - hci_pend_le_conn_add(hdev, params); + list_del_init(¶ms->action); + list_add(¶ms->action, &hdev->pend_le_conns); + hci_update_background_scan(hdev); break; default: From d9b3ad7df101a6490272771f1aabbaa623a196f3 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 4 Jul 2014 12:37:25 +0300 Subject: [PATCH 170/378] Bluetooth: Remove unused hci_pend_le_conn_add function Since there are no more users of this function we can simply go ahead and remove it. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 - net/bluetooth/hci_core.c | 10 ---------- 2 files changed, 11 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 9208b18cb1e9..c2bc9180cb79 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -870,7 +870,6 @@ void hci_conn_params_clear_enabled(struct hci_dev *hdev); struct hci_conn_params *hci_pend_le_conn_lookup(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); -void hci_pend_le_conn_add(struct hci_dev *hdev, struct hci_conn_params *params); void hci_update_background_scan(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 3a1a25dcb2bc..38a0c5cd0ee4 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3461,16 +3461,6 @@ struct hci_conn_params *hci_pend_le_conn_lookup(struct hci_dev *hdev, } /* This function requires the caller holds hdev->lock */ -void hci_pend_le_conn_add(struct hci_dev *hdev, struct hci_conn_params *params) -{ - list_del_init(¶ms->action); - list_add(¶ms->action, &hdev->pend_le_conns); - - BT_DBG("addr %pMR (type %u)", ¶ms->addr, params->addr_type); - - hci_update_background_scan(hdev); -} - struct hci_conn_params *hci_conn_params_add(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type) { From 501f882741b139da22bb3ba4bc615a6eadce5038 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 4 Jul 2014 12:37:26 +0300 Subject: [PATCH 171/378] Bluetooth: Make hci_pend_le_conn_lookup more general purposed In some circumstances we need to look up entries in pend_le_conns and in other in pend_le_reports. This patch converts the existing lookup function for pend_le_conns to something that can be used for both lists. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 5 +++-- net/bluetooth/hci_core.c | 6 +++--- net/bluetooth/hci_event.c | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index c2bc9180cb79..1cae4d3a629d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -868,8 +868,9 @@ void hci_conn_params_clear_all(struct hci_dev *hdev); void hci_conn_params_clear_disabled(struct hci_dev *hdev); void hci_conn_params_clear_enabled(struct hci_dev *hdev); -struct hci_conn_params *hci_pend_le_conn_lookup(struct hci_dev *hdev, - bdaddr_t *addr, u8 addr_type); +struct hci_conn_params *hci_pend_le_action_lookup(struct list_head *list, + bdaddr_t *addr, + u8 addr_type); void hci_update_background_scan(struct hci_dev *hdev); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 38a0c5cd0ee4..038b4748375b 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3442,8 +3442,8 @@ static bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type) } /* This function requires the caller holds hdev->lock */ -struct hci_conn_params *hci_pend_le_conn_lookup(struct hci_dev *hdev, - bdaddr_t *addr, u8 addr_type) +struct hci_conn_params *hci_pend_le_action_lookup(struct list_head *list, + bdaddr_t *addr, u8 addr_type) { struct hci_conn_params *param; @@ -3451,7 +3451,7 @@ struct hci_conn_params *hci_pend_le_conn_lookup(struct hci_dev *hdev, if (!hci_is_identity_address(addr, addr_type)) return NULL; - list_for_each_entry(param, &hdev->pend_le_conns, action) { + list_for_each_entry(param, list, action) { if (bacmp(¶m->addr, addr) == 0 && param->addr_type == addr_type) return param; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 9e0b2e888bba..9274cd228995 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4194,7 +4194,7 @@ static bool check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, { struct hci_conn *conn; - if (!hci_pend_le_conn_lookup(hdev, addr, addr_type)) + if (!hci_pend_le_action_lookup(&hdev->pend_le_conns, addr, addr_type)) return false; conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW, From a7545f2afcfc49fd5341fe51e943064eefe20ea1 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 4 Jul 2014 12:37:27 +0300 Subject: [PATCH 172/378] Bluetooth: Use hci_pend_le_action_lookup to look up report entries Instead of looking through the entire list of entries we can more efficiently use the new hci_pend_le_action_lookup() function to look up entries specifically in the pend_le_reports list. Since the search is now limited to the right list we can also remove an unnecessary check for list_empty() before the lookup. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_event.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 9274cd228995..c380545f1e92 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4244,14 +4244,12 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, return; } - if (list_empty(&hdev->pend_le_reports)) - return; - if (type == LE_ADV_DIRECT_IND) return; - param = hci_conn_params_lookup(hdev, bdaddr, bdaddr_type); - if (!param || param->auto_connect != HCI_AUTO_CONN_REPORT) + param = hci_pend_le_action_lookup(&hdev->pend_le_reports, + bdaddr, bdaddr_type); + if (!param) return; if (type == LE_ADV_NONCONN_IND || type == LE_ADV_SCAN_IND) From 46ebeb26cd976b9902d074c9a51f091a5744f9f3 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 4 Jul 2014 13:23:38 +0200 Subject: [PATCH 173/378] Bluetooth: Fix constant for public address configuration The public address configuration option is value 0x02 since the generic external configuration is value 0x01. So adjust this accordingly and also add the value 0x01 to the list. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 0579eb3952e3..3984678ffab1 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -472,7 +472,8 @@ struct mgmt_rp_read_unconf_index_list { __le16 index[0]; } __packed; -#define MGMT_OPTION_PUBLIC_ADDRESS 0x00000001 +#define MGMT_OPTION_EXTERNAL_CONFIG 0x00000001 +#define MGMT_OPTION_PUBLIC_ADDRESS 0x00000002 #define MGMT_OP_READ_CONFIG_INFO 0x0037 #define MGMT_READ_CONFIG_INFO_SIZE 0 From 99a6768e0e55d19a47934ccd653ff0f9b3236401 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 4 Jul 2014 16:15:49 +0300 Subject: [PATCH 174/378] Bluetooth: Don't take actions on blocked devices when scanning If a found device is marked as blocked while doing passive LE scanning, neither report it nor try to connect to it. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_event.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index c380545f1e92..27c1d2edeebe 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4239,6 +4239,10 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, bdaddr_type = irk->addr_type; } + /* Ignore if the device is blocked */ + if (hci_blacklist_lookup(hdev, bdaddr, bdaddr_type)) + return; + if (type == LE_ADV_IND || type == LE_ADV_DIRECT_IND) { if (check_pending_le_conn(hdev, bdaddr, bdaddr_type)) return; From d1dbf12e3be0befcd3fd1f978202c5f72d2cc67b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 4 Jul 2014 16:17:23 +0300 Subject: [PATCH 175/378] Bluetooth: Use list_del when freeing the list entry It's wasteful to use list_del_init (which re-initializes the list_head) if we're just about to free the element and never use it again. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f1672b15c0f3..90eabcae3ed2 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5225,7 +5225,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, goto unlock; } - list_del_init(¶ms->action); + list_del(¶ms->action); list_del(¶ms->list); kfree(params); hci_update_background_scan(hdev); From 118305b50a73b46ff6c1c0453d7ae642b26dff82 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 4 Jul 2014 16:08:02 +0200 Subject: [PATCH 176/378] Bluetooth: Document the existing device quirks The current existing device quirks are not documented. So instead of spreading bits and pieces somewhere in the code, add proper comments on where these quirks can be used and what behavior they change. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 606a9b1466a3..744713cd5335 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -81,9 +81,34 @@ /* HCI device quirks */ enum { + /* When this quirk is set, the HCI Reset command is send when + * closing the transport instead of when opening it. + * + * This quirk must be set before hci_register_dev is called. + */ HCI_QUIRK_RESET_ON_CLOSE, + + /* When this quirk is set, the device is turned into a raw-only + * device and it will stay in unconfigured state. + * + * This quirk must be set before hci_register_dev is called. + */ HCI_QUIRK_RAW_DEVICE, + + /* When this quirk is set, the buffer sizes reported by + * HCI Read Buffer Size command are corrected if invalid. + * + * This quirk must be set before hci_register_dev is called. + */ HCI_QUIRK_FIXUP_BUFFER_SIZE, + + /* When this quirk is set, then no stored link key handling + * is performed. This is mainly due to the fact that the + * HCI Delete Stored Link Key command is advertised, but + * not supported. + * + * This quirk must be set before hci_register_dev is called. + */ HCI_QUIRK_BROKEN_STORED_LINK_KEY, }; From 89bc22d23f63c2d437f677d7eae0fa922bedcdcb Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 4 Jul 2014 16:54:37 +0200 Subject: [PATCH 177/378] Bluetooth: Add quirk for invalid controller address setting When a Bluetooth controller does not have a valid public Bluetooth address, then allow the driver to indicate this. If the quirk is set, the Bluetooth core will switch to unconfigured state first and will allow userspace to configure the address before starting the full initialization of the controller. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 10 ++++++++++ net/bluetooth/hci_core.c | 6 +++++- net/bluetooth/mgmt.c | 21 +++++++++++++++++---- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 744713cd5335..148b21699446 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -110,6 +110,16 @@ enum { * This quirk must be set before hci_register_dev is called. */ HCI_QUIRK_BROKEN_STORED_LINK_KEY, + + /* When this quirk is set, the public Bluetooth address + * initially reported by HCI Read BD Address command + * is considered invalid. Controller configuration is + * required before this device can be used. + * + * This quirk can be set before hci_register_dev is called or + * during the hdev->setup vendor callback. + */ + HCI_QUIRK_INVALID_BDADDR, }; /* HCI device flags */ diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 038b4748375b..c92bee84413f 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2246,9 +2246,13 @@ static int hci_dev_do_open(struct hci_dev *hdev) atomic_set(&hdev->cmd_cnt, 1); set_bit(HCI_INIT, &hdev->flags); - if (hdev->setup && test_bit(HCI_SETUP, &hdev->dev_flags)) + if (hdev->setup && test_bit(HCI_SETUP, &hdev->dev_flags)) { ret = hdev->setup(hdev); + if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks)) + set_bit(HCI_UNCONFIGURED, &hdev->dev_flags); + } + /* If public address change is configured, ensure that the * address gets programmed. If the driver does not support * changing the public address, fail the power on procedure. diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 90eabcae3ed2..c7e5d4651021 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -441,10 +441,22 @@ static int read_unconf_index_list(struct sock *sk, struct hci_dev *hdev, return err; } +static __le32 get_missing_options(struct hci_dev *hdev) +{ + u32 options = 0; + + if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) && + !bacmp(&hdev->public_addr, BDADDR_ANY)) + options |= MGMT_OPTION_PUBLIC_ADDRESS; + + return cpu_to_le32(options); +} + static int read_config_info(struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len) { struct mgmt_rp_read_config_info rp; + u32 options = 0; BT_DBG("sock %p %s", sk, hdev->name); @@ -452,11 +464,12 @@ static int read_config_info(struct sock *sk, struct hci_dev *hdev, memset(&rp, 0, sizeof(rp)); rp.manufacturer = cpu_to_le16(hdev->manufacturer); + if (hdev->set_bdaddr) - rp.supported_options = cpu_to_le32(MGMT_OPTION_PUBLIC_ADDRESS); - else - rp.supported_options = cpu_to_le32(0); - rp.missing_options = cpu_to_le32(0); + options |= MGMT_OPTION_PUBLIC_ADDRESS; + + rp.supported_options = cpu_to_le32(options); + rp.missing_options = get_missing_options(hdev); hci_dev_unlock(hdev); From 4739b5b185aad15b5c52c39e789ff582ec20796b Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 4 Jul 2014 16:54:38 +0200 Subject: [PATCH 178/378] Bluetooth: Set HCI_QUIRK_INVALID_BADDR for Intel USB default address When the Intel USB controller has a default address, then set the quirk so the Bluetooth core knows that controller configuration is required. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/btusb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 3244e311ca29..825f3e16651f 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -1216,9 +1216,11 @@ static int btusb_check_bdaddr_intel(struct hci_dev *hdev) * fully operational, but have the danger of duplicate addresses * and that in turn can cause problems with Bluetooth operation. */ - if (!bacmp(&rp->bdaddr, BDADDR_INTEL)) + if (!bacmp(&rp->bdaddr, BDADDR_INTEL)) { BT_ERR("%s found Intel default device address (%pMR)", hdev->name, &rp->bdaddr); + set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); + } kfree_skb(skb); From 849e5086b9080d2408fd6223d27548032083b9e8 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 4 Jul 2014 16:54:39 +0200 Subject: [PATCH 179/378] Bluetooth: Set HCI_QUIRK_INVALID_BADDR for BCM20702A0 default address When the Broadcom USB controller has a default address, then set the quirk so the Bluetooth core knows that controller configuration is required. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/btusb.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 825f3e16651f..61d8385666e9 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -1624,9 +1624,11 @@ reset_fw: /* The address 00:20:70:02:A0:00 indicates a BCM20702A0 controller * with no configured address. */ - if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0)) + if (!bacmp(&bda->bdaddr, BDADDR_BCM20702A0)) { BT_INFO("%s: BCM: using default device address (%pMR)", hdev->name, &bda->bdaddr); + set_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks); + } kfree_skb(skb); From 42a9bc148960b2c85f9ec5ef1abe3a87e0155c60 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 4 Jul 2014 16:54:40 +0200 Subject: [PATCH 180/378] Bluetooth: Allow reading configuration info when unconfigured Reading the controller configuration information is a valid management command when the controller is unconfigured. Allow this command, but return invalid index on all other commands. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/mgmt.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index c7e5d4651021..766411e11ac7 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5455,12 +5455,18 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) } if (test_bit(HCI_SETUP, &hdev->dev_flags) || - test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) || test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { err = cmd_status(sk, index, opcode, MGMT_STATUS_INVALID_INDEX); goto done; } + + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) && + opcode != MGMT_OP_READ_CONFIG_INFO) { + err = cmd_status(sk, index, opcode, + MGMT_STATUS_INVALID_INDEX); + goto done; + } } if (opcode >= ARRAY_SIZE(mgmt_handlers) || From eb1904f49d3e11468997e0667e6ec332a66697c9 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 4 Jul 2014 17:23:33 +0200 Subject: [PATCH 181/378] Bluetooth: Add quirk for external configuration requirement When a controller requires external configuration, then setting this quirk will allow indicating this. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 9 +++++++++ net/bluetooth/hci_core.c | 3 ++- net/bluetooth/mgmt.c | 9 ++++++++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 148b21699446..4f1c4c7e2f00 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -111,6 +111,15 @@ enum { */ HCI_QUIRK_BROKEN_STORED_LINK_KEY, + /* When this quirk is set, an external configuration step + * is required and will be indicated with the controller + * configuation. + * + * This quirk can be set before hci_register_dev is called or + * during the hdev->setup vendor callback. + */ + HCI_QUIRK_EXTERNAL_CONFIG, + /* When this quirk is set, the public Bluetooth address * initially reported by HCI Read BD Address command * is considered invalid. Controller configuration is diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index c92bee84413f..c007b3543e70 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2249,7 +2249,8 @@ static int hci_dev_do_open(struct hci_dev *hdev) if (hdev->setup && test_bit(HCI_SETUP, &hdev->dev_flags)) { ret = hdev->setup(hdev); - if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks)) + if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) || + test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks)) set_bit(HCI_UNCONFIGURED, &hdev->dev_flags); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 766411e11ac7..d2d0e051e4f2 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -445,6 +445,9 @@ static __le32 get_missing_options(struct hci_dev *hdev) { u32 options = 0; + if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks)) + options |= MGMT_OPTION_EXTERNAL_CONFIG; + if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) && !bacmp(&hdev->public_addr, BDADDR_ANY)) options |= MGMT_OPTION_PUBLIC_ADDRESS; @@ -465,6 +468,9 @@ static int read_config_info(struct sock *sk, struct hci_dev *hdev, memset(&rp, 0, sizeof(rp)); rp.manufacturer = cpu_to_le16(hdev->manufacturer); + if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks)) + options |= MGMT_OPTION_EXTERNAL_CONFIG; + if (hdev->set_bdaddr) options |= MGMT_OPTION_PUBLIC_ADDRESS; @@ -509,7 +515,8 @@ static u32 get_supported_settings(struct hci_dev *hdev) settings |= MGMT_SETTING_PRIVACY; } - if (hdev->set_bdaddr) + if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) || + hdev->set_bdaddr) settings |= MGMT_SETTING_CONFIGURATION; return settings; From af202f84415a69b964a9ec371e11781f2a1e5396 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 4 Jul 2014 17:23:34 +0200 Subject: [PATCH 182/378] Bluetooth: Fix quirks that are valid during setup driver callback For the quirks that are allow to be set during setup callback, the check needs to be modified so that they are applied even if no setup callback provided by the driver. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index c007b3543e70..b29f3938e6b3 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2246,9 +2246,16 @@ static int hci_dev_do_open(struct hci_dev *hdev) atomic_set(&hdev->cmd_cnt, 1); set_bit(HCI_INIT, &hdev->flags); - if (hdev->setup && test_bit(HCI_SETUP, &hdev->dev_flags)) { - ret = hdev->setup(hdev); + if (test_bit(HCI_SETUP, &hdev->dev_flags)) { + if (hdev->setup) + ret = hdev->setup(hdev); + /* The transport driver can set these quirks before + * creating the HCI device or in its setup callback. + * + * In case any of them is set, the controller has to + * start up as unconfigured. + */ if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) || test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks)) set_bit(HCI_UNCONFIGURED, &hdev->dev_flags); From 0ad184ef5828542e3e73cce8a875fb4e029725b7 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 4 Jul 2014 17:23:35 +0200 Subject: [PATCH 183/378] Bluetooth: Support HCI_QUIRK_EXTERNAL_CONFIG for hci_vhci driver This adds support for configuring the hci_vhci virtual controllers to require a setup stage using HCI_QUIRK_EXTERNAL_CONFIG. With this option the virtual controller will start out as unconfigured. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/hci_vhci.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index aa446c7eb7e5..5bb5872ffee6 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -107,8 +107,8 @@ static int vhci_create_device(struct vhci_data *data, __u8 opcode) if (dev_type != HCI_BREDR && dev_type != HCI_AMP) return -EINVAL; - /* bits 2-6 are reserved (must be zero) */ - if (opcode & 0x7c) + /* bits 2-5 are reserved (must be zero) */ + if (opcode & 0x3c) return -EINVAL; skb = bt_skb_alloc(4, GFP_KERNEL); @@ -132,6 +132,10 @@ static int vhci_create_device(struct vhci_data *data, __u8 opcode) hdev->flush = vhci_flush; hdev->send = vhci_send_frame; + /* bit 6 is for external configuration */ + if (opcode & 0x40) + set_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks); + /* bit 7 is for raw device */ if (opcode & 0x80) set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); From dbece37a3233933ec89f77f04049e13ad9b29634 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 4 Jul 2014 18:11:55 +0200 Subject: [PATCH 184/378] Bluetooth: Add support for Set External Configuration management command The Set External Configuration management command allows for switching between configured and unconfigured start if HCI_QURIK_EXTERNAL_CONFIG is set by the transport driver. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + include/net/bluetooth/mgmt.h | 6 +++ net/bluetooth/mgmt.c | 77 +++++++++++++++++++++++++++++++++++- 3 files changed, 82 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 4f1c4c7e2f00..80c5fc947fbc 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -173,6 +173,7 @@ enum { HCI_UNREGISTER, HCI_UNCONFIGURED, HCI_USER_CHANNEL, + HCI_EXT_CONFIGURED, HCI_LE_SCAN, HCI_SSP_ENABLED, diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 3984678ffab1..c7d537f1bd19 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -483,6 +483,12 @@ struct mgmt_rp_read_config_info { __le32 missing_options; } __packed; +#define MGMT_OP_SET_EXTERNAL_CONFIG 0x0038 +struct mgmt_cp_set_external_config { + __u8 config; +} __packed; +#define MGMT_SET_EXTERNAL_CONFIG_SIZE 1 + #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 d2d0e051e4f2..91bccef63f30 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -91,6 +91,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_LOAD_CONN_PARAM, MGMT_OP_READ_UNCONF_INDEX_LIST, MGMT_OP_READ_CONFIG_INFO, + MGMT_OP_SET_EXTERNAL_CONFIG, }; static const u16 mgmt_events[] = { @@ -441,11 +442,25 @@ static int read_unconf_index_list(struct sock *sk, struct hci_dev *hdev, return err; } +static bool is_configured(struct hci_dev *hdev) +{ + if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) && + !test_bit(HCI_EXT_CONFIGURED, &hdev->dev_flags)) + return false; + + if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) && + !bacmp(&hdev->public_addr, BDADDR_ANY)) + return false; + + return true; +} + static __le32 get_missing_options(struct hci_dev *hdev) { u32 options = 0; - if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks)) + if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) && + !test_bit(HCI_EXT_CONFIGURED, &hdev->dev_flags)) options |= MGMT_OPTION_EXTERNAL_CONFIG; if (test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) && @@ -455,6 +470,14 @@ static __le32 get_missing_options(struct hci_dev *hdev) return cpu_to_le32(options); } +static int send_options_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev) +{ + __le32 options = get_missing_options(hdev); + + return cmd_complete(sk, hdev->id, opcode, 0, &options, + sizeof(options)); +} + static int read_config_info(struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len) { @@ -5355,6 +5378,54 @@ static int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data, return cmd_complete(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM, 0, NULL, 0); } +static int set_external_config(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_cp_set_external_config *cp = data; + bool changed; + int err; + + BT_DBG("%s", hdev->name); + + if (hdev_is_powered(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_EXTERNAL_CONFIG, + MGMT_STATUS_REJECTED); + + if (cp->config != 0x00 && cp->config != 0x01) + return cmd_status(sk, hdev->id, MGMT_OP_SET_EXTERNAL_CONFIG, + MGMT_STATUS_INVALID_PARAMS); + + if (!test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_EXTERNAL_CONFIG, + MGMT_STATUS_NOT_SUPPORTED); + + hci_dev_lock(hdev); + + if (cp->config) + changed = !test_and_set_bit(HCI_EXT_CONFIGURED, + &hdev->dev_flags); + else + changed = test_and_clear_bit(HCI_EXT_CONFIGURED, + &hdev->dev_flags); + + err = send_options_rsp(sk, MGMT_OP_SET_EXTERNAL_CONFIG, hdev); + if (err < 0) + goto unlock; + + if (!changed) + goto unlock; + + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) == is_configured(hdev)) { + mgmt_index_removed(hdev); + change_bit(HCI_UNCONFIGURED, &hdev->dev_flags); + mgmt_index_added(hdev); + } + +unlock: + hci_dev_unlock(hdev); + return err; +} + static const struct mgmt_handler { int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len); @@ -5417,6 +5488,7 @@ static const struct mgmt_handler { { load_conn_param, true, MGMT_LOAD_CONN_PARAM_SIZE }, { read_unconf_index_list, false, MGMT_READ_UNCONF_INDEX_LIST_SIZE }, { read_config_info, false, MGMT_READ_CONFIG_INFO_SIZE }, + { set_external_config, false, MGMT_SET_EXTERNAL_CONFIG_SIZE }, }; int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) @@ -5469,7 +5541,8 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) } if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) && - opcode != MGMT_OP_READ_CONFIG_INFO) { + opcode != MGMT_OP_READ_CONFIG_INFO && + opcode != MGMT_OP_SET_EXTERNAL_CONFIG) { err = cmd_status(sk, index, opcode, MGMT_STATUS_INVALID_INDEX); goto done; From 04c60f05a08aeb5ed412b08da037ed86419344a9 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 4 Jul 2014 19:06:22 +0200 Subject: [PATCH 185/378] Bluetooth: Move mgmt_event helper function to different location Move the mgmt_event function higher up in the code so that no forward declaration is needed. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/mgmt.c | 60 ++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 91bccef63f30..4ef73523d95b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -212,6 +212,36 @@ static u8 mgmt_status(u8 hci_status) return MGMT_STATUS_FAILED; } +static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 data_len, + 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_control(skb, skip_sk); + kfree_skb(skb); + + return 0; +} + static int cmd_status(struct sock *sk, u16 index, u16 cmd, u8 status) { struct sk_buff *skb; @@ -1364,36 +1394,6 @@ failed: return err; } -static int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 data_len, - 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_control(skb, skip_sk); - kfree_skb(skb); - - return 0; -} - static int new_settings(struct hci_dev *hdev, struct sock *skip) { __le32 ev; From f4537c04d387eda86ed89e0eafe0352f7fa0c9d0 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 4 Jul 2014 19:06:23 +0200 Subject: [PATCH 186/378] Bluetooth: Add support for New Configuration Options management event When one or more of the missing configuration options change, then send this even to all the other management interface clients. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 2 ++ net/bluetooth/mgmt.c | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index c7d537f1bd19..80606d2fe086 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -669,3 +669,5 @@ struct mgmt_ev_new_conn_param { #define MGMT_EV_UNCONF_INDEX_ADDED 0x001d #define MGMT_EV_UNCONF_INDEX_REMOVED 0x001e + +#define MGMT_EV_NEW_CONFIG_OPTIONS 0x001f diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 4ef73523d95b..f514eb15e0fb 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -123,6 +123,7 @@ static const u16 mgmt_events[] = { MGMT_EV_NEW_CONN_PARAM, MGMT_EV_UNCONF_INDEX_ADDED, MGMT_EV_UNCONF_INDEX_REMOVED, + MGMT_EV_NEW_CONFIG_OPTIONS, }; #define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000) @@ -500,6 +501,14 @@ static __le32 get_missing_options(struct hci_dev *hdev) return cpu_to_le32(options); } +static int new_options(struct hci_dev *hdev, struct sock *skip) +{ + __le32 options = get_missing_options(hdev); + + return mgmt_event(MGMT_EV_NEW_CONFIG_OPTIONS, hdev, &options, + sizeof(options), skip); +} + static int send_options_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev) { __le32 options = get_missing_options(hdev); @@ -5415,6 +5424,8 @@ static int set_external_config(struct sock *sk, struct hci_dev *hdev, if (!changed) goto unlock; + err = new_options(hdev, sk); + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) == is_configured(hdev)) { mgmt_index_removed(hdev); change_bit(HCI_UNCONFIGURED, &hdev->dev_flags); From 45296acd91b14d32d7d023a08baaf285a0ea2193 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 5 Jul 2014 10:48:01 +0200 Subject: [PATCH 187/378] Bluetooth: Use a more simpler style for HCI event callbacks The HCI event callbacks have grown over the last years and some functions handle status checking different than others. For the simple ones, check the status at the beginning and exit if an error with the HCI command occured. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_event.c | 142 +++++++++++++++++++++++--------------- 1 file changed, 88 insertions(+), 54 deletions(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 27c1d2edeebe..013c371d3c87 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -175,12 +175,14 @@ static void hci_cc_write_def_link_policy(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, status); + if (status) + return; + sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_DEF_LINK_POLICY); if (!sent) return; - if (!status) - hdev->link_policy = get_unaligned_le16(sent); + hdev->link_policy = get_unaligned_le16(sent); } static void hci_cc_reset(struct hci_dev *hdev, struct sk_buff *skb) @@ -270,27 +272,30 @@ static void hci_cc_write_auth_enable(struct hci_dev *hdev, struct sk_buff *skb) static void hci_cc_write_encrypt_mode(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); + __u8 param; void *sent; BT_DBG("%s status 0x%2.2x", hdev->name, status); + if (status) + return; + sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_ENCRYPT_MODE); if (!sent) return; - if (!status) { - __u8 param = *((__u8 *) sent); + param = *((__u8 *) sent); - if (param) - set_bit(HCI_ENCRYPT, &hdev->flags); - else - clear_bit(HCI_ENCRYPT, &hdev->flags); - } + if (param) + set_bit(HCI_ENCRYPT, &hdev->flags); + else + clear_bit(HCI_ENCRYPT, &hdev->flags); } static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) { - __u8 param, status = *((__u8 *) skb->data); + __u8 status = *((__u8 *) skb->data); + __u8 param; int old_pscan, old_iscan; void *sent; @@ -602,8 +607,10 @@ static void hci_cc_read_flow_control_mode(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); - if (!rp->status) - hdev->flow_ctl_mode = rp->mode; + if (rp->status) + return; + + hdev->flow_ctl_mode = rp->mode; } static void hci_cc_read_buffer_size(struct hci_dev *hdev, struct sk_buff *skb) @@ -649,7 +656,10 @@ static void hci_cc_read_page_scan_activity(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); - if (test_bit(HCI_INIT, &hdev->flags) && !rp->status) { + if (rp->status) + return; + + if (test_bit(HCI_INIT, &hdev->flags)) { hdev->page_scan_interval = __le16_to_cpu(rp->interval); hdev->page_scan_window = __le16_to_cpu(rp->window); } @@ -681,7 +691,10 @@ static void hci_cc_read_page_scan_type(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); - if (test_bit(HCI_INIT, &hdev->flags) && !rp->status) + if (rp->status) + return; + + if (test_bit(HCI_INIT, &hdev->flags)) hdev->page_scan_type = rp->type; } @@ -825,8 +838,10 @@ static void hci_cc_read_inq_rsp_tx_power(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); - if (!rp->status) - hdev->inq_tx_power = rp->tx_power; + if (rp->status) + return; + + hdev->inq_tx_power = rp->tx_power; } static void hci_cc_pin_code_reply(struct hci_dev *hdev, struct sk_buff *skb) @@ -897,8 +912,10 @@ static void hci_cc_le_read_local_features(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); - if (!rp->status) - memcpy(hdev->le_features, rp->features, 8); + if (rp->status) + return; + + memcpy(hdev->le_features, rp->features, 8); } static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev, @@ -908,8 +925,10 @@ static void hci_cc_le_read_adv_tx_power(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); - if (!rp->status) - hdev->adv_tx_power = rp->tx_power; + if (rp->status) + return; + + hdev->adv_tx_power = rp->tx_power; } static void hci_cc_user_confirm_reply(struct hci_dev *hdev, struct sk_buff *skb) @@ -1009,14 +1028,16 @@ static void hci_cc_le_set_random_addr(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%2.2x", hdev->name, status); + if (status) + return; + sent = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_RANDOM_ADDR); if (!sent) return; hci_dev_lock(hdev); - if (!status) - bacpy(&hdev->random_addr, sent); + bacpy(&hdev->random_addr, sent); hci_dev_unlock(hdev); } @@ -1027,11 +1048,11 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%2.2x", hdev->name, status); - sent = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_ADV_ENABLE); - if (!sent) + if (status) return; - if (status) + sent = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_ADV_ENABLE); + if (!sent) return; hci_dev_lock(hdev); @@ -1061,14 +1082,16 @@ static void hci_cc_le_set_scan_param(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%2.2x", hdev->name, status); + if (status) + return; + cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_SCAN_PARAM); if (!cp) return; hci_dev_lock(hdev); - if (!status) - hdev->le_scan_type = cp->type; + hdev->le_scan_type = cp->type; hci_dev_unlock(hdev); } @@ -1110,11 +1133,11 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, status); - cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_SCAN_ENABLE); - if (!cp) + if (status) return; - if (status) + cp = hci_sent_cmd_data(hdev, HCI_OP_LE_SET_SCAN_ENABLE); + if (!cp) return; switch (cp->enable) { @@ -1167,8 +1190,10 @@ static void hci_cc_le_read_white_list_size(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x size %u", hdev->name, rp->status, rp->size); - if (!rp->status) - hdev->le_white_list_size = rp->size; + if (rp->status) + return; + + hdev->le_white_list_size = rp->size; } static void hci_cc_le_clear_white_list(struct hci_dev *hdev, @@ -1178,8 +1203,10 @@ static void hci_cc_le_clear_white_list(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, status); - if (!status) - hci_white_list_clear(hdev); + if (status) + return; + + hci_white_list_clear(hdev); } static void hci_cc_le_add_to_white_list(struct hci_dev *hdev, @@ -1190,12 +1217,14 @@ static void hci_cc_le_add_to_white_list(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, status); + if (status) + return; + sent = hci_sent_cmd_data(hdev, HCI_OP_LE_ADD_TO_WHITE_LIST); if (!sent) return; - if (!status) - hci_white_list_add(hdev, &sent->bdaddr, sent->bdaddr_type); + hci_white_list_add(hdev, &sent->bdaddr, sent->bdaddr_type); } static void hci_cc_le_del_from_white_list(struct hci_dev *hdev, @@ -1206,12 +1235,14 @@ static void hci_cc_le_del_from_white_list(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, status); + if (status) + return; + sent = hci_sent_cmd_data(hdev, HCI_OP_LE_DEL_FROM_WHITE_LIST); if (!sent) return; - if (!status) - hci_white_list_del(hdev, &sent->bdaddr, sent->bdaddr_type); + hci_white_list_del(hdev, &sent->bdaddr, sent->bdaddr_type); } static void hci_cc_le_read_supported_states(struct hci_dev *hdev, @@ -1221,8 +1252,10 @@ static void hci_cc_le_read_supported_states(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); - if (!rp->status) - memcpy(hdev->le_states, rp->le_states, 8); + if (rp->status) + return; + + memcpy(hdev->le_states, rp->le_states, 8); } static void hci_cc_write_le_host_supported(struct hci_dev *hdev, @@ -1233,25 +1266,26 @@ static void hci_cc_write_le_host_supported(struct hci_dev *hdev, BT_DBG("%s status 0x%2.2x", hdev->name, status); + if (status) + return; + sent = hci_sent_cmd_data(hdev, HCI_OP_WRITE_LE_HOST_SUPPORTED); if (!sent) return; - if (!status) { - if (sent->le) { - hdev->features[1][0] |= LMP_HOST_LE; - set_bit(HCI_LE_ENABLED, &hdev->dev_flags); - } else { - hdev->features[1][0] &= ~LMP_HOST_LE; - clear_bit(HCI_LE_ENABLED, &hdev->dev_flags); - clear_bit(HCI_ADVERTISING, &hdev->dev_flags); - } - - if (sent->simul) - hdev->features[1][0] |= LMP_HOST_LE_BREDR; - else - hdev->features[1][0] &= ~LMP_HOST_LE_BREDR; + if (sent->le) { + hdev->features[1][0] |= LMP_HOST_LE; + set_bit(HCI_LE_ENABLED, &hdev->dev_flags); + } else { + hdev->features[1][0] &= ~LMP_HOST_LE; + clear_bit(HCI_LE_ENABLED, &hdev->dev_flags); + clear_bit(HCI_ADVERTISING, &hdev->dev_flags); } + + if (sent->simul) + hdev->features[1][0] |= LMP_HOST_LE_BREDR; + else + hdev->features[1][0] &= ~LMP_HOST_LE_BREDR; } static void hci_cc_set_adv_param(struct hci_dev *hdev, struct sk_buff *skb) From 0ebca7d6819a24b7518d518f89597cf7c7854094 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 5 Jul 2014 10:48:02 +0200 Subject: [PATCH 188/378] Bluetooth: Run special init procedure for unconfigured controllers For an unconfigured controller it is required to read at least the local version information. If the set_bdaddr driver callback is provideded, then also the local Bluetooth address will be read. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b29f3938e6b3..f996e2c4815c 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1791,6 +1791,35 @@ static int __hci_init(struct hci_dev *hdev) return 0; } +static void hci_init0_req(struct hci_request *req, unsigned long opt) +{ + struct hci_dev *hdev = req->hdev; + + BT_DBG("%s %ld", hdev->name, opt); + + /* Reset */ + if (!test_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks)) + hci_reset_req(req, 0); + + /* Read Local Version */ + hci_req_add(req, HCI_OP_READ_LOCAL_VERSION, 0, NULL); + + /* Read BD Address */ + if (hdev->set_bdaddr) + hci_req_add(req, HCI_OP_READ_BD_ADDR, 0, NULL); +} + +static int __hci_unconf_init(struct hci_dev *hdev) +{ + int err; + + err = __hci_req_sync(hdev, hci_init0_req, 0, HCI_INIT_TIMEOUT); + if (err < 0) + return err; + + return 0; +} + static void hci_scan_req(struct hci_request *req, unsigned long opt) { __u8 scan = opt; @@ -2259,6 +2288,17 @@ static int hci_dev_do_open(struct hci_dev *hdev) if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) || test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks)) set_bit(HCI_UNCONFIGURED, &hdev->dev_flags); + + /* For an unconfigured controller it is required to + * read at least the version information provided by + * the Read Local Version Information command. + * + * If the set_bdaddr driver callback is provided, then + * also the original Bluetooth public device address + * will be read using the Read BD Address command. + */ + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) + ret = __hci_unconf_init(hdev); } /* If public address change is configured, ensure that the From e30d3f5fef378cd14ba8c331a5c7a2f9239c2770 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 5 Jul 2014 10:48:03 +0200 Subject: [PATCH 189/378] Bluetooth: Store Bluetooth address from controller setup During the setup phase of a controller, the Bluetooth address will be read and to have that original address available for later use, store it as setup address. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_event.c | 8 +++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 1cae4d3a629d..aff285698c85 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -171,6 +171,7 @@ struct hci_dev { __u8 bus; __u8 dev_type; bdaddr_t bdaddr; + bdaddr_t setup_addr; bdaddr_t public_addr; bdaddr_t random_addr; bdaddr_t static_addr; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 013c371d3c87..c2ba79c8fe51 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -645,8 +645,14 @@ static void hci_cc_read_bd_addr(struct hci_dev *hdev, struct sk_buff *skb) BT_DBG("%s status 0x%2.2x", hdev->name, rp->status); - if (!rp->status) + if (rp->status) + return; + + if (test_bit(HCI_INIT, &hdev->flags)) bacpy(&hdev->bdaddr, &rp->bdaddr); + + if (test_bit(HCI_SETUP, &hdev->dev_flags)) + bacpy(&hdev->setup_addr, &rp->bdaddr); } static void hci_cc_read_page_scan_activity(struct hci_dev *hdev, From 9e1d7e15340b14bfb2ac86d77ca72fcebfe67d66 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 6 Jul 2014 11:03:36 +0300 Subject: [PATCH 190/378] Bluetooth: Restrict blocked device check in l2cap_recv_frame to LE BR/EDR has the connection request and connection request rejection, but LE doesn't have anything similar. We still request LE connections to blocked devices to be disconnected but it's possible that ACL data slips through before that. The check in l2cap_recv_frame really only needs to be for LE and not BR/EDR because of this. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/l2cap_core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index dbef22d644e2..f1f544a12c3c 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -6914,7 +6914,11 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) return; } - if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, + /* Since we can't actively block incoming LE connections we must + * at least ensure that we ignore incoming data from them. + */ + if (hcon->type == LE_LINK && + hci_blacklist_lookup(hcon->hdev, &hcon->dst, bdaddr_type(hcon, hcon->dst_type))) { kfree_skb(skb); return; From 19de0825cd8acb1de6fa6a135b1f059446781049 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 6 Jul 2014 13:06:51 +0300 Subject: [PATCH 191/378] Bluetooth: Fix sending Device Removed when clearing all parameters When calling Device Remove with BDADDR_ANY we should in a similar way emit Device Removed events as we do when removing a single device. Since we have to iterate the list and call device_removed() the dedicated hci_conn_params_clear_enabled() is not really useful anymore. This patch removes the helper function and does the event emission and list item removal in a single loop. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 - net/bluetooth/hci_core.c | 18 ------------------ net/bluetooth/mgmt.c | 15 ++++++++++++++- 3 files changed, 14 insertions(+), 20 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index aff285698c85..9d838a072db4 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -867,7 +867,6 @@ int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, void hci_conn_params_del(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); void hci_conn_params_clear_all(struct hci_dev *hdev); void hci_conn_params_clear_disabled(struct hci_dev *hdev); -void hci_conn_params_clear_enabled(struct hci_dev *hdev); struct hci_conn_params *hci_pend_le_action_lookup(struct list_head *list, bdaddr_t *addr, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index f996e2c4815c..d01bd043c231 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3621,24 +3621,6 @@ void hci_conn_params_clear_disabled(struct hci_dev *hdev) BT_DBG("All LE disabled connection parameters were removed"); } -/* This function requires the caller holds hdev->lock */ -void hci_conn_params_clear_enabled(struct hci_dev *hdev) -{ - struct hci_conn_params *params, *tmp; - - list_for_each_entry_safe(params, tmp, &hdev->le_conn_params, list) { - if (params->auto_connect == HCI_AUTO_CONN_DISABLED) - continue; - list_del(¶ms->action); - list_del(¶ms->list); - kfree(params); - } - - hci_update_background_scan(hdev); - - BT_DBG("All enabled LE connection parameters were removed"); -} - /* This function requires the caller holds hdev->lock */ void hci_conn_params_clear_all(struct hci_dev *hdev) { diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index f514eb15e0fb..04a66429ad4d 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5284,6 +5284,8 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, device_removed(sk, hdev, &cp->addr.bdaddr, cp->addr.type); } else { + struct hci_conn_params *p, *tmp; + if (cp->addr.type) { err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, MGMT_STATUS_INVALID_PARAMS, @@ -5291,7 +5293,18 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, goto unlock; } - hci_conn_params_clear_enabled(hdev); + list_for_each_entry_safe(p, tmp, &hdev->le_conn_params, list) { + if (p->auto_connect == HCI_AUTO_CONN_DISABLED) + continue; + device_removed(sk, hdev, &p->addr, p->addr_type); + list_del(&p->action); + list_del(&p->list); + kfree(p); + } + + BT_DBG("All LE connection parameters were removed"); + + hci_update_background_scan(hdev); } err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, From d603b76b0c18c5adf4a3164dff50bb15948cd7bd Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 6 Jul 2014 12:11:14 +0200 Subject: [PATCH 192/378] Bluetooth: Run controller setup after external configuration When the external configuration triggers the switch to a configured controller, it means the setup needs to be run. Controllers that start out unconfigured have only run limited set of HCI commands. This is not enough for complete operation and thus run the setup procedure before announcing the new controller index. This introduces HCI_CONFIG flag as companion to HCI_SETUP flag. The HCI_SETUP flag is only used once for the initial setup procedure. And during that procedure hdev->setup driver callback is called. With the new HCI_CONFIG the switch from unconfigured to configured state is triggering the same setup procedure just without hdev->setup. This is required since bringing a controller back to unconfigured state from configured state is possible. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_core.c | 17 ++++++++++++++--- net/bluetooth/hci_sock.c | 3 ++- net/bluetooth/mgmt.c | 14 ++++++++++++-- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 80c5fc947fbc..46f2458e4bc9 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -163,6 +163,7 @@ enum { */ enum { HCI_SETUP, + HCI_CONFIG, HCI_AUTO_OFF, HCI_RFKILLED, HCI_MGMT, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index d01bd043c231..bf7cf512df88 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2232,7 +2232,8 @@ static int hci_dev_do_open(struct hci_dev *hdev) goto done; } - if (!test_bit(HCI_SETUP, &hdev->dev_flags)) { + if (!test_bit(HCI_SETUP, &hdev->dev_flags) && + !test_bit(HCI_CONFIG, &hdev->dev_flags)) { /* Check for rfkill but allow the HCI setup stage to * proceed (which in itself doesn't cause any RF activity). */ @@ -2326,6 +2327,7 @@ static int hci_dev_do_open(struct hci_dev *hdev) set_bit(HCI_UP, &hdev->flags); hci_notify(hdev, HCI_DEV_UP); if (!test_bit(HCI_SETUP, &hdev->dev_flags) && + !test_bit(HCI_CONFIG, &hdev->dev_flags) && !test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) && !test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) && hdev->dev_type == HCI_BREDR) { @@ -2824,7 +2826,8 @@ static int hci_rfkill_set_block(void *data, bool blocked) if (blocked) { set_bit(HCI_RFKILLED, &hdev->dev_flags); - if (!test_bit(HCI_SETUP, &hdev->dev_flags)) + if (!test_bit(HCI_SETUP, &hdev->dev_flags) && + !test_bit(HCI_CONFIG, &hdev->dev_flags)) hci_dev_do_close(hdev); } else { clear_bit(HCI_RFKILLED, &hdev->dev_flags); @@ -2881,6 +2884,12 @@ static void hci_power_on(struct work_struct *work) * and no event will be send. */ mgmt_index_added(hdev); + } else if (test_and_clear_bit(HCI_CONFIG, &hdev->dev_flags)) { + /* Powering on the controller with HCI_CONFIG set only + * happens with the transition from unconfigured to + * configured. This will send the Index Added event. + */ + mgmt_index_added(hdev); } } @@ -4045,7 +4054,8 @@ void hci_unregister_dev(struct hci_dev *hdev) cancel_work_sync(&hdev->power_on); if (!test_bit(HCI_INIT, &hdev->flags) && - !test_bit(HCI_SETUP, &hdev->dev_flags)) { + !test_bit(HCI_SETUP, &hdev->dev_flags) && + !test_bit(HCI_CONFIG, &hdev->dev_flags)) { hci_dev_lock(hdev); mgmt_index_removed(hdev); hci_dev_unlock(hdev); @@ -5370,6 +5380,7 @@ void hci_update_background_scan(struct hci_dev *hdev) if (!test_bit(HCI_UP, &hdev->flags) || test_bit(HCI_INIT, &hdev->flags) || test_bit(HCI_SETUP, &hdev->dev_flags) || + test_bit(HCI_CONFIG, &hdev->dev_flags) || test_bit(HCI_AUTO_OFF, &hdev->dev_flags) || test_bit(HCI_UNREGISTER, &hdev->dev_flags)) return; diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index ba13ad8e25c6..802665751cc4 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -693,7 +693,8 @@ static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, if (test_bit(HCI_UP, &hdev->flags) || test_bit(HCI_INIT, &hdev->flags) || - test_bit(HCI_SETUP, &hdev->dev_flags)) { + test_bit(HCI_SETUP, &hdev->dev_flags) || + test_bit(HCI_CONFIG, &hdev->dev_flags)) { err = -EBUSY; hci_dev_put(hdev); goto done; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 04a66429ad4d..d66463a52280 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -385,6 +385,7 @@ static int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, count = 0; list_for_each_entry(d, &hci_dev_list, list) { if (test_bit(HCI_SETUP, &d->dev_flags) || + test_bit(HCI_CONFIG, &d->dev_flags) || test_bit(HCI_USER_CHANNEL, &d->dev_flags)) continue; @@ -444,6 +445,7 @@ static int read_unconf_index_list(struct sock *sk, struct hci_dev *hdev, count = 0; list_for_each_entry(d, &hci_dev_list, list) { if (test_bit(HCI_SETUP, &d->dev_flags) || + test_bit(HCI_CONFIG, &d->dev_flags) || test_bit(HCI_USER_CHANNEL, &d->dev_flags)) continue; @@ -5441,8 +5443,15 @@ static int set_external_config(struct sock *sk, struct hci_dev *hdev, if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) == is_configured(hdev)) { mgmt_index_removed(hdev); - change_bit(HCI_UNCONFIGURED, &hdev->dev_flags); - mgmt_index_added(hdev); + + if (test_and_change_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) { + set_bit(HCI_CONFIG, &hdev->dev_flags); + set_bit(HCI_AUTO_OFF, &hdev->dev_flags); + + queue_work(hdev->req_workqueue, &hdev->power_on); + } else { + mgmt_index_added(hdev); + } } unlock: @@ -5558,6 +5567,7 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) } if (test_bit(HCI_SETUP, &hdev->dev_flags) || + test_bit(HCI_CONFIG, &hdev->dev_flags) || test_bit(HCI_USER_CHANNEL, &hdev->dev_flags)) { err = cmd_status(sk, index, opcode, MGMT_STATUS_INVALID_INDEX); From 9713c17b086c1ebfe34ea4d34147a778276e2dab Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 6 Jul 2014 12:11:15 +0200 Subject: [PATCH 193/378] Bluetooth: Add support for changing the public device address This adds support for changing the public device address. This feature is required by controllers that do not provide a public address and have HCI_QUIRK_INVALID_BDADDR set. Even if a controller has a public device address, this is useful when an embedded system wants to use its own value. As long as the driver provides the set_bdaddr callback, this allows changing the device address before powering on the controller. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/mgmt.h | 6 ++++ net/bluetooth/hci_core.c | 14 +++++---- net/bluetooth/mgmt.c | 57 +++++++++++++++++++++++++++++++++++- 3 files changed, 70 insertions(+), 7 deletions(-) diff --git a/include/net/bluetooth/mgmt.h b/include/net/bluetooth/mgmt.h index 80606d2fe086..623d5203c592 100644 --- a/include/net/bluetooth/mgmt.h +++ b/include/net/bluetooth/mgmt.h @@ -489,6 +489,12 @@ struct mgmt_cp_set_external_config { } __packed; #define MGMT_SET_EXTERNAL_CONFIG_SIZE 1 +#define MGMT_OP_SET_PUBLIC_ADDRESS 0x0039 +struct mgmt_cp_set_public_address { + bdaddr_t bdaddr; +} __packed; +#define MGMT_SET_PUBLIC_ADDRESS_SIZE 6 + #define MGMT_EV_CMD_COMPLETE 0x0001 struct mgmt_ev_cmd_complete { __le16 opcode; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index bf7cf512df88..c96b96ca41ac 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2302,12 +2302,14 @@ static int hci_dev_do_open(struct hci_dev *hdev) ret = __hci_unconf_init(hdev); } - /* If public address change is configured, ensure that the - * address gets programmed. If the driver does not support - * changing the public address, fail the power on procedure. - */ - if (!ret && bacmp(&hdev->public_addr, BDADDR_ANY)) { - if (hdev->set_bdaddr) + if (test_bit(HCI_CONFIG, &hdev->dev_flags)) { + /* If public address change is configured, ensure that + * the address gets programmed. If the driver does not + * support changing the public address, fail the power + * on procedure. + */ + if (bacmp(&hdev->public_addr, BDADDR_ANY) && + hdev->set_bdaddr) ret = hdev->set_bdaddr(hdev, &hdev->public_addr); else ret = -EADDRNOTAVAIL; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index d66463a52280..8275316ae099 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -92,6 +92,7 @@ static const u16 mgmt_commands[] = { MGMT_OP_READ_UNCONF_INDEX_LIST, MGMT_OP_READ_CONFIG_INFO, MGMT_OP_SET_EXTERNAL_CONFIG, + MGMT_OP_SET_PUBLIC_ADDRESS, }; static const u16 mgmt_events[] = { @@ -5459,6 +5460,58 @@ unlock: return err; } +static int set_public_address(struct sock *sk, struct hci_dev *hdev, + void *data, u16 len) +{ + struct mgmt_cp_set_public_address *cp = data; + bool changed; + int err; + + BT_DBG("%s", hdev->name); + + if (hdev_is_powered(hdev)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_PUBLIC_ADDRESS, + MGMT_STATUS_REJECTED); + + if (!bacmp(&cp->bdaddr, BDADDR_ANY)) + return cmd_status(sk, hdev->id, MGMT_OP_SET_PUBLIC_ADDRESS, + MGMT_STATUS_INVALID_PARAMS); + + if (!hdev->set_bdaddr) + return cmd_status(sk, hdev->id, MGMT_OP_SET_PUBLIC_ADDRESS, + MGMT_STATUS_NOT_SUPPORTED); + + hci_dev_lock(hdev); + + changed = !!bacmp(&hdev->public_addr, &cp->bdaddr); + bacpy(&hdev->public_addr, &cp->bdaddr); + + err = send_options_rsp(sk, MGMT_OP_SET_PUBLIC_ADDRESS, hdev); + if (err < 0) + goto unlock; + + if (!changed) + goto unlock; + + if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) + err = new_options(hdev, sk); + + if (is_configured(hdev)) { + mgmt_index_removed(hdev); + + clear_bit(HCI_UNCONFIGURED, &hdev->dev_flags); + + set_bit(HCI_CONFIG, &hdev->dev_flags); + set_bit(HCI_AUTO_OFF, &hdev->dev_flags); + + queue_work(hdev->req_workqueue, &hdev->power_on); + } + +unlock: + hci_dev_unlock(hdev); + return err; +} + static const struct mgmt_handler { int (*func) (struct sock *sk, struct hci_dev *hdev, void *data, u16 data_len); @@ -5522,6 +5575,7 @@ static const struct mgmt_handler { { read_unconf_index_list, false, MGMT_READ_UNCONF_INDEX_LIST_SIZE }, { read_config_info, false, MGMT_READ_CONFIG_INFO_SIZE }, { set_external_config, false, MGMT_SET_EXTERNAL_CONFIG_SIZE }, + { set_public_address, false, MGMT_SET_PUBLIC_ADDRESS_SIZE }, }; int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) @@ -5576,7 +5630,8 @@ int mgmt_control(struct sock *sk, struct msghdr *msg, size_t msglen) if (test_bit(HCI_UNCONFIGURED, &hdev->dev_flags) && opcode != MGMT_OP_READ_CONFIG_INFO && - opcode != MGMT_OP_SET_EXTERNAL_CONFIG) { + opcode != MGMT_OP_SET_EXTERNAL_CONFIG && + opcode != MGMT_OP_SET_PUBLIC_ADDRESS) { err = cmd_status(sk, index, opcode, MGMT_STATUS_INVALID_INDEX); goto done; From 5ea234d3e5ff9b6e5c82bde5979307213dde249d Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 6 Jul 2014 12:11:16 +0200 Subject: [PATCH 194/378] Bluetooth: Clear HCI_RAW flag when controller becomes configured When an unconfigured controllers reaches the configured state, it is important to change the HCI_RAW flag. It indicates to userspace that the controller is fully operational. External configuration allows to bring the controller back into an unconfigured state. In that case make sure HCI_RAW flag is set again. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 6 ++++++ net/bluetooth/mgmt.c | 1 + 2 files changed, 7 insertions(+) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index c96b96ca41ac..b1d423efa109 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2887,6 +2887,12 @@ static void hci_power_on(struct work_struct *work) */ mgmt_index_added(hdev); } else if (test_and_clear_bit(HCI_CONFIG, &hdev->dev_flags)) { + /* When the controller is now configured, then it + * is important to clear the HCI_RAW flag. + */ + if (!test_bit(HCI_UNCONFIGURED, &hdev->dev_flags)) + clear_bit(HCI_RAW, &hdev->flags); + /* Powering on the controller with HCI_CONFIG set only * happens with the transition from unconfigured to * configured. This will send the Index Added event. diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 8275316ae099..e253f8e1fa47 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5451,6 +5451,7 @@ static int set_external_config(struct sock *sk, struct hci_dev *hdev, queue_work(hdev->req_workqueue, &hdev->power_on); } else { + set_bit(HCI_RAW, &hdev->flags); mgmt_index_added(hdev); } } From 09ae260ba452c2ed36ec295941a58cb75db213ed Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 6 Jul 2014 13:41:15 +0300 Subject: [PATCH 195/378] Bluetooth: Use lower timeout for LE auto-connections When we establish connections as a consequence of receiving an advertising report it makes no sense to wait the normal 20 second LE connection timeout. This patch modifies the hci_connect_le function to take an extra timeout value and uses a lower 2 second timeout for the auto-connection case. This timeout is intentionally chosen to be just a bit higher than the 1.28 second timeout that High Duty Cycle Advertising uses. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 1 + include/net/bluetooth/hci_core.h | 4 +++- net/bluetooth/hci_conn.c | 4 +++- net/bluetooth/hci_event.c | 6 +++--- net/bluetooth/l2cap_core.c | 2 +- net/bluetooth/mgmt.c | 3 ++- 6 files changed, 13 insertions(+), 7 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 46f2458e4bc9..5481d1c8badb 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -238,6 +238,7 @@ enum { #define HCI_AUTO_OFF_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ #define HCI_POWER_OFF_TIMEOUT msecs_to_jiffies(5000) /* 5 seconds */ #define HCI_LE_CONN_TIMEOUT msecs_to_jiffies(20000) /* 20 seconds */ +#define HCI_LE_AUTOCONN_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ /* HCI data types */ #define HCI_COMMAND_PKT 0x01 diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 9d838a072db4..06039aae3010 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -385,6 +385,7 @@ struct hci_conn { __u32 passkey_notify; __u8 passkey_entered; __u16 disc_timeout; + __u16 conn_timeout; __u16 setting; __u16 le_conn_min_interval; __u16 le_conn_max_interval; @@ -703,7 +704,8 @@ void hci_chan_list_flush(struct hci_conn *conn); struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle); struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, - u8 dst_type, u8 sec_level, u8 auth_type); + u8 dst_type, u8 sec_level, u8 auth_type, + u16 conn_timeout); struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst, u8 sec_level, u8 auth_type); struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst, diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 0d579d036833..faa032fcdaee 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -700,7 +700,8 @@ static void hci_req_directed_advertising(struct hci_request *req, } struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, - u8 dst_type, u8 sec_level, u8 auth_type) + u8 dst_type, u8 sec_level, u8 auth_type, + u16 conn_timeout) { struct hci_conn_params *params; struct hci_conn *conn; @@ -758,6 +759,7 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, conn->sec_level = BT_SECURITY_LOW; conn->pending_sec_level = sec_level; conn->auth_type = auth_type; + conn->conn_timeout = conn_timeout; hci_req_init(&req, hdev); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index c2ba79c8fe51..f452e44eff3c 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1073,7 +1073,7 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) if (conn) queue_delayed_work(hdev->workqueue, &conn->le_conn_timeout, - HCI_LE_CONN_TIMEOUT); + conn->conn_timeout); } mgmt_advertising(hdev, *sent); @@ -1913,7 +1913,7 @@ static void hci_cs_le_create_conn(struct hci_dev *hdev, u8 status) if (cp->filter_policy == HCI_LE_USE_PEER_ADDR) queue_delayed_work(conn->hdev->workqueue, &conn->le_conn_timeout, - HCI_LE_CONN_TIMEOUT); + conn->conn_timeout); unlock: hci_dev_unlock(hdev); @@ -4238,7 +4238,7 @@ static bool check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, return false; conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW, - HCI_AT_NO_BONDING); + HCI_AT_NO_BONDING, HCI_LE_AUTOCONN_TIMEOUT); if (!IS_ERR(conn)) return true; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index f1f544a12c3c..a219276d9f92 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -7135,7 +7135,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, dst_type = ADDR_LE_DEV_RANDOM; hcon = hci_connect_le(hdev, dst, dst_type, chan->sec_level, - auth_type); + auth_type, HCI_LE_CONN_TIMEOUT); } else { hcon = hci_connect_acl(hdev, dst, chan->sec_level, auth_type); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index e253f8e1fa47..be91d55c258b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3116,7 +3116,8 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, hci_conn_params_add(hdev, &cp->addr.bdaddr, addr_type); conn = hci_connect_le(hdev, &cp->addr.bdaddr, addr_type, - sec_level, auth_type); + sec_level, auth_type, + HCI_LE_CONN_TIMEOUT); } if (IS_ERR(conn)) { From 40df783d1ef1989ac454e3dfcda017270b8950e6 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 6 Jul 2014 13:29:58 +0200 Subject: [PATCH 196/378] Bluetooth: Add support for Intel bootloader devices Intel Bluetooth devices that boot up in bootloader mode can not be used as generic HCI devices, but their HCI transport is still valuable and so bring that up as raw-only devices. T: Bus=02 Lev=02 Prnt=03 Port=00 Cnt=01 Dev#= 14 Spd=12 MxCh= 0 D: Ver= 1.10 Cls=ff(vend.) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 P: Vendor=8087 ProdID=0a5a Rev= 0.00 S: Manufacturer=Intel(R) Corporation S: Product=Intel(R) Wilkins Peak 2x2 S: SerialNumber=001122334455 WP_A0 C:* #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=100mA I:* If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none) E: Ad=81(I) Atr=03(Int.) MxPS= 64 Ivl=1ms E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms I:* If#= 1 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none) E: Ad=03(O) Atr=01(Isoc) MxPS= 0 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 0 Ivl=1ms I: If#= 1 Alt= 1 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none) E: Ad=03(O) Atr=01(Isoc) MxPS= 9 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 9 Ivl=1ms I: If#= 1 Alt= 2 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none) E: Ad=03(O) Atr=01(Isoc) MxPS= 17 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 17 Ivl=1ms I: If#= 1 Alt= 3 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none) E: Ad=03(O) Atr=01(Isoc) MxPS= 25 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 25 Ivl=1ms I: If#= 1 Alt= 4 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none) E: Ad=03(O) Atr=01(Isoc) MxPS= 33 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 33 Ivl=1ms I: If#= 1 Alt= 5 #EPs= 2 Cls=ff(vend.) Sub=00 Prot=00 Driver=(none) E: Ad=03(O) Atr=01(Isoc) MxPS= 49 Ivl=1ms E: Ad=83(I) Atr=01(Isoc) MxPS= 49 Ivl=1ms Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/btusb.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 61d8385666e9..21f10cac4fac 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -49,7 +49,8 @@ static struct usb_driver btusb_driver; #define BTUSB_WRONG_SCO_MTU 0x40 #define BTUSB_ATH3012 0x80 #define BTUSB_INTEL 0x100 -#define BTUSB_BCM_PATCHRAM 0x200 +#define BTUSB_INTEL_BOOT 0x200 +#define BTUSB_BCM_PATCHRAM 0x400 static const struct usb_device_id btusb_table[] = { /* Generic Bluetooth USB device */ @@ -121,6 +122,9 @@ static const struct usb_device_id btusb_table[] = { /* IMC Networks - Broadcom based */ { USB_VENDOR_AND_INTERFACE_INFO(0x13d3, 0xff, 0x01, 0x01) }, + /* Intel Bluetooth USB Bootloader (RAM module) */ + { USB_DEVICE(0x8087, 0x0a5a), .driver_info = BTUSB_INTEL_BOOT }, + { } /* Terminating entry */ }; @@ -1770,6 +1774,9 @@ static int btusb_probe(struct usb_interface *intf, hdev->set_bdaddr = btusb_set_bdaddr_intel; } + if (id->driver_info & BTUSB_INTEL_BOOT) + set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); + /* Interface numbers are hardcoded in the specification */ data->isoc = usb_ifnum_to_if(data->udev, 1); From cc78b44ba2231d6a92707407cd199b2e05fe3528 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 6 Jul 2014 13:43:20 +0200 Subject: [PATCH 197/378] Bluetooth: Skip unconfigured init procedure for raw-only devices When the driver sets HCI_QUIRK_RAW_DEVICE, the controller will be set as unconfigured. However running the unconfigured init procecure is not useful since raw-only devices are not allowed to change its configuration. This change skips the init procedure and just allows user channel operation for this device. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b1d423efa109..fc7abd3c012d 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1813,6 +1813,9 @@ static int __hci_unconf_init(struct hci_dev *hdev) { int err; + if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) + return 0; + err = __hci_req_sync(hdev, hci_init0_req, 0, HCI_INIT_TIMEOUT); if (err < 0) return err; From 01bb75ed2696d4f5b252ca773a53474d6d533f68 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 6 Jul 2014 14:27:39 +0200 Subject: [PATCH 198/378] Bluetooth: Remove module parameters for ignoring USB devices The module parameters to ignore devices based on USB VID/PID are not needed at all. So just remove them. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/btusb.c | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 21f10cac4fac..14314a12babe 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -30,9 +30,6 @@ #define VERSION "0.6" -static bool ignore_dga; -static bool ignore_csr; -static bool ignore_sniffer; static bool disable_scofix; static bool force_scofix; @@ -1683,15 +1680,6 @@ static int btusb_probe(struct usb_interface *intf, if (id->driver_info == BTUSB_IGNORE) return -ENODEV; - if (ignore_dga && id->driver_info & BTUSB_DIGIANSWER) - return -ENODEV; - - if (ignore_csr && id->driver_info & BTUSB_CSR) - return -ENODEV; - - if (ignore_sniffer && id->driver_info & BTUSB_SNIFFER) - return -ENODEV; - if (id->driver_info & BTUSB_ATH3012) { struct usb_device *udev = interface_to_usbdev(intf); @@ -1981,15 +1969,6 @@ static struct usb_driver btusb_driver = { module_usb_driver(btusb_driver); -module_param(ignore_dga, bool, 0644); -MODULE_PARM_DESC(ignore_dga, "Ignore devices with id 08fd:0001"); - -module_param(ignore_csr, bool, 0644); -MODULE_PARM_DESC(ignore_csr, "Ignore devices with id 0a12:0001"); - -module_param(ignore_sniffer, bool, 0644); -MODULE_PARM_DESC(ignore_sniffer, "Ignore devices with id 0a12:0002"); - module_param(disable_scofix, bool, 0644); MODULE_PARM_DESC(disable_scofix, "Disable fixup of wrong SCO buffer size"); From 223683a54bf3f371683c401b9a759c54e1452fa3 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Sun, 6 Jul 2014 15:44:23 +0300 Subject: [PATCH 199/378] Bluetooth: Fix updating background scan for LE connect complete When we get an LE connection complete event we should restart background scanning if there are any devices needing it. So far the code was only making the decision based on whether the completed connection had any stored parameters or not. This patch ensures that we trigger background scanning always when necessary. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_event.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index f452e44eff3c..57837cad9919 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4196,12 +4196,11 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_proto_connect_cfm(conn, ev->status); params = hci_conn_params_lookup(hdev, &conn->dst, conn->dst_type); - if (params) { + if (params) list_del_init(¶ms->action); - hci_update_background_scan(hdev); - } unlock: + hci_update_background_scan(hdev); hci_dev_unlock(hdev); } From 3a5ef20c979c0f33b6fb2582d04957397a6bf51f Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 6 Jul 2014 14:53:54 +0200 Subject: [PATCH 200/378] Bluetooth: Handle Intel USB bootloader with buggy interrupt The interrupt interface for the Intel USB bootloader devices is only enabled after receiving SetInterface(0, AltSetting=0). When this USB command is not send, then no HCI events will be received. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/btusb.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index 14314a12babe..fe24f600ed65 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -1807,6 +1807,18 @@ static int btusb_probe(struct usb_interface *intf, data->isoc = NULL; } + if (id->driver_info & BTUSB_INTEL_BOOT) { + /* A bug in the bootloader causes that interrupt interface is + * only enabled after receiving SetInterface(0, AltSetting=0). + */ + err = usb_set_interface(data->udev, 0, 0); + if (err < 0) { + BT_ERR("failed to set interface 0, alt 0 %d", err); + hci_free_dev(hdev); + return err; + } + } + if (data->isoc) { err = usb_driver_claim_interface(&btusb_driver, data->isoc, data); From d92f2df0565ea04101d6ac04bdc10feeb1d93c94 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 6 Jul 2014 14:53:55 +0200 Subject: [PATCH 201/378] Bluetooth: Ignore isochronous endpoints for Intel USB bootloader The isochronous endpoints are not valid when the Intel Bluetooth controller boots up in bootloader mode. So just mark these endpoints as broken and then they will not be configured. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/btusb.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index fe24f600ed65..b7d0fed3417f 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -120,7 +120,8 @@ static const struct usb_device_id btusb_table[] = { { USB_VENDOR_AND_INTERFACE_INFO(0x13d3, 0xff, 0x01, 0x01) }, /* Intel Bluetooth USB Bootloader (RAM module) */ - { USB_DEVICE(0x8087, 0x0a5a), .driver_info = BTUSB_INTEL_BOOT }, + { USB_DEVICE(0x8087, 0x0a5a), + .driver_info = BTUSB_INTEL_BOOT | BTUSB_BROKEN_ISOC }, { } /* Terminating entry */ }; From cdc52faac5f341beaff036828b9459f7c8dd7296 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 6 Jul 2014 15:36:15 +0200 Subject: [PATCH 202/378] Bluetooth: Fix memory leaking when hdev->send returns an error The drivers are allowed to just return an error from hdev->send callback and in that case the driver does not own the SKB. Which means that the caller has to free the SKB. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index fc7abd3c012d..b02454ab07ee 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -4339,6 +4339,8 @@ EXPORT_SYMBOL(hci_unregister_cb); static void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) { + int err; + BT_DBG("%s type %d len %d", hdev->name, bt_cb(skb)->pkt_type, skb->len); /* Time stamp */ @@ -4355,8 +4357,11 @@ static void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb) /* Get rid of skb owner, prior to sending to the driver. */ skb_orphan(skb); - if (hdev->send(hdev, skb) < 0) - BT_ERR("%s sending frame failed", hdev->name); + err = hdev->send(hdev, skb); + if (err < 0) { + BT_ERR("%s sending frame failed (%d)", hdev->name, err); + kfree_skb(skb); + } } void hci_req_init(struct hci_request *req, struct hci_dev *hdev) From 74292d5ac289ff5ec8b565889aaeab332e709099 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 6 Jul 2014 15:50:27 +0200 Subject: [PATCH 203/378] Bluetooth: Enforce providing hdev->send driver callback The hdev->send driver callback is mandatory to be provided by a driver before calling hci_register_dev. So enforce it and return EINVAL in case it is not available. All existing drivers are providing this callback anyway, so this is just an extra sanity check. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b02454ab07ee..623ffe0da4a6 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3932,7 +3932,7 @@ int hci_register_dev(struct hci_dev *hdev) { int id, error; - if (!hdev->open || !hdev->close) + if (!hdev->open || !hdev->close || !hdev->send) return -EINVAL; /* Do not allow HCI_AMP devices to register at index 0, From 4f64fa807a9da14abfa329ae9f16c5ea11ef99ea Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 7 Jul 2014 00:12:04 +0200 Subject: [PATCH 204/378] Bluetooth: Use BTUSB_BROKEN_ISOC flag for CSR USB sniffer devices Instead of setting data->isoc manually, use BTUSB_BROKEN_ISOC to indicate that isochronous endpoints are not needed for CSR USB sniffer devices. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/btusb.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index b7d0fed3417f..ef4375d5c4ed 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -230,10 +230,12 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x08fd, 0x0002), .driver_info = BTUSB_IGNORE }, /* CSR BlueCore Bluetooth Sniffer */ - { USB_DEVICE(0x0a12, 0x0002), .driver_info = BTUSB_SNIFFER }, + { USB_DEVICE(0x0a12, 0x0002), + .driver_info = BTUSB_SNIFFER | BTUSB_BROKEN_ISOC }, /* Frontline ComProbe Bluetooth Sniffer */ - { USB_DEVICE(0x16d3, 0x0002), .driver_info = BTUSB_SNIFFER }, + { USB_DEVICE(0x16d3, 0x0002), + .driver_info = BTUSB_SNIFFER | BTUSB_BROKEN_ISOC }, /* Intel Bluetooth device */ { USB_DEVICE(0x8087, 0x07dc), .driver_info = BTUSB_INTEL }, @@ -1804,8 +1806,6 @@ static int btusb_probe(struct usb_interface *intf, /* New sniffer firmware has crippled HCI interface */ if (le16_to_cpu(udev->descriptor.bcdDevice) > 0x997) set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); - - data->isoc = NULL; } if (id->driver_info & BTUSB_INTEL_BOOT) { From 1c1abcabfa229b4b3d688d0a1f677d0ca7a1c624 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 7 Jul 2014 12:45:53 +0300 Subject: [PATCH 205/378] Bluetooth: Fix connecting devices during LE device discovery If we have devices set as to be connected we should connect to them even during normal discovery if we get a connectable advertising event. If we also have HCI_CONNECTABLE set we should connect ADV_DIRECT_IND events even to devices that we don't have in our pend_le_conns list. This patch implements such behavior by passing the advertising report type to check_pending_le_conn() and calls that function regardless of what type of scanning we are doing. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_event.c | 47 ++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 57837cad9919..8a36abb3cb06 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4229,13 +4229,30 @@ static void hci_le_conn_update_complete_evt(struct hci_dev *hdev, /* This function requires the caller holds hdev->lock */ static bool check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, - u8 addr_type) + u8 addr_type, u8 adv_type) { struct hci_conn *conn; + /* If the event is not connectable don't proceed further */ + if (adv_type != LE_ADV_IND && adv_type != LE_ADV_DIRECT_IND) + return false; + + /* Ignore if the device is blocked */ + if (hci_blacklist_lookup(hdev, addr, addr_type)) + return false; + + /* If we're connectable, always connect any ADV_DIRECT_IND event */ + if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags) && + adv_type == LE_ADV_DIRECT_IND) + goto connect; + + /* If we're not connectable only connect devices that we have in + * our pend_le_conns list. + */ if (!hci_pend_le_action_lookup(&hdev->pend_le_conns, addr, addr_type)) return false; +connect: conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW, HCI_AT_NO_BONDING, HCI_LE_AUTOCONN_TIMEOUT); if (!IS_ERR(conn)) @@ -4260,32 +4277,26 @@ static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, u8 bdaddr_type, s8 rssi, u8 *data, u8 len) { struct discovery_state *d = &hdev->discovery; + struct smp_irk *irk; bool match; u32 flags; + /* Check if we need to convert to identity address */ + irk = hci_get_irk(hdev, bdaddr, bdaddr_type); + if (irk) { + bdaddr = &irk->bdaddr; + bdaddr_type = irk->addr_type; + } + + /* Check if we have been requested to connect to this device */ + check_pending_le_conn(hdev, bdaddr, bdaddr_type, type); + /* Passive scanning shouldn't trigger any device found events, * except for devices marked as CONN_REPORT for which we do send * device found events. */ if (hdev->le_scan_type == LE_SCAN_PASSIVE) { struct hci_conn_params *param; - struct smp_irk *irk; - - /* Check if we need to convert to identity address */ - irk = hci_get_irk(hdev, bdaddr, bdaddr_type); - if (irk) { - bdaddr = &irk->bdaddr; - bdaddr_type = irk->addr_type; - } - - /* Ignore if the device is blocked */ - if (hci_blacklist_lookup(hdev, bdaddr, bdaddr_type)) - return; - - if (type == LE_ADV_IND || type == LE_ADV_DIRECT_IND) { - if (check_pending_le_conn(hdev, bdaddr, bdaddr_type)) - return; - } if (type == LE_ADV_DIRECT_IND) return; From 841c564499dca9f4819ae2a5f5beb31694f9634e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 7 Jul 2014 12:45:54 +0300 Subject: [PATCH 206/378] Bluetooth: Remove redundant IRK lookup in mgmt_device_found() Now that we have the process_adv_report() function doing the IRK lookup and updating the bdaddr we don't need to do this anymore in mgmt.c in the mgmt_device_found() function. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index be91d55c258b..40244fc0e326 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6717,7 +6717,6 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, { char buf[512]; struct mgmt_ev_device_found *ev = (void *) buf; - struct smp_irk *irk; size_t ev_size; /* Don't send events for a non-kernel initiated discovery. With @@ -6739,15 +6738,8 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, memset(buf, 0, sizeof(buf)); - irk = hci_get_irk(hdev, bdaddr, addr_type); - if (irk) { - bacpy(&ev->addr.bdaddr, &irk->bdaddr); - ev->addr.type = link_to_bdaddr(link_type, irk->addr_type); - } else { - bacpy(&ev->addr.bdaddr, bdaddr); - ev->addr.type = link_to_bdaddr(link_type, addr_type); - } - + bacpy(&ev->addr.bdaddr, bdaddr); + ev->addr.type = link_to_bdaddr(link_type, addr_type); ev->rssi = rssi; ev->flags = cpu_to_le32(flags); From 7e899c94939b928173d76d1e5a7c0675f44813f5 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 7 Jul 2014 12:45:55 +0300 Subject: [PATCH 207/378] Bluetooth: Remove unnecessary return value from check_pending_le_conn Since the only caller of this function doesn't care about the return value anymore let's just remove it. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_event.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 8a36abb3cb06..84f4659f136e 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4228,18 +4228,18 @@ static void hci_le_conn_update_complete_evt(struct hci_dev *hdev, } /* This function requires the caller holds hdev->lock */ -static bool check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, +static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type, u8 adv_type) { struct hci_conn *conn; /* If the event is not connectable don't proceed further */ if (adv_type != LE_ADV_IND && adv_type != LE_ADV_DIRECT_IND) - return false; + return; /* Ignore if the device is blocked */ if (hci_blacklist_lookup(hdev, addr, addr_type)) - return false; + return; /* If we're connectable, always connect any ADV_DIRECT_IND event */ if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags) && @@ -4250,13 +4250,13 @@ static bool check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, * our pend_le_conns list. */ if (!hci_pend_le_action_lookup(&hdev->pend_le_conns, addr, addr_type)) - return false; + return; connect: conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW, HCI_AT_NO_BONDING, HCI_LE_AUTOCONN_TIMEOUT); if (!IS_ERR(conn)) - return true; + return; switch (PTR_ERR(conn)) { case -EBUSY: @@ -4269,8 +4269,6 @@ connect: default: BT_DBG("Failed to connect: err %ld", PTR_ERR(conn)); } - - return true; } static void process_adv_report(struct hci_dev *hdev, u8 type, bdaddr_t *bdaddr, From bb3e0a336ae683332c1282d1e84b371b65aba1b4 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 7 Jul 2014 13:24:58 +0300 Subject: [PATCH 208/378] Bluetooth: Update discovery state earlier in hci_discovery_set_state In a subsequent patch the hci_update_background_scan() function will depend on being able to know the current discovery state. For this to be possible we need to set the new state early in the function. Since we also need to check what the old state was this patch introduces an extra variable for tracking it. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_core.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 623ffe0da4a6..6a0e39f69e03 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1903,16 +1903,20 @@ bool hci_discovery_active(struct hci_dev *hdev) void hci_discovery_set_state(struct hci_dev *hdev, int state) { + int old_state = hdev->discovery.state; + BT_DBG("%s state %u -> %u", hdev->name, hdev->discovery.state, state); - if (hdev->discovery.state == state) + if (old_state == state) return; + hdev->discovery.state = state; + switch (state) { case DISCOVERY_STOPPED: hci_update_background_scan(hdev); - if (hdev->discovery.state != DISCOVERY_STARTING) + if (old_state != DISCOVERY_STARTING) mgmt_discovering(hdev, 0); break; case DISCOVERY_STARTING: @@ -1925,8 +1929,6 @@ void hci_discovery_set_state(struct hci_dev *hdev, int state) case DISCOVERY_STOPPING: break; } - - hdev->discovery.state = state; } void hci_inquiry_cache_flush(struct hci_dev *hdev) From ae23ada43d7d644e563a23d4c2cd2aa828be1431 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 7 Jul 2014 13:24:59 +0300 Subject: [PATCH 209/378] Bluetooth: Don't let background scanning interfering with discovery If we have an active discovery going on we shouldn't do any changes to LE scanning when hci_update_background_scan() is called (a call which can happen for many different reasons). This patch fixes the issue by returning from the function if the discovery state is anything else except DISCOVERY_STOPPED. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_core.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 6a0e39f69e03..4f8ba49ac49d 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -5403,6 +5403,10 @@ void hci_update_background_scan(struct hci_dev *hdev) test_bit(HCI_UNREGISTER, &hdev->dev_flags)) return; + /* If discovery is active don't interfere with it */ + if (hdev->discovery.state != DISCOVERY_STOPPED) + return; + hci_req_init(&req, hdev); if (list_empty(&hdev->pend_le_conns) && From 2b7be33e60c631b3080099baf14c43505fa8017d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 7 Jul 2014 14:40:22 +0300 Subject: [PATCH 210/378] Bluetooth: Enable passive scanning whenever we're connectable Enabling passive scanning always when we're connectable aligns us with the BR/EDR page scanning. This is also consistent with the fact that the code dealing with passive scanning results will actively try to connect any direct advertising event when we're connectable. This patch implements the feature by adding the connectable condition to hci_update_background_scan() checks for starting scanning and by calling hci_update_background_scan() whenever the connectable state changes. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_core.c | 3 ++- net/bluetooth/mgmt.c | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 4f8ba49ac49d..6790dc8cff99 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -5409,7 +5409,8 @@ void hci_update_background_scan(struct hci_dev *hdev) hci_req_init(&req, hdev); - if (list_empty(&hdev->pend_le_conns) && + if (!test_bit(HCI_CONNECTABLE, &hdev->dev_flags) && + list_empty(&hdev->pend_le_conns) && list_empty(&hdev->pend_le_reports)) { /* If there is no pending LE connections or devices * to be scanned for, we should stop the background diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 40244fc0e326..ef675acbcfce 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1750,8 +1750,10 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status) send_settings_rsp(cmd->sk, MGMT_OP_SET_CONNECTABLE, hdev); - if (changed) + if (changed) { new_settings(hdev, cmd->sk); + hci_update_background_scan(hdev); + } remove_cmd: mgmt_pending_remove(cmd); From d93375a82da10cb023afc945fa18471bf3c41704 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 7 Jul 2014 15:02:27 +0300 Subject: [PATCH 211/378] Bluetooth: Remove auth_type parameter from hci_connect_le() The auth_type value which gets assigned to hci_conn->auth_type is something that's only used for BR/EDR connections and is of no value for LE connections. It makes therefore little sense to pass it to the hci_connect_le() function. This patch removes the parameter from the function. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 3 +-- net/bluetooth/hci_conn.c | 5 +---- net/bluetooth/hci_event.c | 2 +- net/bluetooth/l2cap_core.c | 6 ++---- net/bluetooth/mgmt.c | 3 +-- 5 files changed, 6 insertions(+), 13 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 06039aae3010..8752ac674db1 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -704,8 +704,7 @@ void hci_chan_list_flush(struct hci_conn *conn); struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle); struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, - u8 dst_type, u8 sec_level, u8 auth_type, - u16 conn_timeout); + u8 dst_type, u8 sec_level, u16 conn_timeout); struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst, u8 sec_level, u8 auth_type); struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst, diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index faa032fcdaee..9323044f01cd 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -700,8 +700,7 @@ static void hci_req_directed_advertising(struct hci_request *req, } struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, - u8 dst_type, u8 sec_level, u8 auth_type, - u16 conn_timeout) + u8 dst_type, u8 sec_level, u16 conn_timeout) { struct hci_conn_params *params; struct hci_conn *conn; @@ -721,7 +720,6 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, dst); if (conn) { conn->pending_sec_level = sec_level; - conn->auth_type = auth_type; goto done; } @@ -758,7 +756,6 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, conn->dst_type = dst_type; conn->sec_level = BT_SECURITY_LOW; conn->pending_sec_level = sec_level; - conn->auth_type = auth_type; conn->conn_timeout = conn_timeout; hci_req_init(&req, hdev); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 84f4659f136e..ad39d9ad6fbc 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4254,7 +4254,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, connect: conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW, - HCI_AT_NO_BONDING, HCI_LE_AUTOCONN_TIMEOUT); + HCI_LE_AUTOCONN_TIMEOUT); if (!IS_ERR(conn)) return; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index a219276d9f92..3daab4588522 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -7048,7 +7048,6 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, struct l2cap_conn *conn; struct hci_conn *hcon; struct hci_dev *hdev; - __u8 auth_type; int err; BT_DBG("%pMR -> %pMR (type %u) psm 0x%2.2x", &chan->src, dst, @@ -7124,8 +7123,6 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, chan->psm = psm; chan->dcid = cid; - auth_type = l2cap_get_auth_type(chan); - if (bdaddr_type_is_le(dst_type)) { /* Convert from L2CAP channel address type to HCI address type */ @@ -7135,8 +7132,9 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, dst_type = ADDR_LE_DEV_RANDOM; hcon = hci_connect_le(hdev, dst, dst_type, chan->sec_level, - auth_type, HCI_LE_CONN_TIMEOUT); + HCI_LE_CONN_TIMEOUT); } else { + u8 auth_type = l2cap_get_auth_type(chan); hcon = hci_connect_acl(hdev, dst, chan->sec_level, auth_type); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index ef675acbcfce..e7047de1ba11 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3118,8 +3118,7 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, hci_conn_params_add(hdev, &cp->addr.bdaddr, addr_type); conn = hci_connect_le(hdev, &cp->addr.bdaddr, addr_type, - sec_level, auth_type, - HCI_LE_CONN_TIMEOUT); + sec_level, HCI_LE_CONN_TIMEOUT); } if (IS_ERR(conn)) { From cdd6275e510bd86c44d3fc85a78306f514bbac9a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 7 Jul 2014 15:02:28 +0300 Subject: [PATCH 212/378] Bluetooth: Pass desired connection role to hci_connect_le() If we have both LE scanning and advertising simultaneously enabled we need a way to tell hci_connect_le() in which role to initiate a connection. This patch adds a new parameter to the function to give it the necessary information. For auto-connect and mgmt_pair_device we always use master role, whereas for L2CAP users (in practice sockets) we use slave role whenever HCI_ADVERTISING is set and master role otherwise. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 3 ++- net/bluetooth/hci_conn.c | 6 ++++-- net/bluetooth/hci_event.c | 3 ++- net/bluetooth/l2cap_core.c | 6 +++++- net/bluetooth/mgmt.c | 3 ++- 5 files changed, 15 insertions(+), 6 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 8752ac674db1..5701d15779dd 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -704,7 +704,8 @@ void hci_chan_list_flush(struct hci_conn *conn); struct hci_chan *hci_chan_lookup_handle(struct hci_dev *hdev, __u16 handle); struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, - u8 dst_type, u8 sec_level, u16 conn_timeout); + u8 dst_type, u8 sec_level, u16 conn_timeout, + bool master); struct hci_conn *hci_connect_acl(struct hci_dev *hdev, bdaddr_t *dst, u8 sec_level, u8 auth_type); struct hci_conn *hci_connect_sco(struct hci_dev *hdev, int type, bdaddr_t *dst, diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 9323044f01cd..16fd55da9c1d 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -700,7 +700,8 @@ static void hci_req_directed_advertising(struct hci_request *req, } struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, - u8 dst_type, u8 sec_level, u16 conn_timeout) + u8 dst_type, u8 sec_level, u16 conn_timeout, + bool master) { struct hci_conn_params *params; struct hci_conn *conn; @@ -760,7 +761,8 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, hci_req_init(&req, hdev); - if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) { + /* If requested to connect as slave use directed advertising */ + if (!master) { hci_req_directed_advertising(&req, conn); goto create_conn; } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index ad39d9ad6fbc..a6816498b0b9 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4253,8 +4253,9 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, return; connect: + /* Request connection in master = true role */ conn = hci_connect_le(hdev, addr, addr_type, BT_SECURITY_LOW, - HCI_LE_AUTOCONN_TIMEOUT); + HCI_LE_AUTOCONN_TIMEOUT, true); if (!IS_ERR(conn)) return; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 3daab4588522..bf379a379fa0 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -7124,6 +7124,8 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, chan->dcid = cid; if (bdaddr_type_is_le(dst_type)) { + bool master; + /* Convert from L2CAP channel address type to HCI address type */ if (dst_type == BDADDR_LE_PUBLIC) @@ -7131,8 +7133,10 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid, else dst_type = ADDR_LE_DEV_RANDOM; + master = !test_bit(HCI_ADVERTISING, &hdev->dev_flags); + hcon = hci_connect_le(hdev, dst, dst_type, chan->sec_level, - HCI_LE_CONN_TIMEOUT); + HCI_LE_CONN_TIMEOUT, master); } else { u8 auth_type = l2cap_get_auth_type(chan); hcon = hci_connect_acl(hdev, dst, chan->sec_level, auth_type); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index e7047de1ba11..b391e2fef4b6 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3117,8 +3117,9 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, */ hci_conn_params_add(hdev, &cp->addr.bdaddr, addr_type); + /* Request a connection with master = true role */ conn = hci_connect_le(hdev, &cp->addr.bdaddr, addr_type, - sec_level, HCI_LE_CONN_TIMEOUT); + sec_level, HCI_LE_CONN_TIMEOUT, true); } if (IS_ERR(conn)) { From a70f4b5f14a029c14c3901f429e4d3d7e5477b4f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 7 Jul 2014 15:19:50 +0300 Subject: [PATCH 213/378] Bluetooth: Don't try background scanning if LE is not enabled For adapters that do not support LE and ones where LE hasn't been enabled we shouldn't be trying to initiate background scanning. This patch adds an extra check to the hci_update_background_scan() to ensure that we bail out if HCI_LE_ENABLED is not set. Since we do allow user space to feed the kernel with LE connection parameters even when LE is not enabled we now need to also call hci_update_background_scan() as soon as LE gets enabled so that scanning gets started if necessary. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_core.c | 4 ++++ net/bluetooth/mgmt.c | 2 ++ 2 files changed, 6 insertions(+) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 6790dc8cff99..f1c5a077e558 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -5403,6 +5403,10 @@ void hci_update_background_scan(struct hci_dev *hdev) test_bit(HCI_UNREGISTER, &hdev->dev_flags)) return; + /* No point in doing scanning if LE support hasn't been enabled */ + if (!test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) + return; + /* If discovery is active don't interfere with it */ if (hdev->discovery.state != DISCOVERY_STOPPED) return; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index b391e2fef4b6..9cc7108f4c45 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -2152,6 +2152,8 @@ static void le_enable_complete(struct hci_dev *hdev, u8 status) update_scan_rsp_data(&req); hci_req_run(&req, NULL); + hci_update_background_scan(hdev); + hci_dev_unlock(hdev); } } From 2c4db12ec469b9fcdad9f6bfd6fa20e65a563ac5 Mon Sep 17 00:00:00 2001 From: Andrea Merello Date: Sat, 5 Jul 2014 21:07:17 +0200 Subject: [PATCH 214/378] rt2800usb: Don't perform DMA from stack Function rt2800usb_autorun_detect() passes the address of a variable allocated onto the stack to be used for DMA by the USB layer. This has been caught by my debugging-enabled kernel. This patch change things in order to allocate that variable via kmalloc, and it adjusts things to handle the kmalloc failure case, propagating the error. [ 7363.238852] ------------[ cut here ]------------ [ 7363.243529] WARNING: CPU: 1 PID: 5235 at lib/dma-debug.c:1153 check_for_stack+0xa4/0xf0() [ 7363.251759] ehci-pci 0000:00:04.1: DMA-API: device driver maps memory fromstack [addr=ffff88006b81bad4] [ 7363.261210] Modules linked in: rt2800usb(O+) rt2800lib(O) rt2x00usb(O) rt2x00lib(O) rtl818x_pci(O) rtl8187 led_class eeprom_93cx6 mac80211 cfg80211 [last unloaded: rt2x00lib] [ 7363.277143] CPU: 1 PID: 5235 Comm: systemd-udevd Tainted: G O 3.16.0-rc3-wl+ #31 [ 7363.285546] Hardware name: System manufacturer System Product Name/M3N78 PRO, BIOS ASUS M3N78 PRO ACPI BIOS Revision 1402 12/04/2009 [ 7363.297511] 0000000000000009 ffff88006b81b710 ffffffff8175dcad ffff88006b81b758 [ 7363.305062] ffff88006b81b748 ffffffff8106d372 ffff88006cf10098 ffff88006cead6a0 [ 7363.312622] ffff88006b81bad4 ffffffff81c1e7c0 ffff88006cf10098 ffff88006b81b7a8 [ 7363.320161] Call Trace: [ 7363.322661] [] dump_stack+0x4d/0x6f [ 7363.327847] [] warn_slowpath_common+0x82/0xb0 [ 7363.333893] [] warn_slowpath_fmt+0x47/0x50 [ 7363.339686] [] check_for_stack+0xa4/0xf0 [ 7363.345298] [] debug_dma_map_page+0x10c/0x150 [ 7363.351367] [] usb_hcd_map_urb_for_dma+0x229/0x720 [ 7363.357890] [] usb_hcd_submit_urb+0x2fd/0x930 [ 7363.363929] [] ? irq_work_queue+0x71/0xd0 [ 7363.369617] [] ? wake_up_klogd+0x37/0x50 [ 7363.375219] [] ? console_unlock+0x1e5/0x420 [ 7363.381081] [] ? vprintk_emit+0x245/0x530 [ 7363.386773] [] usb_submit_urb+0x30c/0x580 [ 7363.392462] [] usb_start_wait_urb+0x65/0xf0 [ 7363.398325] [] usb_control_msg+0xcd/0x110 [ 7363.404014] [] rt2x00usb_vendor_request+0xbd/0x170 [rt2x00usb] [ 7363.411544] [] rt2800usb_autorun_detect+0x32/0x50 [rt2800usb] [ 7363.418986] [] rt2800usb_read_eeprom+0x11/0x70 [rt2800usb] [ 7363.426168] [] rt2800_probe_hw+0x11d/0xf90 [rt2800lib] [ 7363.432989] [] rt2800usb_probe_hw+0xd/0x50 [rt2800usb] [ 7363.439808] [] rt2x00lib_probe_dev+0x238/0x7c0 [rt2x00lib] [ 7363.446992] [] ? ieee80211_led_names+0xb8/0x100 [mac80211] [ 7363.454156] [] rt2x00usb_probe+0x156/0x1f0 [rt2x00usb] [ 7363.460971] [] rt2800usb_probe+0x10/0x20 [rt2800usb] [ 7363.467616] [] usb_probe_interface+0xce/0x1c0 [ 7363.473651] [] really_probe+0x70/0x240 [ 7363.479079] [] __driver_attach+0xa1/0xb0 [ 7363.484682] [] ? __device_attach+0x70/0x70 [ 7363.490461] [] bus_for_each_dev+0x63/0xa0 [ 7363.496146] [] driver_attach+0x19/0x20 [ 7363.501570] [] bus_add_driver+0x178/0x220 [ 7363.507270] [] driver_register+0x5b/0xe0 [ 7363.512874] [] usb_register_driver+0xa0/0x170 [ 7363.518905] [] ? 0xffffffffa0079fff [ 7363.524074] [] rt2800usb_driver_init+0x1e/0x20 [rt2800usb] [ 7363.531247] [] do_one_initcall+0x84/0x1b0 [ 7363.536932] [] ? kfree+0xd0/0x110 [ 7363.541931] [] ? __vunmap+0xaa/0xf0 [ 7363.547538] [] load_module+0x1aee/0x2040 [ 7363.553141] [] ? store_uevent+0x50/0x50 [ 7363.558676] [] SyS_init_module+0x9e/0xc0 [ 7363.564285] [] system_call_fastpath+0x16/0x1b [ 7363.570338] ---[ end trace 01ef5f822bea9882 ]--- Signed-off-by: Andrea Merello Acked-by: Helmut Schaa Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800usb.c | 28 +++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index e11dab2216c6..832006b5aab1 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -231,9 +231,12 @@ static enum hrtimer_restart rt2800usb_tx_sta_fifo_timeout(struct hrtimer *timer) */ static int rt2800usb_autorun_detect(struct rt2x00_dev *rt2x00dev) { - __le32 reg; + __le32 *reg; u32 fw_mode; + reg = kmalloc(sizeof(*reg), GFP_KERNEL); + if (reg == NULL) + return -ENOMEM; /* cannot use rt2x00usb_register_read here as it uses different * mode (MULTI_READ vs. DEVICE_MODE) and does not pass the * magic value USB_MODE_AUTORUN (0x11) to the device, thus the @@ -241,8 +244,9 @@ static int rt2800usb_autorun_detect(struct rt2x00_dev *rt2x00dev) */ rt2x00usb_vendor_request(rt2x00dev, USB_DEVICE_MODE, USB_VENDOR_REQUEST_IN, 0, USB_MODE_AUTORUN, - ®, sizeof(reg), REGISTER_TIMEOUT_FIRMWARE); - fw_mode = le32_to_cpu(reg); + reg, sizeof(*reg), REGISTER_TIMEOUT_FIRMWARE); + fw_mode = le32_to_cpu(*reg); + kfree(reg); if ((fw_mode & 0x00000003) == 2) return 1; @@ -261,6 +265,7 @@ static int rt2800usb_write_firmware(struct rt2x00_dev *rt2x00dev, int status; u32 offset; u32 length; + int retval; /* * Check which section of the firmware we need. @@ -278,7 +283,10 @@ static int rt2800usb_write_firmware(struct rt2x00_dev *rt2x00dev, /* * Write firmware to device. */ - if (rt2800usb_autorun_detect(rt2x00dev)) { + retval = rt2800usb_autorun_detect(rt2x00dev); + if (retval < 0) + return retval; + if (retval) { rt2x00_info(rt2x00dev, "Firmware loading not required - NIC in AutoRun mode\n"); } else { @@ -763,7 +771,12 @@ static void rt2800usb_fill_rxdone(struct queue_entry *entry, */ static int rt2800usb_efuse_detect(struct rt2x00_dev *rt2x00dev) { - if (rt2800usb_autorun_detect(rt2x00dev)) + int retval; + + retval = rt2800usb_autorun_detect(rt2x00dev); + if (retval < 0) + return retval; + if (retval) return 1; return rt2800_efuse_detect(rt2x00dev); } @@ -772,7 +785,10 @@ static int rt2800usb_read_eeprom(struct rt2x00_dev *rt2x00dev) { int retval; - if (rt2800usb_efuse_detect(rt2x00dev)) + retval = rt2800usb_efuse_detect(rt2x00dev); + if (retval < 0) + return retval; + if (retval) retval = rt2800_read_eeprom_efuse(rt2x00dev); else retval = rt2x00usb_eeprom_read(rt2x00dev, rt2x00dev->eeprom, From f5c35e15f940a1580c62715adc3e88710753de4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 2 Jul 2014 17:05:35 +0200 Subject: [PATCH 215/378] b43: N-PHY: initialize hardware tables on new devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/tables_nphy.c | 288 ++++++++++++++++++++++++- drivers/net/wireless/b43/tables_nphy.h | 4 + 2 files changed, 281 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/b43/tables_nphy.c b/drivers/net/wireless/b43/tables_nphy.c index b22171592926..dd876857969e 100644 --- a/drivers/net/wireless/b43/tables_nphy.c +++ b/drivers/net/wireless/b43/tables_nphy.c @@ -2146,6 +2146,192 @@ static const u16 b43_ntab_antswctl_r3[4][32] = { } }; +/* static tables, PHY revision >= 7 */ + +/* Copied from brcmsmac (5.75.11) */ +static const u32 b43_ntab_tmap_r7[] = { + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0xf1111110, 0x11111111, 0x11f11111, 0x00000111, + 0x11000000, 0x1111f111, 0x11111111, 0x111111f1, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x000aa888, + 0x88880000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, + 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, + 0xa2222220, 0x22222222, 0x22c22222, 0x00000222, + 0x22000000, 0x2222a222, 0x22222222, 0x222222a2, + 0xf1111110, 0x11111111, 0x11f11111, 0x00011111, + 0x11110000, 0x1111f111, 0x11111111, 0x111111f1, + 0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00088aaa, + 0xaaaa0000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, + 0xaaa8aaa0, 0x8aaa8aaa, 0xaa8a8a8a, 0x000aaa88, + 0x8aaa0000, 0xaaa8a888, 0x8aa88a8a, 0x8a88a888, + 0x08080a00, 0x0a08080a, 0x080a0a08, 0x00080808, + 0x080a0000, 0x080a0808, 0x080a0808, 0x0a0a0a08, + 0xa0a0a0a0, 0x80a0a080, 0x8080a0a0, 0x00008080, + 0x80a00000, 0x80a080a0, 0xa080a0a0, 0x8080a0a0, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x99999000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, + 0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888, + 0x22000000, 0x2222b222, 0x22222222, 0x222222b2, + 0xb2222220, 0x22222222, 0x22d22222, 0x00000222, + 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, + 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, + 0x33000000, 0x3333b333, 0x33333333, 0x333333b3, + 0xb3333330, 0x33333333, 0x33d33333, 0x00000333, + 0x22000000, 0x2222a222, 0x22222222, 0x222222a2, + 0xa2222220, 0x22222222, 0x22c22222, 0x00000222, + 0x99b99b00, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, + 0x9b99bb99, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888, + 0x22222200, 0x2222f222, 0x22222222, 0x222222f2, + 0x22222222, 0x22222222, 0x22f22222, 0x00000222, + 0x11000000, 0x1111f111, 0x11111111, 0x11111111, + 0xf1111111, 0x11111111, 0x11f11111, 0x01111111, + 0xbb9bb900, 0xb9b9bb99, 0xb99bbbbb, 0xbbbb9b9b, + 0xb9bb99bb, 0xb99999b9, 0xb9b9b99b, 0x00000bbb, + 0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, + 0xa8aa88aa, 0xa88888a8, 0xa8a8a88a, 0x0a888aaa, + 0xaa000000, 0xa8a8aa88, 0xa88aaaaa, 0xaaaa8a8a, + 0xa8aa88a0, 0xa88888a8, 0xa8a8a88a, 0x00000aaa, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0xbbbbbb00, 0x999bbbbb, 0x9bb99b9b, 0xb9b9b9bb, + 0xb9b99bbb, 0xb9b9b9bb, 0xb9bb9b99, 0x00000999, + 0x8a000000, 0xaa88a888, 0xa88888aa, 0xa88a8a88, + 0xa88aa88a, 0x88a8aaaa, 0xa8aa8aaa, 0x0888a88a, + 0x0b0b0b00, 0x090b0b0b, 0x0b090b0b, 0x0909090b, + 0x09090b0b, 0x09090b0b, 0x09090b09, 0x00000909, + 0x0a000000, 0x0a080808, 0x080a080a, 0x080a0a08, + 0x080a080a, 0x0808080a, 0x0a0a0a08, 0x0808080a, + 0xb0b0b000, 0x9090b0b0, 0x90b09090, 0xb0b0b090, + 0xb0b090b0, 0x90b0b0b0, 0xb0b09090, 0x00000090, + 0x80000000, 0xa080a080, 0xa08080a0, 0xa0808080, + 0xa080a080, 0x80a0a0a0, 0xa0a080a0, 0x00a0a0a0, + 0x22000000, 0x2222f222, 0x22222222, 0x222222f2, + 0xf2222220, 0x22222222, 0x22f22222, 0x00000222, + 0x11000000, 0x1111f111, 0x11111111, 0x111111f1, + 0xf1111110, 0x11111111, 0x11f11111, 0x00000111, + 0x33000000, 0x3333f333, 0x33333333, 0x333333f3, + 0xf3333330, 0x33333333, 0x33f33333, 0x00000333, + 0x22000000, 0x2222f222, 0x22222222, 0x222222f2, + 0xf2222220, 0x22222222, 0x22f22222, 0x00000222, + 0x99000000, 0x9b9b99bb, 0x9bb99999, 0x9999b9b9, + 0x9b99bb90, 0x9bbbbb9b, 0x9b9b9bb9, 0x00000999, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x88888000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00aaa888, + 0x88a88a00, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x000aa888, + 0x88880000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa88, 0x8aaaaa8a, 0x8a8a8aa8, 0x08aaa888, + 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, + 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, + 0x11000000, 0x1111a111, 0x11111111, 0x111111a1, + 0xa1111110, 0x11111111, 0x11c11111, 0x00000111, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x88000000, 0x8a8a88aa, 0x8aa88888, 0x8888a8a8, + 0x8a88aa80, 0x8aaaaa8a, 0x8a8a8aa8, 0x00000888, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, +}; + +/* Extracted from MMIO dump of 6.30.223.141 */ +static const u32 b43_ntab_noisevar_r7[] = { + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, + 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, +}; + /* TX gain tables */ static const u32 b43_ntab_tx_gain_rev0_1_2[] = { 0x03cc2b44, 0x03cc2b42, 0x03cc2a44, 0x03cc2a42, @@ -3031,6 +3217,91 @@ void b43_ntab_write_bulk(struct b43_wldev *dev, u32 offset, b43_ntab_write_bulk(dev, offset, ARRAY_SIZE(data), data); \ } while (0) +static void b43_nphy_tables_init_shared_lut(struct b43_wldev *dev) +{ + ntab_upload(dev, B43_NTAB_C0_ESTPLT_R3, b43_ntab_estimatepowerlt0_r3); + ntab_upload(dev, B43_NTAB_C1_ESTPLT_R3, b43_ntab_estimatepowerlt1_r3); + ntab_upload(dev, B43_NTAB_C0_ADJPLT_R3, b43_ntab_adjustpower0_r3); + ntab_upload(dev, B43_NTAB_C1_ADJPLT_R3, b43_ntab_adjustpower1_r3); + ntab_upload(dev, B43_NTAB_C0_GAINCTL_R3, b43_ntab_gainctl0_r3); + ntab_upload(dev, B43_NTAB_C1_GAINCTL_R3, b43_ntab_gainctl1_r3); + ntab_upload(dev, B43_NTAB_C0_IQLT_R3, b43_ntab_iqlt0_r3); + ntab_upload(dev, B43_NTAB_C1_IQLT_R3, b43_ntab_iqlt1_r3); + ntab_upload(dev, B43_NTAB_C0_LOFEEDTH_R3, b43_ntab_loftlt0_r3); + ntab_upload(dev, B43_NTAB_C1_LOFEEDTH_R3, b43_ntab_loftlt1_r3); +} + +static void b43_nphy_tables_init_rev7_volatile(struct b43_wldev *dev) +{ + struct ssb_sprom *sprom = dev->dev->bus_sprom; + u8 antswlut; + int core, offset, i; + + const int antswlut0_offsets[] = { 0, 4, 8, }; /* Offsets for values */ + const u8 antswlut0_values[][3] = { + { 0x2, 0x12, 0x8 }, /* Core 0 */ + { 0x2, 0x18, 0x2 }, /* Core 1 */ + }; + + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) + antswlut = sprom->fem.ghz5.antswlut; + else + antswlut = sprom->fem.ghz2.antswlut; + + switch (antswlut) { + case 0: + for (core = 0; core < 2; core++) { + for (i = 0; i < ARRAY_SIZE(antswlut0_values[0]); i++) { + offset = core ? 0x20 : 0x00; + offset += antswlut0_offsets[i]; + b43_ntab_write(dev, B43_NTAB8(9, offset), + antswlut0_values[core][i]); + } + } + break; + default: + b43err(dev->wl, "Unsupported antswlut: %d\n", antswlut); + break; + } +} + +static void b43_nphy_tables_init_rev16(struct b43_wldev *dev) +{ + /* Static tables */ + if (dev->phy.do_full_init) { + ntab_upload(dev, B43_NTAB_NOISEVAR_R7, b43_ntab_noisevar_r7); + b43_nphy_tables_init_shared_lut(dev); + } + + /* Volatile tables */ + b43_nphy_tables_init_rev7_volatile(dev); +} + +static void b43_nphy_tables_init_rev7(struct b43_wldev *dev) +{ + /* Static tables */ + if (dev->phy.do_full_init) { + ntab_upload(dev, B43_NTAB_FRAMESTRUCT_R3, b43_ntab_framestruct_r3); + ntab_upload(dev, B43_NTAB_PILOT_R3, b43_ntab_pilot_r3); + ntab_upload(dev, B43_NTAB_TMAP_R7, b43_ntab_tmap_r7); + ntab_upload(dev, B43_NTAB_INTLEVEL_R3, b43_ntab_intlevel_r3); + ntab_upload(dev, B43_NTAB_TDTRN_R3, b43_ntab_tdtrn_r3); + ntab_upload(dev, B43_NTAB_NOISEVAR_R7, b43_ntab_noisevar_r7); + ntab_upload(dev, B43_NTAB_MCS_R3, b43_ntab_mcs_r3); + ntab_upload(dev, B43_NTAB_TDI20A0_R3, b43_ntab_tdi20a0_r3); + ntab_upload(dev, B43_NTAB_TDI20A1_R3, b43_ntab_tdi20a1_r3); + ntab_upload(dev, B43_NTAB_TDI40A0_R3, b43_ntab_tdi40a0_r3); + ntab_upload(dev, B43_NTAB_TDI40A1_R3, b43_ntab_tdi40a1_r3); + ntab_upload(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3); + ntab_upload(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3); + ntab_upload(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3); + b43_nphy_tables_init_shared_lut(dev); + } + + /* Volatile tables */ + b43_nphy_tables_init_rev7_volatile(dev); +} + static void b43_nphy_tables_init_rev3(struct b43_wldev *dev) { struct ssb_sprom *sprom = dev->dev->bus_sprom; @@ -3057,16 +3328,7 @@ static void b43_nphy_tables_init_rev3(struct b43_wldev *dev) ntab_upload(dev, B43_NTAB_PILOTLT_R3, b43_ntab_pilotlt_r3); ntab_upload(dev, B43_NTAB_CHANEST_R3, b43_ntab_channelest_r3); ntab_upload(dev, B43_NTAB_FRAMELT_R3, b43_ntab_framelookup_r3); - ntab_upload(dev, B43_NTAB_C0_ESTPLT_R3, b43_ntab_estimatepowerlt0_r3); - ntab_upload(dev, B43_NTAB_C1_ESTPLT_R3, b43_ntab_estimatepowerlt1_r3); - ntab_upload(dev, B43_NTAB_C0_ADJPLT_R3, b43_ntab_adjustpower0_r3); - ntab_upload(dev, B43_NTAB_C1_ADJPLT_R3, b43_ntab_adjustpower1_r3); - ntab_upload(dev, B43_NTAB_C0_GAINCTL_R3, b43_ntab_gainctl0_r3); - ntab_upload(dev, B43_NTAB_C1_GAINCTL_R3, b43_ntab_gainctl1_r3); - ntab_upload(dev, B43_NTAB_C0_IQLT_R3, b43_ntab_iqlt0_r3); - ntab_upload(dev, B43_NTAB_C1_IQLT_R3, b43_ntab_iqlt1_r3); - ntab_upload(dev, B43_NTAB_C0_LOFEEDTH_R3, b43_ntab_loftlt0_r3); - ntab_upload(dev, B43_NTAB_C1_LOFEEDTH_R3, b43_ntab_loftlt1_r3); + b43_nphy_tables_init_shared_lut(dev); } /* Volatile tables */ @@ -3115,7 +3377,11 @@ static void b43_nphy_tables_init_rev0(struct b43_wldev *dev) /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/InitTables */ void b43_nphy_tables_init(struct b43_wldev *dev) { - if (dev->phy.rev >= 3) + if (dev->phy.rev >= 16) + b43_nphy_tables_init_rev16(dev); + else if (dev->phy.rev >= 7) + b43_nphy_tables_init_rev7(dev); + else if (dev->phy.rev >= 3) b43_nphy_tables_init_rev3(dev); else b43_nphy_tables_init_rev0(dev); diff --git a/drivers/net/wireless/b43/tables_nphy.h b/drivers/net/wireless/b43/tables_nphy.h index 3a58aee4c4cf..3ce2e6f3a278 100644 --- a/drivers/net/wireless/b43/tables_nphy.h +++ b/drivers/net/wireless/b43/tables_nphy.h @@ -165,6 +165,10 @@ struct nphy_gain_ctl_workaround_entry *b43_nphy_get_gain_ctl_workaround_ent( #define B43_NTAB_C1_LOFEEDTH_R3 B43_NTAB16(27, 448) /* Local Oscillator Feed Through lookup 1 */ #define B43_NTAB_C1_PAPD_COMP_R3 B43_NTAB16(27, 576) +/* Static N-PHY tables, PHY revision >= 7 */ +#define B43_NTAB_TMAP_R7 B43_NTAB32(12, 0) /* TM AP */ +#define B43_NTAB_NOISEVAR_R7 B43_NTAB32(16, 0) /* noise variance */ + #define B43_NTAB_TX_IQLO_CAL_LOFT_LADDER_40_SIZE 18 #define B43_NTAB_TX_IQLO_CAL_LOFT_LADDER_20_SIZE 18 #define B43_NTAB_TX_IQLO_CAL_IQIMB_LADDER_40_SIZE 18 From 7ef5cd240a133efd506c2e80c060b89406700bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 2 Jul 2014 22:42:45 +0200 Subject: [PATCH 216/378] b43: N-PHY: rework names & picking of TX gain tables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows adding more revisions support, spotting lacking tables and unifies naming schema. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/phy_n.c | 12 ++- drivers/net/wireless/b43/tables_nphy.c | 102 ++++++++++++++++++------- 2 files changed, 84 insertions(+), 30 deletions(-) diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index 0a6f04be9073..50ca6f87d5e8 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -3369,7 +3369,11 @@ static void b43_nphy_tx_power_fix(struct b43_wldev *dev) */ for (i = 0; i < 2; i++) { - txgain = *(b43_nphy_get_tx_gain_table(dev) + txpi[i]); + const u32 *table = b43_nphy_get_tx_gain_table(dev); + + if (!table) + break; + txgain = *(table + txpi[i]); if (dev->phy.rev >= 3) radio_gain = (txgain >> 16) & 0x1FFFF; @@ -3783,6 +3787,9 @@ static void b43_nphy_tx_gain_table_upload(struct b43_wldev *dev) int i; table = b43_nphy_get_tx_gain_table(dev); + if (!table) + return; + b43_ntab_write_bulk(dev, B43_NTAB32(26, 192), 128, table); b43_ntab_write_bulk(dev, B43_NTAB32(27, 192), 128, table); @@ -4488,6 +4495,9 @@ static struct nphy_txgains b43_nphy_get_tx_gains(struct b43_wldev *dev) for (i = 0; i < 2; ++i) { table = b43_nphy_get_tx_gain_table(dev); + if (!table) + break; + if (dev->phy.rev >= 3) { target.ipa[i] = (table[index[i]] >> 16) & 0xF; target.pad[i] = (table[index[i]] >> 20) & 0xF; diff --git a/drivers/net/wireless/b43/tables_nphy.c b/drivers/net/wireless/b43/tables_nphy.c index dd876857969e..3d6dda7c4fe8 100644 --- a/drivers/net/wireless/b43/tables_nphy.c +++ b/drivers/net/wireless/b43/tables_nphy.c @@ -2332,7 +2332,10 @@ static const u32 b43_ntab_noisevar_r7[] = { 0x020c020c, 0x0000014d, 0x020c020c, 0x0000014d, }; -/* TX gain tables */ +/************************************************** + * TX gain tables + **************************************************/ + static const u32 b43_ntab_tx_gain_rev0_1_2[] = { 0x03cc2b44, 0x03cc2b42, 0x03cc2a44, 0x03cc2a42, 0x03cc2944, 0x03c82b44, 0x03c82b42, 0x03c82a44, @@ -2368,7 +2371,9 @@ static const u32 b43_ntab_tx_gain_rev0_1_2[] = { 0x03801442, 0x03801344, 0x03801342, 0x00002b00, }; -static const u32 b43_ntab_tx_gain_rev3plus_2ghz[] = { +/* EPA 2 GHz */ + +static const u32 b43_ntab_tx_gain_epa_rev3_2g[] = { 0x1f410044, 0x1f410042, 0x1f410040, 0x1f41003e, 0x1f41003c, 0x1f41003b, 0x1f410039, 0x1f410037, 0x1e410044, 0x1e410042, 0x1e410040, 0x1e41003e, @@ -2403,7 +2408,9 @@ static const u32 b43_ntab_tx_gain_rev3plus_2ghz[] = { 0x1041003c, 0x1041003b, 0x10410039, 0x10410037, }; -static const u32 b43_ntab_tx_gain_rev3_5ghz[] = { +/* EPA 5 GHz */ + +static const u32 b43_ntab_tx_gain_epa_rev3_5g[] = { 0xcff70044, 0xcff70042, 0xcff70040, 0xcff7003e, 0xcff7003c, 0xcff7003b, 0xcff70039, 0xcff70037, 0xcef70044, 0xcef70042, 0xcef70040, 0xcef7003e, @@ -2438,7 +2445,7 @@ static const u32 b43_ntab_tx_gain_rev3_5ghz[] = { 0xc0f7003c, 0xc0f7003b, 0xc0f70039, 0xc0f70037, }; -static const u32 b43_ntab_tx_gain_rev4_5ghz[] = { +static const u32 b43_ntab_tx_gain_epa_rev4_5g[] = { 0x2ff20044, 0x2ff20042, 0x2ff20040, 0x2ff2003e, 0x2ff2003c, 0x2ff2003b, 0x2ff20039, 0x2ff20037, 0x2ef20044, 0x2ef20042, 0x2ef20040, 0x2ef2003e, @@ -2473,7 +2480,7 @@ static const u32 b43_ntab_tx_gain_rev4_5ghz[] = { 0x20d2003a, 0x20d20038, 0x20d20036, 0x20d20034, }; -static const u32 b43_ntab_tx_gain_rev5plus_5ghz[] = { +static const u32 b43_ntab_tx_gain_epa_rev5_5g[] = { 0x0f62004a, 0x0f620048, 0x0f620046, 0x0f620044, 0x0f620042, 0x0f620040, 0x0f62003e, 0x0f62003c, 0x0e620044, 0x0e620042, 0x0e620040, 0x0e62003e, @@ -2508,7 +2515,9 @@ static const u32 b43_ntab_tx_gain_rev5plus_5ghz[] = { 0x0062003b, 0x00620039, 0x00620037, 0x00620035, }; -static const u32 txpwrctrl_tx_gain_ipa[] = { +/* IPA 2 GHz */ + +static const u32 b43_ntab_tx_gain_ipa_rev3_2g[] = { 0x5ff7002d, 0x5ff7002b, 0x5ff7002a, 0x5ff70029, 0x5ff70028, 0x5ff70027, 0x5ff70026, 0x5ff70025, 0x5ef7002d, 0x5ef7002b, 0x5ef7002a, 0x5ef70029, @@ -2543,7 +2552,7 @@ static const u32 txpwrctrl_tx_gain_ipa[] = { 0x50f70028, 0x50f70027, 0x50f70026, 0x50f70025, }; -static const u32 txpwrctrl_tx_gain_ipa_rev5[] = { +static const u32 b43_ntab_tx_gain_ipa_rev5_2g[] = { 0x1ff7002d, 0x1ff7002b, 0x1ff7002a, 0x1ff70029, 0x1ff70028, 0x1ff70027, 0x1ff70026, 0x1ff70025, 0x1ef7002d, 0x1ef7002b, 0x1ef7002a, 0x1ef70029, @@ -2578,7 +2587,7 @@ static const u32 txpwrctrl_tx_gain_ipa_rev5[] = { 0x10f70028, 0x10f70027, 0x10f70026, 0x10f70025, }; -static const u32 txpwrctrl_tx_gain_ipa_rev6[] = { +static const u32 b43_ntab_tx_gain_ipa_rev6_2g[] = { 0x0ff7002d, 0x0ff7002b, 0x0ff7002a, 0x0ff70029, 0x0ff70028, 0x0ff70027, 0x0ff70026, 0x0ff70025, 0x0ef7002d, 0x0ef7002b, 0x0ef7002a, 0x0ef70029, @@ -2613,7 +2622,9 @@ static const u32 txpwrctrl_tx_gain_ipa_rev6[] = { 0x00f70028, 0x00f70027, 0x00f70026, 0x00f70025, }; -static const u32 txpwrctrl_tx_gain_ipa_5g[] = { +/* IPA 2 5Hz */ + +static const u32 b43_ntab_tx_gain_ipa_rev3_5g[] = { 0x7ff70035, 0x7ff70033, 0x7ff70032, 0x7ff70031, 0x7ff7002f, 0x7ff7002e, 0x7ff7002d, 0x7ff7002b, 0x7ff7002a, 0x7ff70029, 0x7ff70028, 0x7ff70027, @@ -3390,23 +3401,39 @@ void b43_nphy_tables_init(struct b43_wldev *dev) /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/GetIpaGainTbl */ static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev) { + struct b43_phy *phy = &dev->phy; + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { - if (dev->phy.rev >= 6) { - if (dev->dev->chip_id == 47162) - return txpwrctrl_tx_gain_ipa_rev5; - return txpwrctrl_tx_gain_ipa_rev6; - } else if (dev->phy.rev >= 5) { - return txpwrctrl_tx_gain_ipa_rev5; - } else { - return txpwrctrl_tx_gain_ipa; + switch (phy->rev) { + case 6: + if (dev->dev->chip_id == BCMA_CHIP_ID_BCM47162) + return b43_ntab_tx_gain_ipa_rev5_2g; + return b43_ntab_tx_gain_ipa_rev6_2g; + case 5: + return b43_ntab_tx_gain_ipa_rev5_2g; + case 4: + case 3: + return b43_ntab_tx_gain_ipa_rev3_2g; + default: + b43err(dev->wl, + "No 2GHz IPA gain table available for this device\n"); + return NULL; } } else { - return txpwrctrl_tx_gain_ipa_5g; + switch (phy->rev) { + case 3 ... 6: + return b43_ntab_tx_gain_ipa_rev3_5g; + default: + b43err(dev->wl, + "No 5GHz IPA gain table available for this device\n"); + return NULL; + } } } const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev) { + struct b43_phy *phy = &dev->phy; enum ieee80211_band band = b43_current_band(dev->wl); struct ssb_sprom *sprom = dev->dev->bus_sprom; @@ -3418,19 +3445,36 @@ const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev) (dev->phy.n->ipa5g_on && band == IEEE80211_BAND_5GHZ)) { return b43_nphy_get_ipa_gain_table(dev); } else if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { - if (dev->phy.rev == 3) - return b43_ntab_tx_gain_rev3_5ghz; - if (dev->phy.rev == 4) + switch (phy->rev) { + case 6: + case 5: + return b43_ntab_tx_gain_epa_rev5_5g; + case 4: return sprom->fem.ghz5.extpa_gain == 3 ? - b43_ntab_tx_gain_rev4_5ghz : - b43_ntab_tx_gain_rev4_5ghz; /* FIXME */ - else - return b43_ntab_tx_gain_rev5plus_5ghz; + b43_ntab_tx_gain_epa_rev4_5g : + b43_ntab_tx_gain_epa_rev4_5g; /* FIXME */ + case 3: + return b43_ntab_tx_gain_epa_rev3_5g; + default: + b43err(dev->wl, + "No 5GHz EPA gain table available for this device\n"); + return NULL; + } } else { - if (dev->phy.rev >= 5 && sprom->fem.ghz5.extpa_gain == 3) - return b43_ntab_tx_gain_rev3plus_2ghz; /* FIXME */ - else - return b43_ntab_tx_gain_rev3plus_2ghz; + switch (phy->rev) { + case 6: + case 5: + if (sprom->fem.ghz5.extpa_gain == 3) + return b43_ntab_tx_gain_epa_rev3_2g; /* FIXME */ + /* fall through */ + case 4: + case 3: + return b43_ntab_tx_gain_epa_rev3_2g; + default: + b43err(dev->wl, + "No 2GHz EPA gain table available for this device\n"); + return NULL; + } } } From c4e197195a0c38b2c0928a03ed1de1a4a32f52c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 2 Jul 2014 22:42:46 +0200 Subject: [PATCH 217/378] b43: N-PHY: add TX gains tables for radio 0x2057 rev 9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/tables_nphy.c | 78 ++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/drivers/net/wireless/b43/tables_nphy.c b/drivers/net/wireless/b43/tables_nphy.c index 3d6dda7c4fe8..b28dce950e1f 100644 --- a/drivers/net/wireless/b43/tables_nphy.c +++ b/drivers/net/wireless/b43/tables_nphy.c @@ -2622,6 +2622,42 @@ static const u32 b43_ntab_tx_gain_ipa_rev6_2g[] = { 0x00f70028, 0x00f70027, 0x00f70026, 0x00f70025, }; +/* Extracted from MMIO dump of 6.30.223.141 */ +static const u32 b43_ntab_tx_gain_ipa_2057_rev9_2g[] = { + 0x60ff0031, 0x60e7002c, 0x60cf002a, 0x60c70029, + 0x60b70029, 0x60a70029, 0x609f002a, 0x6097002b, + 0x6087002e, 0x60770031, 0x606f0032, 0x60670034, + 0x60670031, 0x605f0033, 0x605f0031, 0x60570033, + 0x60570030, 0x6057002d, 0x6057002b, 0x604f002d, + 0x604f002b, 0x604f0029, 0x604f0026, 0x60470029, + 0x60470027, 0x603f0029, 0x603f0027, 0x603f0025, + 0x60370029, 0x60370027, 0x60370024, 0x602f002a, + 0x602f0028, 0x602f0026, 0x602f0024, 0x6027002a, + 0x60270028, 0x60270026, 0x60270024, 0x60270022, + 0x601f002b, 0x601f0029, 0x601f0027, 0x601f0024, + 0x601f0022, 0x601f0020, 0x601f001f, 0x601f001d, + 0x60170029, 0x60170027, 0x60170025, 0x60170023, + 0x60170021, 0x6017001f, 0x6017001d, 0x6017001c, + 0x6017001a, 0x60170018, 0x60170018, 0x60170016, + 0x60170015, 0x600f0029, 0x600f0027, 0x600f0025, + 0x600f0023, 0x600f0021, 0x600f001f, 0x600f001d, + 0x600f001c, 0x600f001a, 0x600f0019, 0x600f0018, + 0x600f0016, 0x600f0015, 0x600f0115, 0x600f0215, + 0x600f0315, 0x600f0415, 0x600f0515, 0x600f0615, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, + 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, +}; + /* IPA 2 5Hz */ static const u32 b43_ntab_tx_gain_ipa_rev3_5g[] = { @@ -2659,6 +2695,42 @@ static const u32 b43_ntab_tx_gain_ipa_rev3_5g[] = { 0x70f70021, 0x70f70020, 0x70f70020, 0x70f7001f, }; +/* Extracted from MMIO dump of 6.30.223.141 */ +static const u32 b43_ntab_tx_gain_ipa_2057_rev9_5g[] = { + 0x7f7f0053, 0x7f7f004b, 0x7f7f0044, 0x7f7f003f, + 0x7f7f0039, 0x7f7f0035, 0x7f7f0032, 0x7f7f0030, + 0x7f7f002d, 0x7e7f0030, 0x7e7f002d, 0x7d7f0032, + 0x7d7f002f, 0x7d7f002c, 0x7c7f0032, 0x7c7f0030, + 0x7c7f002d, 0x7b7f0030, 0x7b7f002e, 0x7b7f002b, + 0x7a7f0032, 0x7a7f0030, 0x7a7f002d, 0x7a7f002b, + 0x797f0030, 0x797f002e, 0x797f002b, 0x797f0029, + 0x787f0030, 0x787f002d, 0x787f002b, 0x777f0032, + 0x777f0030, 0x777f002d, 0x777f002b, 0x767f0031, + 0x767f002f, 0x767f002c, 0x767f002a, 0x757f0031, + 0x757f002f, 0x757f002c, 0x757f002a, 0x747f0030, + 0x747f002d, 0x747f002b, 0x737f0032, 0x737f002f, + 0x737f002c, 0x737f002a, 0x727f0030, 0x727f002d, + 0x727f002b, 0x727f0029, 0x717f0030, 0x717f002d, + 0x717f002b, 0x707f0031, 0x707f002f, 0x707f002c, + 0x707f002a, 0x707f0027, 0x707f0025, 0x707f0023, + 0x707f0021, 0x707f001f, 0x707f001d, 0x707f001c, + 0x707f001a, 0x707f0019, 0x707f0017, 0x707f0016, + 0x707f0015, 0x707f0014, 0x707f0012, 0x707f0012, + 0x707f0011, 0x707f0010, 0x707f000f, 0x707f000e, + 0x707f000d, 0x707f000d, 0x707f000c, 0x707f000b, + 0x707f000a, 0x707f000a, 0x707f0009, 0x707f0008, + 0x707f0008, 0x707f0008, 0x707f0008, 0x707f0007, + 0x707f0007, 0x707f0006, 0x707f0006, 0x707f0006, + 0x707f0005, 0x707f0005, 0x707f0005, 0x707f0004, + 0x707f0004, 0x707f0004, 0x707f0003, 0x707f0003, + 0x707f0003, 0x707f0003, 0x707f0003, 0x707f0003, + 0x707f0003, 0x707f0003, 0x707f0003, 0x707f0003, + 0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002, + 0x707f0002, 0x707f0002, 0x707f0002, 0x707f0002, + 0x707f0002, 0x707f0001, 0x707f0001, 0x707f0001, + 0x707f0001, 0x707f0001, 0x707f0001, 0x707f0001, +}; + const s8 b43_ntab_papd_pga_gain_delta_ipa_2g[] = { -114, -108, -98, -91, -84, -78, -70, -62, -54, -46, -39, -31, -23, -15, -8, 0 @@ -3405,6 +3477,9 @@ static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev) if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { switch (phy->rev) { + case 16: + if (phy->radio_rev == 9) + return b43_ntab_tx_gain_ipa_2057_rev9_2g; case 6: if (dev->dev->chip_id == BCMA_CHIP_ID_BCM47162) return b43_ntab_tx_gain_ipa_rev5_2g; @@ -3421,6 +3496,9 @@ static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev) } } else { switch (phy->rev) { + case 16: + if (phy->radio_rev == 9) + return b43_ntab_tx_gain_ipa_2057_rev9_5g; case 3 ... 6: return b43_ntab_tx_gain_ipa_rev3_5g; default: From fe5e499f427dadbeeb079e0a796702f4a3da78a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Fri, 4 Jul 2014 09:21:56 +0200 Subject: [PATCH 218/378] b43: fix reading info about radio for new devices (cores 40 & 42) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This changes b43-phy0: Found Radio: Manuf 0x17F, Version 0x7769, Revision 4 to the b43-phy0: Found Radio: Manuf 0x17F, Version 0x2069, Revision 4 which matches what closed source driver reports: $ wl revinfo radiorev 0x42069000 Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index ca4a19077d7e..b2bc593a6513 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4329,6 +4329,7 @@ static char *b43_phy_name(struct b43_wldev *dev, u8 phy_type) static int b43_phy_versioning(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; + const u8 core_rev = dev->dev->core_rev; u32 tmp; u8 analog_type; u8 phy_type; @@ -4394,7 +4395,15 @@ static int b43_phy_versioning(struct b43_wldev *dev) analog_type, phy_type, b43_phy_name(dev, phy_type), phy_rev); /* Get RADIO versioning */ - if (dev->dev->core_rev >= 24) { + if (core_rev == 40 || core_rev == 42) { + radio_manuf = 0x17F; + + b43_write16(dev, B43_MMIO_RADIO24_CONTROL, 0); + radio_rev = b43_read16(dev, B43_MMIO_RADIO24_DATA); + + b43_write16(dev, B43_MMIO_RADIO24_CONTROL, 1); + radio_ver = b43_read16(dev, B43_MMIO_RADIO24_DATA); + } else if (core_rev >= 24) { u16 radio24[3]; for (tmp = 0; tmp < 3; tmp++) { From f473832fece16611520bf54ad52b16c3f6db0a94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sat, 5 Jul 2014 01:10:41 +0200 Subject: [PATCH 219/378] bcma: add driver for PCIe Gen 2 core MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New Broadcom PCIe devices (802.11ac ones?) use Gen2 and have to be initialized differently. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/bcma/Makefile | 1 + drivers/bcma/driver_pcie2.c | 175 +++++++++++++++++++++++++ drivers/bcma/main.c | 8 ++ include/linux/bcma/bcma.h | 2 + include/linux/bcma/bcma_driver_pcie2.h | 158 ++++++++++++++++++++++ 5 files changed, 344 insertions(+) create mode 100644 drivers/bcma/driver_pcie2.c create mode 100644 include/linux/bcma/bcma_driver_pcie2.h diff --git a/drivers/bcma/Makefile b/drivers/bcma/Makefile index 734b32f09c0a..91290f7f61b8 100644 --- a/drivers/bcma/Makefile +++ b/drivers/bcma/Makefile @@ -3,6 +3,7 @@ bcma-y += driver_chipcommon.o driver_chipcommon_pmu.o bcma-$(CONFIG_BCMA_SFLASH) += driver_chipcommon_sflash.o bcma-$(CONFIG_BCMA_NFLASH) += driver_chipcommon_nflash.o bcma-y += driver_pci.o +bcma-y += driver_pcie2.o bcma-$(CONFIG_BCMA_DRIVER_PCI_HOSTMODE) += driver_pci_host.o bcma-$(CONFIG_BCMA_DRIVER_MIPS) += driver_mips.o bcma-$(CONFIG_BCMA_DRIVER_GMAC_CMN) += driver_gmac_cmn.o diff --git a/drivers/bcma/driver_pcie2.c b/drivers/bcma/driver_pcie2.c new file mode 100644 index 000000000000..e4be537b0c66 --- /dev/null +++ b/drivers/bcma/driver_pcie2.c @@ -0,0 +1,175 @@ +/* + * Broadcom specific AMBA + * PCIe Gen 2 Core + * + * Copyright 2014, Broadcom Corporation + * Copyright 2014, Rafał Miłecki + * + * Licensed under the GNU/GPL. See COPYING for details. + */ + +#include "bcma_private.h" +#include + +/************************************************** + * R/W ops. + **************************************************/ + +#if 0 +static u32 bcma_core_pcie2_cfg_read(struct bcma_drv_pcie2 *pcie2, u32 addr) +{ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, addr); + pcie2_read32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR); + return pcie2_read32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA); +} +#endif + +static void bcma_core_pcie2_cfg_write(struct bcma_drv_pcie2 *pcie2, u32 addr, + u32 val) +{ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, addr); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, val); +} + +/************************************************** + * Init. + **************************************************/ + +static u32 bcma_core_pcie2_war_delay_perst_enab(struct bcma_drv_pcie2 *pcie2, + bool enable) +{ + u32 val; + + /* restore back to default */ + val = pcie2_read32(pcie2, BCMA_CORE_PCIE2_CLK_CONTROL); + val |= PCIE2_CLKC_DLYPERST; + val &= ~PCIE2_CLKC_DISSPROMLD; + if (enable) { + val &= ~PCIE2_CLKC_DLYPERST; + val |= PCIE2_CLKC_DISSPROMLD; + } + pcie2_write32(pcie2, (BCMA_CORE_PCIE2_CLK_CONTROL), val); + /* flush */ + return pcie2_read32(pcie2, BCMA_CORE_PCIE2_CLK_CONTROL); +} + +static void bcma_core_pcie2_set_ltr_vals(struct bcma_drv_pcie2 *pcie2) +{ + /* LTR0 */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, 0x844); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x883c883c); + /* LTR1 */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, 0x848); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x88648864); + /* LTR2 */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, 0x84C); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x90039003); +} + +static void bcma_core_pcie2_hw_ltr_war(struct bcma_drv_pcie2 *pcie2) +{ + u8 core_rev = pcie2->core->id.rev; + u32 devstsctr2; + + if (core_rev < 2 || core_rev == 10 || core_rev > 13) + return; + + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, + PCIE2_CAP_DEVSTSCTRL2_OFFSET); + devstsctr2 = pcie2_read32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA); + if (devstsctr2 & PCIE2_CAP_DEVSTSCTRL2_LTRENAB) { + /* force the right LTR values */ + bcma_core_pcie2_set_ltr_vals(pcie2); + + /* TODO: + si_core_wrapperreg(pcie2, 3, 0x60, 0x8080, 0); */ + + /* enable the LTR */ + devstsctr2 |= PCIE2_CAP_DEVSTSCTRL2_LTRENAB; + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, + PCIE2_CAP_DEVSTSCTRL2_OFFSET); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, devstsctr2); + + /* set the LTR state to be active */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_LTR_STATE, + PCIE2_LTR_ACTIVE); + usleep_range(1000, 2000); + + /* set the LTR state to be sleep */ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_LTR_STATE, + PCIE2_LTR_SLEEP); + usleep_range(1000, 2000); + } +} + +static void pciedev_crwlpciegen2(struct bcma_drv_pcie2 *pcie2) +{ + u8 core_rev = pcie2->core->id.rev; + bool pciewar160, pciewar162; + + pciewar160 = core_rev == 7 || core_rev == 9 || core_rev == 11; + pciewar162 = core_rev == 5 || core_rev == 7 || core_rev == 8 || + core_rev == 9 || core_rev == 11; + + if (!pciewar160 && !pciewar162) + return; + +/* TODO */ +#if 0 + pcie2_set32(pcie2, BCMA_CORE_PCIE2_CLK_CONTROL, + PCIE_DISABLE_L1CLK_GATING); +#if 0 + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, + PCIEGEN2_COE_PVT_TL_CTRL_0); + pcie2_mask32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, + ~(1 << COE_PVT_TL_CTRL_0_PM_DIS_L1_REENTRY_BIT)); +#endif +#endif +} + +static void pciedev_crwlpciegen2_180(struct bcma_drv_pcie2 *pcie2) +{ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, PCIE2_PMCR_REFUP); + pcie2_set32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 0x1f); +} + +static void pciedev_crwlpciegen2_182(struct bcma_drv_pcie2 *pcie2) +{ + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, PCIE2_SBMBX); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, 1 << 0); +} + +static void pciedev_reg_pm_clk_period(struct bcma_drv_pcie2 *pcie2) +{ + struct bcma_drv_cc *drv_cc = &pcie2->core->bus->drv_cc; + u8 core_rev = pcie2->core->id.rev; + u32 alp_khz, pm_value; + + if (core_rev <= 13) { + alp_khz = bcma_pmu_get_alp_clock(drv_cc) / 1000; + pm_value = (1000000 * 2) / alp_khz; + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDADDR, + PCIE2_PVT_REG_PM_CLK_PERIOD); + pcie2_write32(pcie2, BCMA_CORE_PCIE2_CONFIGINDDATA, pm_value); + } +} + +void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2) +{ + struct bcma_chipinfo *ci = &pcie2->core->bus->chipinfo; + u32 tmp; + + tmp = pcie2_read32(pcie2, BCMA_CORE_PCIE2_SPROM(54)); + if ((tmp & 0xe) >> 1 == 2) + bcma_core_pcie2_cfg_write(pcie2, 0x4e0, 0x17); + + /* TODO: Do we need pcie_reqsize? */ + + if (ci->id == BCMA_CHIP_ID_BCM4360 && ci->rev > 3) + bcma_core_pcie2_war_delay_perst_enab(pcie2, true); + bcma_core_pcie2_hw_ltr_war(pcie2); + pciedev_crwlpciegen2(pcie2); + pciedev_reg_pm_clk_period(pcie2); + pciedev_crwlpciegen2_180(pcie2); + pciedev_crwlpciegen2_182(pcie2); +} diff --git a/drivers/bcma/main.c b/drivers/bcma/main.c index 34ea4c588d36..0ff8d58831ef 100644 --- a/drivers/bcma/main.c +++ b/drivers/bcma/main.c @@ -132,6 +132,7 @@ static int bcma_register_cores(struct bcma_bus *bus) case BCMA_CORE_CHIPCOMMON: case BCMA_CORE_PCI: case BCMA_CORE_PCIE: + case BCMA_CORE_PCIE2: case BCMA_CORE_MIPS_74K: case BCMA_CORE_4706_MAC_GBIT_COMMON: continue; @@ -281,6 +282,13 @@ int bcma_bus_register(struct bcma_bus *bus) bcma_core_pci_init(&bus->drv_pci[1]); } + /* Init PCIe Gen 2 core */ + core = bcma_find_core_unit(bus, BCMA_CORE_PCIE2, 0); + if (core) { + bus->drv_pcie2.core = core; + bcma_core_pcie2_init(&bus->drv_pcie2); + } + /* Init GBIT MAC COMMON core */ core = bcma_find_core(bus, BCMA_CORE_4706_MAC_GBIT_COMMON); if (core) { diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 0b3bb16c705a..452286a38b2b 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include /* SPROM sharing */ @@ -333,6 +334,7 @@ struct bcma_bus { struct bcma_drv_cc drv_cc; struct bcma_drv_pci drv_pci[2]; + struct bcma_drv_pcie2 drv_pcie2; struct bcma_drv_mips drv_mips; struct bcma_drv_gmac_cmn drv_gmac_cmn; diff --git a/include/linux/bcma/bcma_driver_pcie2.h b/include/linux/bcma/bcma_driver_pcie2.h new file mode 100644 index 000000000000..5988b05781c3 --- /dev/null +++ b/include/linux/bcma/bcma_driver_pcie2.h @@ -0,0 +1,158 @@ +#ifndef LINUX_BCMA_DRIVER_PCIE2_H_ +#define LINUX_BCMA_DRIVER_PCIE2_H_ + +#define BCMA_CORE_PCIE2_CLK_CONTROL 0x0000 +#define PCIE2_CLKC_RST_OE 0x0001 /* When set, drives PCI_RESET out to pin */ +#define PCIE2_CLKC_RST 0x0002 /* Value driven out to pin */ +#define PCIE2_CLKC_SPERST 0x0004 /* SurvivePeRst */ +#define PCIE2_CLKC_DISABLE_L1CLK_GATING 0x0010 +#define PCIE2_CLKC_DLYPERST 0x0100 /* Delay PeRst to CoE Core */ +#define PCIE2_CLKC_DISSPROMLD 0x0200 /* DisableSpromLoadOnPerst */ +#define PCIE2_CLKC_WAKE_MODE_L2 0x1000 /* Wake on L2 */ +#define BCMA_CORE_PCIE2_RC_PM_CONTROL 0x0004 +#define BCMA_CORE_PCIE2_RC_PM_STATUS 0x0008 +#define BCMA_CORE_PCIE2_EP_PM_CONTROL 0x000C +#define BCMA_CORE_PCIE2_EP_PM_STATUS 0x0010 +#define BCMA_CORE_PCIE2_EP_LTR_CONTROL 0x0014 +#define BCMA_CORE_PCIE2_EP_LTR_STATUS 0x0018 +#define BCMA_CORE_PCIE2_EP_OBFF_STATUS 0x001C +#define BCMA_CORE_PCIE2_PCIE_ERR_STATUS 0x0020 +#define BCMA_CORE_PCIE2_RC_AXI_CONFIG 0x0100 +#define BCMA_CORE_PCIE2_EP_AXI_CONFIG 0x0104 +#define BCMA_CORE_PCIE2_RXDEBUG_STATUS0 0x0108 +#define BCMA_CORE_PCIE2_RXDEBUG_CONTROL0 0x010C +#define BCMA_CORE_PCIE2_CONFIGINDADDR 0x0120 +#define BCMA_CORE_PCIE2_CONFIGINDDATA 0x0124 +#define BCMA_CORE_PCIE2_MDIOCONTROL 0x0128 +#define BCMA_CORE_PCIE2_MDIOWRDATA 0x012C +#define BCMA_CORE_PCIE2_MDIORDDATA 0x0130 +#define BCMA_CORE_PCIE2_DATAINTF 0x0180 +#define BCMA_CORE_PCIE2_D2H_INTRLAZY_0 0x0188 +#define BCMA_CORE_PCIE2_H2D_INTRLAZY_0 0x018c +#define BCMA_CORE_PCIE2_H2D_INTSTAT_0 0x0190 +#define BCMA_CORE_PCIE2_H2D_INTMASK_0 0x0194 +#define BCMA_CORE_PCIE2_D2H_INTSTAT_0 0x0198 +#define BCMA_CORE_PCIE2_D2H_INTMASK_0 0x019c +#define BCMA_CORE_PCIE2_LTR_STATE 0x01A0 /* Latency Tolerance Reporting */ +#define PCIE2_LTR_ACTIVE 2 +#define PCIE2_LTR_ACTIVE_IDLE 1 +#define PCIE2_LTR_SLEEP 0 +#define PCIE2_LTR_FINAL_MASK 0x300 +#define PCIE2_LTR_FINAL_SHIFT 8 +#define BCMA_CORE_PCIE2_PWR_INT_STATUS 0x01A4 +#define BCMA_CORE_PCIE2_PWR_INT_MASK 0x01A8 +#define BCMA_CORE_PCIE2_CFG_ADDR 0x01F8 +#define BCMA_CORE_PCIE2_CFG_DATA 0x01FC +#define BCMA_CORE_PCIE2_SYS_EQ_PAGE 0x0200 +#define BCMA_CORE_PCIE2_SYS_MSI_PAGE 0x0204 +#define BCMA_CORE_PCIE2_SYS_MSI_INTREN 0x0208 +#define BCMA_CORE_PCIE2_SYS_MSI_CTRL0 0x0210 +#define BCMA_CORE_PCIE2_SYS_MSI_CTRL1 0x0214 +#define BCMA_CORE_PCIE2_SYS_MSI_CTRL2 0x0218 +#define BCMA_CORE_PCIE2_SYS_MSI_CTRL3 0x021C +#define BCMA_CORE_PCIE2_SYS_MSI_CTRL4 0x0220 +#define BCMA_CORE_PCIE2_SYS_MSI_CTRL5 0x0224 +#define BCMA_CORE_PCIE2_SYS_EQ_HEAD0 0x0250 +#define BCMA_CORE_PCIE2_SYS_EQ_TAIL0 0x0254 +#define BCMA_CORE_PCIE2_SYS_EQ_HEAD1 0x0258 +#define BCMA_CORE_PCIE2_SYS_EQ_TAIL1 0x025C +#define BCMA_CORE_PCIE2_SYS_EQ_HEAD2 0x0260 +#define BCMA_CORE_PCIE2_SYS_EQ_TAIL2 0x0264 +#define BCMA_CORE_PCIE2_SYS_EQ_HEAD3 0x0268 +#define BCMA_CORE_PCIE2_SYS_EQ_TAIL3 0x026C +#define BCMA_CORE_PCIE2_SYS_EQ_HEAD4 0x0270 +#define BCMA_CORE_PCIE2_SYS_EQ_TAIL4 0x0274 +#define BCMA_CORE_PCIE2_SYS_EQ_HEAD5 0x0278 +#define BCMA_CORE_PCIE2_SYS_EQ_TAIL5 0x027C +#define BCMA_CORE_PCIE2_SYS_RC_INTX_EN 0x0330 +#define BCMA_CORE_PCIE2_SYS_RC_INTX_CSR 0x0334 +#define BCMA_CORE_PCIE2_SYS_MSI_REQ 0x0340 +#define BCMA_CORE_PCIE2_SYS_HOST_INTR_EN 0x0344 +#define BCMA_CORE_PCIE2_SYS_HOST_INTR_CSR 0x0348 +#define BCMA_CORE_PCIE2_SYS_HOST_INTR0 0x0350 +#define BCMA_CORE_PCIE2_SYS_HOST_INTR1 0x0354 +#define BCMA_CORE_PCIE2_SYS_HOST_INTR2 0x0358 +#define BCMA_CORE_PCIE2_SYS_HOST_INTR3 0x035C +#define BCMA_CORE_PCIE2_SYS_EP_INT_EN0 0x0360 +#define BCMA_CORE_PCIE2_SYS_EP_INT_EN1 0x0364 +#define BCMA_CORE_PCIE2_SYS_EP_INT_CSR0 0x0370 +#define BCMA_CORE_PCIE2_SYS_EP_INT_CSR1 0x0374 +#define BCMA_CORE_PCIE2_SPROM(wordoffset) (0x0800 + ((wordoffset) * 2)) +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_0 0x0C00 +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_1 0x0C04 +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_2 0x0C08 +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_3 0x0C0C +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_4 0x0C10 +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_5 0x0C14 +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_6 0x0C18 +#define BCMA_CORE_PCIE2_FUNC0_IMAP0_7 0x0C1C +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_0 0x0C20 +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_1 0x0C24 +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_2 0x0C28 +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_3 0x0C2C +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_4 0x0C30 +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_5 0x0C34 +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_6 0x0C38 +#define BCMA_CORE_PCIE2_FUNC1_IMAP0_7 0x0C3C +#define BCMA_CORE_PCIE2_FUNC0_IMAP1 0x0C80 +#define BCMA_CORE_PCIE2_FUNC1_IMAP1 0x0C88 +#define BCMA_CORE_PCIE2_FUNC0_IMAP2 0x0CC0 +#define BCMA_CORE_PCIE2_FUNC1_IMAP2 0x0CC8 +#define BCMA_CORE_PCIE2_IARR0_LOWER 0x0D00 +#define BCMA_CORE_PCIE2_IARR0_UPPER 0x0D04 +#define BCMA_CORE_PCIE2_IARR1_LOWER 0x0D08 +#define BCMA_CORE_PCIE2_IARR1_UPPER 0x0D0C +#define BCMA_CORE_PCIE2_IARR2_LOWER 0x0D10 +#define BCMA_CORE_PCIE2_IARR2_UPPER 0x0D14 +#define BCMA_CORE_PCIE2_OARR0 0x0D20 +#define BCMA_CORE_PCIE2_OARR1 0x0D28 +#define BCMA_CORE_PCIE2_OARR2 0x0D30 +#define BCMA_CORE_PCIE2_OMAP0_LOWER 0x0D40 +#define BCMA_CORE_PCIE2_OMAP0_UPPER 0x0D44 +#define BCMA_CORE_PCIE2_OMAP1_LOWER 0x0D48 +#define BCMA_CORE_PCIE2_OMAP1_UPPER 0x0D4C +#define BCMA_CORE_PCIE2_OMAP2_LOWER 0x0D50 +#define BCMA_CORE_PCIE2_OMAP2_UPPER 0x0D54 +#define BCMA_CORE_PCIE2_FUNC1_IARR1_SIZE 0x0D58 +#define BCMA_CORE_PCIE2_FUNC1_IARR2_SIZE 0x0D5C +#define BCMA_CORE_PCIE2_MEM_CONTROL 0x0F00 +#define BCMA_CORE_PCIE2_MEM_ECC_ERRLOG0 0x0F04 +#define BCMA_CORE_PCIE2_MEM_ECC_ERRLOG1 0x0F08 +#define BCMA_CORE_PCIE2_LINK_STATUS 0x0F0C +#define BCMA_CORE_PCIE2_STRAP_STATUS 0x0F10 +#define BCMA_CORE_PCIE2_RESET_STATUS 0x0F14 +#define BCMA_CORE_PCIE2_RESETEN_IN_LINKDOWN 0x0F18 +#define BCMA_CORE_PCIE2_MISC_INTR_EN 0x0F1C +#define BCMA_CORE_PCIE2_TX_DEBUG_CFG 0x0F20 +#define BCMA_CORE_PCIE2_MISC_CONFIG 0x0F24 +#define BCMA_CORE_PCIE2_MISC_STATUS 0x0F28 +#define BCMA_CORE_PCIE2_INTR_EN 0x0F30 +#define BCMA_CORE_PCIE2_INTR_CLEAR 0x0F34 +#define BCMA_CORE_PCIE2_INTR_STATUS 0x0F38 + +/* PCIE gen2 config regs */ +#define PCIE2_INTSTATUS 0x090 +#define PCIE2_INTMASK 0x094 +#define PCIE2_SBMBX 0x098 + +#define PCIE2_PMCR_REFUP 0x1814 /* Trefup time */ + +#define PCIE2_CAP_DEVSTSCTRL2_OFFSET 0xD4 +#define PCIE2_CAP_DEVSTSCTRL2_LTRENAB 0x400 +#define PCIE2_PVT_REG_PM_CLK_PERIOD 0x184c + +struct bcma_drv_pcie2 { + struct bcma_device *core; +}; + +#define pcie2_read16(pcie2, offset) bcma_read16((pcie2)->core, offset) +#define pcie2_read32(pcie2, offset) bcma_read32((pcie2)->core, offset) +#define pcie2_write16(pcie2, offset, val) bcma_write16((pcie2)->core, offset, val) +#define pcie2_write32(pcie2, offset, val) bcma_write32((pcie2)->core, offset, val) + +#define pcie2_set32(pcie2, offset, set) bcma_set32((pcie2)->core, offset, set) +#define pcie2_mask32(pcie2, offset, mask) bcma_mask32((pcie2)->core, offset, mask) + +void bcma_core_pcie2_init(struct bcma_drv_pcie2 *pcie2); + +#endif /* LINUX_BCMA_DRIVER_PCIE2_H_ */ From 66c417c1ee01398ac5ab1c749a20c4d8ba50e9a9 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 8 Jul 2014 15:07:47 +0300 Subject: [PATCH 220/378] Bluetooth: Add flag to track the real advertising state Having a single HCI_ADVERTISING flag is problematic since it tries to track both the real advertising state and the corresponding mgmt setting. To make the logic simpler and more reliable add a new flag that only tracks the actual advertising state that has been written to the controller. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci.h | 4 ++-- net/bluetooth/hci_event.c | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 5481d1c8badb..6ed1f7288f13 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -175,7 +175,7 @@ enum { HCI_UNCONFIGURED, HCI_USER_CHANNEL, HCI_EXT_CONFIGURED, - + HCI_LE_ADV, HCI_LE_SCAN, HCI_SSP_ENABLED, HCI_SC_ENABLED, @@ -200,7 +200,7 @@ enum { * or the HCI device is closed. */ #define HCI_PERSISTENT_MASK (BIT(HCI_LE_SCAN) | BIT(HCI_PERIODIC_INQ) | \ - BIT(HCI_FAST_CONNECTABLE)) + BIT(HCI_FAST_CONNECTABLE) | BIT(HCI_LE_ADV)) /* HCI ioctl defines */ #define HCIDEVUP _IOW('H', 201, int) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a6816498b0b9..10f8de9400bc 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1069,11 +1069,15 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) if (*sent) { struct hci_conn *conn; + set_bit(HCI_LE_ADV, &hdev->dev_flags); + conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); if (conn) queue_delayed_work(hdev->workqueue, &conn->le_conn_timeout, conn->conn_timeout); + } else { + clear_bit(HCI_LE_ADV, &hdev->dev_flags); } mgmt_advertising(hdev, *sent); From c93bd15033027928709ee15bab2ce1f5582085c6 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 8 Jul 2014 15:07:48 +0300 Subject: [PATCH 221/378] Bluetooth: Remove unnecessary mgmt_advertising function Since the real advertising state is now tracked with its own flag we can simply set/unset the HCI_ADVERTISING flag in the set_advertising_complete function. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 - net/bluetooth/hci_event.c | 2 -- net/bluetooth/mgmt.c | 17 +++++------------ 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 5701d15779dd..c98de309967e 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1292,7 +1292,6 @@ int mgmt_powered(struct hci_dev *hdev, u8 powered); void mgmt_discoverable_timeout(struct hci_dev *hdev); void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable); void mgmt_connectable(struct hci_dev *hdev, u8 connectable); -void mgmt_advertising(struct hci_dev *hdev, u8 advertising); void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status); void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, bool persistent); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 10f8de9400bc..8fbf604ba228 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1080,8 +1080,6 @@ static void hci_cc_le_set_adv_enable(struct hci_dev *hdev, struct sk_buff *skb) clear_bit(HCI_LE_ADV, &hdev->dev_flags); } - mgmt_advertising(hdev, *sent); - hci_dev_unlock(hdev); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 9cc7108f4c45..dda1eb124208 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -4034,6 +4034,11 @@ static void set_advertising_complete(struct hci_dev *hdev, u8 status) return; } + if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) + set_bit(HCI_ADVERTISING, &hdev->dev_flags); + else + clear_bit(HCI_ADVERTISING, &hdev->dev_flags); + mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev, settings_rsp, &match); @@ -5978,18 +5983,6 @@ void mgmt_connectable(struct hci_dev *hdev, u8 connectable) new_settings(hdev, NULL); } -void mgmt_advertising(struct hci_dev *hdev, u8 advertising) -{ - /* Powering off may stop advertising - don't let that interfere */ - if (!advertising && mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) - return; - - if (advertising) - set_bit(HCI_ADVERTISING, &hdev->dev_flags); - else - clear_bit(HCI_ADVERTISING, &hdev->dev_flags); -} - void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status) { u8 mgmt_err = mgmt_status(status); From 5ce194c4a751ac603966dd1567b62035a7dfbf89 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 8 Jul 2014 15:07:49 +0300 Subject: [PATCH 222/378] Bluetooth: Use real advertising state to random address update decision Now that we have a flag for tracking the real advertising state we should use that to determine whether it's safe to update the random address or not. The couple of places that were clearing the flag due to a pending request need to be updated too. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_conn.c | 4 ++-- net/bluetooth/hci_core.c | 2 +- net/bluetooth/mgmt.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 16fd55da9c1d..0db2579ea6c6 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -671,12 +671,12 @@ static void hci_req_directed_advertising(struct hci_request *req, enable = 0x00; hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable); - /* Clear the HCI_ADVERTISING bit temporarily so that the + /* Clear the HCI_LE_ADV bit temporarily so that the * hci_update_random_address knows that it's safe to go ahead * and write a new random address. The flag will be set back on * as soon as the SET_ADV_ENABLE HCI command completes. */ - clear_bit(HCI_ADVERTISING, &hdev->dev_flags); + clear_bit(HCI_LE_ADV, &hdev->dev_flags); /* Set require_privacy to false so that the remote device has a * chance of identifying us. diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index f1c5a077e558..8ffaca0290f8 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3746,7 +3746,7 @@ static void set_random_addr(struct hci_request *req, bdaddr_t *rpa) * In this kind of scenario skip the update and let the random * address be updated at the next cycle. */ - if (test_bit(HCI_ADVERTISING, &hdev->dev_flags) || + if (test_bit(HCI_LE_ADV, &hdev->dev_flags) || hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT)) { BT_DBG("Deferring random address update"); return; diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index dda1eb124208..be589d8d437f 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1046,12 +1046,12 @@ static void enable_advertising(struct hci_request *req) u8 own_addr_type, enable = 0x01; bool connectable; - /* Clear the HCI_ADVERTISING bit temporarily so that the + /* Clear the HCI_LE_ADV bit temporarily so that the * hci_update_random_address knows that it's safe to go ahead * and write a new random address. The flag will be set back on * as soon as the SET_ADV_ENABLE HCI command completes. */ - clear_bit(HCI_ADVERTISING, &hdev->dev_flags); + clear_bit(HCI_LE_ADV, &hdev->dev_flags); connectable = get_connectable(hdev); From 0ec5ae8438af02bf2295f08585d8ab49d15eaf2c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 8 Jul 2014 15:07:50 +0300 Subject: [PATCH 223/378] Bluetooth: Simplify usage of the enable_advertising function By adding support for disabling advertising when necessary and doing the checks for existing LE connections inside the enable_advertising function we can simplify the calling code quite a lot. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 49 +++++++++++++++----------------------------- 1 file changed, 16 insertions(+), 33 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index be589d8d437f..68c0698124fb 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1039,6 +1039,13 @@ static bool get_connectable(struct hci_dev *hdev) return test_bit(HCI_CONNECTABLE, &hdev->dev_flags); } +static void disable_advertising(struct hci_request *req) +{ + u8 enable = 0x00; + + hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable); +} + static void enable_advertising(struct hci_request *req) { struct hci_dev *hdev = req->hdev; @@ -1046,6 +1053,12 @@ static void enable_advertising(struct hci_request *req) u8 own_addr_type, enable = 0x01; bool connectable; + if (hci_conn_num(hdev, LE_LINK) > 0) + return; + + if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) + disable_advertising(req); + /* Clear the HCI_LE_ADV bit temporarily so that the * hci_update_random_address knows that it's safe to go ahead * and write a new random address. The flag will be set back on @@ -1074,13 +1087,6 @@ static void enable_advertising(struct hci_request *req) hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable); } -static void disable_advertising(struct hci_request *req) -{ - u8 enable = 0x00; - - hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable); -} - static void service_cache_off(struct work_struct *work) { struct hci_dev *hdev = container_of(work, struct hci_dev, @@ -1112,19 +1118,14 @@ static void rpa_expired(struct work_struct *work) set_bit(HCI_RPA_EXPIRED, &hdev->dev_flags); - if (!test_bit(HCI_ADVERTISING, &hdev->dev_flags) || - hci_conn_num(hdev, LE_LINK) > 0) + if (!test_bit(HCI_ADVERTISING, &hdev->dev_flags)) return; /* The generation of a new RPA and programming it into the * controller happens in the enable_advertising() function. */ - hci_req_init(&req, hdev); - - disable_advertising(&req); enable_advertising(&req); - hci_req_run(&req, NULL); } @@ -1864,10 +1865,8 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, write_fast_connectable(&req, false); if (test_bit(HCI_ADVERTISING, &hdev->dev_flags) && - hci_conn_num(hdev, LE_LINK) == 0) { - disable_advertising(&req); + !test_bit(HCI_LE_ADV, &hdev->dev_flags)) enable_advertising(&req); - } err = hci_req_run(&req, set_connectable_complete); if (err < 0) { @@ -6809,32 +6808,16 @@ void mgmt_discovering(struct hci_dev *hdev, u8 discovering) static void adv_enable_complete(struct hci_dev *hdev, u8 status) { BT_DBG("%s status %u", hdev->name, status); - - /* Clear the advertising mgmt setting if we failed to re-enable it */ - if (status) { - clear_bit(HCI_ADVERTISING, &hdev->dev_flags); - new_settings(hdev, NULL); - } } void mgmt_reenable_advertising(struct hci_dev *hdev) { struct hci_request req; - if (hci_conn_num(hdev, LE_LINK) > 0) - return; - if (!test_bit(HCI_ADVERTISING, &hdev->dev_flags)) return; hci_req_init(&req, hdev); enable_advertising(&req); - - /* If this fails we have no option but to let user space know - * that we've disabled advertising. - */ - if (hci_req_run(&req, adv_enable_complete) < 0) { - clear_bit(HCI_ADVERTISING, &hdev->dev_flags); - new_settings(hdev, NULL); - } + hci_req_run(&req, adv_enable_complete); } From 73e082f80d990c017c695a6750f7ac96cdc6308a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 8 Jul 2014 15:07:51 +0300 Subject: [PATCH 224/378] Bluetooth: Use the correct flag to decide to disable advertising When deciding to call disable_advertising() we're interested in the real state instead of the mgmt setting. Use therefore HCI_LE_ADV instead of the HCI_ADVERTISING flag. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 68c0698124fb..9549d7366da2 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1301,7 +1301,7 @@ static int clean_up_hci_state(struct hci_dev *hdev) hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); } - if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) + if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) disable_advertising(&req); hci_stop_discovery(&req); @@ -2230,7 +2230,7 @@ static int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) hci_cp.le = val; hci_cp.simul = lmp_le_br_capable(hdev); } else { - if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) + if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) disable_advertising(&req); } From 376f54c171674ac1f9a2eefe67d413db4836d25a Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 8 Jul 2014 15:07:52 +0300 Subject: [PATCH 225/378] Bluetooth: Stop advertising always before initiating a connection Most controllers do not support advertising while initiating an LE connection. We also have to first disable current advertising if the initiation is going to happen through direct advertising. Therefore, simply stop advertising as the first thing when starting to issue commands to establish an LE connection. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_conn.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 0db2579ea6c6..1517f1549f85 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -668,9 +668,6 @@ static void hci_req_directed_advertising(struct hci_request *req, u8 own_addr_type; u8 enable; - enable = 0x00; - hci_req_add(req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), &enable); - /* Clear the HCI_LE_ADV bit temporarily so that the * hci_update_random_address knows that it's safe to go ahead * and write a new random address. The flag will be set back on @@ -761,6 +758,18 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, hci_req_init(&req, hdev); + /* Disable advertising if we're active. For master role + * connections most controllers will refuse to connect if + * advertising is enabled, and for slave role connections we + * anyway have to disable it in order to start directed + * advertising. + */ + if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) { + u8 enable = 0x00; + hci_req_add(&req, HCI_OP_LE_SET_ADV_ENABLE, sizeof(enable), + &enable); + } + /* If requested to connect as slave use directed advertising */ if (!master) { hci_req_directed_advertising(&req, conn); From e8bb6b9739e2e80e0e413f56816af3871388cfe8 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 8 Jul 2014 15:07:53 +0300 Subject: [PATCH 226/378] Bluetooth: Fix advertising and active scanning co-existence Many controllers allow simultaneous active scanning and advertising (e.g. Intel and Broadcom) but some do not (e.g. CSR). It's therefore safest to implement mutual exclusion of these states in the kernel. This patch ensures that the two states are never entered simultaneously. Extra precaution needs to be taken for outgoing connection attempts in slave role (i.e. through directed advertising) in which case the operation that came first has precedence and the one that comes after gets a rejection. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_conn.c | 10 ++++++++++ net/bluetooth/hci_event.c | 10 +++++++++- net/bluetooth/mgmt.c | 24 ++++++++++++++++++------ 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c index 1517f1549f85..490ee8846d9e 100644 --- a/net/bluetooth/hci_conn.c +++ b/net/bluetooth/hci_conn.c @@ -772,6 +772,16 @@ struct hci_conn *hci_connect_le(struct hci_dev *hdev, bdaddr_t *dst, /* If requested to connect as slave use directed advertising */ if (!master) { + /* If we're active scanning most controllers are unable + * to initiate advertising. Simply reject the attempt. + */ + if (test_bit(HCI_LE_SCAN, &hdev->dev_flags) && + hdev->le_scan_type == LE_SCAN_ACTIVE) { + skb_queue_purge(&req.cmd_q); + hci_conn_del(conn); + return ERR_PTR(-EBUSY); + } + hci_req_directed_advertising(&req, conn); goto create_conn; } diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 8fbf604ba228..5d3095d7d4b0 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1176,13 +1176,21 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, cancel_delayed_work(&hdev->le_scan_disable); clear_bit(HCI_LE_SCAN, &hdev->dev_flags); + /* The HCI_LE_SCAN_INTERRUPTED flag indicates that we * interrupted scanning due to a connect request. Mark - * therefore discovery as stopped. + * therefore discovery as stopped. If this was not + * because of a connect request advertising might have + * been disabled because of active scanning, so + * re-enable it again if necessary. */ if (test_and_clear_bit(HCI_LE_SCAN_INTERRUPTED, &hdev->dev_flags)) hci_discovery_set_state(hdev, DISCOVERY_STOPPED); + else if (!test_bit(HCI_LE_ADV, &hdev->dev_flags) && + hdev->discovery.state != DISCOVERY_STARTING) + mgmt_reenable_advertising(hdev); + break; default: diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 9549d7366da2..944e6463fd61 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3726,11 +3726,21 @@ static int start_discovery(struct sock *sk, struct hci_dev *hdev, goto failed; } - if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) { - err = cmd_status(sk, hdev->id, MGMT_OP_START_DISCOVERY, - MGMT_STATUS_REJECTED); - mgmt_pending_remove(cmd); - goto failed; + if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) { + /* Don't let discovery abort an outgoing + * connection attempt that's using directed + * advertising. + */ + if (hci_conn_hash_lookup_state(hdev, LE_LINK, + BT_CONNECT)) { + err = cmd_status(sk, hdev->id, + MGMT_OP_START_DISCOVERY, + MGMT_STATUS_REJECTED); + mgmt_pending_remove(cmd); + goto failed; + } + + disable_advertising(&req); } /* If controller is scanning, it means the background scanning @@ -4078,7 +4088,9 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, * necessary). */ if (!hdev_is_powered(hdev) || val == enabled || - hci_conn_num(hdev, LE_LINK) > 0) { + hci_conn_num(hdev, LE_LINK) > 0 || + (test_bit(HCI_LE_SCAN, &hdev->dev_flags) && + hdev->le_scan_type == LE_SCAN_ACTIVE)) { bool changed = false; if (val != test_bit(HCI_ADVERTISING, &hdev->dev_flags)) { From 34722277045f84d0ee618865d02030a44b1ed257 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 8 Jul 2014 16:05:05 +0300 Subject: [PATCH 227/378] Bluetooth: Fix check for re-enabling advertising There are many different places that can disable LE scanning but we only want to re-enable advertising in hci_cc_le_set_scan_enable() for a very specific use case, which is when the active scanning part of Start Discovery is complete. Because of this, fix the discovery state check to test for the exact state. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_event.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 5d3095d7d4b0..2b3d366e5d98 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1188,7 +1188,7 @@ static void hci_cc_le_set_scan_enable(struct hci_dev *hdev, &hdev->dev_flags)) hci_discovery_set_state(hdev, DISCOVERY_STOPPED); else if (!test_bit(HCI_LE_ADV, &hdev->dev_flags) && - hdev->discovery.state != DISCOVERY_STARTING) + hdev->discovery.state == DISCOVERY_FINDING) mgmt_reenable_advertising(hdev); break; From 23a48093b53999f8539144db1d9567964a84655c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 8 Jul 2014 16:05:06 +0300 Subject: [PATCH 228/378] Bluetooth: Fix setting STOPPING state for discovery If any of the HCI commands from the hci_stop_discovery function were successfully sent we need to set the discovery state to STOPPING. The Stop Discovery code was already handling this, but the code in clean_up_hci_state was not. This patch updates the hci_stop_discovery to return a bool to indicate whether it queued any commands and the clean_up_hci_state() function respectively to look at the return value and call hci_discovery_set_state() if necessary. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 944e6463fd61..a4232bc237f3 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1251,7 +1251,7 @@ static void clean_up_hci_complete(struct hci_dev *hdev, u8 status) } } -static void hci_stop_discovery(struct hci_request *req) +static bool hci_stop_discovery(struct hci_request *req) { struct hci_dev *hdev = req->hdev; struct hci_cp_remote_name_req_cancel cp; @@ -1266,32 +1266,39 @@ static void hci_stop_discovery(struct hci_request *req) hci_req_add_le_scan_disable(req); } - break; + return true; case DISCOVERY_RESOLVING: e = hci_inquiry_cache_lookup_resolve(hdev, BDADDR_ANY, NAME_PENDING); if (!e) - return; + break; bacpy(&cp.bdaddr, &e->data.bdaddr); hci_req_add(req, HCI_OP_REMOTE_NAME_REQ_CANCEL, sizeof(cp), &cp); - break; + return true; default: /* Passive scanning */ - if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) + if (test_bit(HCI_LE_SCAN, &hdev->dev_flags)) { hci_req_add_le_scan_disable(req); + return true; + } + break; } + + return false; } static int clean_up_hci_state(struct hci_dev *hdev) { struct hci_request req; struct hci_conn *conn; + bool discov_stopped; + int err; hci_req_init(&req, hdev); @@ -1304,7 +1311,7 @@ static int clean_up_hci_state(struct hci_dev *hdev) if (test_bit(HCI_LE_ADV, &hdev->dev_flags)) disable_advertising(&req); - hci_stop_discovery(&req); + discov_stopped = hci_stop_discovery(&req); list_for_each_entry(conn, &hdev->conn_hash.list, list) { struct hci_cp_disconnect dc; @@ -1338,7 +1345,11 @@ static int clean_up_hci_state(struct hci_dev *hdev) } } - return hci_req_run(&req, clean_up_hci_complete); + err = hci_req_run(&req, clean_up_hci_complete); + if (!err && discov_stopped) + hci_discovery_set_state(hdev, DISCOVERY_STOPPING); + + return err; } static int set_powered(struct sock *sk, struct hci_dev *hdev, void *data, From 3742abfc4e853f9c926982d8d45be6ff010966ae Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 8 Jul 2014 16:07:34 +0300 Subject: [PATCH 229/378] Bluetooth: Fix connectable and discoverable supported settings values The connectable and discoverable mgmt settings are supported both for LE and BR/EDR controllers so they do not belong behind a lmp_bredr_capable() condition. This patch fixes the issue in get_supported_settings(). Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index a4232bc237f3..376b164c5250 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -555,12 +555,12 @@ static u32 get_supported_settings(struct hci_dev *hdev) settings |= MGMT_SETTING_POWERED; settings |= MGMT_SETTING_PAIRABLE; settings |= MGMT_SETTING_DEBUG_KEYS; + settings |= MGMT_SETTING_CONNECTABLE; + settings |= MGMT_SETTING_DISCOVERABLE; if (lmp_bredr_capable(hdev)) { - settings |= MGMT_SETTING_CONNECTABLE; if (hdev->hci_ver >= BLUETOOTH_VER_1_2) settings |= MGMT_SETTING_FAST_CONNECTABLE; - settings |= MGMT_SETTING_DISCOVERABLE; settings |= MGMT_SETTING_BREDR; settings |= MGMT_SETTING_LINK_SECURITY; From 562064e654d42599ad986812adaded653f3b81df Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 8 Jul 2014 16:35:34 +0300 Subject: [PATCH 230/378] Bluetooth: Fix toggling background scan when changing connectable state If the connectable state change doesn't require any special HCI commands the set_connectable_update_settings() function is used instead of the set_connectable_complete() function. We must therefore make sure to call hci_update_background_scan() there as well. This code path is used also when we're powered off, but that's fine since hci_update_background_scan() has the necessary checks for it. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 376b164c5250..216aa93514b6 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1794,8 +1794,10 @@ static int set_connectable_update_settings(struct hci_dev *hdev, if (err < 0) return err; - if (changed) + if (changed) { + hci_update_background_scan(hdev); return new_settings(hdev, sk); + } return 0; } From fbd96c151cdcadb0ce83b45747d738498d72aa9d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Tue, 8 Jul 2014 17:21:51 +0300 Subject: [PATCH 231/378] Bluetooth: Fix clearing HCI_LE_ADV for LE connections All LE controllers always implicitly stop advertising when establishing connections. Therefore, be sure to clear the flag in the event handler for new LE connections. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_event.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 2b3d366e5d98..f6997901bdd7 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4097,6 +4097,11 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); + /* All controllers implicitly stop advertising in the event of a + * connection, so ensure that the state bit is cleared. + */ + clear_bit(HCI_LE_ADV, &hdev->dev_flags); + conn = hci_conn_hash_lookup_state(hdev, LE_LINK, BT_CONNECT); if (!conn) { conn = hci_conn_add(hdev, LE_LINK, &ev->bdaddr); From fa2f1394fe9c1a217213f02df77812701de6362f Mon Sep 17 00:00:00 2001 From: Anantha Krishnan Date: Tue, 8 Jul 2014 19:25:08 +0530 Subject: [PATCH 232/378] Bluetooth: Add support for Acer [13D3:3432] Add support for the QCA6174 chip. T: Bus=04 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 30 Spd=12 MxCh= 0 D: Ver= 1.10 Cls=e0(wlcon) Sub=01 Prot=01 MxPS=64 #Cfgs= 1 P: Vendor=13d3 ProdID=3432 Rev=00.02 C: #Ifs= 2 Cfg#= 1 Atr=e0 MxPwr=100mA I: If#= 0 Alt= 0 #EPs= 3 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb I: If#= 1 Alt= 0 #EPs= 2 Cls=e0(wlcon) Sub=01 Prot=01 Driver=btusb Signed-off-by: Anantha Krishnan Signed-off-by: Marcel Holtmann --- drivers/bluetooth/ath3k.c | 2 ++ drivers/bluetooth/btusb.c | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/bluetooth/ath3k.c b/drivers/bluetooth/ath3k.c index abe6aecbabb2..230c552daf91 100644 --- a/drivers/bluetooth/ath3k.c +++ b/drivers/bluetooth/ath3k.c @@ -103,6 +103,7 @@ static const struct usb_device_id ath3k_table[] = { { USB_DEVICE(0x13d3, 0x3375) }, { USB_DEVICE(0x13d3, 0x3393) }, { USB_DEVICE(0x13d3, 0x3402) }, + { USB_DEVICE(0x13d3, 0x3432) }, /* Atheros AR5BBU12 with sflash firmware */ { USB_DEVICE(0x0489, 0xE02C) }, @@ -152,6 +153,7 @@ static const struct usb_device_id ath3k_blist_tbl[] = { { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 }, /* Atheros AR5BBU22 with sflash firmware */ { USB_DEVICE(0x0489, 0xE036), .driver_info = BTUSB_ATH3012 }, diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c index ef4375d5c4ed..ed7b33b06b43 100644 --- a/drivers/bluetooth/btusb.c +++ b/drivers/bluetooth/btusb.c @@ -177,6 +177,7 @@ static const struct usb_device_id blacklist_table[] = { { USB_DEVICE(0x13d3, 0x3375), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3393), .driver_info = BTUSB_ATH3012 }, { USB_DEVICE(0x13d3, 0x3402), .driver_info = BTUSB_ATH3012 }, + { USB_DEVICE(0x13d3, 0x3432), .driver_info = BTUSB_ATH3012 }, /* Atheros AR5BBU12 with sflash firmware */ { USB_DEVICE(0x0489, 0xe02c), .driver_info = BTUSB_IGNORE }, From cd7ca0ec5e046c570497b387332560eb42908cc4 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 9 Jul 2014 09:49:05 +0200 Subject: [PATCH 233/378] Bluetooth: Fix enabling Authenticated Payload Timeout Expired event The Authenticated Payload Timeout Expired event is valid for controllers with BR/EDR Secure Connections support, but also for LE only controllers supporting LE Ping feature. When either of them is available enable this event. Previous it was not enabled when the controller was only supporting LE operation. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_core.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 6ed1f7288f13..a01236e2df13 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -357,6 +357,7 @@ enum { /* LE features */ #define HCI_LE_CONN_PARAM_REQ_PROC 0x02 +#define HCI_LE_PING 0x10 /* Connection modes */ #define HCI_CM_ACTIVE 0x0000 diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 8ffaca0290f8..421faf5fa1f5 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1569,7 +1569,7 @@ static void hci_set_event_mask_page_2(struct hci_request *req) } /* Enable Authenticated Payload Timeout Expired event if supported */ - if (lmp_ping_capable(hdev)) + if (lmp_ping_capable(hdev) || hdev->le_features[0] & HCI_LE_PING) events[2] |= 0x80; hci_req_add(req, HCI_OP_SET_EVENT_MASK_PAGE_2, sizeof(events), events); From dcc36c16c2f1c9800146c8416ee5a4c3dc974623 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jul 2014 12:59:13 +0300 Subject: [PATCH 234/378] Bluetooth: Unify helpers for bdaddr_list manipulations We already have several lists with struct bdaddr_list entries, and there will be more in the future. Since the operations for adding, removing, looking up and clearing entries in these lists are exactly the same it doesn't make sense to define new functions for every single list. This patch unifies the functions by passing the list_head to them instead of a hci_dev pointer. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 15 ++---- net/bluetooth/hci_core.c | 85 +++++--------------------------- net/bluetooth/hci_event.c | 15 +++--- net/bluetooth/hci_sock.c | 4 +- net/bluetooth/l2cap_core.c | 7 +-- net/bluetooth/mgmt.c | 6 ++- 6 files changed, 36 insertions(+), 96 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index c98de309967e..3a1caf10cc8d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -849,16 +849,11 @@ int hci_get_conn_info(struct hci_dev *hdev, void __user *arg); int hci_get_auth_info(struct hci_dev *hdev, void __user *arg); int hci_inquiry(void __user *arg); -struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, - bdaddr_t *bdaddr, u8 type); -int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); -int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); - -struct bdaddr_list *hci_white_list_lookup(struct hci_dev *hdev, - bdaddr_t *bdaddr, u8 type); -void hci_white_list_clear(struct hci_dev *hdev); -int hci_white_list_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); -int hci_white_list_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type); +struct bdaddr_list *hci_bdaddr_list_lookup(struct list_head *list, + bdaddr_t *bdaddr, u8 type); +int hci_bdaddr_list_add(struct list_head *list, bdaddr_t *bdaddr, u8 type); +int hci_bdaddr_list_del(struct list_head *list, bdaddr_t *bdaddr, u8 type); +void hci_bdaddr_list_clear(struct list_head *list); struct hci_conn_params *hci_conn_params_lookup(struct hci_dev *hdev, bdaddr_t *addr, u8 addr_type); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 421faf5fa1f5..705f8df7af96 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -3351,12 +3351,12 @@ int hci_add_remote_oob_ext_data(struct hci_dev *hdev, bdaddr_t *bdaddr, return 0; } -struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, +struct bdaddr_list *hci_bdaddr_list_lookup(struct list_head *bdaddr_list, bdaddr_t *bdaddr, u8 type) { struct bdaddr_list *b; - list_for_each_entry(b, &hdev->blacklist, list) { + list_for_each_entry(b, bdaddr_list, list) { if (!bacmp(&b->bdaddr, bdaddr) && b->bdaddr_type == type) return b; } @@ -3364,11 +3364,11 @@ struct bdaddr_list *hci_blacklist_lookup(struct hci_dev *hdev, return NULL; } -static void hci_blacklist_clear(struct hci_dev *hdev) +void hci_bdaddr_list_clear(struct list_head *bdaddr_list) { struct list_head *p, *n; - list_for_each_safe(p, n, &hdev->blacklist) { + list_for_each_safe(p, n, bdaddr_list) { struct bdaddr_list *b = list_entry(p, struct bdaddr_list, list); list_del(p); @@ -3376,14 +3376,14 @@ static void hci_blacklist_clear(struct hci_dev *hdev) } } -int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) +int hci_bdaddr_list_add(struct list_head *list, bdaddr_t *bdaddr, u8 type) { struct bdaddr_list *entry; if (!bacmp(bdaddr, BDADDR_ANY)) return -EBADF; - if (hci_blacklist_lookup(hdev, bdaddr, type)) + if (hci_bdaddr_list_lookup(list, bdaddr, type)) return -EEXIST; entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL); @@ -3393,82 +3393,21 @@ int hci_blacklist_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) bacpy(&entry->bdaddr, bdaddr); entry->bdaddr_type = type; - list_add(&entry->list, &hdev->blacklist); + list_add(&entry->list, list); return 0; } -int hci_blacklist_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) +int hci_bdaddr_list_del(struct list_head *list, bdaddr_t *bdaddr, u8 type) { struct bdaddr_list *entry; if (!bacmp(bdaddr, BDADDR_ANY)) { - hci_blacklist_clear(hdev); + hci_bdaddr_list_clear(list); return 0; } - entry = hci_blacklist_lookup(hdev, bdaddr, type); - if (!entry) - return -ENOENT; - - list_del(&entry->list); - kfree(entry); - - return 0; -} - -struct bdaddr_list *hci_white_list_lookup(struct hci_dev *hdev, - bdaddr_t *bdaddr, u8 type) -{ - struct bdaddr_list *b; - - list_for_each_entry(b, &hdev->le_white_list, list) { - if (!bacmp(&b->bdaddr, bdaddr) && b->bdaddr_type == type) - return b; - } - - return NULL; -} - -void hci_white_list_clear(struct hci_dev *hdev) -{ - struct list_head *p, *n; - - list_for_each_safe(p, n, &hdev->le_white_list) { - struct bdaddr_list *b = list_entry(p, struct bdaddr_list, list); - - list_del(p); - kfree(b); - } -} - -int hci_white_list_add(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) -{ - struct bdaddr_list *entry; - - if (!bacmp(bdaddr, BDADDR_ANY)) - return -EBADF; - - entry = kzalloc(sizeof(struct bdaddr_list), GFP_KERNEL); - if (!entry) - return -ENOMEM; - - bacpy(&entry->bdaddr, bdaddr); - entry->bdaddr_type = type; - - list_add(&entry->list, &hdev->le_white_list); - - return 0; -} - -int hci_white_list_del(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type) -{ - struct bdaddr_list *entry; - - if (!bacmp(bdaddr, BDADDR_ANY)) - return -EBADF; - - entry = hci_white_list_lookup(hdev, bdaddr, type); + entry = hci_bdaddr_list_lookup(list, bdaddr, type); if (!entry) return -ENOENT; @@ -4096,13 +4035,13 @@ void hci_unregister_dev(struct hci_dev *hdev) destroy_workqueue(hdev->req_workqueue); hci_dev_lock(hdev); - hci_blacklist_clear(hdev); + hci_bdaddr_list_clear(&hdev->blacklist); hci_uuids_clear(hdev); hci_link_keys_clear(hdev); hci_smp_ltks_clear(hdev); hci_smp_irks_clear(hdev); hci_remote_oob_data_clear(hdev); - hci_white_list_clear(hdev); + hci_bdaddr_list_clear(&hdev->le_white_list); hci_conn_params_clear_all(hdev); hci_dev_unlock(hdev); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index f6997901bdd7..381c631423f1 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -1222,7 +1222,7 @@ static void hci_cc_le_clear_white_list(struct hci_dev *hdev, if (status) return; - hci_white_list_clear(hdev); + hci_bdaddr_list_clear(&hdev->le_white_list); } static void hci_cc_le_add_to_white_list(struct hci_dev *hdev, @@ -1240,7 +1240,8 @@ static void hci_cc_le_add_to_white_list(struct hci_dev *hdev, if (!sent) return; - hci_white_list_add(hdev, &sent->bdaddr, sent->bdaddr_type); + hci_bdaddr_list_add(&hdev->le_white_list, &sent->bdaddr, + sent->bdaddr_type); } static void hci_cc_le_del_from_white_list(struct hci_dev *hdev, @@ -1258,7 +1259,8 @@ static void hci_cc_le_del_from_white_list(struct hci_dev *hdev, if (!sent) return; - hci_white_list_del(hdev, &sent->bdaddr, sent->bdaddr_type); + hci_bdaddr_list_del(&hdev->le_white_list, &sent->bdaddr, + sent->bdaddr_type); } static void hci_cc_le_read_supported_states(struct hci_dev *hdev, @@ -2132,7 +2134,8 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) &flags); if ((mask & HCI_LM_ACCEPT) && - !hci_blacklist_lookup(hdev, &ev->bdaddr, BDADDR_BREDR)) { + !hci_bdaddr_list_lookup(&hdev->blacklist, &ev->bdaddr, + BDADDR_BREDR)) { /* Connection accepted */ struct inquiry_entry *ie; struct hci_conn *conn; @@ -4184,7 +4187,7 @@ static void hci_le_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *skb) addr_type = BDADDR_LE_RANDOM; /* Drop the connection if he device is blocked */ - if (hci_blacklist_lookup(hdev, &conn->dst, addr_type)) { + if (hci_bdaddr_list_lookup(&hdev->blacklist, &conn->dst, addr_type)) { hci_conn_drop(conn); goto unlock; } @@ -4253,7 +4256,7 @@ static void check_pending_le_conn(struct hci_dev *hdev, bdaddr_t *addr, return; /* Ignore if the device is blocked */ - if (hci_blacklist_lookup(hdev, addr, addr_type)) + if (hci_bdaddr_list_lookup(&hdev->blacklist, addr, addr_type)) return; /* If we're connectable, always connect any ADV_DIRECT_IND event */ diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 802665751cc4..c64728d571ae 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -481,7 +481,7 @@ static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg) hci_dev_lock(hdev); - err = hci_blacklist_add(hdev, &bdaddr, BDADDR_BREDR); + err = hci_bdaddr_list_add(&hdev->blacklist, &bdaddr, BDADDR_BREDR); hci_dev_unlock(hdev); @@ -498,7 +498,7 @@ static int hci_sock_blacklist_del(struct hci_dev *hdev, void __user *arg) hci_dev_lock(hdev); - err = hci_blacklist_del(hdev, &bdaddr, BDADDR_BREDR); + err = hci_bdaddr_list_del(&hdev->blacklist, &bdaddr, BDADDR_BREDR); hci_dev_unlock(hdev); diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index bf379a379fa0..d006e6c0e3b4 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -1460,6 +1460,7 @@ static struct l2cap_chan *l2cap_global_chan_by_scid(int state, u16 cid, static void l2cap_le_conn_ready(struct l2cap_conn *conn) { struct hci_conn *hcon = conn->hcon; + struct hci_dev *hdev = hcon->hdev; struct l2cap_chan *chan, *pchan; u8 dst_type; @@ -1478,7 +1479,7 @@ static void l2cap_le_conn_ready(struct l2cap_conn *conn) dst_type = bdaddr_type(hcon, hcon->dst_type); /* If device is blocked, do not create a channel for it */ - if (hci_blacklist_lookup(hcon->hdev, &hcon->dst, dst_type)) + if (hci_bdaddr_list_lookup(&hdev->blacklist, &hcon->dst, dst_type)) return; /* For LE slave connections, make sure the connection interval @@ -6918,8 +6919,8 @@ static void l2cap_recv_frame(struct l2cap_conn *conn, struct sk_buff *skb) * at least ensure that we ignore incoming data from them. */ if (hcon->type == LE_LINK && - hci_blacklist_lookup(hcon->hdev, &hcon->dst, - bdaddr_type(hcon, hcon->dst_type))) { + hci_bdaddr_list_lookup(&hcon->hdev->blacklist, &hcon->dst, + bdaddr_type(hcon, hcon->dst_type))) { kfree_skb(skb); return; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 216aa93514b6..592e73eea76d 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -3956,7 +3956,8 @@ static int block_device(struct sock *sk, struct hci_dev *hdev, void *data, hci_dev_lock(hdev); - err = hci_blacklist_add(hdev, &cp->addr.bdaddr, cp->addr.type); + err = hci_bdaddr_list_add(&hdev->blacklist, &cp->addr.bdaddr, + cp->addr.type); if (err < 0) { status = MGMT_STATUS_FAILED; goto done; @@ -3991,7 +3992,8 @@ static int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data, hci_dev_lock(hdev); - err = hci_blacklist_del(hdev, &cp->addr.bdaddr, cp->addr.type); + err = hci_bdaddr_list_del(&hdev->blacklist, &cp->addr.bdaddr, + cp->addr.type); if (err < 0) { status = MGMT_STATUS_INVALID_PARAMS; goto done; From 6659358efe617bb46237e62f7303c76e10568d70 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jul 2014 12:59:14 +0300 Subject: [PATCH 235/378] Bluetooth: Introduce a whitelist for BR/EDR devices This patch extends the Add/Remove device commands by letting user space pass BR/EDR addresses to them. The resulting entries get stored in a new hdev->whitelist list. The idea is that we can now selectively accept connections from devices in the list even though HCI_CONNECTABLE is not set (the actual implementation of this is coming in a subsequent patch). Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 29 ++++++++++++++++++++ net/bluetooth/mgmt.c | 46 ++++++++++++++++++++++++++++++-- 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 3a1caf10cc8d..cba4837dcaa5 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -305,6 +305,7 @@ struct hci_dev { struct list_head mgmt_pending; struct list_head blacklist; + struct list_head whitelist; struct list_head uuids; struct list_head link_keys; struct list_head long_term_keys; diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 705f8df7af96..728a6ee471ea 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -191,6 +191,31 @@ static const struct file_operations blacklist_fops = { .release = single_release, }; +static int whitelist_show(struct seq_file *f, void *p) +{ + struct hci_dev *hdev = f->private; + struct bdaddr_list *b; + + hci_dev_lock(hdev); + list_for_each_entry(b, &hdev->whitelist, list) + seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type); + hci_dev_unlock(hdev); + + return 0; +} + +static int whitelist_open(struct inode *inode, struct file *file) +{ + return single_open(file, whitelist_show, inode->i_private); +} + +static const struct file_operations whitelist_fops = { + .open = whitelist_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + static int uuids_show(struct seq_file *f, void *p) { struct hci_dev *hdev = f->private; @@ -1707,6 +1732,8 @@ static int __hci_init(struct hci_dev *hdev) debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev); debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev, &blacklist_fops); + debugfs_create_file("whitelist", 0444, hdev->debugfs, hdev, + &whitelist_fops); debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops); debugfs_create_file("conn_info_min_age", 0644, hdev->debugfs, hdev, @@ -3825,6 +3852,7 @@ struct hci_dev *hci_alloc_dev(void) INIT_LIST_HEAD(&hdev->mgmt_pending); INIT_LIST_HEAD(&hdev->blacklist); + INIT_LIST_HEAD(&hdev->whitelist); INIT_LIST_HEAD(&hdev->uuids); INIT_LIST_HEAD(&hdev->link_keys); INIT_LIST_HEAD(&hdev->long_term_keys); @@ -4036,6 +4064,7 @@ void hci_unregister_dev(struct hci_dev *hdev) hci_dev_lock(hdev); hci_bdaddr_list_clear(&hdev->blacklist); + hci_bdaddr_list_clear(&hdev->whitelist); hci_uuids_clear(hdev); hci_link_keys_clear(hdev); hci_smp_ltks_clear(hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 592e73eea76d..49581e99685c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5219,7 +5219,7 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, BT_DBG("%s", hdev->name); - if (!bdaddr_type_is_le(cp->addr.type) || + if (!bdaddr_type_is_valid(cp->addr.type) || !bacmp(&cp->addr.bdaddr, BDADDR_ANY)) return cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, MGMT_STATUS_INVALID_PARAMS, @@ -5232,6 +5232,22 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, hci_dev_lock(hdev); + if (cp->addr.type == BDADDR_BREDR) { + /* Only "connect" action supported for now */ + if (cp->action != 0x01) { + err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &cp->addr, sizeof(cp->addr)); + goto unlock; + } + + err = hci_bdaddr_list_add(&hdev->whitelist, &cp->addr.bdaddr, + cp->addr.type); + if (err) + goto unlock; + goto added; + } + if (cp->addr.type == BDADDR_LE_PUBLIC) addr_type = ADDR_LE_DEV_PUBLIC; else @@ -5253,6 +5269,7 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, goto unlock; } +added: device_added(sk, hdev, &cp->addr.bdaddr, cp->addr.type, cp->action); err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, @@ -5288,13 +5305,30 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, struct hci_conn_params *params; u8 addr_type; - if (!bdaddr_type_is_le(cp->addr.type)) { + if (!bdaddr_type_is_valid(cp->addr.type)) { err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, MGMT_STATUS_INVALID_PARAMS, &cp->addr, sizeof(cp->addr)); goto unlock; } + if (cp->addr.type == BDADDR_BREDR) { + err = hci_bdaddr_list_del(&hdev->whitelist, + &cp->addr.bdaddr, + cp->addr.type); + if (err) { + err = cmd_complete(sk, hdev->id, + MGMT_OP_REMOVE_DEVICE, + MGMT_STATUS_INVALID_PARAMS, + &cp->addr, sizeof(cp->addr)); + goto unlock; + } + + device_removed(sk, hdev, &cp->addr.bdaddr, + cp->addr.type); + goto complete; + } + if (cp->addr.type == BDADDR_LE_PUBLIC) addr_type = ADDR_LE_DEV_PUBLIC; else @@ -5324,6 +5358,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, device_removed(sk, hdev, &cp->addr.bdaddr, cp->addr.type); } else { struct hci_conn_params *p, *tmp; + struct bdaddr_list *b, *btmp; if (cp->addr.type) { err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, @@ -5332,6 +5367,12 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, goto unlock; } + list_for_each_entry_safe(b, btmp, &hdev->whitelist, list) { + device_removed(sk, hdev, &b->bdaddr, b->bdaddr_type); + list_del(&b->list); + kfree(b); + } + list_for_each_entry_safe(p, tmp, &hdev->le_conn_params, list) { if (p->auto_connect == HCI_AUTO_CONN_DISABLED) continue; @@ -5346,6 +5387,7 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, hci_update_background_scan(hdev); } +complete: err = cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, MGMT_STATUS_SUCCESS, &cp->addr, sizeof(cp->addr)); From a397407f266f8dcb6ea7b28cbff9d9cbd87b6ca8 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jul 2014 12:59:15 +0300 Subject: [PATCH 236/378] Bluetooth: Update page scan when necessary for Add/Remove Device When we're removing the last item in the white list or adding the first one to it and HCI_CONNECTABLE is not set we need to update the current page scan. This patch adds a simple helper function for the purpose and calls it from the respective mgmt command handlers. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 49581e99685c..72ff19f26991 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -5198,6 +5198,27 @@ unlock: return err; } +/* Helper for Add/Remove Device commands */ +static void update_page_scan(struct hci_dev *hdev, u8 scan) +{ + if (!test_bit(HCI_BREDR_ENABLED, &hdev->dev_flags)) + return; + + if (!hdev_is_powered(hdev)) + return; + + /* If HCI_CONNECTABLE is set then Add/Remove Device should not + * make any changes to page scanning. + */ + if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) + return; + + if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) + scan |= SCAN_INQUIRY; + + hci_send_cmd(hdev, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); +} + static void device_added(struct sock *sk, struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type, u8 action) { @@ -5233,6 +5254,8 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, hci_dev_lock(hdev); if (cp->addr.type == BDADDR_BREDR) { + bool update_scan; + /* Only "connect" action supported for now */ if (cp->action != 0x01) { err = cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, @@ -5241,10 +5264,16 @@ static int add_device(struct sock *sk, struct hci_dev *hdev, goto unlock; } + update_scan = list_empty(&hdev->whitelist); + err = hci_bdaddr_list_add(&hdev->whitelist, &cp->addr.bdaddr, cp->addr.type); if (err) goto unlock; + + if (update_scan) + update_page_scan(hdev, SCAN_PAGE); + goto added; } @@ -5324,6 +5353,9 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, goto unlock; } + if (list_empty(&hdev->whitelist)) + update_page_scan(hdev, SCAN_DISABLED); + device_removed(sk, hdev, &cp->addr.bdaddr, cp->addr.type); goto complete; @@ -5373,6 +5405,8 @@ static int remove_device(struct sock *sk, struct hci_dev *hdev, kfree(b); } + update_page_scan(hdev, SCAN_DISABLED); + list_for_each_entry_safe(p, tmp, &hdev->le_conn_params, list) { if (p->auto_connect == HCI_AUTO_CONN_DISABLED) continue; From dee58c1ed5e29eb57fa9358a8ff27848b72f3b7d Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jul 2014 12:59:16 +0300 Subject: [PATCH 237/378] Bluetooth: Fix incorrectly setting HCI_CONNECTABLE Since page scan might be enabled by Add Device we should not implicitly set connectable whenever something else than Set Connectable changes it. This patch makes sure that we don't set HCI_CONNECTABLE for these cases if there are any entries in the white list. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 72ff19f26991..5a866b65371c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6076,6 +6076,14 @@ void mgmt_connectable(struct hci_dev *hdev, u8 connectable) if (!connectable && mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) return; + /* If something else than mgmt changed the page scan state we + * can't differentiate this from a change triggered by adding + * the first element to the whitelist. Therefore, avoid + * incorrectly setting HCI_CONNECTABLE. + */ + if (connectable && !list_empty(&hdev->whitelist)) + return; + if (connectable) changed = !test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags); else From 70c464256310e1c3716099b9d02ece4169272f73 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jul 2014 12:59:17 +0300 Subject: [PATCH 238/378] Bluetooth: Refactor connection request handling The conditions for accepting an incoming connections are already non-trivial and will become more so once a white list is added. This patch breaks up the checks for when to reject the request by creating a helper function for it. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_event.c | 122 ++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 57 deletions(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 381c631423f1..6d1d5b3e9cd4 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2121,10 +2121,21 @@ unlock: hci_conn_check_pending(hdev); } +static void hci_reject_conn(struct hci_dev *hdev, bdaddr_t *bdaddr) +{ + struct hci_cp_reject_conn_req cp; + + bacpy(&cp.bdaddr, bdaddr); + cp.reason = HCI_ERROR_REJ_BAD_ADDR; + hci_send_cmd(hdev, HCI_OP_REJECT_CONN_REQ, sizeof(cp), &cp); +} + static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) { struct hci_ev_conn_request *ev = (void *) skb->data; int mask = hdev->link_mode; + struct inquiry_entry *ie; + struct hci_conn *conn; __u8 flags = 0; BT_DBG("%s bdaddr %pMR type 0x%x", hdev->name, &ev->bdaddr, @@ -2133,74 +2144,71 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) mask |= hci_proto_connect_ind(hdev, &ev->bdaddr, ev->link_type, &flags); - if ((mask & HCI_LM_ACCEPT) && - !hci_bdaddr_list_lookup(&hdev->blacklist, &ev->bdaddr, + if (!(mask & HCI_LM_ACCEPT)) { + hci_reject_conn(hdev, &ev->bdaddr); + return; + } + + if (!hci_bdaddr_list_lookup(&hdev->blacklist, &ev->bdaddr, BDADDR_BREDR)) { - /* Connection accepted */ - struct inquiry_entry *ie; - struct hci_conn *conn; + hci_reject_conn(hdev, &ev->bdaddr); + return; + } - hci_dev_lock(hdev); + /* Connection accepted */ - ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr); - if (ie) - memcpy(ie->data.dev_class, ev->dev_class, 3); + hci_dev_lock(hdev); - conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, - &ev->bdaddr); + ie = hci_inquiry_cache_lookup(hdev, &ev->bdaddr); + if (ie) + memcpy(ie->data.dev_class, ev->dev_class, 3); + + conn = hci_conn_hash_lookup_ba(hdev, ev->link_type, + &ev->bdaddr); + if (!conn) { + conn = hci_conn_add(hdev, ev->link_type, &ev->bdaddr); if (!conn) { - conn = hci_conn_add(hdev, ev->link_type, &ev->bdaddr); - if (!conn) { - BT_ERR("No memory for new connection"); - hci_dev_unlock(hdev); - return; - } + BT_ERR("No memory for new connection"); + hci_dev_unlock(hdev); + return; } + } - memcpy(conn->dev_class, ev->dev_class, 3); + memcpy(conn->dev_class, ev->dev_class, 3); - hci_dev_unlock(hdev); + hci_dev_unlock(hdev); - if (ev->link_type == ACL_LINK || - (!(flags & HCI_PROTO_DEFER) && !lmp_esco_capable(hdev))) { - struct hci_cp_accept_conn_req cp; - conn->state = BT_CONNECT; - - bacpy(&cp.bdaddr, &ev->bdaddr); - - if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER)) - cp.role = 0x00; /* Become master */ - else - cp.role = 0x01; /* Remain slave */ - - hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp), - &cp); - } else if (!(flags & HCI_PROTO_DEFER)) { - struct hci_cp_accept_sync_conn_req cp; - conn->state = BT_CONNECT; - - bacpy(&cp.bdaddr, &ev->bdaddr); - cp.pkt_type = cpu_to_le16(conn->pkt_type); - - cp.tx_bandwidth = cpu_to_le32(0x00001f40); - cp.rx_bandwidth = cpu_to_le32(0x00001f40); - cp.max_latency = cpu_to_le16(0xffff); - cp.content_format = cpu_to_le16(hdev->voice_setting); - cp.retrans_effort = 0xff; - - hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ, - sizeof(cp), &cp); - } else { - conn->state = BT_CONNECT2; - hci_proto_connect_cfm(conn, 0); - } - } else { - /* Connection rejected */ - struct hci_cp_reject_conn_req cp; + if (ev->link_type == ACL_LINK || + (!(flags & HCI_PROTO_DEFER) && !lmp_esco_capable(hdev))) { + struct hci_cp_accept_conn_req cp; + conn->state = BT_CONNECT; bacpy(&cp.bdaddr, &ev->bdaddr); - cp.reason = HCI_ERROR_REJ_BAD_ADDR; - hci_send_cmd(hdev, HCI_OP_REJECT_CONN_REQ, sizeof(cp), &cp); + + if (lmp_rswitch_capable(hdev) && (mask & HCI_LM_MASTER)) + cp.role = 0x00; /* Become master */ + else + cp.role = 0x01; /* Remain slave */ + + hci_send_cmd(hdev, HCI_OP_ACCEPT_CONN_REQ, sizeof(cp), &cp); + } else if (!(flags & HCI_PROTO_DEFER)) { + struct hci_cp_accept_sync_conn_req cp; + conn->state = BT_CONNECT; + + bacpy(&cp.bdaddr, &ev->bdaddr); + cp.pkt_type = cpu_to_le16(conn->pkt_type); + + cp.tx_bandwidth = cpu_to_le32(0x00001f40); + cp.rx_bandwidth = cpu_to_le32(0x00001f40); + cp.max_latency = cpu_to_le16(0xffff); + cp.content_format = cpu_to_le16(hdev->voice_setting); + cp.retrans_effort = 0xff; + + hci_send_cmd(hdev, HCI_OP_ACCEPT_SYNC_CONN_REQ, sizeof(cp), + &cp); + } else { + conn->state = BT_CONNECT2; + hci_proto_connect_cfm(conn, 0); } } From 6cebb9e73a88a4ffea586a2bf2873c0901f4e912 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jul 2014 12:59:18 +0300 Subject: [PATCH 239/378] Bluetooth: Enable page scan also if there are white list entries Page scan should be enabled either if the connectable setting is set or if there are any entries in the BR/EDR white list. This patch implements such behavior by updating the two places that were making decisions on whether to enable page scan or not. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 5a866b65371c..98392d61b78f 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -4357,7 +4357,8 @@ static void set_bredr_scan(struct hci_request *req) */ write_fast_connectable(req, false); - if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) + if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags) || + !list_empty(&hdev->whitelist)) scan |= SCAN_PAGE; if (test_bit(HCI_DISCOVERABLE, &hdev->dev_flags)) scan |= SCAN_INQUIRY; @@ -4471,7 +4472,8 @@ static int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) hci_req_init(&req, hdev); - if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) + if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags) || + !list_empty(&hdev->whitelist)) set_bredr_scan(&req); /* Since only the advertising data flags will change, there From a55bd29d522729e0cb125474396acdc2a107d4d9 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jul 2014 12:59:19 +0300 Subject: [PATCH 240/378] Bluetooth: Add white list lookup for incoming connection requests This patch adds support for looking up entries in the white list when HCI_CONNECTABLE is not set. The logic is fairly simple: if we're connectable check the black list, if we're not connectable check the white list. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_event.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 6d1d5b3e9cd4..c8ae9ee3cb12 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -2149,10 +2149,18 @@ static void hci_conn_request_evt(struct hci_dev *hdev, struct sk_buff *skb) return; } - if (!hci_bdaddr_list_lookup(&hdev->blacklist, &ev->bdaddr, - BDADDR_BREDR)) { - hci_reject_conn(hdev, &ev->bdaddr); - return; + if (test_bit(HCI_CONNECTABLE, &hdev->dev_flags)) { + if (hci_bdaddr_list_lookup(&hdev->blacklist, &ev->bdaddr, + BDADDR_BREDR)) { + hci_reject_conn(hdev, &ev->bdaddr); + return; + } + } else { + if (!hci_bdaddr_list_lookup(&hdev->whitelist, &ev->bdaddr, + BDADDR_BREDR)) { + hci_reject_conn(hdev, &ev->bdaddr); + return; + } } /* Connection accepted */ From 91a668b0565dddc9f556f9bce65da58264c74623 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jul 2014 13:28:26 +0300 Subject: [PATCH 241/378] Bluetooth: Fix setting HCI_CONNECTABLE from ioctl code When the white list is in use the code would not update the HCI_CONNECTABLE flag if it gets changed through the ioctl code (e.g. hciconfig hci0 pscan). Since the flag is important for properly accepting incoming connections add code to fix it up if necessary and emit a New Settings mgmt event. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 + net/bluetooth/hci_core.c | 17 +++++++++++++++++ net/bluetooth/mgmt.c | 5 +++++ 3 files changed, 23 insertions(+) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index cba4837dcaa5..e69c2b08c0c6 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1281,6 +1281,7 @@ void hci_sock_dev_event(struct hci_dev *hdev, int event); #define DISCOV_BREDR_INQUIRY_LEN 0x08 int mgmt_control(struct sock *sk, struct msghdr *msg, size_t len); +int mgmt_new_settings(struct hci_dev *hdev); void mgmt_index_added(struct hci_dev *hdev); void mgmt_index_removed(struct hci_dev *hdev); void mgmt_set_powered_failed(struct hci_dev *hdev, int err); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 728a6ee471ea..84431b86af96 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2715,6 +2715,23 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) case HCISETSCAN: err = hci_req_sync(hdev, hci_scan_req, dr.dev_opt, HCI_INIT_TIMEOUT); + + /* Ensure that the connectable state gets correctly + * notified if the whitelist is in use. + */ + if (!err && !list_empty(&hdev->whitelist)) { + bool changed; + + if ((dr.dev_opt & SCAN_PAGE)) + changed = !test_and_set_bit(HCI_CONNECTABLE, + &hdev->dev_flags); + else + changed = test_and_set_bit(HCI_CONNECTABLE, + &hdev->dev_flags); + + if (changed) + mgmt_new_settings(hdev); + } break; case HCISETLINKPOL: diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 98392d61b78f..91b1f92c681e 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1427,6 +1427,11 @@ static int new_settings(struct hci_dev *hdev, struct sock *skip) return mgmt_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, sizeof(ev), skip); } +int mgmt_new_settings(struct hci_dev *hdev) +{ + return new_settings(hdev, NULL); +} + struct cmd_lookup { struct sock *sk; struct hci_dev *hdev; From 6fea7ad1d338dba592d65423427a997f1b7ff485 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 9 Jul 2014 11:53:35 +0200 Subject: [PATCH 242/378] Bluetooth: Don't send ERTM configuration option when disabled When ERTM support is disabled, then do not even send ERTM configuration option even if the remote side supports it. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/l2cap_core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index d006e6c0e3b4..8680aae678ce 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -3234,6 +3234,9 @@ done: switch (chan->mode) { case L2CAP_MODE_BASIC: + if (disable_ertm) + break; + if (!(chan->conn->feat_mask & L2CAP_FEAT_ERTM) && !(chan->conn->feat_mask & L2CAP_FEAT_STREAMING)) break; From 2bcd4003b8fafe200eb31f43d50e305e5dc44058 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jul 2014 19:18:09 +0300 Subject: [PATCH 243/378] Bluetooth: Always confirm incoming SMP just-works requests For incoming requests we want to let the user know that pairing is happening since otherwise there could be access to MEDIUM security services without any user interaction at all. Therefore, set the selected method to JUST_CFM instead of JUST_WORKS and let it be converted back to JUST_WORKS later if we are the initators. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 55c41de2f5a0..a17761c83820 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -391,10 +391,12 @@ static const u8 gen_method[5][5] = { static u8 get_auth_method(struct smp_chan *smp, u8 local_io, u8 remote_io) { - /* If either side has unknown io_caps, use JUST WORKS */ + /* If either side has unknown io_caps, use JUST_CFM (which gets + * converted later to JUST_WORKS if we're initiators. + */ if (local_io > SMP_IO_KEYBOARD_DISPLAY || remote_io > SMP_IO_KEYBOARD_DISPLAY) - return JUST_WORKS; + return JUST_CFM; return gen_method[remote_io][local_io]; } @@ -414,10 +416,14 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, BT_DBG("tk_request: auth:%d lcl:%d rem:%d", auth, local_io, remote_io); - /* If neither side wants MITM, use JUST WORKS */ - /* Otherwise, look up method from the table */ + /* If neither side wants MITM, either "just" confirm an incoming + * request or use just-works for outgoing ones. The JUST_CFM + * will be converted to JUST_WORKS if necessary later in this + * function. If either side has MITM look up the method from the + * table. + */ if (!(auth & SMP_AUTH_MITM)) - method = JUST_WORKS; + method = JUST_CFM; else method = get_auth_method(smp, local_io, remote_io); From e247605a6247aff887d435d2334ffe0e581dae24 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jul 2014 19:18:10 +0300 Subject: [PATCH 244/378] Bluetooth: Fix forcing SMP just-works with no-bonding Whether we bond or not should not have any impact on the user interaction model. This patch removes an incorrect fall-back from JUST_CFM to JUST_WORKS in case we're not bonding. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index a17761c83820..a5e51c686469 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -427,10 +427,6 @@ static int tk_request(struct l2cap_conn *conn, u8 remote_oob, u8 auth, else method = get_auth_method(smp, local_io, remote_io); - /* If not bonding, don't ask user to confirm a Zero TK */ - if (!(auth & SMP_AUTH_BONDING) && method == JUST_CFM) - method = JUST_WORKS; - /* Don't confirm locally initiated pairing attempts */ if (method == JUST_CFM && test_bit(SMP_FLAG_INITIATOR, &smp->flags)) method = JUST_WORKS; From c072d546c20390fea0e5332d00fd1b67366ca013 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Wed, 9 Jul 2014 19:18:11 +0300 Subject: [PATCH 245/378] Bluetooth: Fix incorrect clearing of SMP_FLAG_INITIATOR When the SMP context is created all flags default to zero. To determine that we are the initiators it's therefore best to simply change the flag value when we know we're sending the first SMP PDU. Clearing the flag when receiving a Pairing Request is not correct since the request may be a response to a previous Security Request from us (for which we would already have correctly set the flag). Same goes for receiving a Security Request which may be coming after us already starting pairing by sending a Pairing Request. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index a5e51c686469..627d683203cf 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -735,8 +735,6 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) if (ret) return SMP_UNSPECIFIED; - clear_bit(SMP_FLAG_INITIATOR, &smp->flags); - return 0; } @@ -927,8 +925,6 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) smp_send_cmd(conn, SMP_CMD_PAIRING_REQ, sizeof(cp), &cp); - clear_bit(SMP_FLAG_INITIATOR, &smp->flags); - return 0; } From e8b1202ce6ab67341660812e7d66db4c3e2a5649 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 10 Jul 2014 10:51:27 +0300 Subject: [PATCH 246/378] Bluetooth: Fix advertising parameter update when toggling connectable When we change the connectable state and have advertising enabled we should update the advertising parameters no matter what. The code was incorrectly only updating them if advertising was not already active. This patch fixes the issue. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/mgmt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 91b1f92c681e..38f05386bc0c 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -1882,8 +1882,8 @@ static int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, if (cp->val || test_bit(HCI_FAST_CONNECTABLE, &hdev->dev_flags)) write_fast_connectable(&req, false); - if (test_bit(HCI_ADVERTISING, &hdev->dev_flags) && - !test_bit(HCI_LE_ADV, &hdev->dev_flags)) + /* Update the advertising parameters if necessary */ + if (test_bit(HCI_ADVERTISING, &hdev->dev_flags)) enable_advertising(&req); err = hci_req_run(&req, set_connectable_complete); From b3c6410b8c75cd48e4242af0173bb55701939b9b Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 10 Jul 2014 11:02:07 +0300 Subject: [PATCH 247/378] Bluteooth: Reject SMP bonding if HCI_PAIRABLE is not set If the remote device tries to initiate bonding with us and we don't have HCI_PAIRABLE set we should just flat out reject the request. This brings SMP in line with how the flag is used for BR/EDR SSP. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index 627d683203cf..bf3568c46847 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -676,6 +676,7 @@ int smp_user_confirm_reply(struct hci_conn *hcon, u16 mgmt_op, __le32 passkey) static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) { struct smp_cmd_pairing rsp, *req = (void *) skb->data; + struct hci_dev *hdev = conn->hcon->hdev; struct smp_chan *smp; u8 key_size, auth, sec_level; int ret; @@ -696,6 +697,10 @@ static u8 smp_cmd_pairing_req(struct l2cap_conn *conn, struct sk_buff *skb) if (!smp) return SMP_UNSPECIFIED; + if (!test_bit(HCI_PAIRABLE, &hdev->dev_flags) && + (req->auth_req & SMP_AUTH_BONDING)) + return SMP_PAIRING_NOTSUPP; + smp->preq[0] = SMP_CMD_PAIRING_REQ; memcpy(&smp->preq[1], req, sizeof(*req)); skb_pull(skb, sizeof(*req)); @@ -911,6 +916,10 @@ static u8 smp_cmd_security_req(struct l2cap_conn *conn, struct sk_buff *skb) if (test_and_set_bit(HCI_CONN_LE_SMP_PEND, &hcon->flags)) return 0; + if (!test_bit(HCI_PAIRABLE, &hcon->hdev->dev_flags) && + (rp->auth_req & SMP_AUTH_BONDING)) + return SMP_PAIRING_NOTSUPP; + smp = smp_chan_create(conn); if (!smp) return SMP_UNSPECIFIED; From 7fabc0f4c7bd2206c368a79e3ed79b7d36625cfd Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 10 Jul 2014 11:50:27 +0300 Subject: [PATCH 248/378] Bluetooth: Fix using test_and_clear instead of test_and_set The code for updating the HCI_CONNECTABLE flag was incorrectly using test_and_set_bit instead of test_and_clear_bit when HCI_CONNECTABLE is to be cleared. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 84431b86af96..8f9df768f250 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2726,8 +2726,8 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) changed = !test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags); else - changed = test_and_set_bit(HCI_CONNECTABLE, - &hdev->dev_flags); + changed = test_and_clear_bit(HCI_CONNECTABLE, + &hdev->dev_flags); if (changed) mgmt_new_settings(hdev); From 031547d8688a0fc5da875b504bf11e6c2e18390e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 10 Jul 2014 12:09:06 +0300 Subject: [PATCH 249/378] Bluetooth: Remove unneeded mgmt_connectable function The mgmt_connectable function has been used to ensure that the right actions to HCI_CONNECTABLE are taken when the HCI_Write_Scan_Enable command is triggered by something else than mgmt. The only other user that we really care about is the HCISETSCAN ioctl code, so we can actually more simply perform the needed changes there instead. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 - net/bluetooth/hci_core.c | 6 +++--- net/bluetooth/hci_event.c | 6 +----- net/bluetooth/mgmt.c | 32 -------------------------------- 4 files changed, 4 insertions(+), 41 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index e69c2b08c0c6..76675c55be15 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1288,7 +1288,6 @@ void mgmt_set_powered_failed(struct hci_dev *hdev, int err); int mgmt_powered(struct hci_dev *hdev, u8 powered); void mgmt_discoverable_timeout(struct hci_dev *hdev); void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable); -void mgmt_connectable(struct hci_dev *hdev, u8 connectable); void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status); void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, bool persistent); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 8f9df768f250..3844eeb85453 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2717,9 +2717,9 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) HCI_INIT_TIMEOUT); /* Ensure that the connectable state gets correctly - * notified if the whitelist is in use. + * modified as this was a non-mgmt change. */ - if (!err && !list_empty(&hdev->whitelist)) { + if (!err) { bool changed; if ((dr.dev_opt & SCAN_PAGE)) @@ -2729,7 +2729,7 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) changed = test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags); - if (changed) + if (changed && test_bit(HCI_MGMT, &hdev->dev_flags)) mgmt_new_settings(hdev); } break; diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index c8ae9ee3cb12..38a0e457eaf9 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -330,12 +330,8 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) } else if (old_iscan) mgmt_discoverable(hdev, 0); - if (param & SCAN_PAGE) { + if (param & SCAN_PAGE) set_bit(HCI_PSCAN, &hdev->flags); - if (!old_pscan) - mgmt_connectable(hdev, 1); - } else if (old_pscan) - mgmt_connectable(hdev, 0); done: hci_dev_unlock(hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 38f05386bc0c..9f9f11c8488b 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6068,38 +6068,6 @@ void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable) } } -void mgmt_connectable(struct hci_dev *hdev, u8 connectable) -{ - bool changed; - - /* Nothing needed here if there's a pending command since that - * commands request completion callback takes care of everything - * necessary. - */ - if (mgmt_pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) - return; - - /* Powering off may clear the scan mode - don't let that interfere */ - if (!connectable && mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) - return; - - /* If something else than mgmt changed the page scan state we - * can't differentiate this from a change triggered by adding - * the first element to the whitelist. Therefore, avoid - * incorrectly setting HCI_CONNECTABLE. - */ - if (connectable && !list_empty(&hdev->whitelist)) - return; - - if (connectable) - changed = !test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags); - else - changed = test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags); - - if (changed) - new_settings(hdev, NULL); -} - void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status) { u8 mgmt_err = mgmt_status(status); From 123abc0833181aec4796c628d9ffd608b2b41d5c Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 10 Jul 2014 12:09:07 +0300 Subject: [PATCH 250/378] Bluetooth: Refactor ioctl scan state update to its own function With subsequent patches we'll also need to update the discoverable state. As the code grows bigger it's better to move this out from the switch statement into its own function. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_core.c | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 3844eeb85453..27c40e4901a3 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2655,6 +2655,26 @@ done: return ret; } +static void hci_update_scan_state(struct hci_dev *hdev, u8 scan) +{ + bool conn_changed; + + BT_DBG("%s scan 0x%02x", hdev->name, scan); + + if ((scan & SCAN_PAGE)) + conn_changed = !test_and_set_bit(HCI_CONNECTABLE, + &hdev->dev_flags); + else + conn_changed = test_and_clear_bit(HCI_CONNECTABLE, + &hdev->dev_flags); + + if (!test_bit(HCI_MGMT, &hdev->dev_flags)) + return; + + if (conn_changed) + mgmt_new_settings(hdev); +} + int hci_dev_cmd(unsigned int cmd, void __user *arg) { struct hci_dev *hdev; @@ -2719,19 +2739,8 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) /* Ensure that the connectable state gets correctly * modified as this was a non-mgmt change. */ - if (!err) { - bool changed; - - if ((dr.dev_opt & SCAN_PAGE)) - changed = !test_and_set_bit(HCI_CONNECTABLE, - &hdev->dev_flags); - else - changed = test_and_clear_bit(HCI_CONNECTABLE, - &hdev->dev_flags); - - if (changed && test_bit(HCI_MGMT, &hdev->dev_flags)) - mgmt_new_settings(hdev); - } + if (!err) + hci_update_scan_state(hdev, dr.dev_opt); break; case HCISETLINKPOL: From bc6d2d04182877b198c1a945b7c401decbbb8c02 Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 10 Jul 2014 12:09:08 +0300 Subject: [PATCH 251/378] Bluetooth: Remove unneeded mgmt_discoverable function Since the HCISETSCAN ioctl is the only non-mgmt user we care about for setting the right discoverable state we can simply do the necessary updates in the ioctl handler function instead. This then allows the removal of the mgmt_discoverable function and should simplify that state handling considerably. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 2 +- net/bluetooth/hci_core.c | 24 ++++++++++-- net/bluetooth/hci_event.c | 19 +++------ net/bluetooth/mgmt.c | 67 ++++++++++++-------------------- 4 files changed, 50 insertions(+), 62 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 76675c55be15..b65efb22be54 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1286,8 +1286,8 @@ void mgmt_index_added(struct hci_dev *hdev); void mgmt_index_removed(struct hci_dev *hdev); void mgmt_set_powered_failed(struct hci_dev *hdev, int err); int mgmt_powered(struct hci_dev *hdev, u8 powered); +int mgmt_update_adv_data(struct hci_dev *hdev); void mgmt_discoverable_timeout(struct hci_dev *hdev); -void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable); void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status); void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, bool persistent); diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 27c40e4901a3..3321c65c73ac 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2657,7 +2657,7 @@ done: static void hci_update_scan_state(struct hci_dev *hdev, u8 scan) { - bool conn_changed; + bool conn_changed, discov_changed; BT_DBG("%s scan 0x%02x", hdev->name, scan); @@ -2668,11 +2668,27 @@ static void hci_update_scan_state(struct hci_dev *hdev, u8 scan) conn_changed = test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags); + if ((scan & SCAN_INQUIRY)) { + discov_changed = !test_and_set_bit(HCI_DISCOVERABLE, + &hdev->dev_flags); + } else { + clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); + discov_changed = test_and_clear_bit(HCI_DISCOVERABLE, + &hdev->dev_flags); + } + if (!test_bit(HCI_MGMT, &hdev->dev_flags)) return; - if (conn_changed) + if (conn_changed || discov_changed) { + /* In case this was disabled through mgmt */ + set_bit(HCI_BREDR_ENABLED, &hdev->dev_flags); + + if (test_bit(HCI_LE_ENABLED, &hdev->dev_flags)) + mgmt_update_adv_data(hdev); + mgmt_new_settings(hdev); + } } int hci_dev_cmd(unsigned int cmd, void __user *arg) @@ -2736,8 +2752,8 @@ int hci_dev_cmd(unsigned int cmd, void __user *arg) err = hci_req_sync(hdev, hci_scan_req, dr.dev_opt, HCI_INIT_TIMEOUT); - /* Ensure that the connectable state gets correctly - * modified as this was a non-mgmt change. + /* Ensure that the connectable and discoverable states + * get correctly modified as this was a non-mgmt change. */ if (!err) hci_update_scan_state(hdev, dr.dev_opt); diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 38a0e457eaf9..760bbd88f3c5 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -296,7 +296,6 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) { __u8 status = *((__u8 *) skb->data); __u8 param; - int old_pscan, old_iscan; void *sent; BT_DBG("%s status 0x%2.2x", hdev->name, status); @@ -315,23 +314,15 @@ 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); - - if (param & SCAN_INQUIRY) { + if (param & SCAN_INQUIRY) set_bit(HCI_ISCAN, &hdev->flags); - if (!old_iscan) - mgmt_discoverable(hdev, 1); - } else if (old_iscan) - mgmt_discoverable(hdev, 0); + else + clear_bit(HCI_ISCAN, &hdev->flags); if (param & SCAN_PAGE) set_bit(HCI_PSCAN, &hdev->flags); + else + clear_bit(HCI_ISCAN, &hdev->flags); done: hci_dev_unlock(hdev); diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 9f9f11c8488b..1dad7bffc6af 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -906,6 +906,16 @@ static void update_adv_data(struct hci_request *req) hci_req_add(req, HCI_OP_LE_SET_ADV_DATA, sizeof(cp), &cp); } +int mgmt_update_adv_data(struct hci_dev *hdev) +{ + struct hci_request req; + + hci_req_init(&req, hdev); + update_adv_data(&req); + + return hci_req_run(&req, NULL); +} + static void create_eir(struct hci_dev *hdev, u8 *data) { u8 *ptr = data; @@ -1743,7 +1753,7 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status) { struct pending_cmd *cmd; struct mgmt_mode *cp; - bool changed; + bool conn_changed, discov_changed; BT_DBG("status 0x%02x", status); @@ -1760,15 +1770,23 @@ static void set_connectable_complete(struct hci_dev *hdev, u8 status) } cp = cmd->param; - if (cp->val) - changed = !test_and_set_bit(HCI_CONNECTABLE, &hdev->dev_flags); - else - changed = test_and_clear_bit(HCI_CONNECTABLE, &hdev->dev_flags); + if (cp->val) { + conn_changed = !test_and_set_bit(HCI_CONNECTABLE, + &hdev->dev_flags); + discov_changed = false; + } else { + conn_changed = test_and_clear_bit(HCI_CONNECTABLE, + &hdev->dev_flags); + discov_changed = test_and_clear_bit(HCI_DISCOVERABLE, + &hdev->dev_flags); + } send_settings_rsp(cmd->sk, MGMT_OP_SET_CONNECTABLE, hdev); - if (changed) { + if (conn_changed || discov_changed) { new_settings(hdev, cmd->sk); + if (discov_changed) + mgmt_update_adv_data(hdev); hci_update_background_scan(hdev); } @@ -6031,43 +6049,6 @@ void mgmt_discoverable_timeout(struct hci_dev *hdev) hci_dev_unlock(hdev); } -void mgmt_discoverable(struct hci_dev *hdev, u8 discoverable) -{ - bool changed; - - /* Nothing needed here if there's a pending command since that - * commands request completion callback takes care of everything - * necessary. - */ - if (mgmt_pending_find(MGMT_OP_SET_DISCOVERABLE, hdev)) - return; - - /* Powering off may clear the scan mode - don't let that interfere */ - if (!discoverable && mgmt_pending_find(MGMT_OP_SET_POWERED, hdev)) - return; - - if (discoverable) { - changed = !test_and_set_bit(HCI_DISCOVERABLE, &hdev->dev_flags); - } else { - clear_bit(HCI_LIMITED_DISCOVERABLE, &hdev->dev_flags); - changed = test_and_clear_bit(HCI_DISCOVERABLE, &hdev->dev_flags); - } - - if (changed) { - struct hci_request req; - - /* In case this change in discoverable was triggered by - * a disabling of connectable there could be a need to - * update the advertising flags. - */ - hci_req_init(&req, hdev); - update_adv_data(&req); - hci_req_run(&req, NULL); - - new_settings(hdev, NULL); - } -} - void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status) { u8 mgmt_err = mgmt_status(status); From 13a779e42251184d0f53a8f8299ced614faa028f Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Thu, 10 Jul 2014 12:09:09 +0300 Subject: [PATCH 252/378] Bluetooth: Remove unneeded mgmt_write_scan_failed function The Set Connectable/Discoverable mgmt handlers use a hci_request with a proper callback to handle the HCI command sending. It makes therefore little sense to have this extra function to be called from hci_event.c for command failures. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- include/net/bluetooth/hci_core.h | 1 - net/bluetooth/hci_event.c | 1 - net/bluetooth/mgmt.c | 13 ------------- 3 files changed, 15 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index b65efb22be54..7e9e95633a85 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1288,7 +1288,6 @@ void mgmt_set_powered_failed(struct hci_dev *hdev, int err); int mgmt_powered(struct hci_dev *hdev, u8 powered); int mgmt_update_adv_data(struct hci_dev *hdev); void mgmt_discoverable_timeout(struct hci_dev *hdev); -void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status); void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, bool persistent); void mgmt_device_connected(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index 760bbd88f3c5..a62e918d2641 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -309,7 +309,6 @@ static void hci_cc_write_scan_enable(struct hci_dev *hdev, struct sk_buff *skb) hci_dev_lock(hdev); if (status) { - mgmt_write_scan_failed(hdev, param, status); hdev->discov_timeout = 0; goto done; } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 1dad7bffc6af..7703b72653ff 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -6049,19 +6049,6 @@ void mgmt_discoverable_timeout(struct hci_dev *hdev) hci_dev_unlock(hdev); } -void mgmt_write_scan_failed(struct hci_dev *hdev, u8 scan, u8 status) -{ - u8 mgmt_err = mgmt_status(status); - - if (scan & SCAN_PAGE) - mgmt_pending_foreach(MGMT_OP_SET_CONNECTABLE, hdev, - cmd_status_rsp, &mgmt_err); - - if (scan & SCAN_INQUIRY) - mgmt_pending_foreach(MGMT_OP_SET_DISCOVERABLE, hdev, - cmd_status_rsp, &mgmt_err); -} - void mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, bool persistent) { From 2e84d8db91e45964c2b54ccc7da1f9b4bfb1222b Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 10 Jul 2014 13:17:37 +0200 Subject: [PATCH 253/378] Bluetooth: Mark controller is down when HCI_AUTO_OFF is set During the initial setup phase, the controller is powered on and will be powered off again if it is not used within the auto-off timeout. Userspace using ioctl does not know about the difference between the initial setup phase and a controller being present. It is a bad idea to keep the controller powered by just looking at the device list or device information. Instead just tell userspace that the controller is still down. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 3321c65c73ac..96e0acc3fc92 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2817,14 +2817,20 @@ int hci_get_dev_list(void __user *arg) read_lock(&hci_dev_list_lock); list_for_each_entry(hdev, &hci_dev_list, list) { - if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) - cancel_delayed_work(&hdev->power_off); + unsigned long flags = hdev->flags; + + /* When the auto-off is configured it means the transport + * is running, but in that case still indicate that the + * device is actually down. + */ + if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) + flags &= ~BIT(HCI_UP); if (!test_bit(HCI_MGMT, &hdev->dev_flags)) set_bit(HCI_PAIRABLE, &hdev->dev_flags); (dr + n)->dev_id = hdev->id; - (dr + n)->dev_opt = hdev->flags; + (dr + n)->dev_opt = flags; if (++n >= dev_num) break; @@ -2844,6 +2850,7 @@ int hci_get_dev_info(void __user *arg) { struct hci_dev *hdev; struct hci_dev_info di; + unsigned long flags; int err = 0; if (copy_from_user(&di, arg, sizeof(di))) @@ -2853,8 +2860,14 @@ int hci_get_dev_info(void __user *arg) if (!hdev) return -ENODEV; - if (test_and_clear_bit(HCI_AUTO_OFF, &hdev->dev_flags)) - cancel_delayed_work_sync(&hdev->power_off); + /* When the auto-off is configured it means the transport + * is running, but in that case still indicate that the + * device is actually down. + */ + if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) + flags = hdev->flags & ~BIT(HCI_UP); + else + flags = hdev->flags; if (!test_bit(HCI_MGMT, &hdev->dev_flags)) set_bit(HCI_PAIRABLE, &hdev->dev_flags); @@ -2862,7 +2875,7 @@ int hci_get_dev_info(void __user *arg) strcpy(di.name, hdev->name); di.bdaddr = hdev->bdaddr; di.type = (hdev->bus & 0x0f) | ((hdev->dev_type & 0x03) << 4); - di.flags = hdev->flags; + di.flags = flags; di.pkt_type = hdev->pkt_type; if (lmp_bredr_capable(hdev)) { di.acl_mtu = hdev->acl_mtu; From 12aa4f0a3d0576c554efc770a6e16c8bb897b966 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Thu, 10 Jul 2014 15:25:22 +0200 Subject: [PATCH 254/378] Bluetooth: Set HCI_PAIRABLE during power on for legacy ioctl When the controller is brought up using legacy ioctl, the setting of the HCI_PAIRABLE flag should happen then. Previously it was set during enumeration and when retrieving device information. This change also will not set the HCI_PAIRABLE flag when the controller is used with the HCI User Channel operation. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 96e0acc3fc92..52e8c91ea3e9 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2435,6 +2435,16 @@ int hci_dev_open(__u16 dev) */ flush_workqueue(hdev->req_workqueue); + /* For controllers not using the management interface and that + * are brought up using legacy ioctl, set the HCI_PAIRABLE bit + * so that pairing works for them. Once the management interface + * is in use this bit will be cleared again and userspace has + * to explicitly enable it. + */ + if (!test_bit(HCI_USER_CHANNEL, &hdev->dev_flags) && + !test_bit(HCI_MGMT, &hdev->dev_flags)) + set_bit(HCI_PAIRABLE, &hdev->dev_flags); + err = hci_dev_do_open(hdev); done: @@ -2826,9 +2836,6 @@ int hci_get_dev_list(void __user *arg) if (test_bit(HCI_AUTO_OFF, &hdev->dev_flags)) flags &= ~BIT(HCI_UP); - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) - set_bit(HCI_PAIRABLE, &hdev->dev_flags); - (dr + n)->dev_id = hdev->id; (dr + n)->dev_opt = flags; @@ -2869,9 +2876,6 @@ int hci_get_dev_info(void __user *arg) else flags = hdev->flags; - if (!test_bit(HCI_MGMT, &hdev->dev_flags)) - set_bit(HCI_PAIRABLE, &hdev->dev_flags); - strcpy(di.name, hdev->name); di.bdaddr = hdev->bdaddr; di.type = (hdev->bus & 0x0f) | ((hdev->dev_type & 0x03) << 4); From f49daa8190ebf25eae907048266b590a9cdccb95 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 11 Jul 2014 04:58:29 +0200 Subject: [PATCH 255/378] Bluetooth: Move HCI socket definitions into its own header file All the HCI sockets and ioctl based definitions have been in a global header file that also includes all the HCI protocol structures. To make this a bit cleaner, move them into its own file. This also adjusts fs/compat_ioctl.c to only include this new file and not all the protocol structures that are not needed. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- fs/compat_ioctl.c | 2 +- include/net/bluetooth/hci.h | 149 -------------------------- include/net/bluetooth/hci_core.h | 1 + include/net/bluetooth/hci_sock.h | 175 +++++++++++++++++++++++++++++++ 4 files changed, 177 insertions(+), 150 deletions(-) create mode 100644 include/net/bluetooth/hci_sock.h diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index e82289047272..afec6450450f 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -59,7 +59,7 @@ #include #include -#include +#include #include #include diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index a01236e2df13..7f754a246ca5 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -202,33 +202,6 @@ enum { #define HCI_PERSISTENT_MASK (BIT(HCI_LE_SCAN) | BIT(HCI_PERIODIC_INQ) | \ BIT(HCI_FAST_CONNECTABLE) | BIT(HCI_LE_ADV)) -/* HCI ioctl defines */ -#define HCIDEVUP _IOW('H', 201, int) -#define HCIDEVDOWN _IOW('H', 202, int) -#define HCIDEVRESET _IOW('H', 203, int) -#define HCIDEVRESTAT _IOW('H', 204, int) - -#define HCIGETDEVLIST _IOR('H', 210, int) -#define HCIGETDEVINFO _IOR('H', 211, int) -#define HCIGETCONNLIST _IOR('H', 212, int) -#define HCIGETCONNINFO _IOR('H', 213, int) -#define HCIGETAUTHINFO _IOR('H', 215, int) - -#define HCISETRAW _IOW('H', 220, int) -#define HCISETSCAN _IOW('H', 221, int) -#define HCISETAUTH _IOW('H', 222, int) -#define HCISETENCRYPT _IOW('H', 223, int) -#define HCISETPTYPE _IOW('H', 224, int) -#define HCISETLINKPOL _IOW('H', 225, int) -#define HCISETLINKMODE _IOW('H', 226, int) -#define HCISETACLMTU _IOW('H', 227, int) -#define HCISETSCOMTU _IOW('H', 228, int) - -#define HCIBLOCKADDR _IOW('H', 230, int) -#define HCIUNBLOCKADDR _IOW('H', 231, int) - -#define HCIINQUIRY _IOR('H', 240, int) - /* HCI timeouts */ #define HCI_DISCONN_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ #define HCI_PAIRING_TIMEOUT msecs_to_jiffies(60000) /* 60 seconds */ @@ -1871,126 +1844,4 @@ static inline struct hci_sco_hdr *hci_sco_hdr(const struct sk_buff *skb) #define hci_handle(h) (h & 0x0fff) #define hci_flags(h) (h >> 12) -/* ---- HCI Sockets ---- */ - -/* Socket options */ -#define HCI_DATA_DIR 1 -#define HCI_FILTER 2 -#define HCI_TIME_STAMP 3 - -/* CMSG flags */ -#define HCI_CMSG_DIR 0x0001 -#define HCI_CMSG_TSTAMP 0x0002 - -struct sockaddr_hci { - sa_family_t hci_family; - unsigned short hci_dev; - unsigned short hci_channel; -}; -#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 - -struct hci_filter { - unsigned long type_mask; - unsigned long event_mask[2]; - __le16 opcode; -}; - -struct hci_ufilter { - __u32 type_mask; - __u32 event_mask[2]; - __le16 opcode; -}; - -#define HCI_FLT_TYPE_BITS 31 -#define HCI_FLT_EVENT_BITS 63 -#define HCI_FLT_OGF_BITS 63 -#define HCI_FLT_OCF_BITS 127 - -/* ---- HCI Ioctl requests structures ---- */ -struct hci_dev_stats { - __u32 err_rx; - __u32 err_tx; - __u32 cmd_tx; - __u32 evt_rx; - __u32 acl_tx; - __u32 acl_rx; - __u32 sco_tx; - __u32 sco_rx; - __u32 byte_rx; - __u32 byte_tx; -}; - -struct hci_dev_info { - __u16 dev_id; - char name[8]; - - bdaddr_t bdaddr; - - __u32 flags; - __u8 type; - - __u8 features[8]; - - __u32 pkt_type; - __u32 link_policy; - __u32 link_mode; - - __u16 acl_mtu; - __u16 acl_pkts; - __u16 sco_mtu; - __u16 sco_pkts; - - struct hci_dev_stats stat; -}; - -struct hci_conn_info { - __u16 handle; - bdaddr_t bdaddr; - __u8 type; - __u8 out; - __u16 state; - __u32 link_mode; -}; - -struct hci_dev_req { - __u16 dev_id; - __u32 dev_opt; -}; - -struct hci_dev_list_req { - __u16 dev_num; - struct hci_dev_req dev_req[0]; /* hci_dev_req structures */ -}; - -struct hci_conn_list_req { - __u16 dev_id; - __u16 conn_num; - struct hci_conn_info conn_info[0]; -}; - -struct hci_conn_info_req { - bdaddr_t bdaddr; - __u8 type; - struct hci_conn_info conn_info[0]; -}; - -struct hci_auth_info_req { - bdaddr_t bdaddr; - __u8 type; -}; - -struct hci_inquiry_req { - __u16 dev_id; - __u16 flags; - __u8 lap[3]; - __u8 length; - __u8 num_rsp; -}; -#define IREQ_CACHE_FLUSH 0x0001 - #endif /* __HCI_H */ diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 7e9e95633a85..5c52a17d6326 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -26,6 +26,7 @@ #define __HCI_CORE_H #include +#include /* HCI priority */ #define HCI_PRIO_MAX 7 diff --git a/include/net/bluetooth/hci_sock.h b/include/net/bluetooth/hci_sock.h new file mode 100644 index 000000000000..9a46d665c1b5 --- /dev/null +++ b/include/net/bluetooth/hci_sock.h @@ -0,0 +1,175 @@ +/* + BlueZ - Bluetooth protocol stack for Linux + Copyright (C) 2000-2001 Qualcomm Incorporated + + Written 2000,2001 by Maxim Krasnyansky + + 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. +*/ + +#ifndef __HCI_SOCK_H +#define __HCI_SOCK_H + +/* Socket options */ +#define HCI_DATA_DIR 1 +#define HCI_FILTER 2 +#define HCI_TIME_STAMP 3 + +/* CMSG flags */ +#define HCI_CMSG_DIR 0x0001 +#define HCI_CMSG_TSTAMP 0x0002 + +struct sockaddr_hci { + sa_family_t hci_family; + unsigned short hci_dev; + unsigned short hci_channel; +}; +#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 + +struct hci_filter { + unsigned long type_mask; + unsigned long event_mask[2]; + __le16 opcode; +}; + +struct hci_ufilter { + __u32 type_mask; + __u32 event_mask[2]; + __le16 opcode; +}; + +#define HCI_FLT_TYPE_BITS 31 +#define HCI_FLT_EVENT_BITS 63 +#define HCI_FLT_OGF_BITS 63 +#define HCI_FLT_OCF_BITS 127 + +/* Ioctl defines */ +#define HCIDEVUP _IOW('H', 201, int) +#define HCIDEVDOWN _IOW('H', 202, int) +#define HCIDEVRESET _IOW('H', 203, int) +#define HCIDEVRESTAT _IOW('H', 204, int) + +#define HCIGETDEVLIST _IOR('H', 210, int) +#define HCIGETDEVINFO _IOR('H', 211, int) +#define HCIGETCONNLIST _IOR('H', 212, int) +#define HCIGETCONNINFO _IOR('H', 213, int) +#define HCIGETAUTHINFO _IOR('H', 215, int) + +#define HCISETRAW _IOW('H', 220, int) +#define HCISETSCAN _IOW('H', 221, int) +#define HCISETAUTH _IOW('H', 222, int) +#define HCISETENCRYPT _IOW('H', 223, int) +#define HCISETPTYPE _IOW('H', 224, int) +#define HCISETLINKPOL _IOW('H', 225, int) +#define HCISETLINKMODE _IOW('H', 226, int) +#define HCISETACLMTU _IOW('H', 227, int) +#define HCISETSCOMTU _IOW('H', 228, int) + +#define HCIBLOCKADDR _IOW('H', 230, int) +#define HCIUNBLOCKADDR _IOW('H', 231, int) + +#define HCIINQUIRY _IOR('H', 240, int) + +/* Ioctl requests structures */ +struct hci_dev_stats { + __u32 err_rx; + __u32 err_tx; + __u32 cmd_tx; + __u32 evt_rx; + __u32 acl_tx; + __u32 acl_rx; + __u32 sco_tx; + __u32 sco_rx; + __u32 byte_rx; + __u32 byte_tx; +}; + +struct hci_dev_info { + __u16 dev_id; + char name[8]; + + bdaddr_t bdaddr; + + __u32 flags; + __u8 type; + + __u8 features[8]; + + __u32 pkt_type; + __u32 link_policy; + __u32 link_mode; + + __u16 acl_mtu; + __u16 acl_pkts; + __u16 sco_mtu; + __u16 sco_pkts; + + struct hci_dev_stats stat; +}; + +struct hci_conn_info { + __u16 handle; + bdaddr_t bdaddr; + __u8 type; + __u8 out; + __u16 state; + __u32 link_mode; +}; + +struct hci_dev_req { + __u16 dev_id; + __u32 dev_opt; +}; + +struct hci_dev_list_req { + __u16 dev_num; + struct hci_dev_req dev_req[0]; /* hci_dev_req structures */ +}; + +struct hci_conn_list_req { + __u16 dev_id; + __u16 conn_num; + struct hci_conn_info conn_info[0]; +}; + +struct hci_conn_info_req { + bdaddr_t bdaddr; + __u8 type; + struct hci_conn_info conn_info[0]; +}; + +struct hci_auth_info_req { + bdaddr_t bdaddr; + __u8 type; +}; + +struct hci_inquiry_req { + __u16 dev_id; + __u16 flags; + __u8 lap[3]; + __u8 length; + __u8 num_rsp; +}; +#define IREQ_CACHE_FLUSH 0x0001 + +#endif /* __HCI_SOCK_H */ From 3ad254f7f6fd3b66ce47a156cbb92cb02002103e Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 11 Jul 2014 05:36:39 +0200 Subject: [PATCH 256/378] Bluetooth: Move struct hci_sec_filter next to its user There is only single location using struct hci_sec_filter and with that there is no point in putting this declaration into a global header file. So move it right next to its user and make the code a lot more simpler. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 9 --------- net/bluetooth/hci_sock.c | 8 ++++++++ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index 5c52a17d6326..e1930749fac7 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1360,15 +1360,6 @@ struct hci_pinfo { unsigned short channel; }; -/* HCI security filter */ -#define HCI_SFLT_MAX_OGF 5 - -struct hci_sec_filter { - __u32 type_mask; - __u32 event_mask[2]; - __u32 ocf_mask[HCI_SFLT_MAX_OGF + 1][4]; -}; - /* ----- HCI requests ----- */ #define HCI_REQ_DONE 0 #define HCI_REQ_PEND 1 diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index c64728d571ae..7805fd1b4a78 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -41,6 +41,14 @@ static inline int hci_test_bit(int nr, void *addr) } /* Security filter */ +#define HCI_SFLT_MAX_OGF 5 + +struct hci_sec_filter { + __u32 type_mask; + __u32 event_mask[2]; + __u32 ocf_mask[HCI_SFLT_MAX_OGF + 1][4]; +}; + static struct hci_sec_filter hci_sec_filter = { /* Packet types */ 0x10, From 7e67c112a007fafef6dd36c33d1029a65250ca9b Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 11 Jul 2014 05:36:40 +0200 Subject: [PATCH 257/378] Bluetooth: Delcare the hci_sec_filter as const The hci_sec_filter socket filter details do not change. They are fixed and with that they can also be delcared as const. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_sock.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 7805fd1b4a78..0753fa4970c0 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -49,7 +49,7 @@ struct hci_sec_filter { __u32 ocf_mask[HCI_SFLT_MAX_OGF + 1][4]; }; -static struct hci_sec_filter hci_sec_filter = { +static const struct hci_sec_filter hci_sec_filter = { /* Packet types */ 0x10, /* Events */ From 863def58fec2fa494c8e9ca45471819c6d731ec3 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 11 Jul 2014 05:41:00 +0200 Subject: [PATCH 258/378] Bluetooth: Move struct hci_pinfo into net/bluetooth/hci_sock.c There exists no external user of struct hci_pinfo and hci_pi and thus move it into the one place that is actually using it. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 11 ----------- net/bluetooth/hci_sock.c | 11 +++++++++++ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index e1930749fac7..fffd0da2e0fc 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1349,17 +1349,6 @@ void mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr, void mgmt_reenable_advertising(struct hci_dev *hdev); void mgmt_smp_complete(struct hci_conn *conn, bool complete); -/* HCI info for socket */ -#define hci_pi(sk) ((struct hci_pinfo *) sk) - -struct hci_pinfo { - struct bt_sock bt; - struct hci_dev *hdev; - struct hci_filter filter; - __u32 cmsg_mask; - unsigned short channel; -}; - /* ----- HCI requests ----- */ #define HCI_REQ_DONE 0 #define HCI_REQ_PEND 1 diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 0753fa4970c0..115f149362ba 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -35,6 +35,17 @@ static atomic_t monitor_promisc = ATOMIC_INIT(0); /* ----- HCI socket interface ----- */ +/* Socket info */ +#define hci_pi(sk) ((struct hci_pinfo *) sk) + +struct hci_pinfo { + struct bt_sock bt; + struct hci_dev *hdev; + struct hci_filter filter; + __u32 cmsg_mask; + unsigned short channel; +}; + static inline int hci_test_bit(int nr, void *addr) { return *((__u32 *) addr + (nr >> 5)) & ((__u32) 1 << (nr & 31)); From 899de765667b63bb51526f0a31693aed6ad5f828 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 11 Jul 2014 05:51:58 +0200 Subject: [PATCH 259/378] Bluetooth: Move HCI request internals to net/bluetooth/hci_core.c The internals of the HCI request framework should not be leaking to its users. Move them all into net/bluetooth/hci_core.c and provide a simple hci_req_pending helper function for the one user outside the framework. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci_core.h | 9 +-------- net/bluetooth/hci_core.c | 14 ++++++++++++++ net/bluetooth/hci_event.c | 2 +- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h index fffd0da2e0fc..b52c2ef3f56d 100644 --- a/include/net/bluetooth/hci_core.h +++ b/include/net/bluetooth/hci_core.h @@ -1240,6 +1240,7 @@ void hci_req_add(struct hci_request *req, u16 opcode, u32 plen, void hci_req_add_ev(struct hci_request *req, u16 opcode, u32 plen, const void *param, u8 event); void hci_req_cmd_complete(struct hci_dev *hdev, u16 opcode, u8 status); +bool hci_req_pending(struct hci_dev *hdev); void hci_req_add_le_scan_disable(struct hci_request *req); void hci_req_add_le_passive_scan(struct hci_request *req); @@ -1349,14 +1350,6 @@ void mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr, void mgmt_reenable_advertising(struct hci_dev *hdev); void mgmt_smp_complete(struct hci_conn *conn, bool complete); -/* ----- HCI requests ----- */ -#define HCI_REQ_DONE 0 -#define HCI_REQ_PEND 1 -#define HCI_REQ_CANCELED 2 - -#define hci_req_lock(d) mutex_lock(&d->req_lock) -#define hci_req_unlock(d) mutex_unlock(&d->req_lock) - u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency, u16 to_multiplier); void hci_le_start_enc(struct hci_conn *conn, __le16 ediv, __le64 rand, diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 52e8c91ea3e9..347f84fb66f9 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -54,6 +54,15 @@ DEFINE_RWLOCK(hci_cb_list_lock); /* HCI ID Numbering */ static DEFINE_IDA(hci_index_ida); +/* ----- HCI requests ----- */ + +#define HCI_REQ_DONE 0 +#define HCI_REQ_PEND 1 +#define HCI_REQ_CANCELED 2 + +#define hci_req_lock(d) mutex_lock(&d->req_lock) +#define hci_req_unlock(d) mutex_unlock(&d->req_lock) + /* ---- HCI notifications ---- */ static void hci_notify(struct hci_dev *hdev, int event) @@ -4432,6 +4441,11 @@ int hci_req_run(struct hci_request *req, hci_req_complete_t complete) return 0; } +bool hci_req_pending(struct hci_dev *hdev) +{ + return (hdev->req_status == HCI_REQ_PEND); +} + static struct sk_buff *hci_prepare_cmd(struct hci_dev *hdev, u16 opcode, u32 plen, const void *param) { diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index a62e918d2641..f0f220057f21 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -4624,7 +4624,7 @@ void hci_event_packet(struct hci_dev *hdev, struct sk_buff *skb) /* Received events are (currently) only needed when a request is * ongoing so avoid unnecessary memory allocation. */ - if (hdev->req_status == HCI_REQ_PEND) { + if (hci_req_pending(hdev)) { kfree_skb(hdev->recv_evt); hdev->recv_evt = skb_clone(skb, GFP_KERNEL); } From a6801ca9856c24298cba2c1fafd3da818acdaab6 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 11 Jul 2014 06:03:08 +0200 Subject: [PATCH 260/378] Bluetooth: Update the list of L2CAP fixed channels The list of L2CAP fixed channels increased with newer versions of the specification. This just updates the constants for it. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/l2cap.h | 5 ++++- net/bluetooth/l2cap_core.c | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index e0c6a9abdb62..d8e7b9330ab8 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -134,9 +134,12 @@ struct l2cap_conninfo { #define L2CAP_FCS_CRC16 0x01 /* L2CAP fixed channels */ -#define L2CAP_FC_L2CAP 0x02 +#define L2CAP_FC_SIG_BREDR 0x02 #define L2CAP_FC_CONNLESS 0x04 #define L2CAP_FC_A2MP 0x08 +#define L2CAP_FC_ATT 0x10 +#define L2CAP_FC_SIG_LE 0x20 +#define L2CAP_FC_SMP_LE 0x40 /* L2CAP Control Field bit masks */ #define L2CAP_CTRL_SAR 0xC000 diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 8680aae678ce..0c3c4935f635 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -46,7 +46,7 @@ bool disable_ertm; static u32 l2cap_feat_mask = L2CAP_FEAT_FIXED_CHAN | L2CAP_FEAT_UCD; -static u8 l2cap_fixed_chan[8] = { L2CAP_FC_L2CAP | L2CAP_FC_CONNLESS, }; +static u8 l2cap_fixed_chan[8] = { L2CAP_FC_SIG_BREDR | L2CAP_FC_CONNLESS, }; static LIST_HEAD(chan_list); static DEFINE_RWLOCK(chan_list_lock); From 2a0dccb3df2c8775d1a6e962aa88c4c22693c887 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 11 Jul 2014 06:19:41 +0200 Subject: [PATCH 261/378] Bluetooth: Move struct sco_pinfo into net/bluetooth/sco.c There exists no external user of struct sco_pinfo and sco_pi and thus move it into the one place that is actually using it. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/sco.h | 12 ------------ net/bluetooth/sco.c | 12 ++++++++++++ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/net/bluetooth/sco.h b/include/net/bluetooth/sco.h index 2019d1a0996a..7fec7cbd7396 100644 --- a/include/net/bluetooth/sco.h +++ b/include/net/bluetooth/sco.h @@ -64,16 +64,4 @@ struct sco_conn { #define sco_conn_lock(c) spin_lock(&c->lock); #define sco_conn_unlock(c) spin_unlock(&c->lock); -/* ----- SCO socket info ----- */ -#define sco_pi(sk) ((struct sco_pinfo *) sk) - -struct sco_pinfo { - struct bt_sock bt; - bdaddr_t src; - bdaddr_t dst; - __u32 flags; - __u16 setting; - struct sco_conn *conn; -}; - #endif /* __SCO_H */ diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index c06dbd3938e8..c71698642ec6 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -46,6 +46,18 @@ static void sco_chan_del(struct sock *sk, int err); static void sco_sock_close(struct sock *sk); static void sco_sock_kill(struct sock *sk); +/* ----- SCO socket info ----- */ +#define sco_pi(sk) ((struct sco_pinfo *) sk) + +struct sco_pinfo { + struct bt_sock bt; + bdaddr_t src; + bdaddr_t dst; + __u32 flags; + __u16 setting; + struct sco_conn *conn; +}; + /* ---- SCO timers ---- */ static void sco_sock_timeout(unsigned long arg) { From fc8f525a6f7c81743bec6b5d497988313b211383 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 11 Jul 2014 06:19:42 +0200 Subject: [PATCH 262/378] Bluetooth: Move struct sco_conn into net/bluetooth/sco.c There exists no external user of struct sco_conn and thus move it into the one place that is actually using it. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/sco.h | 13 ------------- net/bluetooth/sco.c | 13 +++++++++++++ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/include/net/bluetooth/sco.h b/include/net/bluetooth/sco.h index 7fec7cbd7396..42a0d089d715 100644 --- a/include/net/bluetooth/sco.h +++ b/include/net/bluetooth/sco.h @@ -51,17 +51,4 @@ struct sco_conninfo { __u8 dev_class[3]; }; -/* ---- SCO connections ---- */ -struct sco_conn { - struct hci_conn *hcon; - - spinlock_t lock; - struct sock *sk; - - unsigned int mtu; -}; - -#define sco_conn_lock(c) spin_lock(&c->lock); -#define sco_conn_unlock(c) spin_unlock(&c->lock); - #endif /* __SCO_H */ diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index c71698642ec6..9868ec7c7dc4 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -40,6 +40,19 @@ static struct bt_sock_list sco_sk_list = { .lock = __RW_LOCK_UNLOCKED(sco_sk_list.lock) }; +/* ---- SCO connections ---- */ +struct sco_conn { + struct hci_conn *hcon; + + spinlock_t lock; + struct sock *sk; + + unsigned int mtu; +}; + +#define sco_conn_lock(c) spin_lock(&c->lock); +#define sco_conn_unlock(c) spin_unlock(&c->lock); + static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent); static void sco_chan_del(struct sock *sk, int err); From 6190ae7a184bf488aeb051a5713aa57b07d4c59b Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 11 Jul 2014 06:19:43 +0200 Subject: [PATCH 263/378] Bluetooth: Remove unused SCO_DEFAULT_FLUSH_TO constant The SCO_DEFAULT_FLUSH_TO constant has been defined, but it is not used anywhere and so just remove it. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/sco.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/net/bluetooth/sco.h b/include/net/bluetooth/sco.h index 42a0d089d715..ab8b1ed550d2 100644 --- a/include/net/bluetooth/sco.h +++ b/include/net/bluetooth/sco.h @@ -27,7 +27,6 @@ /* SCO defaults */ #define SCO_DEFAULT_MTU 500 -#define SCO_DEFAULT_FLUSH_TO 0xFFFF #define SCO_CONN_TIMEOUT (HZ * 40) #define SCO_DISCONN_TIMEOUT (HZ * 2) From 068d69e5bbd85291aa8ad0d81062f047609a173c Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 11 Jul 2014 06:19:44 +0200 Subject: [PATCH 264/378] Bluetooth: Move SCO timeout constants into net/bluetooth/sco.c There is no external user of the SCO timeout constants and thus move them into net/bluetooth/sco.c where they are actuallu used. In addition just remove SCO_CONN_IDLE_TIMEOUT since it is unused. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/sco.h | 4 ---- net/bluetooth/sco.c | 3 +++ 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/include/net/bluetooth/sco.h b/include/net/bluetooth/sco.h index ab8b1ed550d2..f40ddb4264fc 100644 --- a/include/net/bluetooth/sco.h +++ b/include/net/bluetooth/sco.h @@ -28,10 +28,6 @@ /* SCO defaults */ #define SCO_DEFAULT_MTU 500 -#define SCO_CONN_TIMEOUT (HZ * 40) -#define SCO_DISCONN_TIMEOUT (HZ * 2) -#define SCO_CONN_IDLE_TIMEOUT (HZ * 60) - /* SCO socket address */ struct sockaddr_sco { sa_family_t sco_family; diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 9868ec7c7dc4..e6c7b636b901 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -72,6 +72,9 @@ struct sco_pinfo { }; /* ---- SCO timers ---- */ +#define SCO_CONN_TIMEOUT (HZ * 40) +#define SCO_DISCONN_TIMEOUT (HZ * 2) + static void sco_sock_timeout(unsigned long arg) { struct sock *sk = (struct sock *) arg; From bb72bd68fd87a4347b2a891ab16aac6014e69a00 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 11 Jul 2014 07:12:57 +0200 Subject: [PATCH 265/378] Bluetooth: Check for valid HCI UART driver flags Providing unknown or invalid flags to the HCI UART driver should result in an error. So check which flags are valid and otherwise return an error. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/hci_ldisc.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index e00f8f5b5c8e..a49ee1b42439 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -477,6 +477,21 @@ static int hci_uart_set_proto(struct hci_uart *hu, int id) return 0; } +static int hci_uart_set_flags(struct hci_uart *hu, unsigned long flags) +{ + unsigned long valid_flags = BIT(HCI_UART_RAW_DEVICE) | + BIT(HCI_UART_RESET_ON_INIT) | + BIT(HCI_UART_CREATE_AMP) | + BIT(HCI_UART_INIT_PENDING); + + if ((flags & ~valid_flags)) + return -EINVAL; + + hu->hdev_flags = flags; + + return 0; +} + /* hci_uart_tty_ioctl() * * Process IOCTL system call for the tty device. @@ -527,7 +542,9 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file, case HCIUARTSETFLAGS: if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) return -EBUSY; - hu->hdev_flags = arg; + err = hci_uart_set_flags(hu, arg); + if (err) + return err; break; case HCIUARTGETFLAGS: From 6afd04ad6b6608fe2d9abce120bd8c0bc6aba287 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Fri, 11 Jul 2014 07:12:58 +0200 Subject: [PATCH 266/378] Bluetooth: Add support for external configuration with UART driver The quirk for enabling external configuration with UART needs to be provided via the HCI UART flags. Add a new flag for it and declare it as valid. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/hci_ldisc.c | 6 +++++- drivers/bluetooth/hci_uart.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index a49ee1b42439..401a3be57cda 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -431,6 +431,9 @@ static int hci_uart_register_dev(struct hci_uart *hu) if (test_bit(HCI_UART_RAW_DEVICE, &hu->hdev_flags)) set_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks); + if (test_bit(HCI_UART_EXT_CONFIG, &hu->hdev_flags)) + set_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks); + if (!test_bit(HCI_UART_RESET_ON_INIT, &hu->hdev_flags)) set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks); @@ -482,7 +485,8 @@ static int hci_uart_set_flags(struct hci_uart *hu, unsigned long flags) unsigned long valid_flags = BIT(HCI_UART_RAW_DEVICE) | BIT(HCI_UART_RESET_ON_INIT) | BIT(HCI_UART_CREATE_AMP) | - BIT(HCI_UART_INIT_PENDING); + BIT(HCI_UART_INIT_PENDING) | + BIT(HCI_UART_EXT_CONFIG); if ((flags & ~valid_flags)) return -EINVAL; diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h index 12df101ca942..247488edcbf9 100644 --- a/drivers/bluetooth/hci_uart.h +++ b/drivers/bluetooth/hci_uart.h @@ -48,6 +48,7 @@ #define HCI_UART_RESET_ON_INIT 1 #define HCI_UART_CREATE_AMP 2 #define HCI_UART_INIT_PENDING 3 +#define HCI_UART_EXT_CONFIG 4 struct hci_uart; From 6c53823ae0e10e723131055e1e65dd6a328a228e Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Fri, 11 Jul 2014 15:32:23 +0300 Subject: [PATCH 267/378] Bluetooth: Fix tracking local SSP authentication requirement When we need to make the decision whether to perform just-works or real user confirmation we need to know the exact local authentication requirement that was passed to the controller. So far conn->auth_type (the local requirement) wasn't in one case updated appropriately in fear of the user confirmation being rejected later. The real problem however was not really that conn->auth_type couldn't represent the true value but that we were checking the local MITM requirement in an incorrect way. It's perfectly fine to let auth_type follow what we tell the controller since we're still tracking the target security level with conn->pending_sec_level. This patch updates the check for local MITM requirement in the hci_user_confirm_request_evt function to use the locally requested security level and ensures that auth_type always represents what we tell the controller. All other code in hci_user_confirm_request_evt still uses the auth_type instead of pending_sec_level for determining whether to do just-works or not, since that's the only value that's in sync with what the remote device knows. Signed-off-by: Johan Hedberg Tested-by: Szymon Janc Signed-off-by: Marcel Holtmann Cc: stable@vger.kernel.org # 3.16 --- net/bluetooth/hci_event.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c index f0f220057f21..8980bd24b8c0 100644 --- a/net/bluetooth/hci_event.c +++ b/net/bluetooth/hci_event.c @@ -3664,18 +3664,14 @@ static void hci_io_capa_request_evt(struct hci_dev *hdev, struct sk_buff *skb) /* If we are initiators, there is no remote information yet */ if (conn->remote_auth == 0xff) { - cp.authentication = conn->auth_type; - /* Request MITM protection if our IO caps allow it * except for the no-bonding case. - * conn->auth_type is not updated here since - * that might cause the user confirmation to be - * rejected in case the remote doesn't have the - * IO capabilities for MITM. */ if (conn->io_capability != HCI_IO_NO_INPUT_OUTPUT && cp.authentication != HCI_AT_NO_BONDING) - cp.authentication |= 0x01; + conn->auth_type |= 0x01; + + cp.authentication = conn->auth_type; } else { conn->auth_type = hci_get_auth_req(conn); cp.authentication = conn->auth_type; @@ -3747,9 +3743,12 @@ static void hci_user_confirm_request_evt(struct hci_dev *hdev, rem_mitm = (conn->remote_auth & 0x01); /* If we require MITM but the remote device can't provide that - * (it has NoInputNoOutput) then reject the confirmation request + * (it has NoInputNoOutput) then reject the confirmation + * request. We check the security level here since it doesn't + * necessarily match conn->auth_type. */ - if (loc_mitm && conn->remote_cap == HCI_IO_NO_INPUT_OUTPUT) { + if (conn->pending_sec_level > BT_SECURITY_MEDIUM && + conn->remote_cap == HCI_IO_NO_INPUT_OUTPUT) { BT_DBG("Rejecting request: remote device can't provide MITM"); hci_send_cmd(hdev, HCI_OP_USER_CONFIRM_NEG_REPLY, sizeof(ev->bdaddr), &ev->bdaddr); From 2c6bed7cfcd3f594ed9e4d6919fa2ebea2243d19 Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Fri, 11 Jul 2014 10:24:18 +0200 Subject: [PATCH 268/378] 6lowpan: introduce new net/6lowpan directory This patch moves generic code which is used by bluetooth and ieee802154 6lowpan to a new net/6lowpan directory. This directory contains generic 6LoWPAN code which is shared between bluetooth and ieee802154 MAC-Layer. This is the IPHC - "IPv6 Header Compression" format at the moment. Which is described by RFC 6282 [0]. The BLTE 6LoWPAN draft describes that the IPHC is the same format like IEEE 802.15.4, see [1]. Futuremore we can put more code into this directory which is shared between BLTE and IEEE 802.15.4 6LoWPAN like RFC 6775 or the routing protocol RPL RFC 6550. To avoid naming conflicts I renamed 6lowpan-y to ieee802154_6lowpan-y in net/ieee802154/Makefile. [0] http://tools.ietf.org/html/rfc6282 [1] http://tools.ietf.org/html/draft-ietf-6lowpan-btle-12#section-3.2 [2] http://tools.ietf.org/html/rfc6775 [3] http://tools.ietf.org/html/rfc6550 Signed-off-by: Alexander Aring Acked-by: Jukka Rissanen Signed-off-by: Marcel Holtmann --- net/6lowpan/Kconfig | 6 ++++++ net/6lowpan/Makefile | 3 +++ net/{ieee802154/6lowpan_iphc.c => 6lowpan/iphc.c} | 0 net/Kconfig | 1 + net/Makefile | 3 ++- net/bluetooth/Kconfig | 3 +-- net/ieee802154/Kconfig | 9 +-------- net/ieee802154/Makefile | 5 ++--- 8 files changed, 16 insertions(+), 14 deletions(-) create mode 100644 net/6lowpan/Kconfig create mode 100644 net/6lowpan/Makefile rename net/{ieee802154/6lowpan_iphc.c => 6lowpan/iphc.c} (100%) diff --git a/net/6lowpan/Kconfig b/net/6lowpan/Kconfig new file mode 100644 index 000000000000..028a5c6d1f61 --- /dev/null +++ b/net/6lowpan/Kconfig @@ -0,0 +1,6 @@ +config 6LOWPAN + bool "6LoWPAN Support" + depends on IPV6 + ---help--- + This enables IPv6 over Low power Wireless Personal Area Network - + "6LoWPAN" which is supported by IEEE 802.15.4 or Bluetooth stacks. diff --git a/net/6lowpan/Makefile b/net/6lowpan/Makefile new file mode 100644 index 000000000000..415886bb456a --- /dev/null +++ b/net/6lowpan/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_6LOWPAN) := 6lowpan.o + +6lowpan-y := iphc.o diff --git a/net/ieee802154/6lowpan_iphc.c b/net/6lowpan/iphc.c similarity index 100% rename from net/ieee802154/6lowpan_iphc.c rename to net/6lowpan/iphc.c diff --git a/net/Kconfig b/net/Kconfig index d92afe4204d9..4051fdfa4367 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -214,6 +214,7 @@ source "drivers/net/appletalk/Kconfig" source "net/x25/Kconfig" source "net/lapb/Kconfig" source "net/phonet/Kconfig" +source "net/6lowpan/Kconfig" source "net/ieee802154/Kconfig" source "net/mac802154/Kconfig" source "net/sched/Kconfig" diff --git a/net/Makefile b/net/Makefile index cbbbe6d657ca..7ed1970074b0 100644 --- a/net/Makefile +++ b/net/Makefile @@ -57,7 +57,8 @@ obj-$(CONFIG_CAIF) += caif/ ifneq ($(CONFIG_DCB),) obj-y += dcb/ endif -obj-y += ieee802154/ +obj-$(CONFIG_6LOWPAN) += 6lowpan/ +obj-$(CONFIG_IEEE802154) += ieee802154/ obj-$(CONFIG_MAC802154) += mac802154/ ifeq ($(CONFIG_NET),y) diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index f5afaa22f6ec..600fb29288f4 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -41,8 +41,7 @@ menuconfig BT config BT_6LOWPAN tristate "Bluetooth 6LoWPAN support" - depends on BT && IPV6 - select 6LOWPAN_IPHC if BT_6LOWPAN + depends on BT && 6LOWPAN help IPv6 compression over Bluetooth Low Energy. diff --git a/net/ieee802154/Kconfig b/net/ieee802154/Kconfig index 8af1330b3137..c0d4154d144f 100644 --- a/net/ieee802154/Kconfig +++ b/net/ieee802154/Kconfig @@ -12,13 +12,6 @@ config IEEE802154 config IEEE802154_6LOWPAN tristate "6lowpan support over IEEE 802.15.4" - depends on IEEE802154 && IPV6 - select 6LOWPAN_IPHC + depends on IEEE802154 && 6LOWPAN ---help--- IPv6 compression over IEEE 802.15.4. - -config 6LOWPAN_IPHC - tristate - ---help--- - 6lowpan compression code which is shared between IEEE 802.15.4 and Bluetooth - stacks. diff --git a/net/ieee802154/Makefile b/net/ieee802154/Makefile index bf1b51497a41..3914b1ed4274 100644 --- a/net/ieee802154/Makefile +++ b/net/ieee802154/Makefile @@ -1,8 +1,7 @@ obj-$(CONFIG_IEEE802154) += ieee802154.o af_802154.o -obj-$(CONFIG_IEEE802154_6LOWPAN) += 6lowpan.o -obj-$(CONFIG_6LOWPAN_IPHC) += 6lowpan_iphc.o +obj-$(CONFIG_IEEE802154_6LOWPAN) += ieee802154_6lowpan.o -6lowpan-y := 6lowpan_rtnl.o reassembly.o +ieee802154_6lowpan-y := 6lowpan_rtnl.o reassembly.o ieee802154-y := netlink.o nl-mac.o nl-phy.o nl_policy.o wpan-class.o \ header_ops.o af_802154-y := af_ieee802154.o raw.o dgram.o From 68d96dcfc6c09b565d57897c127b61afbab74c6f Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Fri, 11 Jul 2014 10:24:19 +0200 Subject: [PATCH 269/378] MAINTAINERS: add net/6lowpan/ maintainer entry This patch add a maintainer entry for "net/6lowpan". Also add the current IEEE 802.15.4 mailing list and bluetooth mailinglist to this branch, because this code is shared between them. Signed-off-by: Alexander Aring Acked-by: Jukka Rissanen Signed-off-by: Marcel Holtmann --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 54ba0e643c39..0057ff580536 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -148,6 +148,13 @@ L: linux-scsi@vger.kernel.org S: Maintained F: drivers/scsi/53c700* +6LOWPAN GENERIC (BTLE/IEEE 802.15.4) +M: Alexander Aring +L: linux-zigbee-devel@lists.sourceforge.net (moderated for non-subscribers) +L: linux-bluetooth@vger.kernel.org +S: Maintained +F: net/6lowpan/ + 6PACK NETWORK DRIVER FOR AX.25 M: Andreas Koensgen L: linux-hams@vger.kernel.org From c7e0c14115db67063a5f68fd9d4a12a54e649dc7 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 12 Jul 2014 17:00:29 +0200 Subject: [PATCH 270/378] Bluetooth: Fix HCIUARTGETDEVICE ioctl when UART is not registered The protocol for the UART might be configured, but that does not mean the HCI device is registered. Return an error in that case and only return the index number when HCI_UART_REGISTERED is set. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- drivers/bluetooth/hci_ldisc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c index 401a3be57cda..dc487b5d1156 100644 --- a/drivers/bluetooth/hci_ldisc.c +++ b/drivers/bluetooth/hci_ldisc.c @@ -539,7 +539,7 @@ static int hci_uart_tty_ioctl(struct tty_struct *tty, struct file * file, return -EUNATCH; case HCIUARTGETDEVICE: - if (test_bit(HCI_UART_PROTO_SET, &hu->flags)) + if (test_bit(HCI_UART_REGISTERED, &hu->flags)) return hu->hdev->id; return -EUNATCH; From 15a49cca98c3380271ced8db2ea69ee95db5c709 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 12 Jul 2014 23:20:50 +0200 Subject: [PATCH 271/378] Bluetooth: Read LE Advertising Channel TX Power only when available The Read LE Advertising Channel TX Power command is not mandatory for a Bluetooth HCI controller only supporting receiption. Move the command to the third stage of the controller initialization and only execute it when support for it has been indicated. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 347f84fb66f9..b29a984f7dd4 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1348,9 +1348,6 @@ static void le_setup(struct hci_request *req) /* Read LE Supported States */ hci_req_add(req, HCI_OP_LE_READ_SUPPORTED_STATES, 0, NULL); - /* Read LE Advertising Channel TX Power */ - hci_req_add(req, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL); - /* Read LE White List Size */ hci_req_add(req, HCI_OP_LE_READ_WHITE_LIST_SIZE, 0, NULL); @@ -1657,6 +1654,11 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt) hci_req_add(req, HCI_OP_LE_SET_EVENT_MASK, sizeof(events), events); + if (hdev->commands[25] & 0x40) { + /* Read LE Advertising Channel TX Power */ + hci_req_add(req, HCI_OP_LE_READ_ADV_TX_POWER, 0, NULL); + } + hci_set_le_support(req); } From 0da71f1bf90f259debf50aaaa547e2cc31b5cf67 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sat, 12 Jul 2014 23:36:16 +0200 Subject: [PATCH 272/378] Bluetooth: Enable LE encryption events only when supported The support for LE encryption is optional. When encryption is not supported then also do not enable the encryption related events. This moves the event mask setting to the third initialization stage to ensure that the LE features are available. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/hci.h | 1 + net/bluetooth/hci_core.c | 11 +++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h index 7f754a246ca5..2fee852816ee 100644 --- a/include/net/bluetooth/hci.h +++ b/include/net/bluetooth/hci.h @@ -329,6 +329,7 @@ enum { #define LMP_HOST_SC 0x08 /* LE features */ +#define HCI_LE_ENCRYPTION 0x01 #define HCI_LE_CONN_PARAM_REQ_PROC 0x02 #define HCI_LE_PING 0x10 diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index b29a984f7dd4..ff150e3c7ad1 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1422,14 +1422,17 @@ static void hci_setup_event_mask(struct hci_request *req) /* Use a different default for LE-only devices */ memset(events, 0, sizeof(events)); events[0] |= 0x10; /* Disconnection Complete */ - events[0] |= 0x80; /* Encryption Change */ events[1] |= 0x08; /* Read Remote Version Information Complete */ events[1] |= 0x20; /* Command Complete */ events[1] |= 0x40; /* Command Status */ events[1] |= 0x80; /* Hardware Error */ events[2] |= 0x04; /* Number of Completed Packets */ events[3] |= 0x02; /* Data Buffer Overflow */ - events[5] |= 0x80; /* Encryption Key Refresh Complete */ + + if (hdev->le_features[0] & HCI_LE_ENCRYPTION) { + events[0] |= 0x80; /* Encryption Change */ + events[5] |= 0x80; /* Encryption Key Refresh Complete */ + } } if (lmp_inq_rssi_capable(hdev)) @@ -1482,8 +1485,6 @@ static void hci_init2_req(struct hci_request *req, unsigned long opt) if (lmp_le_capable(hdev)) le_setup(req); - hci_setup_event_mask(req); - /* AVM Berlin (31), aka "BlueFRITZ!", doesn't support the read * local supported commands HCI command. */ @@ -1611,6 +1612,8 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt) struct hci_dev *hdev = req->hdev; u8 p; + hci_setup_event_mask(req); + /* Some Broadcom based Bluetooth controllers do not support the * Delete Stored Link Key command. They are clearly indicating its * absence in the bit mask of supported commands. From 4d6c705bbd9e845bcfbe119bb017a5653c0d9efb Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 13 Jul 2014 00:29:22 +0200 Subject: [PATCH 273/378] Bluetooth: Enable LE Long Term Key Request event only when supported The support for LE encryption is optional and with that also the LE Long Term Key Request event. If encryption is not supported, then do not bother enabling this event. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index ff150e3c7ad1..188bfd3d7c43 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -1644,7 +1644,10 @@ static void hci_init3_req(struct hci_request *req, unsigned long opt) u8 events[8]; memset(events, 0, sizeof(events)); - events[0] = 0x1f; + events[0] = 0x0f; + + if (hdev->le_features[0] & HCI_LE_ENCRYPTION) + events[0] |= 0x10; /* LE Long Term Key Request */ /* If controller supports the Connection Parameters Request * Link Layer Procedure, enable the corresponding event. From 395365eaf125e53b9d7e1c11c91ee01bf5a4b792 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 13 Jul 2014 17:22:25 +0200 Subject: [PATCH 274/378] Bluetooth: Allocate struct inquiry_entry with GFP_KERNEL The allocation of inquiry cache entries is triggered as a result of processing HCI events. Since the processing is done in the context of a workqueue, there is no needed to allocate with GFP_ATOMIC in that case. Switch it to GFP_KERNEL. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/hci_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c index 188bfd3d7c43..172041e2b15a 100644 --- a/net/bluetooth/hci_core.c +++ b/net/bluetooth/hci_core.c @@ -2088,7 +2088,7 @@ u32 hci_inquiry_cache_update(struct hci_dev *hdev, struct inquiry_data *data, } /* Entry not in the cache. Add new one. */ - ie = kzalloc(sizeof(struct inquiry_entry), GFP_ATOMIC); + ie = kzalloc(sizeof(struct inquiry_entry), GFP_KERNEL); if (!ie) { flags |= MGMT_DEV_FOUND_CONFIRM_NAME; goto done; From 015b01cbca20276a6b09ad25ce4002e811a53130 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 13 Jul 2014 19:54:48 +0200 Subject: [PATCH 275/378] Bluetooth: Remove unneeded forward declaration of __sco_chan_add The forward declaration of __sco_chan_add is not needed and thus just remove it. Move __sco_chan_add into the proper location. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/sco.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index e6c7b636b901..75cffc14ab6e 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -53,7 +53,6 @@ struct sco_conn { #define sco_conn_lock(c) spin_lock(&c->lock); #define sco_conn_unlock(c) spin_unlock(&c->lock); -static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent); static void sco_chan_del(struct sock *sk, int err); static void sco_sock_close(struct sock *sk); @@ -164,6 +163,17 @@ static int sco_conn_del(struct hci_conn *hcon, int err) return 0; } +static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent) +{ + BT_DBG("conn %p", conn); + + sco_pi(sk)->conn = conn; + conn->sk = sk; + + if (parent) + bt_accept_enqueue(parent, sk); +} + static int sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent) { @@ -968,17 +978,6 @@ static int sco_sock_release(struct socket *sock) return err; } -static void __sco_chan_add(struct sco_conn *conn, struct sock *sk, struct sock *parent) -{ - BT_DBG("conn %p", conn); - - sco_pi(sk)->conn = conn; - conn->sk = sk; - - if (parent) - bt_accept_enqueue(parent, sk); -} - /* Delete channel. * Must be called on the locked socket. */ static void sco_chan_del(struct sock *sk, int err) From e03ab5199df597d3f1737ef638a7f692daaaef9f Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 13 Jul 2014 19:54:49 +0200 Subject: [PATCH 276/378] Bluetooth: Remove unneeded forward declaration of sco_chan_del The forward declaration of sco_chan_del is not needed and thus just remove it. Move sco_chan_del into the proper location. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/sco.c | 56 ++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 75cffc14ab6e..4d67b0354d23 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -53,8 +53,6 @@ struct sco_conn { #define sco_conn_lock(c) spin_lock(&c->lock); #define sco_conn_unlock(c) spin_unlock(&c->lock); -static void sco_chan_del(struct sock *sk, int err); - static void sco_sock_close(struct sock *sk); static void sco_sock_kill(struct sock *sk); @@ -138,6 +136,33 @@ static struct sock *sco_chan_get(struct sco_conn *conn) return sk; } +/* Delete channel. + * Must be called on the locked socket. */ +static void sco_chan_del(struct sock *sk, int err) +{ + struct sco_conn *conn; + + conn = sco_pi(sk)->conn; + + BT_DBG("sk %p, conn %p, err %d", sk, conn, err); + + if (conn) { + sco_conn_lock(conn); + conn->sk = NULL; + sco_pi(sk)->conn = NULL; + sco_conn_unlock(conn); + + if (conn->hcon) + hci_conn_drop(conn->hcon); + } + + sk->sk_state = BT_CLOSED; + sk->sk_err = err; + sk->sk_state_change(sk); + + sock_set_flag(sk, SOCK_ZAPPED); +} + static int sco_conn_del(struct hci_conn *hcon, int err) { struct sco_conn *conn = hcon->sco_data; @@ -978,33 +1003,6 @@ static int sco_sock_release(struct socket *sock) return err; } -/* Delete channel. - * Must be called on the locked socket. */ -static void sco_chan_del(struct sock *sk, int err) -{ - struct sco_conn *conn; - - conn = sco_pi(sk)->conn; - - BT_DBG("sk %p, conn %p, err %d", sk, conn, err); - - if (conn) { - sco_conn_lock(conn); - conn->sk = NULL; - sco_pi(sk)->conn = NULL; - sco_conn_unlock(conn); - - if (conn->hcon) - hci_conn_drop(conn->hcon); - } - - sk->sk_state = BT_CLOSED; - sk->sk_err = err; - sk->sk_state_change(sk); - - sock_set_flag(sk, SOCK_ZAPPED); -} - static void sco_conn_ready(struct sco_conn *conn) { struct sock *parent; From 5a54e7c85b8303b84a9b2fbf978a74d625f85a99 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Sun, 13 Jul 2014 20:50:15 +0200 Subject: [PATCH 277/378] Bluetooth: Convert L2CAP ident spinlock into a mutex The spinlock protecting the L2CAP ident number can be converted into a mutex since the whole processing is run in a workqueue. So instead of using a spinlock, just use a mutex here. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- include/net/bluetooth/l2cap.h | 3 +-- net/bluetooth/l2cap_core.c | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h index d8e7b9330ab8..1fffd92808b0 100644 --- a/include/net/bluetooth/l2cap.h +++ b/include/net/bluetooth/l2cap.h @@ -625,11 +625,10 @@ struct l2cap_conn { struct delayed_work info_timer; - spinlock_t lock; - struct sk_buff *rx_skb; __u32 rx_len; __u8 tx_ident; + struct mutex ident_lock; struct sk_buff_head pending_rx; struct work_struct pending_rx_work; diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c index 0c3c4935f635..8538cb07b0c0 100644 --- a/net/bluetooth/l2cap_core.c +++ b/net/bluetooth/l2cap_core.c @@ -798,14 +798,14 @@ static u8 l2cap_get_ident(struct l2cap_conn *conn) * 200 - 254 are used by utilities like l2ping, etc. */ - spin_lock(&conn->lock); + mutex_lock(&conn->ident_lock); if (++conn->tx_ident > 128) conn->tx_ident = 1; id = conn->tx_ident; - spin_unlock(&conn->lock); + mutex_unlock(&conn->ident_lock); return id; } @@ -7016,7 +7016,7 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon) conn->hs_enabled = test_bit(HCI_HS_ENABLED, &hcon->hdev->dev_flags); - spin_lock_init(&conn->lock); + mutex_init(&conn->ident_lock); mutex_init(&conn->chan_lock); INIT_LIST_HEAD(&conn->chan_l); From eb5a4de80f266d0bb7edd43c61894da74faaa91a Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 14 Jul 2014 01:30:15 +0200 Subject: [PATCH 278/378] Bluetooth: Remove sco_chan_get helper function The sco_chan_get helper function is only used in two places and really only protects conn->sk with a lock. So instead of hiding that fact, just put the actual code in place where it is used. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/sco.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c index 4d67b0354d23..ebf7ee6a446c 100644 --- a/net/bluetooth/sco.c +++ b/net/bluetooth/sco.c @@ -127,15 +127,6 @@ static struct sco_conn *sco_conn_add(struct hci_conn *hcon) return conn; } -static struct sock *sco_chan_get(struct sco_conn *conn) -{ - struct sock *sk = NULL; - sco_conn_lock(conn); - sk = conn->sk; - sco_conn_unlock(conn); - return sk; -} - /* Delete channel. * Must be called on the locked socket. */ static void sco_chan_del(struct sock *sk, int err) @@ -174,7 +165,10 @@ static int sco_conn_del(struct hci_conn *hcon, int err) BT_DBG("hcon %p conn %p, err %d", hcon, conn, err); /* Kill socket */ - sk = sco_chan_get(conn); + sco_conn_lock(conn); + sk = conn->sk; + sco_conn_unlock(conn); + if (sk) { bh_lock_sock(sk); sco_sock_clear_timer(sk); @@ -303,7 +297,11 @@ static int sco_send_frame(struct sock *sk, struct msghdr *msg, int len) static void sco_recv_frame(struct sco_conn *conn, struct sk_buff *skb) { - struct sock *sk = sco_chan_get(conn); + struct sock *sk; + + sco_conn_lock(conn); + sk = conn->sk; + sco_conn_unlock(conn); if (!sk) goto drop; From b2d5e254ebfe9e4a926265c23bc5943f212fd1ac Mon Sep 17 00:00:00 2001 From: Johan Hedberg Date: Mon, 14 Jul 2014 14:34:55 +0300 Subject: [PATCH 279/378] Bluetooth: Fix trying LTK re-encryption when we don't have an LTK In the case that the key distribution bits cause us not to generate a local LTK we should not try to re-encrypt if we're currently encrypted with an STK. This patch fixes the check for this in the smp_sufficient_security function. Signed-off-by: Johan Hedberg Signed-off-by: Marcel Holtmann --- net/bluetooth/smp.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c index bf3568c46847..8339d6b0f2b8 100644 --- a/net/bluetooth/smp.c +++ b/net/bluetooth/smp.c @@ -876,9 +876,12 @@ bool smp_sufficient_security(struct hci_conn *hcon, u8 sec_level) /* If we're encrypted with an STK always claim insufficient * security. This way we allow the connection to be re-encrypted * with an LTK, even if the LTK provides the same level of - * security. + * security. Only exception is if we don't have an LTK (e.g. + * because of key distribution bits). */ - if (test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags)) + if (test_bit(HCI_CONN_STK_ENCRYPT, &hcon->flags) && + hci_find_ltk_by_addr(hcon->hdev, &hcon->dst, hcon->dst_type, + hcon->out)) return false; if (hcon->sec_level >= sec_level) From d385623a78145889692074c170ecac7232e547ab Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Mon, 2 Jun 2014 21:19:46 +0300 Subject: [PATCH 280/378] ath10k: add implementation for configure max amsdu, ampdu Allow to setup maximum subframes for AMSDU and AMPDU aggregation via debugfs htt_max_amsdu_ampdu file. Eg. echo "2 64" > htt_max_amsdu_ampdu will setup maximum amsdu subframes equal 2 and maximum ampdu subframes equal to 64. Signed-off-by: Janusz Dziedzic Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.h | 3 + drivers/net/wireless/ath/ath10k/debug.c | 73 ++++++++++++++++++++++++ drivers/net/wireless/ath/ath10k/htt.h | 13 ++--- drivers/net/wireless/ath/ath10k/htt_tx.c | 46 +++++++++++++++ 4 files changed, 127 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index 68ceef61933d..83a5fa91531d 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -290,6 +290,9 @@ struct ath10k_debug { struct ath_dfs_pool_stats dfs_pool_stats; u32 fw_dbglog_mask; + + u8 htt_max_amsdu; + u8 htt_max_ampdu; }; enum ath10k_state { diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 1b7ff4ba122c..3030158c478e 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -671,6 +671,72 @@ static const struct file_operations fops_htt_stats_mask = { .llseek = default_llseek, }; +static ssize_t ath10k_read_htt_max_amsdu_ampdu(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + char buf[64]; + u8 amsdu = 3, ampdu = 64; + unsigned int len; + + mutex_lock(&ar->conf_mutex); + + if (ar->debug.htt_max_amsdu) + amsdu = ar->debug.htt_max_amsdu; + + if (ar->debug.htt_max_ampdu) + ampdu = ar->debug.htt_max_ampdu; + + mutex_unlock(&ar->conf_mutex); + + len = scnprintf(buf, sizeof(buf), "%u %u\n", amsdu, ampdu); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static ssize_t ath10k_write_htt_max_amsdu_ampdu(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath10k *ar = file->private_data; + int res; + char buf[64]; + unsigned int amsdu, ampdu; + + simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); + + /* make sure that buf is null terminated */ + buf[sizeof(buf) - 1] = 0; + + res = sscanf(buf, "%u %u", &amsdu, &du); + + if (res != 2) + return -EINVAL; + + mutex_lock(&ar->conf_mutex); + + res = ath10k_htt_h2t_aggr_cfg_msg(&ar->htt, ampdu, amsdu); + if (res) + goto out; + + res = count; + ar->debug.htt_max_amsdu = amsdu; + ar->debug.htt_max_ampdu = ampdu; + +out: + mutex_unlock(&ar->conf_mutex); + return res; +} + +static const struct file_operations fops_htt_max_amsdu_ampdu = { + .read = ath10k_read_htt_max_amsdu_ampdu, + .write = ath10k_write_htt_max_amsdu_ampdu, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + static ssize_t ath10k_read_fw_dbglog(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) @@ -757,6 +823,9 @@ void ath10k_debug_stop(struct ath10k *ar) * warning from del_timer(). */ if (ar->debug.htt_stats_mask != 0) cancel_delayed_work(&ar->debug.htt_stats_dwork); + + ar->debug.htt_max_amsdu = 0; + ar->debug.htt_max_ampdu = 0; } static ssize_t ath10k_write_simulate_radar(struct file *file, @@ -867,6 +936,10 @@ int ath10k_debug_create(struct ath10k *ar) debugfs_create_file("htt_stats_mask", S_IRUSR, ar->debug.debugfs_phy, ar, &fops_htt_stats_mask); + debugfs_create_file("htt_max_amsdu_ampdu", S_IRUSR | S_IWUSR, + ar->debug.debugfs_phy, ar, + &fops_htt_max_amsdu_ampdu); + debugfs_create_file("fw_dbglog", S_IRUSR, ar->debug.debugfs_phy, ar, &fops_fw_dbglog); diff --git a/drivers/net/wireless/ath/ath10k/htt.h b/drivers/net/wireless/ath/ath10k/htt.h index 9a263462c793..6c93f3885ee5 100644 --- a/drivers/net/wireless/ath/ath10k/htt.h +++ b/drivers/net/wireless/ath/ath10k/htt.h @@ -240,16 +240,10 @@ struct htt_oob_sync_req { __le16 rsvd0; } __packed; -#define HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_MASK 0x1F -#define HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_LSB 0 - struct htt_aggr_conf { u8 max_num_ampdu_subframes; - union { - /* dont use bitfields; undefined behaviour */ - u8 flags; /* see %HTT_AGGR_CONF_MAX_NUM_AMSDU_SUBFRAMES_ */ - u8 max_num_amsdu_subframes:5; - } __packed; + /* amsdu_subframes is limited by 0x1F mask */ + u8 max_num_amsdu_subframes; } __packed; #define HTT_MGMT_FRM_HDR_DOWNLOAD_LEN 32 @@ -1343,6 +1337,9 @@ void ath10k_htt_t2h_msg_handler(struct ath10k *ar, struct sk_buff *skb); int ath10k_htt_h2t_ver_req_msg(struct ath10k_htt *htt); int ath10k_htt_h2t_stats_req(struct ath10k_htt *htt, u8 mask, u64 cookie); int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt); +int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt, + u8 max_subfrms_ampdu, + u8 max_subfrms_amsdu); void __ath10k_htt_tx_dec_pending(struct ath10k_htt *htt); int ath10k_htt_tx_alloc_msdu_id(struct ath10k_htt *htt); diff --git a/drivers/net/wireless/ath/ath10k/htt_tx.c b/drivers/net/wireless/ath/ath10k/htt_tx.c index 7064354d1f4f..accb6b4f6faf 100644 --- a/drivers/net/wireless/ath/ath10k/htt_tx.c +++ b/drivers/net/wireless/ath/ath10k/htt_tx.c @@ -307,6 +307,52 @@ int ath10k_htt_send_rx_ring_cfg_ll(struct ath10k_htt *htt) return 0; } +int ath10k_htt_h2t_aggr_cfg_msg(struct ath10k_htt *htt, + u8 max_subfrms_ampdu, + u8 max_subfrms_amsdu) +{ + struct htt_aggr_conf *aggr_conf; + struct sk_buff *skb; + struct htt_cmd *cmd; + int len; + int ret; + + /* Firmware defaults are: amsdu = 3 and ampdu = 64 */ + + if (max_subfrms_ampdu == 0 || max_subfrms_ampdu > 64) + return -EINVAL; + + if (max_subfrms_amsdu == 0 || max_subfrms_amsdu > 31) + return -EINVAL; + + len = sizeof(cmd->hdr); + len += sizeof(cmd->aggr_conf); + + skb = ath10k_htc_alloc_skb(len); + if (!skb) + return -ENOMEM; + + skb_put(skb, len); + cmd = (struct htt_cmd *)skb->data; + cmd->hdr.msg_type = HTT_H2T_MSG_TYPE_AGGR_CFG; + + aggr_conf = &cmd->aggr_conf; + aggr_conf->max_num_ampdu_subframes = max_subfrms_ampdu; + aggr_conf->max_num_amsdu_subframes = max_subfrms_amsdu; + + ath10k_dbg(ATH10K_DBG_HTT, "htt h2t aggr cfg msg amsdu %d ampdu %d", + aggr_conf->max_num_amsdu_subframes, + aggr_conf->max_num_ampdu_subframes); + + ret = ath10k_htc_send(&htt->ar->htc, htt->eid, skb); + if (ret) { + dev_kfree_skb_any(skb); + return ret; + } + + return 0; +} + int ath10k_htt_mgmt_tx(struct ath10k_htt *htt, struct sk_buff *msdu) { struct device *dev = htt->ar->dev; From 1c3d95edf026c6fb446f53913c61ff1036c469cd Mon Sep 17 00:00:00 2001 From: Frederic Danis Date: Mon, 2 Jun 2014 21:19:46 +0300 Subject: [PATCH 281/378] ath6kl: Fix ath6kl_bmi_read_hi32 macro tmp may be used uninitialized if ath6kl_bmi_read() returns an error. Signed-off-by: Frederic Danis Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/bmi.h | 3 ++- drivers/net/wireless/ath/ath6kl/init.c | 12 ++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/bmi.h b/drivers/net/wireless/ath/ath6kl/bmi.h index 18fdd69e1f71..397a52f2628b 100644 --- a/drivers/net/wireless/ath/ath6kl/bmi.h +++ b/drivers/net/wireless/ath/ath6kl/bmi.h @@ -242,7 +242,8 @@ struct ath6kl_bmi_target_info { (void) (check_type == val); \ addr = ath6kl_get_hi_item_addr(ar, HI_ITEM(item)); \ ret = ath6kl_bmi_read(ar, addr, (u8 *) &tmp, 4); \ - *val = le32_to_cpu(tmp); \ + if (!ret) \ + *val = le32_to_cpu(tmp); \ ret; \ }) diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index d5ef211f261c..6e1e6995a634 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1161,11 +1161,19 @@ static int ath6kl_upload_board_file(struct ath6kl *ar) ath6kl_bmi_write_hi32(ar, hi_board_data, board_address); } else { - ath6kl_bmi_read_hi32(ar, hi_board_data, &board_address); + ret = ath6kl_bmi_read_hi32(ar, hi_board_data, &board_address); + if (ret) { + ath6kl_err("Failed to get board file target address.\n"); + return ret; + } } /* determine where in target ram to write extended board data */ - ath6kl_bmi_read_hi32(ar, hi_board_ext_data, &board_ext_address); + ret = ath6kl_bmi_read_hi32(ar, hi_board_ext_data, &board_ext_address); + if (ret) { + ath6kl_err("Failed to get extended board file target address.\n"); + return ret; + } if (ar->target_type == TARGET_TYPE_AR6003 && board_ext_address == 0) { From eba95bceb4c9f537c6c8a5aeba4277e76599e269 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 17 Jun 2014 12:40:52 +0300 Subject: [PATCH 282/378] ath6kl: convert ar6004 hardware flags to firmware feature flags The functionality defined through these flags were actually firmware features which can change between firmware versions. To make it possible to support different firmware versions with the same driver, convert the flags to firmware feature flags. For backwards compatibility support for old ar6004 firmware FW API 3 or smaller images we forcefully set the feature bits in the driver. Starting from FW API 5 the firmware image needs to set them. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/cfg80211.c | 6 ++++-- drivers/net/wireless/ath/ath6kl/core.c | 16 ++++++++++++++++ drivers/net/wireless/ath/ath6kl/core.h | 12 +++++++++--- drivers/net/wireless/ath/ath6kl/init.c | 16 +++++++--------- drivers/net/wireless/ath/ath6kl/usb.c | 6 ++++-- drivers/net/wireless/ath/ath6kl/wmi.c | 3 ++- 6 files changed, 42 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 0e26f4a34fda..d139f2a7160a 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -2899,7 +2899,8 @@ static int ath6kl_start_ap(struct wiphy *wiphy, struct net_device *dev, if (info->inactivity_timeout) { inactivity_timeout = info->inactivity_timeout; - if (ar->hw.flags & ATH6KL_HW_AP_INACTIVITY_MINS) + if (test_bit(ATH6KL_FW_CAPABILITY_AP_INACTIVITY_MINS, + ar->fw_capabilities)) inactivity_timeout = DIV_ROUND_UP(inactivity_timeout, 60); @@ -3782,7 +3783,8 @@ int ath6kl_cfg80211_init(struct ath6kl *ar) ath6kl_band_5ghz.ht_cap.ht_supported = false; } - if (ar->hw.flags & ATH6KL_HW_64BIT_RATES) { + if (test_bit(ATH6KL_FW_CAPABILITY_64BIT_RATES, + ar->fw_capabilities)) { ath6kl_band_2ghz.ht_cap.mcs.rx_mask[0] = 0xff; ath6kl_band_5ghz.ht_cap.mcs.rx_mask[0] = 0xff; ath6kl_band_2ghz.ht_cap.mcs.rx_mask[1] = 0xff; diff --git a/drivers/net/wireless/ath/ath6kl/core.c b/drivers/net/wireless/ath/ath6kl/core.c index b0b652042760..0df74b245af4 100644 --- a/drivers/net/wireless/ath/ath6kl/core.c +++ b/drivers/net/wireless/ath/ath6kl/core.c @@ -123,6 +123,22 @@ int ath6kl_core_init(struct ath6kl *ar, enum ath6kl_htc_type htc_type) /* FIXME: we should free all firmwares in the error cases below */ + /* + * Backwards compatibility support for older ar6004 firmware images + * which do not set these feature flags. + */ + if (ar->target_type == TARGET_TYPE_AR6004 && + ar->fw_api <= 4) { + __set_bit(ATH6KL_FW_CAPABILITY_64BIT_RATES, + ar->fw_capabilities); + __set_bit(ATH6KL_FW_CAPABILITY_AP_INACTIVITY_MINS, + ar->fw_capabilities); + + if (ar->hw.id == AR6004_HW_1_3_VERSION) + __set_bit(ATH6KL_FW_CAPABILITY_MAP_LP_ENDPOINT, + ar->fw_capabilities); + } + /* Indicate that WMI is enabled (although not ready yet) */ set_bit(WMI_ENABLED, &ar->flag); ar->wmi = ath6kl_wmi_init(ar); diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 26b0f92424e1..bd91c4465580 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -136,6 +136,15 @@ enum ath6kl_fw_capability { */ ATH6KL_FW_CAPABILITY_HEART_BEAT_POLL, + /* WMI_SET_TX_SELECT_RATES_CMDID uses 64 bit size rate table */ + ATH6KL_FW_CAPABILITY_64BIT_RATES, + + /* WMI_AP_CONN_INACT_CMDID uses minutes as units */ + ATH6KL_FW_CAPABILITY_AP_INACTIVITY_MINS, + + /* use low priority endpoint for all data */ + ATH6KL_FW_CAPABILITY_MAP_LP_ENDPOINT, + /* this needs to be last */ ATH6KL_FW_CAPABILITY_MAX, }; @@ -149,9 +158,6 @@ struct ath6kl_fw_ie { }; enum ath6kl_hw_flags { - ATH6KL_HW_64BIT_RATES = BIT(0), - ATH6KL_HW_AP_INACTIVITY_MINS = BIT(1), - ATH6KL_HW_MAP_LP_ENDPOINT = BIT(2), ATH6KL_HW_SDIO_CRC_ERROR_WAR = BIT(3), }; diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 6e1e6995a634..ed086ead2601 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -93,8 +93,7 @@ static const struct ath6kl_hw hw_list[] = { .board_addr = 0x433900, .refclk_hz = 26000000, .uarttx_pin = 11, - .flags = ATH6KL_HW_64BIT_RATES | - ATH6KL_HW_AP_INACTIVITY_MINS, + .flags = 0, .fw = { .dir = AR6004_HW_1_0_FW_DIR, @@ -114,8 +113,7 @@ static const struct ath6kl_hw hw_list[] = { .board_addr = 0x43d400, .refclk_hz = 40000000, .uarttx_pin = 11, - .flags = ATH6KL_HW_64BIT_RATES | - ATH6KL_HW_AP_INACTIVITY_MINS, + .flags = 0, .fw = { .dir = AR6004_HW_1_1_FW_DIR, .fw = AR6004_HW_1_1_FIRMWARE_FILE, @@ -134,8 +132,7 @@ static const struct ath6kl_hw hw_list[] = { .board_addr = 0x435c00, .refclk_hz = 40000000, .uarttx_pin = 11, - .flags = ATH6KL_HW_64BIT_RATES | - ATH6KL_HW_AP_INACTIVITY_MINS, + .flags = 0, .fw = { .dir = AR6004_HW_1_2_FW_DIR, @@ -154,9 +151,7 @@ static const struct ath6kl_hw hw_list[] = { .board_addr = 0x436400, .refclk_hz = 40000000, .uarttx_pin = 11, - .flags = ATH6KL_HW_64BIT_RATES | - ATH6KL_HW_AP_INACTIVITY_MINS | - ATH6KL_HW_MAP_LP_ENDPOINT, + .flags = 0, .fw = { .dir = AR6004_HW_1_3_FW_DIR, @@ -1575,6 +1570,9 @@ static const struct fw_capa_str_map { { ATH6KL_FW_CAPABILITY_REGDOMAIN, "regdomain" }, { ATH6KL_FW_CAPABILITY_SCHED_SCAN_V2, "sched-scan-v2" }, { ATH6KL_FW_CAPABILITY_HEART_BEAT_POLL, "hb-poll" }, + { ATH6KL_FW_CAPABILITY_64BIT_RATES, "64bit-rates" }, + { ATH6KL_FW_CAPABILITY_AP_INACTIVITY_MINS, "ap-inactivity-mins" }, + { ATH6KL_FW_CAPABILITY_MAP_LP_ENDPOINT, "map-lp-endpoint" }, }; static const char *ath6kl_init_get_fw_capa_name(unsigned int id) diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c index 3afc5a463d06..e5a9e7fe2cea 100644 --- a/drivers/net/wireless/ath/ath6kl/usb.c +++ b/drivers/net/wireless/ath/ath6kl/usb.c @@ -802,7 +802,8 @@ static int ath6kl_usb_map_service_pipe(struct ath6kl *ar, u16 svc_id, break; case WMI_DATA_VI_SVC: - if (ar->hw.flags & ATH6KL_HW_MAP_LP_ENDPOINT) + if (test_bit(ATH6KL_FW_CAPABILITY_MAP_LP_ENDPOINT, + ar->fw_capabilities)) *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_LP; else *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_MP; @@ -814,7 +815,8 @@ static int ath6kl_usb_map_service_pipe(struct ath6kl *ar, u16 svc_id, break; case WMI_DATA_VO_SVC: - if (ar->hw.flags & ATH6KL_HW_MAP_LP_ENDPOINT) + if (test_bit(ATH6KL_FW_CAPABILITY_MAP_LP_ENDPOINT, + ar->fw_capabilities)) *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_LP; else *ul_pipe = ATH6KL_USB_PIPE_TX_DATA_MP; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 4d7f9e4712e9..6ecc0a419c1a 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -2838,7 +2838,8 @@ int ath6kl_wmi_set_bitrate_mask(struct wmi *wmi, u8 if_idx, { struct ath6kl *ar = wmi->parent_dev; - if (ar->hw.flags & ATH6KL_HW_64BIT_RATES) + if (test_bit(ATH6KL_FW_CAPABILITY_64BIT_RATES, + ar->fw_capabilities)) return ath6kl_set_bitrate_mask64(wmi, if_idx, mask); else return ath6kl_set_bitrate_mask32(wmi, if_idx, mask); From b056397e98a9d7fb8fed9f5c837461f3dd8b0132 Mon Sep 17 00:00:00 2001 From: Jessica Wu Date: Tue, 17 Jun 2014 12:40:58 +0300 Subject: [PATCH 283/378] ath6kl: implement rx flush for htc pipe rx flush was not implemented for htc pipe, add that now. Doesn't fix any known issues. Also free the skb if htc control messages get canceled. Signed-off-by: Jessica Wu Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/htc_pipe.c | 32 ++++++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/htc_pipe.c b/drivers/net/wireless/ath/ath6kl/htc_pipe.c index 756fe52a12c8..ca1a18c86c0d 100644 --- a/drivers/net/wireless/ath/ath6kl/htc_pipe.c +++ b/drivers/net/wireless/ath/ath6kl/htc_pipe.c @@ -1170,8 +1170,12 @@ static int htc_wait_recv_ctrl_message(struct htc_target *target) static void htc_rxctrl_complete(struct htc_target *context, struct htc_packet *packet) { - /* TODO, can't really receive HTC control messages yet.... */ - ath6kl_dbg(ATH6KL_DBG_HTC, "%s: invalid call function\n", __func__); + struct sk_buff *skb = packet->skb; + + if (packet->endpoint == ENDPOINT_0 && + packet->status == -ECANCELED && + skb != NULL) + dev_kfree_skb(skb); } /* htc pipe initialization */ @@ -1678,7 +1682,29 @@ static void ath6kl_htc_pipe_activity_changed(struct htc_target *target, static void ath6kl_htc_pipe_flush_rx_buf(struct htc_target *target) { - /* TODO */ + struct htc_endpoint *endpoint; + struct htc_packet *packet, *tmp_pkt; + int i; + + for (i = ENDPOINT_0; i < ENDPOINT_MAX; i++) { + endpoint = &target->endpoint[i]; + + spin_lock_bh(&target->rx_lock); + + list_for_each_entry_safe(packet, tmp_pkt, + &endpoint->rx_bufq, list) { + list_del(&packet->list); + spin_unlock_bh(&target->rx_lock); + ath6kl_dbg(ATH6KL_DBG_HTC, + "htc rx flush pkt 0x%p len %d ep %d\n", + packet, packet->buf_len, + packet->endpoint); + dev_kfree_skb(packet->pkt_cntxt); + spin_lock_bh(&target->rx_lock); + } + + spin_unlock_bh(&target->rx_lock); + } } static int ath6kl_htc_pipe_credit_setup(struct htc_target *target, From 958e1be848c92006ee4b95190d3725daf3a70034 Mon Sep 17 00:00:00 2001 From: Kalle Valo Date: Tue, 17 Jun 2014 12:41:04 +0300 Subject: [PATCH 284/378] ath6kl: don't set hi_refclk_hz if hardware version doesn't need it Needed for ar6004 hw3.0 support. Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/init.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index ed086ead2601..a0400a12b592 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -624,9 +624,12 @@ int ath6kl_configure_target(struct ath6kl *ar) return status; /* Configure target refclk_hz */ - status = ath6kl_bmi_write_hi32(ar, hi_refclk_hz, ar->hw.refclk_hz); - if (status) - return status; + if (ar->hw.refclk_hz != 0) { + status = ath6kl_bmi_write_hi32(ar, hi_refclk_hz, + ar->hw.refclk_hz); + if (status) + return status; + } return 0; } From c1d32d3038ff4d366b837cedb95aeb1801730f2c Mon Sep 17 00:00:00 2001 From: Jessica Wu Date: Tue, 17 Jun 2014 12:41:10 +0300 Subject: [PATCH 285/378] ath6kl: add support wmi rate tables with mcs15 Some of the firmware versions support rate tables up to mcs15, add support for that. Signed-off-by: Jessica Wu Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 3 ++ drivers/net/wireless/ath/ath6kl/init.c | 1 + drivers/net/wireless/ath/ath6kl/main.c | 11 ++-- drivers/net/wireless/ath/ath6kl/wmi.c | 69 ++++++++++++++++++++++++-- drivers/net/wireless/ath/ath6kl/wmi.h | 2 +- 5 files changed, 77 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index bd91c4465580..23a776391e44 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -145,6 +145,9 @@ enum ath6kl_fw_capability { /* use low priority endpoint for all data */ ATH6KL_FW_CAPABILITY_MAP_LP_ENDPOINT, + /* ratetable is the 2 stream version (max MCS15) */ + ATH6KL_FW_CAPABILITY_RATETABLE_MCS15, + /* this needs to be last */ ATH6KL_FW_CAPABILITY_MAX, }; diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index a0400a12b592..8cd0cdfdb800 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1576,6 +1576,7 @@ static const struct fw_capa_str_map { { ATH6KL_FW_CAPABILITY_64BIT_RATES, "64bit-rates" }, { ATH6KL_FW_CAPABILITY_AP_INACTIVITY_MINS, "ap-inactivity-mins" }, { ATH6KL_FW_CAPABILITY_MAP_LP_ENDPOINT, "map-lp-endpoint" }, + { ATH6KL_FW_CAPABILITY_RATETABLE_MCS15, "ratetable-mcs15" }, }; static const char *ath6kl_init_get_fw_capa_name(unsigned int id) diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index d56554674da4..baa447f50fda 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -702,6 +702,7 @@ static void ath6kl_update_target_stats(struct ath6kl_vif *vif, u8 *ptr, u32 len) struct ath6kl *ar = vif->ar; struct target_stats *stats = &vif->target_stats; struct tkip_ccmp_stats *ccmp_stats; + s32 rate; u8 ac; if (len < sizeof(*tgt_stats)) @@ -731,8 +732,9 @@ static void ath6kl_update_target_stats(struct ath6kl_vif *vif, u8 *ptr, u32 len) le32_to_cpu(tgt_stats->stats.tx.mult_retry_cnt); stats->tx_rts_fail_cnt += le32_to_cpu(tgt_stats->stats.tx.rts_fail_cnt); - stats->tx_ucast_rate = - ath6kl_wmi_get_rate(a_sle32_to_cpu(tgt_stats->stats.tx.ucast_rate)); + + rate = a_sle32_to_cpu(tgt_stats->stats.tx.ucast_rate); + stats->tx_ucast_rate = ath6kl_wmi_get_rate(ar->wmi, rate); stats->rx_pkt += le32_to_cpu(tgt_stats->stats.rx.pkt); stats->rx_byte += le32_to_cpu(tgt_stats->stats.rx.byte); @@ -749,8 +751,9 @@ static void ath6kl_update_target_stats(struct ath6kl_vif *vif, u8 *ptr, u32 len) le32_to_cpu(tgt_stats->stats.rx.key_cache_miss); stats->rx_decrypt_err += le32_to_cpu(tgt_stats->stats.rx.decrypt_err); stats->rx_dupl_frame += le32_to_cpu(tgt_stats->stats.rx.dupl_frame); - stats->rx_ucast_rate = - ath6kl_wmi_get_rate(a_sle32_to_cpu(tgt_stats->stats.rx.ucast_rate)); + + rate = a_sle32_to_cpu(tgt_stats->stats.rx.ucast_rate); + stats->rx_ucast_rate = ath6kl_wmi_get_rate(ar->wmi, rate); ccmp_stats = &tgt_stats->stats.tkip_ccmp_stats; diff --git a/drivers/net/wireless/ath/ath6kl/wmi.c b/drivers/net/wireless/ath/ath6kl/wmi.c index 6ecc0a419c1a..94df345d08c2 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.c +++ b/drivers/net/wireless/ath/ath6kl/wmi.c @@ -59,6 +59,55 @@ static const s32 wmi_rate_tbl[][2] = { {0, 0} }; +static const s32 wmi_rate_tbl_mcs15[][2] = { + /* {W/O SGI, with SGI} */ + {1000, 1000}, + {2000, 2000}, + {5500, 5500}, + {11000, 11000}, + {6000, 6000}, + {9000, 9000}, + {12000, 12000}, + {18000, 18000}, + {24000, 24000}, + {36000, 36000}, + {48000, 48000}, + {54000, 54000}, + {6500, 7200}, /* HT 20, MCS 0 */ + {13000, 14400}, + {19500, 21700}, + {26000, 28900}, + {39000, 43300}, + {52000, 57800}, + {58500, 65000}, + {65000, 72200}, + {13000, 14400}, /* HT 20, MCS 8 */ + {26000, 28900}, + {39000, 43300}, + {52000, 57800}, + {78000, 86700}, + {104000, 115600}, + {117000, 130000}, + {130000, 144400}, /* HT 20, MCS 15 */ + {13500, 15000}, /*HT 40, MCS 0 */ + {27000, 30000}, + {40500, 45000}, + {54000, 60000}, + {81000, 90000}, + {108000, 120000}, + {121500, 135000}, + {135000, 150000}, + {27000, 30000}, /*HT 40, MCS 8 */ + {54000, 60000}, + {81000, 90000}, + {108000, 120000}, + {162000, 180000}, + {216000, 240000}, + {243000, 270000}, + {270000, 300000}, /*HT 40, MCS 15 */ + {0, 0} +}; + /* 802.1d to AC mapping. Refer pg 57 of WMM-test-plan-v1.2 */ static const u8 up_to_ac[] = { WMM_AC_BE, @@ -3280,9 +3329,11 @@ int ath6kl_wmi_set_regdomain_cmd(struct wmi *wmi, const char *alpha2) NO_SYNC_WMIFLAG); } -s32 ath6kl_wmi_get_rate(s8 rate_index) +s32 ath6kl_wmi_get_rate(struct wmi *wmi, s8 rate_index) { + struct ath6kl *ar = wmi->parent_dev; u8 sgi = 0; + s32 ret; if (rate_index == RATE_AUTO) return 0; @@ -3293,10 +3344,20 @@ s32 ath6kl_wmi_get_rate(s8 rate_index) sgi = 1; } - if (WARN_ON(rate_index > RATE_MCS_7_40)) - rate_index = RATE_MCS_7_40; + if (test_bit(ATH6KL_FW_CAPABILITY_RATETABLE_MCS15, + ar->fw_capabilities)) { + if (WARN_ON(rate_index >= ARRAY_SIZE(wmi_rate_tbl_mcs15))) + return 0; - return wmi_rate_tbl[(u32) rate_index][sgi]; + ret = wmi_rate_tbl_mcs15[(u32) rate_index][sgi]; + } else { + if (WARN_ON(rate_index >= ARRAY_SIZE(wmi_rate_tbl))) + return 0; + + ret = wmi_rate_tbl[(u32) rate_index][sgi]; + } + + return ret; } static int ath6kl_wmi_get_pmkid_list_event_rx(struct wmi *wmi, u8 *datap, diff --git a/drivers/net/wireless/ath/ath6kl/wmi.h b/drivers/net/wireless/ath/ath6kl/wmi.h index 7809afbb3e93..8d4d88531404 100644 --- a/drivers/net/wireless/ath/ath6kl/wmi.h +++ b/drivers/net/wireless/ath/ath6kl/wmi.h @@ -2632,7 +2632,7 @@ int ath6kl_wmi_set_htcap_cmd(struct wmi *wmi, u8 if_idx, struct ath6kl_htcap *htcap); int ath6kl_wmi_test_cmd(struct wmi *wmi, void *buf, size_t len); -s32 ath6kl_wmi_get_rate(s8 rate_index); +s32 ath6kl_wmi_get_rate(struct wmi *wmi, s8 rate_index); int ath6kl_wmi_set_ip_cmd(struct wmi *wmi, u8 if_idx, __be32 ips0, __be32 ips1); From 7880377012ef48bf75498648c3bcbcb60460ff28 Mon Sep 17 00:00:00 2001 From: Jessica Wu Date: Tue, 17 Jun 2014 12:41:16 +0300 Subject: [PATCH 286/378] ath6kl: add support for ar6004 hw3.0 This change enables ath6kl driver to support ar6004 hw3.0. At the same time do some fixes in firmware initialisation which applies to ar6004 hw1.3 as well. Signed-off-by: Jessica Wu Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath6kl/core.h | 21 ++++++++++- drivers/net/wireless/ath/ath6kl/init.c | 52 ++++++++++++++++++++++++-- drivers/net/wireless/ath/ath6kl/main.c | 6 ++- drivers/net/wireless/ath/ath6kl/usb.c | 1 + 4 files changed, 73 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/ath/ath6kl/core.h b/drivers/net/wireless/ath/ath6kl/core.h index 23a776391e44..2b78c863d030 100644 --- a/drivers/net/wireless/ath/ath6kl/core.h +++ b/drivers/net/wireless/ath/ath6kl/core.h @@ -148,6 +148,9 @@ enum ath6kl_fw_capability { /* ratetable is the 2 stream version (max MCS15) */ ATH6KL_FW_CAPABILITY_RATETABLE_MCS15, + /* firmare doesn't support IP checksumming */ + ATH6KL_FW_CAPABILITY_NO_IP_CHECKSUM, + /* this needs to be last */ ATH6KL_FW_CAPABILITY_MAX, }; @@ -167,6 +170,7 @@ enum ath6kl_hw_flags { #define ATH6KL_FW_API2_FILE "fw-2.bin" #define ATH6KL_FW_API3_FILE "fw-3.bin" #define ATH6KL_FW_API4_FILE "fw-4.bin" +#define ATH6KL_FW_API5_FILE "fw-5.bin" /* AR6003 1.0 definitions */ #define AR6003_HW_1_0_VERSION 0x300002ba @@ -224,8 +228,21 @@ enum ath6kl_hw_flags { #define AR6004_HW_1_3_VERSION 0x31c8088a #define AR6004_HW_1_3_FW_DIR "ath6k/AR6004/hw1.3" #define AR6004_HW_1_3_FIRMWARE_FILE "fw.ram.bin" -#define AR6004_HW_1_3_BOARD_DATA_FILE "ath6k/AR6004/hw1.3/bdata.bin" -#define AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE "ath6k/AR6004/hw1.3/bdata.bin" +#define AR6004_HW_1_3_TCMD_FIRMWARE_FILE "utf.bin" +#define AR6004_HW_1_3_UTF_FIRMWARE_FILE "utf.bin" +#define AR6004_HW_1_3_TESTSCRIPT_FILE "nullTestFlow.bin" +#define AR6004_HW_1_3_BOARD_DATA_FILE AR6004_HW_1_3_FW_DIR "/bdata.bin" +#define AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE AR6004_HW_1_3_FW_DIR "/bdata.bin" + +/* AR6004 3.0 definitions */ +#define AR6004_HW_3_0_VERSION 0x31C809F8 +#define AR6004_HW_3_0_FW_DIR "ath6k/AR6004/hw3.0" +#define AR6004_HW_3_0_FIRMWARE_FILE "fw.ram.bin" +#define AR6004_HW_3_0_TCMD_FIRMWARE_FILE "utf.bin" +#define AR6004_HW_3_0_UTF_FIRMWARE_FILE "utf.bin" +#define AR6004_HW_3_0_TESTSCRIPT_FILE "nullTestFlow.bin" +#define AR6004_HW_3_0_BOARD_DATA_FILE AR6004_HW_3_0_FW_DIR "/bdata.bin" +#define AR6004_HW_3_0_DEFAULT_BOARD_DATA_FILE AR6004_HW_3_0_FW_DIR "/bdata.bin" /* Per STA data, used in AP mode */ #define STA_PS_AWAKE BIT(0) diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 8cd0cdfdb800..a61118484de6 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -149,18 +149,43 @@ static const struct ath6kl_hw hw_list[] = { .board_ext_data_addr = 0x437000, .reserved_ram_size = 7168, .board_addr = 0x436400, - .refclk_hz = 40000000, + .refclk_hz = 0, .uarttx_pin = 11, .flags = 0, .fw = { .dir = AR6004_HW_1_3_FW_DIR, .fw = AR6004_HW_1_3_FIRMWARE_FILE, + .tcmd = AR6004_HW_1_3_TCMD_FIRMWARE_FILE, + .utf = AR6004_HW_1_3_UTF_FIRMWARE_FILE, + .testscript = AR6004_HW_1_3_TESTSCRIPT_FILE, }, .fw_board = AR6004_HW_1_3_BOARD_DATA_FILE, .fw_default_board = AR6004_HW_1_3_DEFAULT_BOARD_DATA_FILE, }, + { + .id = AR6004_HW_3_0_VERSION, + .name = "ar6004 hw 3.0", + .dataset_patch_addr = 0, + .app_load_addr = 0x1234, + .board_ext_data_addr = 0, + .reserved_ram_size = 7168, + .board_addr = 0x436400, + .testscript_addr = 0, + .flags = 0, + + .fw = { + .dir = AR6004_HW_3_0_FW_DIR, + .fw = AR6004_HW_3_0_FIRMWARE_FILE, + .tcmd = AR6004_HW_3_0_TCMD_FIRMWARE_FILE, + .utf = AR6004_HW_3_0_UTF_FIRMWARE_FILE, + .testscript = AR6004_HW_3_0_TESTSCRIPT_FILE, + }, + + .fw_board = AR6004_HW_3_0_BOARD_DATA_FILE, + .fw_default_board = AR6004_HW_3_0_DEFAULT_BOARD_DATA_FILE, + }, }; /* @@ -596,7 +621,9 @@ int ath6kl_configure_target(struct ath6kl *ar) * but possible in theory. */ - if (ar->target_type == TARGET_TYPE_AR6003) { + if ((ar->target_type == TARGET_TYPE_AR6003) || + (ar->version.target_ver == AR6004_HW_1_3_VERSION) || + (ar->version.target_ver == AR6004_HW_3_0_VERSION)) { param = ar->hw.board_ext_data_addr; ram_reserved_size = ar->hw.reserved_ram_size; @@ -1110,6 +1137,12 @@ int ath6kl_init_fetch_firmwares(struct ath6kl *ar) if (ret) return ret; + ret = ath6kl_fetch_fw_apin(ar, ATH6KL_FW_API5_FILE); + if (ret == 0) { + ar->fw_api = 5; + goto out; + } + ret = ath6kl_fetch_fw_apin(ar, ATH6KL_FW_API4_FILE); if (ret == 0) { ar->fw_api = 4; @@ -1236,7 +1269,13 @@ static int ath6kl_upload_board_file(struct ath6kl *ar) } /* record the fact that Board Data IS initialized */ - ath6kl_bmi_write_hi32(ar, hi_board_data_initialized, 1); + if ((ar->version.target_ver == AR6004_HW_1_3_VERSION) || + (ar->version.target_ver == AR6004_HW_3_0_VERSION)) + param = board_data_size; + else + param = 1; + + ath6kl_bmi_write_hi32(ar, hi_board_data_initialized, param); return ret; } @@ -1367,7 +1406,11 @@ static int ath6kl_upload_testscript(struct ath6kl *ar) } ath6kl_bmi_write_hi32(ar, hi_ota_testscript, address); - ath6kl_bmi_write_hi32(ar, hi_end_ram_reserve_sz, 4096); + + if ((ar->version.target_ver != AR6004_HW_1_3_VERSION) && + (ar->version.target_ver != AR6004_HW_3_0_VERSION)) + ath6kl_bmi_write_hi32(ar, hi_end_ram_reserve_sz, 4096); + ath6kl_bmi_write_hi32(ar, hi_test_apps_related, 1); return 0; @@ -1577,6 +1620,7 @@ static const struct fw_capa_str_map { { ATH6KL_FW_CAPABILITY_AP_INACTIVITY_MINS, "ap-inactivity-mins" }, { ATH6KL_FW_CAPABILITY_MAP_LP_ENDPOINT, "map-lp-endpoint" }, { ATH6KL_FW_CAPABILITY_RATETABLE_MCS15, "ratetable-mcs15" }, + { ATH6KL_FW_CAPABILITY_NO_IP_CHECKSUM, "no-ip-checksum" }, }; static const char *ath6kl_init_get_fw_capa_name(unsigned int id) diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index baa447f50fda..21516bc65785 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -1293,6 +1293,8 @@ static const struct net_device_ops ath6kl_netdev_ops = { void init_netdev(struct net_device *dev) { + struct ath6kl *ar = ath6kl_priv(dev); + dev->netdev_ops = &ath6kl_netdev_ops; dev->destructor = free_netdev; dev->watchdog_timeo = ATH6KL_TX_TIMEOUT; @@ -1304,7 +1306,9 @@ void init_netdev(struct net_device *dev) WMI_MAX_TX_META_SZ + ATH6KL_HTC_ALIGN_BYTES, 4); - dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM; + if (!test_bit(ATH6KL_FW_CAPABILITY_NO_IP_CHECKSUM, + ar->fw_capabilities)) + dev->hw_features |= NETIF_F_IP_CSUM | NETIF_F_RXCSUM; return; } diff --git a/drivers/net/wireless/ath/ath6kl/usb.c b/drivers/net/wireless/ath/ath6kl/usb.c index e5a9e7fe2cea..c44325856b81 100644 --- a/drivers/net/wireless/ath/ath6kl/usb.c +++ b/drivers/net/wireless/ath/ath6kl/usb.c @@ -1210,6 +1210,7 @@ static int ath6kl_usb_pm_reset_resume(struct usb_interface *intf) /* table of devices that work with this driver */ static struct usb_device_id ath6kl_usb_ids[] = { + {USB_DEVICE(0x0cf3, 0x9375)}, {USB_DEVICE(0x0cf3, 0x9374)}, { /* Terminating entry */ }, }; From a491a920ff5c22cc09700a2660f6eac55b1ce4c1 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Mon, 14 Jul 2014 16:07:29 +0300 Subject: [PATCH 287/378] ath10k: fix unregister deadlock when fw probe fails If firmware probing worker failed it called device_release_driver() which synchronously called remove() pci callback. The callback in turn waited for the worker that called it to finish resulting in a deadlock. Waiting for a completion instead of a worker, like some other drivers do, doesn't seem like the best idea either: Syscall Worker probe_fw() rmmod dev_lock() pci->remove() wait_for_completion() complete_all() device_release_driver() dev_lock() [sleep] free(ar) dev_unlock() [resume] There's no guarantee that Worker upon resuming can still access any data/code of the module. Leaving device bound to a driver is not as harmful as deadlocking so remove the call to device_release_driver() while a proper solution is figured out. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index 68bed4e5c9f4..aaf5f0eea2dc 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -990,7 +990,9 @@ err_unregister_mac: err_release_fw: ath10k_core_free_firmware_files(ar); err: - device_release_driver(ar->dev); + /* TODO: It's probably a good idea to release device from the driver + * but calling device_release_driver() here will cause a deadlock. + */ return; } From 4d042654afb342386cb5c33e29843b76d598ab61 Mon Sep 17 00:00:00 2001 From: Himangi Saraogi Date: Tue, 15 Jul 2014 02:04:13 +0530 Subject: [PATCH 288/378] Bluetooth: cmtp: Remove unnecessary null test This patch removes the null test on ctrl. ctrl is initialized at the beginning of the function to &session->ctrl. Since session is dereferenced prior to the null test, session must be a valid pointer, and &session->ctrl cannot be null. The following Coccinelle script is used for detecting the change: @r@ expression e,f; identifier g,y; statement S1,S2; @@ *e = &f->g <+... f->y ...+> *if (e != NULL || ...) S1 else S2 Signed-off-by: Himangi Saraogi Acked-by: Julia Lawall Signed-off-by: Marcel Holtmann --- net/bluetooth/cmtp/capi.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/net/bluetooth/cmtp/capi.c b/net/bluetooth/cmtp/capi.c index cd75e4d64b90..1ca8a87a0787 100644 --- a/net/bluetooth/cmtp/capi.c +++ b/net/bluetooth/cmtp/capi.c @@ -362,12 +362,6 @@ void cmtp_recv_capimsg(struct cmtp_session *session, struct sk_buff *skb) CAPIMSG_SETCONTROL(skb->data, contr); } - if (!ctrl) { - BT_ERR("Can't find controller %d for message", session->num); - kfree_skb(skb); - return; - } - capi_ctr_handle_message(ctrl, appl, skb); } From 2374b18684dfed2a0588efe4df716d16554da467 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Mon, 14 Jul 2014 16:25:25 +0300 Subject: [PATCH 289/378] ath10k: fix bmi exchange tx/rx race It was possible for tx completion not to be processed. In that case an old stack pointer was left on copy engine tx ring. Next bmi exchange would immediately pop it and use complete() on the completion struct there causing corruption. Make sure to wait for both tx and rx completions properly. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/pci.c | 11 +++-------- drivers/net/wireless/ath/ath10k/pci.h | 3 ++- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index d0004d59c97e..06840d101c45 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -1362,8 +1362,6 @@ static int ath10k_pci_hif_exchange_bmi_msg(struct ath10k *ar, ath10k_ce_recv_buf_enqueue(ce_rx, &xfer, resp_paddr); } - init_completion(&xfer.done); - ret = ath10k_ce_send(ce_tx, &xfer, req_paddr, req_len, -1, 0); if (ret) goto err_resp; @@ -1414,10 +1412,7 @@ static void ath10k_pci_bmi_send_done(struct ath10k_ce_pipe *ce_state) &nbytes, &transfer_id)) return; - if (xfer->wait_for_resp) - return; - - complete(&xfer->done); + xfer->tx_done = true; } static void ath10k_pci_bmi_recv_data(struct ath10k_ce_pipe *ce_state) @@ -1438,7 +1433,7 @@ static void ath10k_pci_bmi_recv_data(struct ath10k_ce_pipe *ce_state) } xfer->resp_len = nbytes; - complete(&xfer->done); + xfer->rx_done = true; } static int ath10k_pci_bmi_wait(struct ath10k_ce_pipe *tx_pipe, @@ -1451,7 +1446,7 @@ static int ath10k_pci_bmi_wait(struct ath10k_ce_pipe *tx_pipe, ath10k_pci_bmi_send_done(tx_pipe); ath10k_pci_bmi_recv_data(rx_pipe); - if (completion_done(&xfer->done)) + if (xfer->tx_done && (xfer->rx_done == xfer->wait_for_resp)) return 0; schedule(); diff --git a/drivers/net/wireless/ath/ath10k/pci.h b/drivers/net/wireless/ath/ath10k/pci.h index dfdebb4157aa..940129209990 100644 --- a/drivers/net/wireless/ath/ath10k/pci.h +++ b/drivers/net/wireless/ath/ath10k/pci.h @@ -38,7 +38,8 @@ #define DIAG_TRANSFER_LIMIT 2048 struct bmi_xfer { - struct completion done; + bool tx_done; + bool rx_done; bool wait_for_resp; u32 resp_len; }; From 993619443774f7ef4df3b98655df4c3bf298548c Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Mon, 14 Jul 2014 16:25:25 +0300 Subject: [PATCH 290/378] ath10k: sanitize tx ring index access properly The tx ring index was immediately trimmed with a bitmask. This discarded the 0xFFFFFFFF error case (which theoretically can happen when a device is abruptly disconnected) and led to using an invalid tx ring index. This could lead to memory corruption. Signed-off-by: Michal Kazior Signed-off-by: Kalle Valo --- drivers/net/wireless/ath/ath10k/ce.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index d185dc0cd12b..4333107ecf37 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -603,16 +603,19 @@ static int ath10k_ce_completed_send_next_nolock(struct ath10k_ce_pipe *ce_state, if (ret) return ret; - src_ring->hw_index = - ath10k_ce_src_ring_read_index_get(ar, ctrl_addr); - src_ring->hw_index &= nentries_mask; + read_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr); + if (read_index == 0xffffffff) + return -ENODEV; + + read_index &= nentries_mask; + src_ring->hw_index = read_index; ath10k_pci_sleep(ar); } read_index = src_ring->hw_index; - if ((read_index == sw_index) || (read_index == 0xffffffff)) + if (read_index == sw_index) return -EIO; sbase = src_ring->shadow_base; From 88d825bffd3d537350d0000b7ef15cb00532d417 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Wed, 2 Jul 2014 19:07:43 +0200 Subject: [PATCH 291/378] b43: always print info about radio (manuf, id, revision) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Type of radio has a major meaning for the driver. There is quite some code that does initialization/calibration depending on the radio rev. Knowing radio params is quite important to provide help to users, so print it even with debugging disabled. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 15aaeb132a32..83b2d568bcae 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4478,13 +4478,13 @@ static int b43_phy_versioning(struct b43_wldev *dev) B43_WARN_ON(1); } if (unsupported) { - b43err(dev->wl, "FOUND UNSUPPORTED RADIO " - "(Manuf 0x%X, Version 0x%X, Revision %u)\n", + b43err(dev->wl, + "FOUND UNSUPPORTED RADIO (Manuf 0x%X, ID 0x%X, Revision %u)\n", radio_manuf, radio_ver, radio_rev); return -EOPNOTSUPP; } - b43dbg(dev->wl, "Found Radio: Manuf 0x%X, Version 0x%X, Revision %u\n", - radio_manuf, radio_ver, radio_rev); + b43info(dev->wl, "Found Radio: Manuf 0x%X, ID 0x%X, Revision %u\n", + radio_manuf, radio_ver, radio_rev); phy->radio_manuf = radio_manuf; phy->radio_ver = radio_ver; From 0933ecf9ab1f0ff7ae1ff4309aee0b909fc785d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 8 Jul 2014 15:11:04 +0200 Subject: [PATCH 292/378] b43: N-PHY: drop reg 0x1 access restriction on new PHY revs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initialization of N-PHY radio revs 5 and 7 requires writing to 0x1. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/phy_n.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index 50ca6f87d5e8..1dba6409a05f 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -5834,7 +5834,7 @@ static void b43_nphy_op_maskset(struct b43_wldev *dev, u16 reg, u16 mask, static u16 b43_nphy_op_radio_read(struct b43_wldev *dev, u16 reg) { /* Register 1 is a 32-bit register. */ - B43_WARN_ON(reg == 1); + B43_WARN_ON(dev->phy.rev < 7 && reg == 1); if (dev->phy.rev >= 7) reg |= 0x200; /* Radio 0x2057 */ @@ -5848,7 +5848,7 @@ static u16 b43_nphy_op_radio_read(struct b43_wldev *dev, u16 reg) static void b43_nphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) { /* Register 1 is a 32-bit register. */ - B43_WARN_ON(reg == 1); + B43_WARN_ON(dev->phy.rev < 7 && reg == 1); b43_write16(dev, B43_MMIO_RADIO_CONTROL, reg); b43_write16(dev, B43_MMIO_RADIO_DATA_LOW, value); From 12db5481ba0ed561c9105b7e28528aa2a551e38b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 8 Jul 2014 15:11:05 +0200 Subject: [PATCH 293/378] b43: N-PHY: add TX gain tables for devices with specific EPA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/tables_nphy.c | 74 +++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/b43/tables_nphy.c b/drivers/net/wireless/b43/tables_nphy.c index b28dce950e1f..3a5cd902ff1f 100644 --- a/drivers/net/wireless/b43/tables_nphy.c +++ b/drivers/net/wireless/b43/tables_nphy.c @@ -2408,6 +2408,41 @@ static const u32 b43_ntab_tx_gain_epa_rev3_2g[] = { 0x1041003c, 0x1041003b, 0x10410039, 0x10410037, }; +static const u32 b43_ntab_tx_gain_epa_rev3_hi_pwr_2g[] = { + 0x0f410044, 0x0f410042, 0x0f410040, 0x0f41003e, + 0x0f41003c, 0x0f41003b, 0x0f410039, 0x0f410037, + 0x0e410044, 0x0e410042, 0x0e410040, 0x0e41003e, + 0x0e41003c, 0x0e41003b, 0x0e410039, 0x0e410037, + 0x0d410044, 0x0d410042, 0x0d410040, 0x0d41003e, + 0x0d41003c, 0x0d41003b, 0x0d410039, 0x0d410037, + 0x0c410044, 0x0c410042, 0x0c410040, 0x0c41003e, + 0x0c41003c, 0x0c41003b, 0x0c410039, 0x0c410037, + 0x0b410044, 0x0b410042, 0x0b410040, 0x0b41003e, + 0x0b41003c, 0x0b41003b, 0x0b410039, 0x0b410037, + 0x0a410044, 0x0a410042, 0x0a410040, 0x0a41003e, + 0x0a41003c, 0x0a41003b, 0x0a410039, 0x0a410037, + 0x09410044, 0x09410042, 0x09410040, 0x0941003e, + 0x0941003c, 0x0941003b, 0x09410039, 0x09410037, + 0x08410044, 0x08410042, 0x08410040, 0x0841003e, + 0x0841003c, 0x0841003b, 0x08410039, 0x08410037, + 0x07410044, 0x07410042, 0x07410040, 0x0741003e, + 0x0741003c, 0x0741003b, 0x07410039, 0x07410037, + 0x06410044, 0x06410042, 0x06410040, 0x0641003e, + 0x0641003c, 0x0641003b, 0x06410039, 0x06410037, + 0x05410044, 0x05410042, 0x05410040, 0x0541003e, + 0x0541003c, 0x0541003b, 0x05410039, 0x05410037, + 0x04410044, 0x04410042, 0x04410040, 0x0441003e, + 0x0441003c, 0x0441003b, 0x04410039, 0x04410037, + 0x03410044, 0x03410042, 0x03410040, 0x0341003e, + 0x0341003c, 0x0341003b, 0x03410039, 0x03410037, + 0x02410044, 0x02410042, 0x02410040, 0x0241003e, + 0x0241003c, 0x0241003b, 0x02410039, 0x02410037, + 0x01410044, 0x01410042, 0x01410040, 0x0141003e, + 0x0141003c, 0x0141003b, 0x01410039, 0x01410037, + 0x00410044, 0x00410042, 0x00410040, 0x0041003e, + 0x0041003c, 0x0041003b, 0x00410039, 0x00410037 +}; + /* EPA 5 GHz */ static const u32 b43_ntab_tx_gain_epa_rev3_5g[] = { @@ -2480,6 +2515,41 @@ static const u32 b43_ntab_tx_gain_epa_rev4_5g[] = { 0x20d2003a, 0x20d20038, 0x20d20036, 0x20d20034, }; +static const u32 b43_ntab_tx_gain_epa_rev4_hi_pwr_5g[] = { + 0x2ff10044, 0x2ff10042, 0x2ff10040, 0x2ff1003e, + 0x2ff1003c, 0x2ff1003b, 0x2ff10039, 0x2ff10037, + 0x2ef10044, 0x2ef10042, 0x2ef10040, 0x2ef1003e, + 0x2ef1003c, 0x2ef1003b, 0x2ef10039, 0x2ef10037, + 0x2df10044, 0x2df10042, 0x2df10040, 0x2df1003e, + 0x2df1003c, 0x2df1003b, 0x2df10039, 0x2df10037, + 0x2cf10044, 0x2cf10042, 0x2cf10040, 0x2cf1003e, + 0x2cf1003c, 0x2cf1003b, 0x2cf10039, 0x2cf10037, + 0x2bf10044, 0x2bf10042, 0x2bf10040, 0x2bf1003e, + 0x2bf1003c, 0x2bf1003b, 0x2bf10039, 0x2bf10037, + 0x2af10044, 0x2af10042, 0x2af10040, 0x2af1003e, + 0x2af1003c, 0x2af1003b, 0x2af10039, 0x2af10037, + 0x29f10044, 0x29f10042, 0x29f10040, 0x29f1003e, + 0x29f1003c, 0x29f1003b, 0x29f10039, 0x29f10037, + 0x28f10044, 0x28f10042, 0x28f10040, 0x28f1003e, + 0x28f1003c, 0x28f1003b, 0x28f10039, 0x28f10037, + 0x27f10044, 0x27f10042, 0x27f10040, 0x27f1003e, + 0x27f1003c, 0x27f1003b, 0x27f10039, 0x27f10037, + 0x26f10044, 0x26f10042, 0x26f10040, 0x26f1003e, + 0x26f1003c, 0x26f1003b, 0x26f10039, 0x26f10037, + 0x25f10044, 0x25f10042, 0x25f10040, 0x25f1003e, + 0x25f1003c, 0x25f1003b, 0x25f10039, 0x25f10037, + 0x24f10044, 0x24f10042, 0x24f10040, 0x24f1003e, + 0x24f1003c, 0x24f1003b, 0x24f10039, 0x24f10038, + 0x23f10041, 0x23f10040, 0x23f1003f, 0x23f1003e, + 0x23f1003c, 0x23f1003b, 0x23f10039, 0x23f10037, + 0x22f10044, 0x22f10042, 0x22f10040, 0x22f1003e, + 0x22f1003c, 0x22f1003b, 0x22f10039, 0x22f10037, + 0x21f10044, 0x21f10042, 0x21f10040, 0x21f1003e, + 0x21f1003c, 0x21f1003b, 0x21f10039, 0x21f10037, + 0x20d10043, 0x20d10041, 0x20d1003e, 0x20d1003c, + 0x20d1003a, 0x20d10038, 0x20d10036, 0x20d10034 +}; + static const u32 b43_ntab_tx_gain_epa_rev5_5g[] = { 0x0f62004a, 0x0f620048, 0x0f620046, 0x0f620044, 0x0f620042, 0x0f620040, 0x0f62003e, 0x0f62003c, @@ -3530,7 +3600,7 @@ const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev) case 4: return sprom->fem.ghz5.extpa_gain == 3 ? b43_ntab_tx_gain_epa_rev4_5g : - b43_ntab_tx_gain_epa_rev4_5g; /* FIXME */ + b43_ntab_tx_gain_epa_rev4_hi_pwr_5g; case 3: return b43_ntab_tx_gain_epa_rev3_5g; default: @@ -3543,7 +3613,7 @@ const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev) case 6: case 5: if (sprom->fem.ghz5.extpa_gain == 3) - return b43_ntab_tx_gain_epa_rev3_2g; /* FIXME */ + return b43_ntab_tx_gain_epa_rev3_hi_pwr_2g; /* fall through */ case 4: case 3: From 303415e2f1c6617db5ae5988e1fceac353b8df4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 8 Jul 2014 15:11:06 +0200 Subject: [PATCH 294/378] b43: N-PHY: add placeholders for new devices support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/phy_n.c | 285 +++++++++++++++++++++++++------ 1 file changed, 231 insertions(+), 54 deletions(-) diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index 1dba6409a05f..4bb4d0a3134b 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -140,11 +140,19 @@ ok: b43_phy_write(dev, B43_NPHY_RFSEQMODE, seq_mode); } +static void b43_nphy_rf_ctl_override_rev19(struct b43_wldev *dev, u16 field, + u16 value, u8 core, bool off, + u8 override_id) +{ + /* TODO */ +} + /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlOverrideRev7 */ static void b43_nphy_rf_ctl_override_rev7(struct b43_wldev *dev, u16 field, u16 value, u8 core, bool off, u8 override) { + struct b43_phy *phy = &dev->phy; const struct nphy_rf_control_override_rev7 *e; u16 en_addrs[3][2] = { { 0x0E7, 0x0EC }, { 0x342, 0x343 }, { 0x346, 0x347 } @@ -154,6 +162,11 @@ static void b43_nphy_rf_ctl_override_rev7(struct b43_wldev *dev, u16 field, u16 val_addr; u8 i; + if (phy->rev >= 19 || phy->rev < 3) { + B43_WARN_ON(1); + return; + } + /* Remember: we can get NULL! */ e = b43_nphy_get_rf_ctl_over_rev7(dev, field, override); @@ -264,6 +277,8 @@ static void b43_nphy_rf_ctl_intc_override_rev7(struct b43_wldev *dev, u16 reg, tmp, tmp2, val; int core; + /* TODO: What about rev19+? Revs 3+ and 7+ are a bit similar */ + for (core = 0; core < 2; core++) { if ((core_sel == 1 && core != 0) || (core_sel == 2 && core != 1)) @@ -1386,6 +1401,7 @@ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops, u16 wait, bool iqmode, bool dac_test, bool modify_bbmult) { + struct b43_phy *phy = &dev->phy; struct b43_phy_n *nphy = dev->phy.n; int i; u16 seq_mode; @@ -1393,6 +1409,10 @@ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops, b43_nphy_stay_in_carrier_search(dev, true); + if (phy->rev >= 7) { + /* TODO */ + } + if ((nphy->bb_mult_save & 0x80000000) == 0) { tmp = b43_ntab_read(dev, B43_NTAB16(15, 87)); nphy->bb_mult_save = (tmp & 0xFFFF) | 0x80000000; @@ -1520,6 +1540,12 @@ static void b43_nphy_scale_offset_rssi(struct b43_wldev *dev, u16 scale, } } +static void b43_nphy_rssi_select_rev19(struct b43_wldev *dev, u8 code, + enum n_rssi_type rssi_type) +{ + /* TODO */ +} + static void b43_nphy_rev3_rssi_select(struct b43_wldev *dev, u8 code, enum n_rssi_type rssi_type) { @@ -1589,13 +1615,15 @@ static void b43_nphy_rev3_rssi_select(struct b43_wldev *dev, u8 code, enum ieee80211_band band = b43_current_band(dev->wl); - if (b43_nphy_ipa(dev)) - val = (band == IEEE80211_BAND_5GHZ) ? 0xC : 0xE; - else - val = 0x11; - reg = (i == 0) ? 0x2000 : 0x3000; - reg |= B2055_PADDRV; - b43_radio_write(dev, reg, val); + if (dev->phy.rev < 7) { + if (b43_nphy_ipa(dev)) + val = (band == IEEE80211_BAND_5GHZ) ? 0xC : 0xE; + else + val = 0x11; + reg = (i == 0) ? B2056_TX0 : B2056_TX1; + reg |= B2056_TX_TX_SSI_MUX; + b43_radio_write(dev, reg, val); + } reg = (i == 0) ? B43_NPHY_AFECTL_OVER1 : @@ -1682,7 +1710,9 @@ static void b43_nphy_rev2_rssi_select(struct b43_wldev *dev, u8 code, static void b43_nphy_rssi_select(struct b43_wldev *dev, u8 code, enum n_rssi_type type) { - if (dev->phy.rev >= 3) + if (dev->phy.rev >= 19) + b43_nphy_rssi_select_rev19(dev, code, type); + else if (dev->phy.rev >= 3) b43_nphy_rev3_rssi_select(dev, code, type); else b43_nphy_rev2_rssi_select(dev, code, type); @@ -1726,6 +1756,8 @@ static int b43_nphy_poll_rssi(struct b43_wldev *dev, enum n_rssi_type rssi_type, u16 save_regs_phy[9]; u16 s[2]; + /* TODO: rev7+ is treated like rev3+, what about rev19+? */ + if (dev->phy.rev >= 3) { save_regs_phy[0] = b43_phy_read(dev, B43_NPHY_AFECTL_C1); save_regs_phy[1] = b43_phy_read(dev, B43_NPHY_AFECTL_C2); @@ -2209,7 +2241,9 @@ static void b43_nphy_rev2_rssi_cal(struct b43_wldev *dev, enum n_rssi_type type) */ static void b43_nphy_rssi_cal(struct b43_wldev *dev) { - if (dev->phy.rev >= 3) { + if (dev->phy.rev >= 19) { + /* TODO */ + } else if (dev->phy.rev >= 3) { b43_nphy_rev3_rssi_cal(dev); } else { b43_nphy_rev2_rssi_cal(dev, N_RSSI_NB); @@ -2222,7 +2256,21 @@ static void b43_nphy_rssi_cal(struct b43_wldev *dev) * Workarounds **************************************************/ -static void b43_nphy_gain_ctl_workarounds_rev3plus(struct b43_wldev *dev) +static void b43_nphy_gain_ctl_workarounds_rev19(struct b43_wldev *dev) +{ + /* TODO */ +} + +static void b43_nphy_gain_ctl_workarounds_rev7(struct b43_wldev *dev) +{ + struct b43_phy *phy = &dev->phy; + + switch (phy->rev) { + /* TODO */ + } +} + +static void b43_nphy_gain_ctl_workarounds_rev3(struct b43_wldev *dev) { struct ssb_sprom *sprom = dev->dev->bus_sprom; @@ -2419,10 +2467,12 @@ static void b43_nphy_gain_ctl_workarounds_rev1_2(struct b43_wldev *dev) /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/WorkaroundsGainCtrl */ static void b43_nphy_gain_ctl_workarounds(struct b43_wldev *dev) { - if (dev->phy.rev >= 7) - ; /* TODO */ + if (dev->phy.rev >= 19) + b43_nphy_gain_ctl_workarounds_rev19(dev); + else if (dev->phy.rev >= 7) + b43_nphy_gain_ctl_workarounds_rev7(dev); else if (dev->phy.rev >= 3) - b43_nphy_gain_ctl_workarounds_rev3plus(dev); + b43_nphy_gain_ctl_workarounds_rev3(dev); else b43_nphy_gain_ctl_workarounds_rev1_2(dev); } @@ -3059,6 +3109,7 @@ static void b43_nphy_workarounds(struct b43_wldev *dev) b43_phy_set(dev, B43_NPHY_IQFLIP, B43_NPHY_IQFLIP_ADC1 | B43_NPHY_IQFLIP_ADC2); + /* TODO: rev19+ */ if (dev->phy.rev >= 7) b43_nphy_workarounds_rev7plus(dev); else if (dev->phy.rev >= 3) @@ -3140,6 +3191,10 @@ static void b43_nphy_stop_playback(struct b43_wldev *dev) nphy->bb_mult_save = 0; } + if (dev->phy.rev >= 7) { + /* TODO */ + } + if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, 0); } @@ -3149,6 +3204,7 @@ static void b43_nphy_iq_cal_gain_params(struct b43_wldev *dev, u16 core, struct nphy_txgains target, struct nphy_iqcal_params *params) { + struct b43_phy *phy = &dev->phy; int i, j, indx; u16 gain; @@ -3157,8 +3213,13 @@ static void b43_nphy_iq_cal_gain_params(struct b43_wldev *dev, u16 core, params->pga = target.pga[core]; params->pad = target.pad[core]; params->ipa = target.ipa[core]; - params->cal_gain = (params->txgm << 12) | (params->pga << 8) | - (params->pad << 4) | (params->ipa); + if (phy->rev >= 19) { + /* TODO */ + } else if (phy->rev >= 7) { + /* TODO */ + } else { + params->cal_gain = (params->txgm << 12) | (params->pga << 8) | (params->pad << 4) | (params->ipa); + } for (j = 0; j < 5; j++) params->ncorr[j] = 0x79; } else { @@ -3199,6 +3260,7 @@ static enum b43_txpwr_result b43_nphy_op_recalc_txpower(struct b43_wldev *dev, /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxPwrCtrlEnable */ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable) { + struct b43_phy *phy = &dev->phy; struct b43_phy_n *nphy = dev->phy.n; u8 i; u16 bmask, val, tmp; @@ -3268,12 +3330,20 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable) b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, ~(bmask), val); if (band == IEEE80211_BAND_5GHZ) { - b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, - ~B43_NPHY_TXPCTL_CMD_INIT, 0x64); - if (dev->phy.rev > 1) - b43_phy_maskset(dev, B43_NPHY_TXPCTL_INIT, - ~B43_NPHY_TXPCTL_INIT_PIDXI1, + if (phy->rev >= 19) { + /* TODO */ + } else if (phy->rev >= 7) { + /* TODO */ + } else { + b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, + ~B43_NPHY_TXPCTL_CMD_INIT, 0x64); + if (phy->rev > 1) + b43_phy_maskset(dev, + B43_NPHY_TXPCTL_INIT, + ~B43_NPHY_TXPCTL_INIT_PIDXI1, + 0x64); + } } if (dev->phy.rev >= 3) { @@ -3290,6 +3360,10 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable) } } + if (phy->rev >= 7) { + /* TODO */ + } + if (dev->phy.rev >= 3) { b43_phy_mask(dev, B43_NPHY_AFECTL_OVER1, ~0x100); b43_phy_mask(dev, B43_NPHY_AFECTL_OVER, ~0x100); @@ -3331,6 +3405,7 @@ static void b43_nphy_tx_power_fix(struct b43_wldev *dev) if (nphy->hang_avoid) b43_nphy_stay_in_carrier_search(dev, 1); + /* TODO: rev19+ */ if (dev->phy.rev >= 7) { txpi[0] = txpi[1] = 30; } else if (dev->phy.rev >= 3) { @@ -3433,7 +3508,9 @@ static void b43_nphy_ipa_internal_tssi_setup(struct b43_wldev *dev) u8 core; u16 r; /* routing */ - if (phy->rev >= 7) { + if (phy->rev >= 19) { + /* TODO */ + } else if (phy->rev >= 7) { for (core = 0; core < 2; core++) { r = core ? 0x190 : 0x170; if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { @@ -3521,7 +3598,9 @@ static void b43_nphy_tx_power_ctl_idle_tssi(struct b43_wldev *dev) if (b43_nphy_ipa(dev)) b43_nphy_ipa_internal_tssi_setup(dev); - if (phy->rev >= 7) + if (phy->rev >= 19) + b43_nphy_rf_ctl_override_rev19(dev, 0x2000, 0, 3, false, 0); + else if (phy->rev >= 7) b43_nphy_rf_ctl_override_rev7(dev, 0x2000, 0, 3, false, 0); else if (phy->rev >= 3) b43_nphy_rf_ctl_override(dev, 0x2000, 0, 3, false); @@ -3531,14 +3610,20 @@ static void b43_nphy_tx_power_ctl_idle_tssi(struct b43_wldev *dev) udelay(20); tmp = b43_nphy_poll_rssi(dev, N_RSSI_TSSI_2G, rssi, 1); b43_nphy_stop_playback(dev); + b43_nphy_rssi_select(dev, 0, N_RSSI_W1); - if (phy->rev >= 7) + if (phy->rev >= 19) + b43_nphy_rf_ctl_override_rev19(dev, 0x2000, 0, 3, true, 0); + else if (phy->rev >= 7) b43_nphy_rf_ctl_override_rev7(dev, 0x2000, 0, 3, true, 0); else if (phy->rev >= 3) b43_nphy_rf_ctl_override(dev, 0x2000, 0, 3, true); - if (phy->rev >= 3) { + if (phy->rev >= 19) { + /* TODO */ + return; + } else if (phy->rev >= 3) { nphy->pwr_ctl_info[0].idle_tssi_5g = (tmp >> 24) & 0xFF; nphy->pwr_ctl_info[1].idle_tssi_5g = (tmp >> 8) & 0xFF; } else { @@ -3730,7 +3815,9 @@ static void b43_nphy_tx_power_ctl_setup(struct b43_wldev *dev) udelay(1); } - if (dev->phy.rev >= 7) { + if (phy->rev >= 19) { + /* TODO */ + } else if (phy->rev >= 7) { b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, ~B43_NPHY_TXPCTL_CMD_INIT, 0x19); b43_phy_maskset(dev, B43_NPHY_TXPCTL_INIT, @@ -3793,24 +3880,30 @@ static void b43_nphy_tx_gain_table_upload(struct b43_wldev *dev) b43_ntab_write_bulk(dev, B43_NTAB32(26, 192), 128, table); b43_ntab_write_bulk(dev, B43_NTAB32(27, 192), 128, table); - if (phy->rev >= 3) { + if (phy->rev < 3) + return; + #if 0 - nphy->gmval = (table[0] >> 16) & 0x7000; + nphy->gmval = (table[0] >> 16) & 0x7000; #endif - for (i = 0; i < 128; i++) { + for (i = 0; i < 128; i++) { + if (phy->rev >= 19) { + /* TODO */ + return; + } else if (phy->rev >= 7) { + /* TODO */ + return; + } else { pga_gain = (table[i] >> 24) & 0xF; if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) - rfpwr_offset = - b43_ntab_papd_pga_gain_delta_ipa_2g[pga_gain]; + rfpwr_offset = b43_ntab_papd_pga_gain_delta_ipa_2g[pga_gain]; else - rfpwr_offset = - 0; /* FIXME */ - b43_ntab_write(dev, B43_NTAB32(26, 576 + i), - rfpwr_offset); - b43_ntab_write(dev, B43_NTAB32(27, 576 + i), - rfpwr_offset); + rfpwr_offset = 0; /* FIXME */ } + + b43_ntab_write(dev, B43_NTAB32(26, 576 + i), rfpwr_offset); + b43_ntab_write(dev, B43_NTAB32(27, 576 + i), rfpwr_offset); } } @@ -3827,7 +3920,10 @@ static void b43_nphy_pa_override(struct b43_wldev *dev, bool enable) nphy->rfctrl_intc2_save = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2); band = b43_current_band(dev->wl); - if (dev->phy.rev >= 3) { + if (dev->phy.rev >= 7) { + /* TODO */ + return; + } else if (dev->phy.rev >= 3) { if (band == IEEE80211_BAND_5GHZ) tmp = 0x600; else @@ -4274,7 +4370,10 @@ static void b43_nphy_restore_rssi_cal(struct b43_wldev *dev) rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_5G; } - if (dev->phy.rev >= 7) { + if (dev->phy.rev >= 19) { + /* TODO */ + } else if (dev->phy.rev >= 7) { + /* TODO */ } else { b43_radio_maskset(dev, B2056_RX0 | B2056_RX_RSSI_MISC, 0xE3, rssical_radio_regs[0]); @@ -4298,15 +4397,30 @@ static void b43_nphy_restore_rssi_cal(struct b43_wldev *dev) b43_phy_write(dev, B43_NPHY_RSSIMC_1Q_RSSI_Y, rssical_phy_regs[11]); } +static void b43_nphy_tx_cal_radio_setup_rev19(struct b43_wldev *dev) +{ + /* TODO */ +} + +static void b43_nphy_tx_cal_radio_setup_rev7(struct b43_wldev *dev) +{ + /* TODO */ +} + /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxCalRadioSetup */ static void b43_nphy_tx_cal_radio_setup(struct b43_wldev *dev) { + struct b43_phy *phy = &dev->phy; struct b43_phy_n *nphy = dev->phy.n; u16 *save = nphy->tx_rx_cal_radio_saveregs; u16 tmp; u8 offset, i; - if (dev->phy.rev >= 3) { + if (phy->rev >= 19) { + b43_nphy_tx_cal_radio_setup_rev19(dev); + } else if (phy->rev >= 7) { + b43_nphy_tx_cal_radio_setup_rev7(dev); + } else if (phy->rev >= 3) { for (i = 0; i < 2; i++) { tmp = (i == 0) ? 0x2000 : 0x3000; offset = i * 11; @@ -4547,6 +4661,7 @@ static void b43_nphy_tx_cal_phy_cleanup(struct b43_wldev *dev) /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxCalPhySetup */ static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev) { + struct b43_phy *phy = &dev->phy; u16 *regs = dev->phy.n->tx_rx_cal_phy_saveregs; u16 tmp; @@ -4586,6 +4701,21 @@ static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev) regs[10] = b43_phy_read(dev, B43_NPHY_PAPD_EN1); b43_phy_mask(dev, B43_NPHY_PAPD_EN0, ~0x0001); b43_phy_mask(dev, B43_NPHY_PAPD_EN1, ~0x0001); + + if (phy->rev >= 19) + ; /* TODO */ + else if (phy->rev >= 7) + ; /* TODO */ + + if (0 /* FIXME */) { + if (phy->rev >= 19) { + /* TODO */ + } else if (phy->rev >= 8) { + /* TODO */ + } else if (phy->rev == 7) { + /* TODO */ + } + } } else { b43_phy_maskset(dev, B43_NPHY_AFECTL_C1, 0x0FFF, 0xA000); b43_phy_maskset(dev, B43_NPHY_AFECTL_C2, 0x0FFF, 0xA000); @@ -4614,6 +4744,7 @@ static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev) /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SaveCal */ static void b43_nphy_save_cal(struct b43_wldev *dev) { + struct b43_phy *phy = &dev->phy; struct b43_phy_n *nphy = dev->phy.n; struct b43_phy_n_iq_comp *rxcal_coeffs = NULL; @@ -4638,7 +4769,11 @@ static void b43_nphy_save_cal(struct b43_wldev *dev) b43_nphy_rx_iq_coeffs(dev, false, rxcal_coeffs); /* TODO use some definitions */ - if (dev->phy.rev >= 3) { + if (phy->rev >= 19) { + /* TODO */ + } else if (phy->rev >= 7) { + /* TODO */ + } else if (phy->rev >= 3) { txcal_radio_regs[0] = b43_radio_read(dev, 0x2021); txcal_radio_regs[1] = b43_radio_read(dev, 0x2022); txcal_radio_regs[2] = b43_radio_read(dev, 0x3021); @@ -4665,6 +4800,7 @@ static void b43_nphy_save_cal(struct b43_wldev *dev) /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RestoreCal */ static void b43_nphy_restore_cal(struct b43_wldev *dev) { + struct b43_phy *phy = &dev->phy; struct b43_phy_n *nphy = dev->phy.n; u16 coef[4]; @@ -4712,7 +4848,11 @@ static void b43_nphy_restore_cal(struct b43_wldev *dev) } /* TODO use some definitions */ - if (dev->phy.rev >= 3) { + if (phy->rev >= 19) { + /* TODO */ + } else if (phy->rev >= 7) { + /* TODO */ + } else if (phy->rev >= 3) { b43_radio_write(dev, 0x2021, txcal_radio_regs[0]); b43_radio_write(dev, 0x2022, txcal_radio_regs[1]); b43_radio_write(dev, 0x3021, txcal_radio_regs[2]); @@ -4789,7 +4929,13 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev, } } - b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8AA9); + if (phy->rev >= 19) { + /* TODO */ + } else if (phy->rev >= 7) { + /* TODO */ + } else { + b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8AA9); + } if (!b43_is_40mhz(dev)) freq = 2500; @@ -5183,6 +5329,9 @@ static int b43_nphy_rev3_cal_rx_iq(struct b43_wldev *dev, static int b43_nphy_cal_rx_iq(struct b43_wldev *dev, struct nphy_txgains target, u8 type, bool debug) { + if (dev->phy.rev >= 7) + type = 0; + if (dev->phy.rev >= 3) return b43_nphy_rev3_cal_rx_iq(dev, target, type, debug); else @@ -5269,6 +5418,9 @@ static void b43_nphy_bphy_init(struct b43_wldev *dev) /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/SuperSwitchInit */ static void b43_nphy_superswitch_init(struct b43_wldev *dev, bool init) { + if (dev->phy.rev >= 7) + return; + if (dev->phy.rev >= 3) { if (!init) return; @@ -5353,6 +5505,13 @@ static int b43_phy_initn(struct b43_wldev *dev) if (dev->phy.rev >= 3) { b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S1, 0); b43_phy_write(dev, B43_NPHY_RFCTL_OVER, 0); + if (phy->rev >= 7) { + /* TODO */ + } + if (phy->rev >= 19) { + /* TODO */ + } + b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S0, 0); b43_phy_write(dev, B43_NPHY_TXF_40CO_B32S1, 0); } else { @@ -5390,7 +5549,9 @@ static int b43_phy_initn(struct b43_wldev *dev) b43_phy_write(dev, B43_NPHY_PLOAD_CSENSE_EXTLEN, 0x50); b43_phy_write(dev, B43_NPHY_TXRIFS_FRDEL, 0x30); - b43_nphy_update_mimo_config(dev, nphy->preamble_override); + if (phy->rev < 8) + b43_nphy_update_mimo_config(dev, nphy->preamble_override); + b43_nphy_update_txrx_chain(dev); if (phy->rev < 2) { @@ -5422,10 +5583,12 @@ static int b43_phy_initn(struct b43_wldev *dev) b43_mac_phy_clock_set(dev, true); - b43_nphy_pa_override(dev, false); - b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RX2TX); - b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); - b43_nphy_pa_override(dev, true); + if (phy->rev < 7) { + b43_nphy_pa_override(dev, false); + b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RX2TX); + b43_nphy_force_rf_sequence(dev, B43_RFSEQ_RESET2RX); + b43_nphy_pa_override(dev, true); + } b43_nphy_classifier(dev, 0, 0); b43_nphy_read_clip_detection(dev, clip); @@ -5644,7 +5807,10 @@ static int b43_nphy_set_channel(struct b43_wldev *dev, u8 tmp; - if (phy->rev >= 7) { + if (phy->rev >= 19) { + return -ESRCH; + /* TODO */ + } else if (phy->rev >= 7) { r2057_get_chantabent_rev7(dev, channel->center_freq, &tabent_r7, &tabent_r7_2g); if (!tabent_r7 && !tabent_r7_2g) @@ -5681,7 +5847,9 @@ static int b43_nphy_set_channel(struct b43_wldev *dev, b43_phy_mask(dev, 0x310, (u16)~0x8000); } - if (phy->rev >= 7) { + if (phy->rev >= 19) { + /* TODO */ + } else if (phy->rev >= 7) { const struct b43_phy_n_sfo_cfg *phy_regs = tabent_r7 ? &(tabent_r7->phy_regs) : &(tabent_r7_2g->phy_regs); @@ -5858,15 +6026,19 @@ static void b43_nphy_op_radio_write(struct b43_wldev *dev, u16 reg, u16 value) static void b43_nphy_op_software_rfkill(struct b43_wldev *dev, bool blocked) { + struct b43_phy *phy = &dev->phy; + if (b43_read32(dev, B43_MMIO_MACCTL) & B43_MACCTL_ENABLED) b43err(dev->wl, "MAC not suspended\n"); if (blocked) { b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, ~B43_NPHY_RFCTL_CMD_CHIP0PU); - if (dev->phy.rev >= 7) { + if (phy->rev >= 19) { /* TODO */ - } else if (dev->phy.rev >= 3) { + } else if (phy->rev >= 7) { + /* TODO */ + } else if (phy->rev >= 3) { b43_radio_mask(dev, 0x09, ~0x2); b43_radio_write(dev, 0x204D, 0); @@ -5884,11 +6056,13 @@ static void b43_nphy_op_software_rfkill(struct b43_wldev *dev, b43_radio_write(dev, 0x3064, 0); } } else { - if (dev->phy.rev >= 7) { + if (phy->rev >= 19) { + /* TODO */ + } else if (phy->rev >= 7) { if (!dev->phy.radio_on) b43_radio_2057_init(dev); b43_switch_channel(dev, dev->phy.channel); - } else if (dev->phy.rev >= 3) { + } else if (phy->rev >= 3) { if (!dev->phy.radio_on) b43_radio_init2056(dev); b43_switch_channel(dev, dev->phy.channel); @@ -5901,10 +6075,13 @@ static void b43_nphy_op_software_rfkill(struct b43_wldev *dev, /* http://bcm-v4.sipsolutions.net/802.11/PHY/Anacore */ static void b43_nphy_op_switch_analog(struct b43_wldev *dev, bool on) { + struct b43_phy *phy = &dev->phy; u16 override = on ? 0x0 : 0x7FFF; u16 core = on ? 0xD : 0x00FD; - if (dev->phy.rev >= 3) { + if (phy->rev >= 19) { + /* TODO */ + } else if (phy->rev >= 3) { if (on) { b43_phy_write(dev, B43_NPHY_AFECTL_C1, core); b43_phy_write(dev, B43_NPHY_AFECTL_OVER1, override); From 40c68f20e63c9cd589ebfcf672ef912452967caf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 8 Jul 2014 15:11:07 +0200 Subject: [PATCH 295/378] b43; N-PHY: write most of the missing code for revs 7+ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 2 +- drivers/net/wireless/b43/phy_n.c | 306 ++++++++++++++++++++++---- drivers/net/wireless/b43/phy_n.h | 11 + drivers/net/wireless/b43/radio_2057.h | 10 + 4 files changed, 290 insertions(+), 39 deletions(-) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 83b2d568bcae..8dd69a3ae7ac 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -4360,7 +4360,7 @@ static int b43_phy_versioning(struct b43_wldev *dev) #endif #ifdef CONFIG_B43_PHY_N case B43_PHYTYPE_N: - if (phy_rev > 9) + if (phy_rev >= 19) unsupported = 1; break; #endif diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index 4bb4d0a3134b..44349f5b99c6 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -36,6 +36,7 @@ #include "main.h" struct nphy_txgains { + u16 tx_lpf[2]; u16 txgm[2]; u16 pga[2]; u16 pad[2]; @@ -43,6 +44,7 @@ struct nphy_txgains { }; struct nphy_iqcal_params { + u16 tx_lpf; u16 txgm; u16 pga; u16 pad; @@ -69,6 +71,14 @@ enum b43_nphy_rf_sequence { B43_RFSEQ_UPDATE_GAINU, }; +enum n_rf_ctl_over_cmd { + N_RF_CTL_OVER_CMD_RXRF_PU = 0, + N_RF_CTL_OVER_CMD_RX_PU = 1, + N_RF_CTL_OVER_CMD_TX_PU = 2, + N_RF_CTL_OVER_CMD_RX_GAIN = 3, + N_RF_CTL_OVER_CMD_TX_GAIN = 4, +}; + enum n_intc_override { N_INTC_OVERRIDE_OFF = 0, N_INTC_OVERRIDE_TRSW = 1, @@ -194,6 +204,50 @@ static void b43_nphy_rf_ctl_override_rev7(struct b43_wldev *dev, u16 field, } } +/* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlOverideOneToMany */ +static void b43_nphy_rf_ctl_override_one_to_many(struct b43_wldev *dev, + enum n_rf_ctl_over_cmd cmd, + u16 value, u8 core, bool off) +{ + struct b43_phy *phy = &dev->phy; + u16 tmp; + + B43_WARN_ON(phy->rev < 7); + + switch (cmd) { + case N_RF_CTL_OVER_CMD_RXRF_PU: + b43_nphy_rf_ctl_override_rev7(dev, 0x20, value, core, off, 1); + b43_nphy_rf_ctl_override_rev7(dev, 0x10, value, core, off, 1); + b43_nphy_rf_ctl_override_rev7(dev, 0x08, value, core, off, 1); + break; + case N_RF_CTL_OVER_CMD_RX_PU: + b43_nphy_rf_ctl_override_rev7(dev, 0x4, value, core, off, 1); + b43_nphy_rf_ctl_override_rev7(dev, 0x2, value, core, off, 1); + b43_nphy_rf_ctl_override_rev7(dev, 0x1, value, core, off, 1); + b43_nphy_rf_ctl_override_rev7(dev, 0x2, value, core, off, 2); + b43_nphy_rf_ctl_override_rev7(dev, 0x0800, value, core, off, 1); + break; + case N_RF_CTL_OVER_CMD_TX_PU: + b43_nphy_rf_ctl_override_rev7(dev, 0x4, value, core, off, 0); + b43_nphy_rf_ctl_override_rev7(dev, 0x2, value, core, off, 1); + b43_nphy_rf_ctl_override_rev7(dev, 0x1, value, core, off, 2); + b43_nphy_rf_ctl_override_rev7(dev, 0x0800, value, core, off, 1); + break; + case N_RF_CTL_OVER_CMD_RX_GAIN: + tmp = value & 0xFF; + b43_nphy_rf_ctl_override_rev7(dev, 0x0800, tmp, core, off, 0); + tmp = value >> 8; + b43_nphy_rf_ctl_override_rev7(dev, 0x6000, tmp, core, off, 0); + break; + case N_RF_CTL_OVER_CMD_TX_GAIN: + tmp = value & 0x7FFF; + b43_nphy_rf_ctl_override_rev7(dev, 0x1000, tmp, core, off, 0); + tmp = value >> 14; + b43_nphy_rf_ctl_override_rev7(dev, 0x4000, tmp, core, off, 0); + break; + } +} + /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/RFCtrlOverride */ static void b43_nphy_rf_ctl_override(struct b43_wldev *dev, u16 field, u16 value, u8 core, bool off) @@ -520,6 +574,14 @@ static void b43_nphy_stay_in_carrier_search(struct b43_wldev *dev, bool enable) } } +/* http://bcm-v4.sipsolutions.net/PHY/N/Read_Lpf_Bw_Ctl */ +static u16 b43_nphy_read_lpf_ctl(struct b43_wldev *dev, u16 offset) +{ + if (!offset) + offset = b43_is_40mhz(dev) ? 0x159 : 0x154; + return b43_ntab_read(dev, B43_NTAB16(7, offset)) & 0x7; +} + /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/AdjustLnaGainTbl */ static void b43_nphy_adjust_lna_gain_table(struct b43_wldev *dev) { @@ -1410,7 +1472,23 @@ static void b43_nphy_run_samples(struct b43_wldev *dev, u16 samps, u16 loops, b43_nphy_stay_in_carrier_search(dev, true); if (phy->rev >= 7) { - /* TODO */ + bool lpf_bw3, lpf_bw4; + + lpf_bw3 = b43_phy_read(dev, B43_NPHY_REV7_RF_CTL_OVER3) & 0x80; + lpf_bw4 = b43_phy_read(dev, B43_NPHY_REV7_RF_CTL_OVER3) & 0x80; + + if (lpf_bw3 || lpf_bw4) { + /* TODO */ + } else { + u16 value = b43_nphy_read_lpf_ctl(dev, 0); + if (phy->rev >= 19) + b43_nphy_rf_ctl_override_rev19(dev, 0x80, value, + 0, false, 1); + else + b43_nphy_rf_ctl_override_rev7(dev, 0x80, value, + 0, false, 1); + nphy->lpf_bw_overrode_for_sample_play = true; + } } if ((nphy->bb_mult_save & 0x80000000) == 0) { @@ -1857,12 +1935,14 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev) B43_NPHY_AFECTL_OVER1, B43_NPHY_AFECTL_OVER, B43_NPHY_AFECTL_C1, B43_NPHY_AFECTL_C2, B43_NPHY_TXF_40CO_B1S1, B43_NPHY_RFCTL_OVER, - 0x342, 0x343, 0x346, 0x347, + B43_NPHY_REV7_RF_CTL_OVER3, B43_NPHY_REV7_RF_CTL_OVER4, + B43_NPHY_REV7_RF_CTL_OVER5, B43_NPHY_REV7_RF_CTL_OVER6, 0x2ff, B43_NPHY_TXF_40CO_B1S0, B43_NPHY_TXF_40CO_B32S1, B43_NPHY_RFCTL_CMD, B43_NPHY_RFCTL_LUT_TRSW_UP1, B43_NPHY_RFCTL_LUT_TRSW_UP2, - 0x340, 0x341, 0x344, 0x345, + B43_NPHY_REV7_RF_CTL_MISC_REG3, B43_NPHY_REV7_RF_CTL_MISC_REG4, + B43_NPHY_REV7_RF_CTL_MISC_REG5, B43_NPHY_REV7_RF_CTL_MISC_REG6, B43_NPHY_RFCTL_RSSIO1, B43_NPHY_RFCTL_RSSIO2 }; u16 *regs_to_store; @@ -1909,9 +1989,24 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev) b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW, 1, 7); if (dev->phy.rev >= 7) { - /* TODO */ + b43_nphy_rf_ctl_override_one_to_many(dev, + N_RF_CTL_OVER_CMD_RXRF_PU, + 0, 0, false); + b43_nphy_rf_ctl_override_one_to_many(dev, + N_RF_CTL_OVER_CMD_RX_PU, + 1, 0, false); + b43_nphy_rf_ctl_override_rev7(dev, 0x80, 1, 0, false, 0); + b43_nphy_rf_ctl_override_rev7(dev, 0x80, 1, 0, false, 0); if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + b43_nphy_rf_ctl_override_rev7(dev, 0x20, 0, 0, false, + 0); + b43_nphy_rf_ctl_override_rev7(dev, 0x10, 1, 0, false, + 0); } else { + b43_nphy_rf_ctl_override_rev7(dev, 0x10, 0, 0, false, + 0); + b43_nphy_rf_ctl_override_rev7(dev, 0x20, 1, 0, false, + 0); } } else { b43_nphy_rf_ctl_override(dev, 0x1, 0, 0, false); @@ -1940,7 +2035,10 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev) /* Grab RSSI results for every possible VCM */ for (vcm = 0; vcm < 8; vcm++) { if (dev->phy.rev >= 7) - ; + b43_radio_maskset(dev, + core ? R2057_NB_MASTER_CORE1 : + R2057_NB_MASTER_CORE0, + ~R2057_VCM_MASK, vcm); else b43_radio_maskset(dev, r | B2056_RX_RSSI_MISC, 0xE3, vcm << 2); @@ -1971,7 +2069,10 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev) /* Select the best VCM */ if (dev->phy.rev >= 7) - ; + b43_radio_maskset(dev, + core ? R2057_NB_MASTER_CORE1 : + R2057_NB_MASTER_CORE0, + ~R2057_VCM_MASK, vcm); else b43_radio_maskset(dev, r | B2056_RX_RSSI_MISC, 0xE3, vcm_final << 2); @@ -2041,6 +2142,10 @@ static void b43_nphy_rev3_rssi_cal(struct b43_wldev *dev) rssical_phy_regs = nphy->rssical_cache.rssical_phy_regs_5G; } if (dev->phy.rev >= 7) { + rssical_radio_regs[0] = b43_radio_read(dev, + R2057_NB_MASTER_CORE0); + rssical_radio_regs[1] = b43_radio_read(dev, + R2057_NB_MASTER_CORE1); } else { rssical_radio_regs[0] = b43_radio_read(dev, B2056_RX0 | B2056_RX_RSSI_MISC); @@ -2477,14 +2582,6 @@ static void b43_nphy_gain_ctl_workarounds(struct b43_wldev *dev) b43_nphy_gain_ctl_workarounds_rev1_2(dev); } -/* http://bcm-v4.sipsolutions.net/PHY/N/Read_Lpf_Bw_Ctl */ -static u16 b43_nphy_read_lpf_ctl(struct b43_wldev *dev, u16 offset) -{ - if (!offset) - offset = b43_is_40mhz(dev) ? 0x159 : 0x154; - return b43_ntab_read(dev, B43_NTAB16(7, offset)) & 0x7; -} - static void b43_nphy_workarounds_rev7plus(struct b43_wldev *dev) { struct ssb_sprom *sprom = dev->dev->bus_sprom; @@ -3171,6 +3268,7 @@ static void b43_nphy_update_txrx_chain(struct b43_wldev *dev) /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/stop-playback */ static void b43_nphy_stop_playback(struct b43_wldev *dev) { + struct b43_phy *phy = &dev->phy; struct b43_phy_n *nphy = dev->phy.n; u16 tmp; @@ -3191,8 +3289,13 @@ static void b43_nphy_stop_playback(struct b43_wldev *dev) nphy->bb_mult_save = 0; } - if (dev->phy.rev >= 7) { - /* TODO */ + if (phy->rev >= 7) { + if (phy->rev >= 19) + b43_nphy_rf_ctl_override_rev19(dev, 0x80, 0, 0, true, + 1); + else + b43_nphy_rf_ctl_override_rev7(dev, 0x80, 0, 0, true, 1); + nphy->lpf_bw_overrode_for_sample_play = false; } if (nphy->hang_avoid) @@ -3209,6 +3312,7 @@ static void b43_nphy_iq_cal_gain_params(struct b43_wldev *dev, u16 core, u16 gain; if (dev->phy.rev >= 3) { + params->tx_lpf = target.tx_lpf[core]; /* Rev 7+ */ params->txgm = target.txgm[core]; params->pga = target.pga[core]; params->pad = target.pad[core]; @@ -3216,7 +3320,7 @@ static void b43_nphy_iq_cal_gain_params(struct b43_wldev *dev, u16 core, if (phy->rev >= 19) { /* TODO */ } else if (phy->rev >= 7) { - /* TODO */ + params->cal_gain = (params->txgm << 12) | (params->pga << 8) | (params->pad << 3) | (params->ipa) | (params->tx_lpf << 15); } else { params->cal_gain = (params->txgm << 12) | (params->pga << 8) | (params->pad << 4) | (params->ipa); } @@ -3333,7 +3437,12 @@ static void b43_nphy_tx_power_ctrl(struct b43_wldev *dev, bool enable) if (phy->rev >= 19) { /* TODO */ } else if (phy->rev >= 7) { - /* TODO */ + b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, + ~B43_NPHY_TXPCTL_CMD_INIT, + 0x32); + b43_phy_maskset(dev, B43_NPHY_TXPCTL_INIT, + ~B43_NPHY_TXPCTL_INIT_PIDXI1, + 0x32); } else { b43_phy_maskset(dev, B43_NPHY_TXPCTL_CMD, ~B43_NPHY_TXPCTL_CMD_INIT, @@ -3921,8 +4030,7 @@ static void b43_nphy_pa_override(struct b43_wldev *dev, bool enable) B43_NPHY_RFCTL_INTC2); band = b43_current_band(dev->wl); if (dev->phy.rev >= 7) { - /* TODO */ - return; + tmp = 0x1480; } else if (dev->phy.rev >= 3) { if (band == IEEE80211_BAND_5GHZ) tmp = 0x600; @@ -4373,7 +4481,10 @@ static void b43_nphy_restore_rssi_cal(struct b43_wldev *dev) if (dev->phy.rev >= 19) { /* TODO */ } else if (dev->phy.rev >= 7) { - /* TODO */ + b43_radio_maskset(dev, R2057_NB_MASTER_CORE0, ~R2057_VCM_MASK, + rssical_radio_regs[0]); + b43_radio_maskset(dev, R2057_NB_MASTER_CORE1, ~R2057_VCM_MASK, + rssical_radio_regs[1]); } else { b43_radio_maskset(dev, B2056_RX0 | B2056_RX_RSSI_MISC, 0xE3, rssical_radio_regs[0]); @@ -4404,7 +4515,55 @@ static void b43_nphy_tx_cal_radio_setup_rev19(struct b43_wldev *dev) static void b43_nphy_tx_cal_radio_setup_rev7(struct b43_wldev *dev) { - /* TODO */ + struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = dev->phy.n; + u16 *save = nphy->tx_rx_cal_radio_saveregs; + int core, off; + u16 r, tmp; + + for (core = 0; core < 2; core++) { + r = core ? 0x20 : 0; + off = core * 11; + + save[off + 0] = b43_radio_read(dev, r + R2057_TX0_TX_SSI_MASTER); + save[off + 1] = b43_radio_read(dev, r + R2057_TX0_IQCAL_VCM_HG); + save[off + 2] = b43_radio_read(dev, r + R2057_TX0_IQCAL_IDAC); + save[off + 3] = b43_radio_read(dev, r + R2057_TX0_TSSI_VCM); + save[off + 4] = 0; + save[off + 5] = b43_radio_read(dev, r + R2057_TX0_TX_SSI_MUX); + if (phy->radio_rev != 5) + save[off + 6] = b43_radio_read(dev, r + R2057_TX0_TSSIA); + save[off + 7] = b43_radio_read(dev, r + R2057_TX0_TSSIG); + save[off + 8] = b43_radio_read(dev, r + R2057_TX0_TSSI_MISC1); + + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + b43_radio_write(dev, r + R2057_TX0_TX_SSI_MASTER, 0xA); + b43_radio_write(dev, r + R2057_TX0_IQCAL_VCM_HG, 0x43); + b43_radio_write(dev, r + R2057_TX0_IQCAL_IDAC, 0x55); + b43_radio_write(dev, r + R2057_TX0_TSSI_VCM, 0); + b43_radio_write(dev, r + R2057_TX0_TSSIG, 0); + if (nphy->use_int_tx_iq_lo_cal) { + b43_radio_write(dev, r + R2057_TX0_TX_SSI_MUX, 0x4); + tmp = true ? 0x31 : 0x21; /* TODO */ + b43_radio_write(dev, r + R2057_TX0_TSSIA, tmp); + } + b43_radio_write(dev, r + R2057_TX0_TSSI_MISC1, 0x00); + } else { + b43_radio_write(dev, r + R2057_TX0_TX_SSI_MASTER, 0x6); + b43_radio_write(dev, r + R2057_TX0_IQCAL_VCM_HG, 0x43); + b43_radio_write(dev, r + R2057_TX0_IQCAL_IDAC, 0x55); + b43_radio_write(dev, r + R2057_TX0_TSSI_VCM, 0); + + if (phy->radio_rev != 5) + b43_radio_write(dev, r + R2057_TX0_TSSIA, 0); + if (nphy->use_int_tx_iq_lo_cal) { + b43_radio_write(dev, r + R2057_TX0_TX_SSI_MUX, 0x6); + tmp = true ? 0x31 : 0x21; /* TODO */ + b43_radio_write(dev, r + R2057_TX0_TSSIG, tmp); + } + b43_radio_write(dev, r + R2057_TX0_TSSI_MISC1, 0); + } + } } /* http://bcm-v4.sipsolutions.net/802.11/PHY/N/TxCalRadioSetup */ @@ -4585,7 +4744,13 @@ static struct nphy_txgains b43_nphy_get_tx_gains(struct b43_wldev *dev) b43_nphy_stay_in_carrier_search(dev, false); for (i = 0; i < 2; ++i) { - if (dev->phy.rev >= 3) { + if (dev->phy.rev >= 7) { + target.ipa[i] = curr_gain[i] & 0x0007; + target.pad[i] = (curr_gain[i] & 0x00F8) >> 3; + target.pga[i] = (curr_gain[i] & 0x0F00) >> 8; + target.txgm[i] = (curr_gain[i] & 0x7000) >> 12; + target.tx_lpf[i] = (curr_gain[i] & 0x8000) >> 15; + } else if (dev->phy.rev >= 3) { target.ipa[i] = curr_gain[i] & 0x000F; target.pad[i] = (curr_gain[i] & 0x00F0) >> 4; target.pga[i] = (curr_gain[i] & 0x0F00) >> 8; @@ -4612,7 +4777,13 @@ static struct nphy_txgains b43_nphy_get_tx_gains(struct b43_wldev *dev) if (!table) break; - if (dev->phy.rev >= 3) { + if (dev->phy.rev >= 7) { + target.ipa[i] = (table[index[i]] >> 16) & 0x7; + target.pad[i] = (table[index[i]] >> 19) & 0x1F; + target.pga[i] = (table[index[i]] >> 24) & 0xF; + target.txgm[i] = (table[index[i]] >> 28) & 0x7; + target.tx_lpf[i] = (table[index[i]] >> 31) & 0x1; + } else if (dev->phy.rev >= 3) { target.ipa[i] = (table[index[i]] >> 16) & 0xF; target.pad[i] = (table[index[i]] >> 20) & 0xF; target.pga[i] = (table[index[i]] >> 24) & 0xF; @@ -4662,6 +4833,7 @@ static void b43_nphy_tx_cal_phy_cleanup(struct b43_wldev *dev) static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; + struct b43_phy_n *nphy = dev->phy.n; u16 *regs = dev->phy.n->tx_rx_cal_phy_saveregs; u16 tmp; @@ -4693,7 +4865,12 @@ static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev) regs[7] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC1); regs[8] = b43_phy_read(dev, B43_NPHY_RFCTL_INTC2); - b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_PA, 1, 3); + if (!nphy->use_int_tx_iq_lo_cal) + b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_PA, + 1, 3); + else + b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_PA, + 0, 3); b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW, 2, 1); b43_nphy_rf_ctl_intc_override(dev, N_INTC_OVERRIDE_TRSW, 8, 2); @@ -4702,18 +4879,30 @@ static void b43_nphy_tx_cal_phy_setup(struct b43_wldev *dev) b43_phy_mask(dev, B43_NPHY_PAPD_EN0, ~0x0001); b43_phy_mask(dev, B43_NPHY_PAPD_EN1, ~0x0001); + tmp = b43_nphy_read_lpf_ctl(dev, 0); if (phy->rev >= 19) - ; /* TODO */ + b43_nphy_rf_ctl_override_rev19(dev, 0x80, tmp, 0, false, + 1); else if (phy->rev >= 7) - ; /* TODO */ + b43_nphy_rf_ctl_override_rev7(dev, 0x80, tmp, 0, false, + 1); - if (0 /* FIXME */) { + if (nphy->use_int_tx_iq_lo_cal && true /* FIXME */) { if (phy->rev >= 19) { - /* TODO */ + b43_nphy_rf_ctl_override_rev19(dev, 0x8, 0, 0x3, + false, 0); } else if (phy->rev >= 8) { - /* TODO */ + b43_nphy_rf_ctl_override_rev7(dev, 0x8, 0, 0x3, + false, 0); } else if (phy->rev == 7) { - /* TODO */ + b43_radio_maskset(dev, R2057_OVR_REG0, 1 << 4, 1 << 4); + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + b43_radio_maskset(dev, R2057_PAD2G_TUNE_PUS_CORE0, ~1, 0); + b43_radio_maskset(dev, R2057_PAD2G_TUNE_PUS_CORE1, ~1, 0); + } else { + b43_radio_maskset(dev, R2057_IPA5G_CASCOFFV_PU_CORE0, ~1, 0); + b43_radio_maskset(dev, R2057_IPA5G_CASCOFFV_PU_CORE1, ~1, 0); + } } } } else { @@ -4772,7 +4961,22 @@ static void b43_nphy_save_cal(struct b43_wldev *dev) if (phy->rev >= 19) { /* TODO */ } else if (phy->rev >= 7) { - /* TODO */ + txcal_radio_regs[0] = b43_radio_read(dev, + R2057_TX0_LOFT_FINE_I); + txcal_radio_regs[1] = b43_radio_read(dev, + R2057_TX0_LOFT_FINE_Q); + txcal_radio_regs[4] = b43_radio_read(dev, + R2057_TX0_LOFT_COARSE_I); + txcal_radio_regs[5] = b43_radio_read(dev, + R2057_TX0_LOFT_COARSE_Q); + txcal_radio_regs[2] = b43_radio_read(dev, + R2057_TX1_LOFT_FINE_I); + txcal_radio_regs[3] = b43_radio_read(dev, + R2057_TX1_LOFT_FINE_Q); + txcal_radio_regs[6] = b43_radio_read(dev, + R2057_TX1_LOFT_COARSE_I); + txcal_radio_regs[7] = b43_radio_read(dev, + R2057_TX1_LOFT_COARSE_Q); } else if (phy->rev >= 3) { txcal_radio_regs[0] = b43_radio_read(dev, 0x2021); txcal_radio_regs[1] = b43_radio_read(dev, 0x2022); @@ -4851,7 +5055,22 @@ static void b43_nphy_restore_cal(struct b43_wldev *dev) if (phy->rev >= 19) { /* TODO */ } else if (phy->rev >= 7) { - /* TODO */ + b43_radio_write(dev, R2057_TX0_LOFT_FINE_I, + txcal_radio_regs[0]); + b43_radio_write(dev, R2057_TX0_LOFT_FINE_Q, + txcal_radio_regs[1]); + b43_radio_write(dev, R2057_TX0_LOFT_COARSE_I, + txcal_radio_regs[4]); + b43_radio_write(dev, R2057_TX0_LOFT_COARSE_Q, + txcal_radio_regs[5]); + b43_radio_write(dev, R2057_TX1_LOFT_FINE_I, + txcal_radio_regs[2]); + b43_radio_write(dev, R2057_TX1_LOFT_FINE_Q, + txcal_radio_regs[3]); + b43_radio_write(dev, R2057_TX1_LOFT_COARSE_I, + txcal_radio_regs[6]); + b43_radio_write(dev, R2057_TX1_LOFT_COARSE_Q, + txcal_radio_regs[7]); } else if (phy->rev >= 3) { b43_radio_write(dev, 0x2021, txcal_radio_regs[0]); b43_radio_write(dev, 0x2022, txcal_radio_regs[1]); @@ -4932,7 +5151,7 @@ static int b43_nphy_cal_tx_iq_lo(struct b43_wldev *dev, if (phy->rev >= 19) { /* TODO */ } else if (phy->rev >= 7) { - /* TODO */ + b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8AD9); } else { b43_phy_write(dev, B43_NPHY_IQLOCAL_CMDGCTL, 0x8AA9); } @@ -5496,6 +5715,10 @@ static int b43_phy_initn(struct b43_wldev *dev) #endif } } + nphy->use_int_tx_iq_lo_cal = b43_nphy_ipa(dev) || + phy->rev >= 7 || + (phy->rev >= 5 && + sprom->boardflags2_hi & B43_BFH2_INTERNDET_TXIQCAL); nphy->deaf_count = 0; b43_nphy_tables_init(dev); nphy->crsminpwr_adjusted = false; @@ -5506,7 +5729,10 @@ static int b43_phy_initn(struct b43_wldev *dev) b43_phy_write(dev, B43_NPHY_TXF_40CO_B1S1, 0); b43_phy_write(dev, B43_NPHY_RFCTL_OVER, 0); if (phy->rev >= 7) { - /* TODO */ + b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER3, 0); + b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER4, 0); + b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER5, 0); + b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER6, 0); } if (phy->rev >= 19) { /* TODO */ @@ -6032,13 +6258,17 @@ static void b43_nphy_op_software_rfkill(struct b43_wldev *dev, b43err(dev->wl, "MAC not suspended\n"); if (blocked) { - b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, - ~B43_NPHY_RFCTL_CMD_CHIP0PU); if (phy->rev >= 19) { /* TODO */ + } else if (phy->rev >= 8) { + b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, + ~B43_NPHY_RFCTL_CMD_CHIP0PU); } else if (phy->rev >= 7) { - /* TODO */ + /* Nothing needed */ } else if (phy->rev >= 3) { + b43_phy_mask(dev, B43_NPHY_RFCTL_CMD, + ~B43_NPHY_RFCTL_CMD_CHIP0PU); + b43_radio_mask(dev, 0x09, ~0x2); b43_radio_write(dev, 0x204D, 0); diff --git a/drivers/net/wireless/b43/phy_n.h b/drivers/net/wireless/b43/phy_n.h index ecfbf66dbc3b..7fd5d5f9161d 100644 --- a/drivers/net/wireless/b43/phy_n.h +++ b/drivers/net/wireless/b43/phy_n.h @@ -857,6 +857,15 @@ #define B43_NPHY_REV3_C2_CLIP2_GAIN_A B43_PHY_N(0x2AF) #define B43_NPHY_REV3_C2_CLIP2_GAIN_B B43_PHY_N(0x2B0) +#define B43_NPHY_REV7_RF_CTL_MISC_REG3 B43_PHY_N(0x340) +#define B43_NPHY_REV7_RF_CTL_MISC_REG4 B43_PHY_N(0x341) +#define B43_NPHY_REV7_RF_CTL_OVER3 B43_PHY_N(0x342) +#define B43_NPHY_REV7_RF_CTL_OVER4 B43_PHY_N(0x343) +#define B43_NPHY_REV7_RF_CTL_MISC_REG5 B43_PHY_N(0x344) +#define B43_NPHY_REV7_RF_CTL_MISC_REG6 B43_PHY_N(0x345) +#define B43_NPHY_REV7_RF_CTL_OVER5 B43_PHY_N(0x346) +#define B43_NPHY_REV7_RF_CTL_OVER6 B43_PHY_N(0x347) + #define B43_PHY_B_BBCFG B43_PHY_N_BMODE(0x001) /* BB config */ #define B43_PHY_B_TEST B43_PHY_N_BMODE(0x00A) @@ -935,6 +944,8 @@ struct b43_phy_n { bool gain_boost; bool elna_gain_config; bool band5g_pwrgain; + bool use_int_tx_iq_lo_cal; + bool lpf_bw_overrode_for_sample_play; u8 mphase_cal_phase_id; u16 mphase_txcal_cmdidx; diff --git a/drivers/net/wireless/b43/radio_2057.h b/drivers/net/wireless/b43/radio_2057.h index 675d1bb64429..220d080238ff 100644 --- a/drivers/net/wireless/b43/radio_2057.h +++ b/drivers/net/wireless/b43/radio_2057.h @@ -84,6 +84,8 @@ #define R2057_CMOSBUF_RX_RCCR 0x04c #define R2057_LOGEN_SEL_PKDET 0x04d #define R2057_CMOSBUF_SHAREIQ_PTAT 0x04e + +/* MISC core 0 */ #define R2057_RXTXBIAS_CONFIG_CORE0 0x04f #define R2057_TXGM_TXRF_PUS_CORE0 0x050 #define R2057_TXGM_IDAC_BLEED_CORE0 0x051 @@ -204,6 +206,8 @@ #define R2057_RXBB_GPAIOSEL_RXLPF_RCCAL_CORE0 0x0d1 #define R2057_LPF_GAIN_CORE0 0x0d2 #define R2057_DACBUF_IDACS_BW_CORE0 0x0d3 + +/* MISC core 1 */ #define R2057_RXTXBIAS_CONFIG_CORE1 0x0d4 #define R2057_TXGM_TXRF_PUS_CORE1 0x0d5 #define R2057_TXGM_IDAC_BLEED_CORE1 0x0d6 @@ -324,6 +328,7 @@ #define R2057_RXBB_GPAIOSEL_RXLPF_RCCAL_CORE1 0x156 #define R2057_LPF_GAIN_CORE1 0x157 #define R2057_DACBUF_IDACS_BW_CORE1 0x158 + #define R2057_DACBUF_VINCM_CORE1 0x159 #define R2057_RCCAL_START_R1_Q1_P1 0x15a #define R2057_RCCAL_X1 0x15b @@ -345,6 +350,8 @@ #define R2057_RCCAL_BCAP_VAL 0x16b #define R2057_RCCAL_HPC_VAL 0x16c #define R2057_RCCAL_OVERRIDES 0x16d + +/* TX core 0 */ #define R2057_TX0_IQCAL_GAIN_BW 0x170 #define R2057_TX0_LOFT_FINE_I 0x171 #define R2057_TX0_LOFT_FINE_Q 0x172 @@ -362,6 +369,8 @@ #define R2057_TX0_TXRXCOUPLE_2G_PWRUP 0x17e #define R2057_TX0_TXRXCOUPLE_5G_ATTEN 0x17f #define R2057_TX0_TXRXCOUPLE_5G_PWRUP 0x180 + +/* TX core 1 */ #define R2057_TX1_IQCAL_GAIN_BW 0x190 #define R2057_TX1_LOFT_FINE_I 0x191 #define R2057_TX1_LOFT_FINE_Q 0x192 @@ -379,6 +388,7 @@ #define R2057_TX1_TXRXCOUPLE_2G_PWRUP 0x19e #define R2057_TX1_TXRXCOUPLE_5G_ATTEN 0x19f #define R2057_TX1_TXRXCOUPLE_5G_PWRUP 0x1a0 + #define R2057_AFE_VCM_CAL_MASTER_CORE0 0x1a1 #define R2057_AFE_SET_VCM_I_CORE0 0x1a2 #define R2057_AFE_SET_VCM_Q_CORE0 0x1a3 From 162bee1a3e5714abd9a429d85c64830bacaca682 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 8 Jul 2014 15:11:08 +0200 Subject: [PATCH 296/378] b43: N-PHY: init and channel switching of radio 0x2057 rev 9 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/phy_n.c | 47 ++++++- drivers/net/wireless/b43/radio_2057.c | 188 ++++++++++++++++++++++++++ 2 files changed, 234 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index 44349f5b99c6..479cda88ca5e 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -746,10 +746,55 @@ static void b43_radio_2057_setup(struct b43_wldev *dev, b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, 0x8); } break; + case 9: /* e.g. PHY rev 16 */ + b43_radio_write(dev, R2057_LOGEN_PTAT_RESETS, 0x20); + b43_radio_write(dev, R2057_VCOBUF_IDACS, 0x18); + if (b43_current_band(dev->wl) == IEEE80211_BAND_5GHZ) { + b43_radio_write(dev, R2057_LOGEN_PTAT_RESETS, 0x38); + b43_radio_write(dev, R2057_VCOBUF_IDACS, 0x0f); + + if (b43_is_40mhz(dev)) { + /* TODO */ + } else { + b43_radio_write(dev, + R2057_PAD_BIAS_FILTER_BWS_CORE0, + 0x3c); + b43_radio_write(dev, + R2057_PAD_BIAS_FILTER_BWS_CORE1, + 0x3c); + } + } + break; /* TODO */ } - /* TODO */ + if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { + u16 txmix2g_tune_boost_pu = 0; + u16 pad2g_tune_pus = 0; + + if (b43_nphy_ipa(dev)) { + switch (phy->radio_rev) { + case 9: + txmix2g_tune_boost_pu = 0x0041; + /* TODO */ + break; + } + /* TODO */ + } + + if (txmix2g_tune_boost_pu) + b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE0, + txmix2g_tune_boost_pu); + if (pad2g_tune_pus) + b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE0, + pad2g_tune_pus); + if (txmix2g_tune_boost_pu) + b43_radio_write(dev, R2057_TXMIX2G_TUNE_BOOST_PU_CORE1, + txmix2g_tune_boost_pu); + if (pad2g_tune_pus) + b43_radio_write(dev, R2057_PAD2G_TUNE_PUS_CORE1, + pad2g_tune_pus); + } usleep_range(50, 100); diff --git a/drivers/net/wireless/b43/radio_2057.c b/drivers/net/wireless/b43/radio_2057.c index df3574545819..941cf3db5a64 100644 --- a/drivers/net/wireless/b43/radio_2057.c +++ b/drivers/net/wireless/b43/radio_2057.c @@ -105,6 +105,18 @@ static u16 r2057_rev8_init[][2] = { }; */ +/* Extracted from MMIO dump of 6.30.223.141 */ +static u16 r2057_rev9_init[][2] = { + { 0x27, 0x1f }, { 0x28, 0x0a }, { 0x29, 0x2f }, { 0x42, 0x1f }, + { 0x48, 0x3f }, { 0x5c, 0x41 }, { 0x63, 0x14 }, { 0x64, 0x12 }, + { 0x66, 0xff }, { 0x74, 0xa3 }, { 0x7b, 0x14 }, { 0x7c, 0x14 }, + { 0x7d, 0xee }, { 0x86, 0xc0 }, { 0xc4, 0x10 }, { 0xc9, 0x01 }, + { 0xe1, 0x41 }, { 0xe8, 0x14 }, { 0xe9, 0x12 }, { 0xeb, 0xff }, + { 0xf5, 0x0a }, { 0xf8, 0x09 }, { 0xf9, 0xa3 }, { 0x100, 0x14 }, + { 0x101, 0x10 }, { 0x102, 0xee }, { 0x10b, 0xc0 }, { 0x149, 0x10 }, + { 0x14e, 0x01 }, { 0x1b7, 0x05 }, { 0x1c2, 0xa0 }, +}; + #define RADIOREGS7(r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, \ r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, \ r20, r21, r22, r23, r24, r25, r26, r27) \ @@ -145,6 +157,170 @@ static u16 r2057_rev8_init[][2] = { .phy_regs.phy_bw5 = r4, \ .phy_regs.phy_bw6 = r5 +/* Extracted from MMIO dump of 6.30.223.141 */ +static const struct b43_nphy_chantabent_rev7 b43_nphy_chantab_phy_rev16_radio_rev9[] = { + { + .freq = 2412, + RADIOREGS7(0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, + 0x09, 0x0f, 0x0a, 0x00, 0x0a, 0x00, 0x41, 0x63, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, + 0x00, 0x00, 0xf0, 0x00), + PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), + }, + { + .freq = 2417, + RADIOREGS7(0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, + 0x09, 0x0f, 0x0a, 0x00, 0x0a, 0x00, 0x41, 0x63, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, + 0x00, 0x00, 0xf0, 0x00), + PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), + }, + { + .freq = 2422, + RADIOREGS7(0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, + 0x09, 0x0f, 0x09, 0x00, 0x09, 0x00, 0x41, 0x63, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, + 0x00, 0x00, 0xf0, 0x00), + PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), + }, + { + .freq = 2427, + RADIOREGS7(0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, + 0x09, 0x0f, 0x09, 0x00, 0x09, 0x00, 0x41, 0x63, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, + 0x00, 0x00, 0xf0, 0x00), + PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), + }, + { + .freq = 2432, + RADIOREGS7(0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, + 0x09, 0x0f, 0x08, 0x00, 0x08, 0x00, 0x41, 0x63, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, + 0x00, 0x00, 0xf0, 0x00), + PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), + }, + { + .freq = 2437, + RADIOREGS7(0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, + 0x09, 0x0f, 0x08, 0x00, 0x08, 0x00, 0x41, 0x63, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, + 0x00, 0x00, 0xf0, 0x00), + PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), + }, + { + .freq = 2442, + RADIOREGS7(0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, + 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x41, 0x63, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, + 0x00, 0x00, 0xf0, 0x00), + PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), + }, + { + .freq = 2447, + RADIOREGS7(0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, + 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x41, 0x63, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, + 0x00, 0x00, 0xf0, 0x00), + PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), + }, + { + .freq = 2452, + RADIOREGS7(0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, + 0x09, 0x0f, 0x07, 0x00, 0x07, 0x00, 0x41, 0x63, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, + 0x00, 0x00, 0xf0, 0x00), + PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), + }, + { + .freq = 2457, + RADIOREGS7(0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, + 0x09, 0x0f, 0x06, 0x00, 0x06, 0x00, 0x41, 0x63, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, + 0x00, 0x00, 0xf0, 0x00), + PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), + }, + { + .freq = 2462, + RADIOREGS7(0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, + 0x09, 0x0f, 0x06, 0x00, 0x06, 0x00, 0x41, 0x63, + 0x00, 0x00, 0x00, 0xf0, 0x00, 0x41, 0x63, 0x00, + 0x00, 0x00, 0xf0, 0x00), + PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), + }, + { + .freq = 5180, + RADIOREGS7(0xbe, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x06, + 0x02, 0x0e, 0x00, 0x0e, 0x00, 0x9e, 0x00, 0x00, + 0x9f, 0x2f, 0xa3, 0x00, 0xfc, 0x00, 0x00, 0x4f, + 0x3a, 0x83, 0x00, 0xfc), + PHYREGS(0x081c, 0x0818, 0x0814, 0x01f9, 0x01fa, 0x01fb), + }, + { + .freq = 5200, + RADIOREGS7(0xc5, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x08, + 0x02, 0x0e, 0x00, 0x0e, 0x00, 0x9e, 0x00, 0x00, + 0x7f, 0x2f, 0x83, 0x00, 0xf8, 0x00, 0x00, 0x4c, + 0x4a, 0x83, 0x00, 0xf8), + PHYREGS(0x0824, 0x0820, 0x081c, 0x01f7, 0x01f8, 0x01f9), + }, + { + .freq = 5220, + RADIOREGS7(0xcc, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x0a, + 0x02, 0x0e, 0x00, 0x0e, 0x00, 0x9e, 0x00, 0x00, + 0x6d, 0x3d, 0x83, 0x00, 0xf8, 0x00, 0x00, 0x2d, + 0x2a, 0x73, 0x00, 0xf8), + PHYREGS(0x082c, 0x0828, 0x0824, 0x01f5, 0x01f6, 0x01f7), + }, + { + .freq = 5240, + RADIOREGS7(0xd2, 0x16, 0x10, 0x1f, 0x08, 0x08, 0x3f, 0x0c, + 0x02, 0x0d, 0x00, 0x0d, 0x00, 0x8d, 0x00, 0x00, + 0x4d, 0x1c, 0x73, 0x00, 0xf8, 0x00, 0x00, 0x4d, + 0x2b, 0x73, 0x00, 0xf8), + PHYREGS(0x0834, 0x0830, 0x082c, 0x01f3, 0x01f4, 0x01f5), + }, + { + .freq = 5745, + RADIOREGS7(0x7b, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x7d, + 0x04, 0x08, 0x00, 0x06, 0x00, 0x15, 0x00, 0x00, + 0x08, 0x03, 0x03, 0x00, 0x30, 0x00, 0x00, 0x06, + 0x02, 0x03, 0x00, 0x30), + PHYREGS(0x08fe, 0x08fa, 0x08f6, 0x01c8, 0x01c8, 0x01c9), + }, + { + .freq = 5765, + RADIOREGS7(0x81, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x81, + 0x04, 0x08, 0x00, 0x06, 0x00, 0x15, 0x00, 0x00, + 0x06, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x02, 0x03, 0x00, 0x00), + PHYREGS(0x0906, 0x0902, 0x08fe, 0x01c6, 0x01c7, 0x01c8), + }, + { + .freq = 5785, + RADIOREGS7(0x88, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x85, + 0x04, 0x08, 0x00, 0x06, 0x00, 0x15, 0x00, 0x00, + 0x08, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x21, 0x03, 0x00, 0x00), + PHYREGS(0x090e, 0x090a, 0x0906, 0x01c4, 0x01c5, 0x01c6), + }, + { + .freq = 5805, + RADIOREGS7(0x8f, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x89, + 0x04, 0x07, 0x00, 0x06, 0x00, 0x04, 0x00, 0x00, + 0x06, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x03, 0x00, 0x00), + PHYREGS(0x0916, 0x0912, 0x090e, 0x01c3, 0x01c4, 0x01c4), + }, + { + .freq = 5825, + RADIOREGS7(0x95, 0x17, 0x20, 0x1f, 0x08, 0x08, 0x3f, 0x8d, + 0x04, 0x07, 0x00, 0x05, 0x00, 0x03, 0x00, 0x00, + 0x05, 0x03, 0x03, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x03, 0x00, 0x00), + PHYREGS(0x091e, 0x091a, 0x0916, 0x01c1, 0x01c2, 0x01c3), + }, +}; + void r2057_upload_inittabs(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; @@ -171,6 +347,12 @@ void r2057_upload_inittabs(struct b43_wldev *dev) size = ARRAY_SIZE(r2057_rev5a_init); } break; + case 16: + if (phy->radio_rev == 9) { + table = r2057_rev9_init[0]; + size = ARRAY_SIZE(r2057_rev9_init); + } + break; } B43_WARN_ON(!table); @@ -195,6 +377,12 @@ void r2057_get_chantabent_rev7(struct b43_wldev *dev, u16 freq, /* TODO */ switch (phy->rev) { + case 16: + if (phy->radio_rev == 9) { + e_r7 = b43_nphy_chantab_phy_rev16_radio_rev9; + len = ARRAY_SIZE(b43_nphy_chantab_phy_rev16_radio_rev9); + } + break; default: break; } From 785e7dbb75d2b3109daad37a261b9b66ece393c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 8 Jul 2014 15:11:09 +0200 Subject: [PATCH 297/378] b43: N-PHY: implement channel switching of radio 0x2057 rev 5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/radio_2057.c | 129 ++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) diff --git a/drivers/net/wireless/b43/radio_2057.c b/drivers/net/wireless/b43/radio_2057.c index 941cf3db5a64..ca22faa41d28 100644 --- a/drivers/net/wireless/b43/radio_2057.c +++ b/drivers/net/wireless/b43/radio_2057.c @@ -149,6 +149,27 @@ static u16 r2057_rev9_init[][2] = { .radio_lna2g_tune_core1 = r26, \ .radio_lna5g_tune_core1 = r27 +#define RADIOREGS7_2G(r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, \ + r10, r11, r12, r13, r14, r15, r16, r17) \ + .radio_vcocal_countval0 = r00, \ + .radio_vcocal_countval1 = r01, \ + .radio_rfpll_refmaster_sparextalsize = r02, \ + .radio_rfpll_loopfilter_r1 = r03, \ + .radio_rfpll_loopfilter_c2 = r04, \ + .radio_rfpll_loopfilter_c1 = r05, \ + .radio_cp_kpd_idac = r06, \ + .radio_rfpll_mmd0 = r07, \ + .radio_rfpll_mmd1 = r08, \ + .radio_vcobuf_tune = r09, \ + .radio_logen_mx2g_tune = r10, \ + .radio_logen_indbuf2g_tune = r11, \ + .radio_lna2g_tune_core0 = r12, \ + .radio_txmix2g_tune_boost_pu_core0 = r13, \ + .radio_pad2g_tune_pus_core0 = r14, \ + .radio_lna2g_tune_core1 = r15, \ + .radio_txmix2g_tune_boost_pu_core1 = r16, \ + .radio_pad2g_tune_pus_core1 = r17 + #define PHYREGS(r0, r1, r2, r3, r4, r5) \ .phy_regs.phy_bw1a = r0, \ .phy_regs.phy_bw2 = r1, \ @@ -157,6 +178,108 @@ static u16 r2057_rev9_init[][2] = { .phy_regs.phy_bw5 = r4, \ .phy_regs.phy_bw6 = r5 +/* Copied from brcmsmac (5.75.11): chan_info_nphyrev8_2057_rev5 */ +static const struct b43_nphy_chantabent_rev7_2g b43_nphy_chantab_phy_rev8_radio_rev5[] = { + { + .freq = 2412, + RADIOREGS7_2G(0x48, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x6c, + 0x09, 0x0d, 0x08, 0x0e, 0x61, 0x03, 0xff, 0x61, + 0x03, 0xff), + PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), + }, + { + .freq = 2417, + RADIOREGS7_2G(0x4b, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x71, + 0x09, 0x0d, 0x08, 0x0e, 0x61, 0x03, 0xff, 0x61, + 0x03, 0xff), + PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), + }, + { + .freq = 2422, + RADIOREGS7_2G(0x4e, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x76, + 0x09, 0x0d, 0x08, 0x0e, 0x61, 0x03, 0xef, 0x61, + 0x03, 0xef), + PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), + }, + { + .freq = 2427, + RADIOREGS7_2G(0x52, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x7b, + 0x09, 0x0c, 0x08, 0x0e, 0x61, 0x03, 0xdf, 0x61, + 0x03, 0xdf), + PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), + }, + { + .freq = 2432, + RADIOREGS7_2G(0x55, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x80, + 0x09, 0x0c, 0x07, 0x0d, 0x61, 0x03, 0xcf, 0x61, + 0x03, 0xcf), + PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), + }, + { + .freq = 2437, + RADIOREGS7_2G(0x58, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x85, + 0x09, 0x0c, 0x07, 0x0d, 0x61, 0x03, 0xbf, 0x61, + 0x03, 0xbf), + PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), + }, + { + .freq = 2442, + RADIOREGS7_2G(0x5c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8a, + 0x09, 0x0b, 0x07, 0x0d, 0x61, 0x03, 0xaf, 0x61, + 0x03, 0xaf), + PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), + }, + { + .freq = 2447, + RADIOREGS7_2G(0x5f, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x8f, + 0x09, 0x0b, 0x07, 0x0d, 0x61, 0x03, 0x9f, 0x61, + 0x03, 0x9f), + PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), + }, + { + .freq = 2452, + RADIOREGS7_2G(0x62, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x94, + 0x09, 0x0b, 0x07, 0x0d, 0x61, 0x03, 0x8f, 0x61, + 0x03, 0x8f), + PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), + }, + { + .freq = 2457, + RADIOREGS7_2G(0x66, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x99, + 0x09, 0x0b, 0x07, 0x0c, 0x61, 0x03, 0x7f, 0x61, + 0x03, 0x7f), + PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), + }, + { + .freq = 2462, + RADIOREGS7_2G(0x69, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0x9e, + 0x09, 0x0b, 0x07, 0x0c, 0x61, 0x03, 0x6f, 0x61, + 0x03, 0x6f), + PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), + }, + { + .freq = 2467, + RADIOREGS7_2G(0x6c, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa3, + 0x09, 0x0b, 0x06, 0x0c, 0x61, 0x03, 0x5f, 0x61, + 0x03, 0x5f), + PHYREGS(0x03df, 0x03db, 0x03d7, 0x0422, 0x0427, 0x042b), + }, + { + .freq = 2472, + RADIOREGS7_2G(0x70, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xa8, + 0x09, 0x0a, 0x06, 0x0b, 0x61, 0x03, 0x4f, 0x61, + 0x03, 0x4f), + PHYREGS(0x03e1, 0x03dd, 0x03d9, 0x0420, 0x0424, 0x0429), + }, + { + .freq = 2484, + RADIOREGS7_2G(0x78, 0x16, 0x30, 0x1b, 0x0a, 0x0a, 0x30, 0xb4, + 0x09, 0x0a, 0x06, 0x0b, 0x61, 0x03, 0x3f, 0x61, + 0x03, 0x3f), + PHYREGS(0x03e6, 0x03e2, 0x03de, 0x041b, 0x041f, 0x0424), + } +}; + /* Extracted from MMIO dump of 6.30.223.141 */ static const struct b43_nphy_chantabent_rev7 b43_nphy_chantab_phy_rev16_radio_rev9[] = { { @@ -377,6 +500,12 @@ void r2057_get_chantabent_rev7(struct b43_wldev *dev, u16 freq, /* TODO */ switch (phy->rev) { + case 8: + if (phy->radio_rev == 5) { + e_r7_2g = b43_nphy_chantab_phy_rev8_radio_rev5; + len = ARRAY_SIZE(b43_nphy_chantab_phy_rev8_radio_rev5); + } + break; case 16: if (phy->radio_rev == 9) { e_r7 = b43_nphy_chantab_phy_rev16_radio_rev9; From 3695b9324ee9bb801d7e0e76fa991683997758d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 8 Jul 2014 15:11:10 +0200 Subject: [PATCH 298/378] b43: enable radio 0x2057 rev 9 (AKA BCM43228) support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support for N-PHY rev 8 with 0x2057 rev 5 is almost ready, but we still need to figure out how to handle rev 9 first. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 8dd69a3ae7ac..ad335307a3d4 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -210,6 +210,9 @@ static struct ieee80211_channel b43_2ghz_chantable[] = { CHAN2G(13, 2472, 0), CHAN2G(14, 2484, 0), }; + +/* No support for the last 3 channels (12, 13, 14) */ +#define b43_2ghz_chantable_limited_size 11 #undef CHAN2G #define CHAN4G(_channel, _flags) { \ @@ -335,6 +338,14 @@ static struct ieee80211_supported_band b43_band_2GHz = { .n_bitrates = b43_g_ratetable_size, }; +static struct ieee80211_supported_band b43_band_2ghz_limited = { + .band = IEEE80211_BAND_2GHZ, + .channels = b43_2ghz_chantable, + .n_channels = b43_2ghz_chantable_limited_size, + .bitrates = b43_g_ratetable, + .n_bitrates = b43_g_ratetable_size, +}; + static void b43_wireless_core_exit(struct b43_wldev *dev); static int b43_wireless_core_init(struct b43_wldev *dev); static struct b43_wldev * b43_wireless_core_stop(struct b43_wldev *dev); @@ -4459,7 +4470,10 @@ static int b43_phy_versioning(struct b43_wldev *dev) unsupported = 1; break; case B43_PHYTYPE_N: - if (radio_ver != 0x2055 && radio_ver != 0x2056) + if (radio_ver != 0x2055 && radio_ver != 0x2056 && + radio_ver != 0x2057) + unsupported = 1; + if (radio_ver == 0x2057 && !(radio_rev == 9)) unsupported = 1; break; case B43_PHYTYPE_LP: @@ -5095,9 +5109,15 @@ static int b43_setup_bands(struct b43_wldev *dev, bool have_2ghz_phy, bool have_5ghz_phy) { struct ieee80211_hw *hw = dev->wl->hw; + struct b43_phy *phy = &dev->phy; + bool limited_2g; + + /* We don't support all 2 GHz channels on some devices */ + limited_2g = phy->radio_ver == 0x2057 && phy->radio_rev == 9; if (have_2ghz_phy) - hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &b43_band_2GHz; + hw->wiphy->bands[IEEE80211_BAND_2GHZ] = limited_2g ? + &b43_band_2ghz_limited : &b43_band_2GHz; if (dev->phy.type == B43_PHYTYPE_N) { if (have_5ghz_phy) hw->wiphy->bands[IEEE80211_BAND_5GHZ] = &b43_band_5GHz_nphy; From 72fcd3d16c16cd47a897cd72cd9a231aab01ac5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 8 Jul 2014 21:00:19 +0200 Subject: [PATCH 299/378] b43: don't warn about no 5 GHz support on 2.4 GHz devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This could be a bit confusing to see warning about lacking support for 5 GHz band if your device supports 2.4 GHz only. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index ad335307a3d4..3dcd3aa38608 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -5268,14 +5268,16 @@ static int b43_wireless_core_attach(struct b43_wldev *dev) b43_supported_bands(dev, &have_2ghz_phy, &have_5ghz_phy); /* We don't support 5 GHz on some PHYs yet */ - switch (dev->phy.type) { - case B43_PHYTYPE_A: - case B43_PHYTYPE_G: - case B43_PHYTYPE_N: - case B43_PHYTYPE_LP: - case B43_PHYTYPE_HT: - b43warn(wl, "5 GHz band is unsupported on this PHY\n"); - have_5ghz_phy = false; + if (have_5ghz_phy) { + switch (dev->phy.type) { + case B43_PHYTYPE_A: + case B43_PHYTYPE_G: + case B43_PHYTYPE_N: + case B43_PHYTYPE_LP: + case B43_PHYTYPE_HT: + b43warn(wl, "5 GHz band is unsupported on this PHY\n"); + have_5ghz_phy = false; + } } if (!have_2ghz_phy && !have_5ghz_phy) { From 50d26aa338fb290f0488e8f87c1c080d2de26e21 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Fri, 11 Jul 2014 03:01:26 +0300 Subject: [PATCH 300/378] wlcore: save seq num only between recoveries We want seq num (freed_pkts) to be initialized on each new connection, but keep persistent between recoveries/suspends. Save the freed_pkts in the private block of the sta struct (we already do a similar thing for AP's stations). However, keep the old wlvif->total_freed_pkts in order to avoid too intrusive change. Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- drivers/net/wireless/ti/wlcore/cmd.c | 5 +- drivers/net/wireless/ti/wlcore/main.c | 77 +++++++++++++++++------ drivers/net/wireless/ti/wlcore/wlcore_i.h | 17 +++-- 3 files changed, 68 insertions(+), 31 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index e269c0a57017..d298ead88c70 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -372,9 +372,8 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) wl1271_tx_reset_link_queues(wl, *hlid); wl->links[*hlid].wlvif = NULL; - if (wlvif->bss_type == BSS_TYPE_STA_BSS || - (wlvif->bss_type == BSS_TYPE_AP_BSS && - *hlid == wlvif->ap.bcast_hlid)) { + if (wlvif->bss_type == BSS_TYPE_AP_BSS && + *hlid == wlvif->ap.bcast_hlid) { /* * save the total freed packets in the wlvif, in case this is * recovery or suspend diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 48f83868f9cb..2996cefe4aed 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -898,6 +898,41 @@ out: wlcore_set_partition(wl, &old_part); } +static void wlcore_save_freed_pkts(struct wl1271 *wl, struct wl12xx_vif *wlvif, + u8 hlid, struct ieee80211_sta *sta) +{ + struct wl1271_station *wl_sta; + + wl_sta = (void *)sta->drv_priv; + wl_sta->total_freed_pkts = wl->links[hlid].total_freed_pkts; + + /* + * increment the initial seq number on recovery to account for + * transmitted packets that we haven't yet got in the FW status + */ + if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) + wl_sta->total_freed_pkts += + WL1271_TX_SQN_POST_RECOVERY_PADDING; +} + +static void wlcore_save_freed_pkts_addr(struct wl1271 *wl, + struct wl12xx_vif *wlvif, + u8 hlid, const u8 *addr) +{ + struct ieee80211_sta *sta; + struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); + + if (WARN_ON(hlid == WL12XX_INVALID_LINK_ID || + is_zero_ether_addr(addr))) + return; + + rcu_read_lock(); + sta = ieee80211_find_sta(vif, addr); + if (sta) + wlcore_save_freed_pkts(wl, wlvif, hlid, sta); + rcu_read_unlock(); +} + static void wlcore_print_recovery(struct wl1271 *wl) { u32 pc = 0; @@ -961,6 +996,13 @@ static void wl1271_recovery_work(struct work_struct *work) wlvif = list_first_entry(&wl->wlvif_list, struct wl12xx_vif, list); vif = wl12xx_wlvif_to_vif(wlvif); + + if (wlvif->bss_type == BSS_TYPE_STA_BSS && + test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) { + wlcore_save_freed_pkts_addr(wl, wlvif, wlvif->sta.hlid, + vif->bss_conf.bssid); + } + __wl1271_op_remove_interface(wl, vif, false); } @@ -4703,10 +4745,6 @@ static int wl1271_allocate_sta(struct wl1271 *wl, void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid) { - struct wl1271_station *wl_sta; - struct ieee80211_sta *sta; - struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); - if (!test_bit(hlid, wlvif->ap.sta_hlid_map)) return; @@ -4718,21 +4756,7 @@ void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid) * save the last used PN in the private part of iee80211_sta, * in case of recovery/suspend */ - rcu_read_lock(); - sta = ieee80211_find_sta(vif, wl->links[hlid].addr); - if (sta) { - wl_sta = (void *)sta->drv_priv; - wl_sta->total_freed_pkts = wl->links[hlid].total_freed_pkts; - - /* - * increment the initial seq number on recovery to account for - * transmitted packets that we haven't yet got in the FW status - */ - if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) - wl_sta->total_freed_pkts += - WL1271_TX_SQN_POST_RECOVERY_PADDING; - } - rcu_read_unlock(); + wlcore_save_freed_pkts_addr(wl, wlvif, hlid, wl->links[hlid].addr); wl12xx_free_link(wl, wlvif, &hlid); wl->active_sta_count--; @@ -4915,6 +4939,21 @@ static int wl12xx_update_sta_state(struct wl1271 *wl, clear_bit(WLVIF_FLAG_STA_STATE_SENT, &wlvif->flags); } + /* save seq number on disassoc (suspend) */ + if (is_sta && + old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTH) { + wlcore_save_freed_pkts(wl, wlvif, wlvif->sta.hlid, sta); + wlvif->total_freed_pkts = 0; + } + + /* restore seq number on assoc (resume) */ + if (is_sta && + old_state == IEEE80211_STA_AUTH && + new_state == IEEE80211_STA_ASSOC) { + wlvif->total_freed_pkts = wl_sta->total_freed_pkts; + } + /* clear ROCs on failure or authorization */ if (is_sta && (new_state == IEEE80211_STA_AUTHORIZED || diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index c2c34a84ff3d..986da43ecfdd 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -324,6 +324,7 @@ struct wl1271_station { * total freed FW packets on the link to the STA - used for tracking the * AES/TKIP PN across recoveries. Re-initialized each time from the * wl1271_station structure. + * Used in both AP and STA mode. */ u64 total_freed_pkts; }; @@ -459,6 +460,13 @@ struct wl12xx_vif { /* work for canceling ROC after pending auth reply */ struct delayed_work pending_auth_complete_work; + /* + * total freed FW packets on the link. + * For STA this holds the PN of the link to the AP. + * For AP this holds the PN of the broadcast link. + */ + u64 total_freed_pkts; + /* * This struct must be last! * data that has to be saved acrossed reconfigs (e.g. recovery) @@ -466,15 +474,6 @@ struct wl12xx_vif { */ struct { u8 persistent[0]; - - /* - * total freed FW packets on the link - used for - * storing the AES/TKIP PN during recovery, as this - * structure is not zeroed out. - * For STA this holds the PN of the link to the AP. - * For AP this holds the PN of the broadcast link. - */ - u64 total_freed_pkts; }; }; From 30a003588898924964dfa537670f35aac7cd9629 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Fri, 11 Jul 2014 03:01:27 +0300 Subject: [PATCH 301/378] wlcore: user smaller sqn padding for GEM On recovery, we increase the current seq num by WL1271_TX_SQN_POST_RECOVERY_PADDING in order to compensate for packets we might have missed during recovery. It seems that some GEM APs have issues when the gap is too big, so use a smaller padding in this case. Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- drivers/net/wireless/ti/wlcore/cmd.c | 7 +++++-- drivers/net/wireless/ti/wlcore/main.c | 7 +++++-- drivers/net/wireless/ti/wlcore/wlcore_i.h | 3 +++ 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ti/wlcore/cmd.c b/drivers/net/wireless/ti/wlcore/cmd.c index d298ead88c70..05604ee31224 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.c +++ b/drivers/net/wireless/ti/wlcore/cmd.c @@ -374,6 +374,7 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) if (wlvif->bss_type == BSS_TYPE_AP_BSS && *hlid == wlvif->ap.bcast_hlid) { + u32 sqn_padding = WL1271_TX_SQN_POST_RECOVERY_PADDING; /* * save the total freed packets in the wlvif, in case this is * recovery or suspend @@ -384,9 +385,11 @@ void wl12xx_free_link(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 *hlid) * increment the initial seq number on recovery to account for * transmitted packets that we haven't yet got in the FW status */ + if (wlvif->encryption_type == KEY_GEM) + sqn_padding = WL1271_TX_SQN_POST_RECOVERY_PADDING_GEM; + if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) - wlvif->total_freed_pkts += - WL1271_TX_SQN_POST_RECOVERY_PADDING; + wlvif->total_freed_pkts += sqn_padding; } wl->links[*hlid].total_freed_pkts = 0; diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 2996cefe4aed..1ab6dbdb47f3 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -902,6 +902,7 @@ static void wlcore_save_freed_pkts(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid, struct ieee80211_sta *sta) { struct wl1271_station *wl_sta; + u32 sqn_recovery_padding = WL1271_TX_SQN_POST_RECOVERY_PADDING; wl_sta = (void *)sta->drv_priv; wl_sta->total_freed_pkts = wl->links[hlid].total_freed_pkts; @@ -910,9 +911,11 @@ static void wlcore_save_freed_pkts(struct wl1271 *wl, struct wl12xx_vif *wlvif, * increment the initial seq number on recovery to account for * transmitted packets that we haven't yet got in the FW status */ + if (wlvif->encryption_type == KEY_GEM) + sqn_recovery_padding = WL1271_TX_SQN_POST_RECOVERY_PADDING_GEM; + if (test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS, &wl->flags)) - wl_sta->total_freed_pkts += - WL1271_TX_SQN_POST_RECOVERY_PADDING; + wl_sta->total_freed_pkts += sqn_recovery_padding; } static void wlcore_save_freed_pkts_addr(struct wl1271 *wl, diff --git a/drivers/net/wireless/ti/wlcore/wlcore_i.h b/drivers/net/wireless/ti/wlcore/wlcore_i.h index 986da43ecfdd..0e52556044d9 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore_i.h +++ b/drivers/net/wireless/ti/wlcore/wlcore_i.h @@ -45,6 +45,9 @@ #define WL1271_TX_SECURITY_LO16(s) ((u16)((s) & 0xffff)) #define WL1271_TX_SECURITY_HI32(s) ((u32)(((s) >> 16) & 0xffffffff)) #define WL1271_TX_SQN_POST_RECOVERY_PADDING 0xff +/* Use smaller padding for GEM, as some APs have issues when it's too big */ +#define WL1271_TX_SQN_POST_RECOVERY_PADDING_GEM 0x20 + #define WL1271_CIPHER_SUITE_GEM 0x00147201 From 601d6c4e701133ae23dd1b9507bf9d3a4172e586 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Fri, 11 Jul 2014 03:01:28 +0300 Subject: [PATCH 302/378] wl18xx: fix last tx rate calculation The last tx rate calculation didn't take into account the different indices of 11a and 11g rates tables. Add the required alignment (count only from the first 11a rate in case of 11a) Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- drivers/net/wireless/ti/wl18xx/tx.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ti/wl18xx/tx.c b/drivers/net/wireless/ti/wl18xx/tx.c index be1ebd55ac88..3406ffb53325 100644 --- a/drivers/net/wireless/ti/wl18xx/tx.c +++ b/drivers/net/wireless/ti/wl18xx/tx.c @@ -30,7 +30,7 @@ static void wl18xx_get_last_tx_rate(struct wl1271 *wl, struct ieee80211_vif *vif, - struct ieee80211_tx_rate *rate) + u8 band, struct ieee80211_tx_rate *rate) { u8 fw_rate = wl->fw_status->counters.tx_last_rate; @@ -43,6 +43,8 @@ void wl18xx_get_last_tx_rate(struct wl1271 *wl, struct ieee80211_vif *vif, if (fw_rate <= CONF_HW_RATE_INDEX_54MBPS) { rate->idx = fw_rate; + if (band == IEEE80211_BAND_5GHZ) + rate->idx -= CONF_HW_RATE_INDEX_6MBPS; rate->flags = 0; } else { rate->flags = IEEE80211_TX_RC_MCS; @@ -102,7 +104,8 @@ static void wl18xx_tx_complete_packet(struct wl1271 *wl, u8 tx_stat_byte) * first pass info->control.vif while it's valid, and then fill out * the info->status structures */ - wl18xx_get_last_tx_rate(wl, info->control.vif, &info->status.rates[0]); + wl18xx_get_last_tx_rate(wl, info->control.vif, + info->band, &info->status.rates[0]); info->status.rates[0].count = 1; /* no data about retries */ info->status.ack_signal = -1; From 71a301bb461da1e42e6d2764d99c683289c96f33 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Fri, 11 Jul 2014 03:01:29 +0300 Subject: [PATCH 303/378] wlcore: use correct LAA bit The LAA bit is second bit of the MSB, not of the third byte. Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- drivers/net/wireless/ti/wlcore/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 1ab6dbdb47f3..4c16262afa16 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5661,7 +5661,7 @@ static void wl12xx_derive_mac_addresses(struct wl1271 *wl, u32 oui, u32 nic) memcpy(&wl->addresses[idx], &wl->addresses[0], sizeof(wl->addresses[0])); /* LAA bit */ - wl->addresses[idx].addr[2] |= BIT(1); + wl->addresses[idx].addr[0] |= BIT(1); } wl->hw->wiphy->n_addresses = WLCORE_NUM_MAC_ADDRESSES; From 936c50dd0605d7d81772f53700ef42f45525ffad Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Fri, 11 Jul 2014 03:01:30 +0300 Subject: [PATCH 304/378] wlcore: add smart config definitions Add definitions for the smart config commands. Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- drivers/net/wireless/ti/wl18xx/cmd.h | 14 ++++++++++++++ drivers/net/wireless/ti/wlcore/cmd.h | 3 +++ 2 files changed, 17 insertions(+) diff --git a/drivers/net/wireless/ti/wl18xx/cmd.h b/drivers/net/wireless/ti/wl18xx/cmd.h index 6687d10899ac..03b746bf84f7 100644 --- a/drivers/net/wireless/ti/wl18xx/cmd.h +++ b/drivers/net/wireless/ti/wl18xx/cmd.h @@ -45,6 +45,20 @@ struct wl18xx_cmd_channel_switch { u8 padding[2]; } __packed; +struct wl18xx_cmd_smart_config_start { + struct wl1271_cmd_header header; + + __le32 group_id_bitmask; +} __packed; + +struct wl18xx_cmd_smart_config_set_group_key { + struct wl1271_cmd_header header; + + __le32 group_id; + + u8 key[16]; +} __packed; + int wl18xx_cmd_channel_switch(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_channel_switch *ch_switch); diff --git a/drivers/net/wireless/ti/wlcore/cmd.h b/drivers/net/wireless/ti/wlcore/cmd.h index 6788d7356ca5..ca6a28b03f8f 100644 --- a/drivers/net/wireless/ti/wlcore/cmd.h +++ b/drivers/net/wireless/ti/wlcore/cmd.h @@ -170,6 +170,9 @@ enum wl1271_commands { /* start of 18xx specific commands */ CMD_DFS_CHANNEL_CONFIG = 60, + CMD_SMART_CONFIG_START = 61, + CMD_SMART_CONFIG_STOP = 62, + CMD_SMART_CONFIG_SET_GROUP_KEY = 63, MAX_COMMAND_ID = 0xFFFF, }; From ccb1df948085abcac0a91154e9cabeb563b65833 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Fri, 11 Jul 2014 03:01:31 +0300 Subject: [PATCH 305/378] wlcore/wl18xx: add smart config commands These commands configures the fw to set key, enter smart config mode, and exit it. Add relevant hw ops as well. Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- drivers/net/wireless/ti/wl18xx/cmd.c | 89 +++++++++++++++++++++++++ drivers/net/wireless/ti/wl18xx/cmd.h | 5 +- drivers/net/wireless/ti/wl18xx/main.c | 3 + drivers/net/wireless/ti/wlcore/hw_ops.h | 27 ++++++++ drivers/net/wireless/ti/wlcore/wlcore.h | 4 ++ 5 files changed, 127 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ti/wl18xx/cmd.c b/drivers/net/wireless/ti/wl18xx/cmd.c index 7649c75cd68d..44f0b205b065 100644 --- a/drivers/net/wireless/ti/wl18xx/cmd.c +++ b/drivers/net/wireless/ti/wl18xx/cmd.c @@ -78,3 +78,92 @@ out_free: out: return ret; } + +int wl18xx_cmd_smart_config_start(struct wl1271 *wl, u32 group_bitmap) +{ + struct wl18xx_cmd_smart_config_start *cmd; + int ret = 0; + + wl1271_debug(DEBUG_CMD, "cmd smart config start group_bitmap=0x%x", + group_bitmap); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + cmd->group_id_bitmask = cpu_to_le32(group_bitmap); + + ret = wl1271_cmd_send(wl, CMD_SMART_CONFIG_START, cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("failed to send smart config start command"); + goto out_free; + } + +out_free: + kfree(cmd); +out: + return ret; +} + +int wl18xx_cmd_smart_config_stop(struct wl1271 *wl) +{ + struct wl1271_cmd_header *cmd; + int ret = 0; + + wl1271_debug(DEBUG_CMD, "cmd smart config stop"); + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + ret = wl1271_cmd_send(wl, CMD_SMART_CONFIG_STOP, cmd, sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("failed to send smart config stop command"); + goto out_free; + } + +out_free: + kfree(cmd); +out: + return ret; +} + +int wl18xx_cmd_smart_config_set_group_key(struct wl1271 *wl, u16 group_id, + u8 key_len, u8 *key) +{ + struct wl18xx_cmd_smart_config_set_group_key *cmd; + int ret = 0; + + wl1271_debug(DEBUG_CMD, "cmd smart config set group key id=0x%x", + group_id); + + if (key_len != sizeof(cmd->key)) { + wl1271_error("invalid group key size: %d", key_len); + return -E2BIG; + } + + cmd = kzalloc(sizeof(*cmd), GFP_KERNEL); + if (!cmd) { + ret = -ENOMEM; + goto out; + } + + cmd->group_id = cpu_to_le32(group_id); + memcpy(cmd->key, key, key_len); + + ret = wl1271_cmd_send(wl, CMD_SMART_CONFIG_SET_GROUP_KEY, cmd, + sizeof(*cmd), 0); + if (ret < 0) { + wl1271_error("failed to send smart config set group key cmd"); + goto out_free; + } + +out_free: + kfree(cmd); +out: + return ret; +} diff --git a/drivers/net/wireless/ti/wl18xx/cmd.h b/drivers/net/wireless/ti/wl18xx/cmd.h index 03b746bf84f7..92499e2dfa83 100644 --- a/drivers/net/wireless/ti/wl18xx/cmd.h +++ b/drivers/net/wireless/ti/wl18xx/cmd.h @@ -62,5 +62,8 @@ struct wl18xx_cmd_smart_config_set_group_key { int wl18xx_cmd_channel_switch(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct ieee80211_channel_switch *ch_switch); - +int wl18xx_cmd_smart_config_start(struct wl1271 *wl, u32 group_bitmap); +int wl18xx_cmd_smart_config_stop(struct wl1271 *wl); +int wl18xx_cmd_smart_config_set_group_key(struct wl1271 *wl, u16 group_id, + u8 key_len, u8 *key); #endif diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index de5b4fa5d166..2727ca38807c 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1687,6 +1687,9 @@ static struct wlcore_ops wl18xx_ops = { .convert_hwaddr = wl18xx_convert_hwaddr, .lnk_high_prio = wl18xx_lnk_high_prio, .lnk_low_prio = wl18xx_lnk_low_prio, + .smart_config_start = wl18xx_cmd_smart_config_start, + .smart_config_stop = wl18xx_cmd_smart_config_stop, + .smart_config_set_group_key = wl18xx_cmd_smart_config_set_group_key, }; /* HT cap appropriate for wide channels in 2Ghz */ diff --git a/drivers/net/wireless/ti/wlcore/hw_ops.h b/drivers/net/wireless/ti/wlcore/hw_ops.h index 1555ff970050..aa9f82c72296 100644 --- a/drivers/net/wireless/ti/wlcore/hw_ops.h +++ b/drivers/net/wireless/ti/wlcore/hw_ops.h @@ -260,4 +260,31 @@ wlcore_hw_lnk_low_prio(struct wl1271 *wl, u8 hlid, return wl->ops->lnk_low_prio(wl, hlid, lnk); } +static inline int +wlcore_smart_config_start(struct wl1271 *wl, u32 group_bitmap) +{ + if (!wl->ops->smart_config_start) + return -EINVAL; + + return wl->ops->smart_config_start(wl, group_bitmap); +} + +static inline int +wlcore_smart_config_stop(struct wl1271 *wl) +{ + if (!wl->ops->smart_config_stop) + return -EINVAL; + + return wl->ops->smart_config_stop(wl); +} + +static inline int +wlcore_smart_config_set_group_key(struct wl1271 *wl, u16 group_id, + u8 key_len, u8 *key) +{ + if (!wl->ops->smart_config_set_group_key) + return -EINVAL; + + return wl->ops->smart_config_set_group_key(wl, group_id, key_len, key); +} #endif diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 71320509b56d..13459c4f74d0 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -117,6 +117,10 @@ struct wlcore_ops { struct wl1271_link *lnk); bool (*lnk_low_prio)(struct wl1271 *wl, u8 hlid, struct wl1271_link *lnk); + int (*smart_config_start)(struct wl1271 *wl, u32 group_bitmap); + int (*smart_config_stop)(struct wl1271 *wl); + int (*smart_config_set_group_key)(struct wl1271 *wl, u16 group_id, + u8 key_len, u8 *key); }; enum wlcore_partitions { From 80ff8063e87c352072c6d96fb2d87becaf591966 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Fri, 11 Jul 2014 03:01:32 +0300 Subject: [PATCH 306/378] wlcore: handle smart config vendor commands userspace can ask to perform various smart config actions via custom vendor commands. Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- drivers/net/wireless/ti/wlcore/Makefile | 2 +- drivers/net/wireless/ti/wlcore/vendor_cmd.c | 184 ++++++++++++++++++++ drivers/net/wireless/ti/wlcore/vendor_cmd.h | 36 ++++ 3 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 drivers/net/wireless/ti/wlcore/vendor_cmd.c create mode 100644 drivers/net/wireless/ti/wlcore/vendor_cmd.h diff --git a/drivers/net/wireless/ti/wlcore/Makefile b/drivers/net/wireless/ti/wlcore/Makefile index 4f23931d7bd5..0a69c1373643 100644 --- a/drivers/net/wireless/ti/wlcore/Makefile +++ b/drivers/net/wireless/ti/wlcore/Makefile @@ -1,5 +1,5 @@ wlcore-objs = main.o cmd.o io.o event.o tx.o rx.o ps.o acx.o \ - boot.o init.o debugfs.o scan.o sysfs.o + boot.o init.o debugfs.o scan.o sysfs.o vendor_cmd.o wlcore_spi-objs = spi.o wlcore_sdio-objs = sdio.o diff --git a/drivers/net/wireless/ti/wlcore/vendor_cmd.c b/drivers/net/wireless/ti/wlcore/vendor_cmd.c new file mode 100644 index 000000000000..98852b2ceb4d --- /dev/null +++ b/drivers/net/wireless/ti/wlcore/vendor_cmd.c @@ -0,0 +1,184 @@ +/* + * This file is part of wlcore + * + * Copyright (C) 2014 Texas Instruments. All rights reserved. + * + * 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. + */ + +#include +#include + +#include "wlcore.h" +#include "debug.h" +#include "ps.h" +#include "hw_ops.h" +#include "vendor_cmd.h" + +static const +struct nla_policy wlcore_vendor_attr_policy[NUM_WLCORE_VENDOR_ATTR] = { + [WLCORE_VENDOR_ATTR_FREQ] = { .type = NLA_U32 }, + [WLCORE_VENDOR_ATTR_GROUP_ID] = { .type = NLA_U32 }, + [WLCORE_VENDOR_ATTR_GROUP_KEY] = { .type = NLA_U32, + .len = WLAN_MAX_KEY_LEN }, +}; + +static int +wlcore_vendor_cmd_smart_config_start(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct wl1271 *wl = hw->priv; + struct nlattr *tb[NUM_WLCORE_VENDOR_ATTR]; + int ret; + + wl1271_debug(DEBUG_CMD, "vendor cmd smart config start"); + + if (!data) + return -EINVAL; + + ret = nla_parse(tb, MAX_WLCORE_VENDOR_ATTR, data, data_len, + wlcore_vendor_attr_policy); + if (ret) + return ret; + + if (!tb[WLCORE_VENDOR_ATTR_GROUP_ID]) + return -EINVAL; + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state != WLCORE_STATE_ON)) { + ret = -EINVAL; + goto out; + } + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + ret = wlcore_smart_config_start(wl, + nla_get_u32(tb[WLCORE_VENDOR_ATTR_GROUP_ID])); + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); + + return 0; +} + +static int +wlcore_vendor_cmd_smart_config_stop(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct wl1271 *wl = hw->priv; + int ret; + + wl1271_debug(DEBUG_CMD, "testmode cmd smart config stop"); + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state != WLCORE_STATE_ON)) { + ret = -EINVAL; + goto out; + } + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + ret = wlcore_smart_config_stop(wl); + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); + + return ret; +} + +static int +wlcore_vendor_cmd_smart_config_set_group_key(struct wiphy *wiphy, + struct wireless_dev *wdev, + const void *data, int data_len) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct wl1271 *wl = hw->priv; + struct nlattr *tb[NUM_WLCORE_VENDOR_ATTR]; + int ret; + + wl1271_debug(DEBUG_CMD, "testmode cmd smart config set group key"); + + if (!data) + return -EINVAL; + + ret = nla_parse(tb, MAX_WLCORE_VENDOR_ATTR, data, data_len, + wlcore_vendor_attr_policy); + if (ret) + return ret; + + if (!tb[WLCORE_VENDOR_ATTR_GROUP_ID] || + !tb[WLCORE_VENDOR_ATTR_GROUP_KEY]) + return -EINVAL; + + mutex_lock(&wl->mutex); + + if (unlikely(wl->state != WLCORE_STATE_ON)) { + ret = -EINVAL; + goto out; + } + + ret = wl1271_ps_elp_wakeup(wl); + if (ret < 0) + goto out; + + ret = wlcore_smart_config_set_group_key(wl, + nla_get_u32(tb[WLCORE_VENDOR_ATTR_GROUP_ID]), + nla_len(tb[WLCORE_VENDOR_ATTR_GROUP_KEY]), + nla_data(tb[WLCORE_VENDOR_ATTR_GROUP_KEY])); + + wl1271_ps_elp_sleep(wl); +out: + mutex_unlock(&wl->mutex); + + return ret; +} + +static const struct wiphy_vendor_command wlcore_vendor_commands[] = { + { + .info = { + .vendor_id = TI_OUI, + .subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_START, + }, + .flags = WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlcore_vendor_cmd_smart_config_start, + }, + { + .info = { + .vendor_id = TI_OUI, + .subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_STOP, + }, + .flags = WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlcore_vendor_cmd_smart_config_stop, + }, + { + .info = { + .vendor_id = TI_OUI, + .subcmd = WLCORE_VENDOR_CMD_SMART_CONFIG_SET_GROUP_KEY, + }, + .flags = WIPHY_VENDOR_CMD_NEED_NETDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = wlcore_vendor_cmd_smart_config_set_group_key, + }, +}; + +void wlcore_set_vendor_commands(struct wiphy *wiphy) +{ + wiphy->vendor_commands = wlcore_vendor_commands; + wiphy->n_vendor_commands = ARRAY_SIZE(wlcore_vendor_commands); +} diff --git a/drivers/net/wireless/ti/wlcore/vendor_cmd.h b/drivers/net/wireless/ti/wlcore/vendor_cmd.h new file mode 100644 index 000000000000..7e8e92fad16c --- /dev/null +++ b/drivers/net/wireless/ti/wlcore/vendor_cmd.h @@ -0,0 +1,36 @@ +/* + * This file is part of wlcore + * + * Copyright (C) 2014 Texas Instruments. All rights reserved. + * + * 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. + */ + +#ifndef __WLCORE_VENDOR_H__ +#define __WLCORE_VENDOR_H__ + +#define TI_OUI 0x080028 + +enum wlcore_vendor_commands { + WLCORE_VENDOR_CMD_SMART_CONFIG_START, + WLCORE_VENDOR_CMD_SMART_CONFIG_STOP, + WLCORE_VENDOR_CMD_SMART_CONFIG_SET_GROUP_KEY, + + NUM_WLCORE_VENDOR_CMD, + MAX_WLCORE_VENDOR_CMD = NUM_WLCORE_VENDOR_CMD - 1 +}; + +enum wlcore_vendor_attributes { + WLCORE_VENDOR_ATTR_FREQ, + WLCORE_VENDOR_ATTR_PSK, + WLCORE_VENDOR_ATTR_SSID, + WLCORE_VENDOR_ATTR_GROUP_ID, + WLCORE_VENDOR_ATTR_GROUP_KEY, + + NUM_WLCORE_VENDOR_ATTR, + MAX_WLCORE_VENDOR_ATTR = NUM_WLCORE_VENDOR_ATTR - 1 +}; + +#endif /* __WLCORE_VENDOR_H__ */ From e93e15fb47e53bd5dc256e2c3e40785b39ff8ff7 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Fri, 11 Jul 2014 03:01:33 +0300 Subject: [PATCH 307/378] wlcore/wl18xx: handle smart config events add defintions and handling for smart config events (SMART_CONFIG_SYNC_EVENT_ID and SMART_CONFIG_DECODE_EVENT_ID) parse the relevant info and send it to userspace as vendor event. Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- drivers/net/wireless/ti/wl18xx/event.c | 65 +++++++++++++++++++++ drivers/net/wireless/ti/wl18xx/event.h | 2 + drivers/net/wireless/ti/wl18xx/main.c | 5 +- drivers/net/wireless/ti/wlcore/vendor_cmd.c | 13 +++++ drivers/net/wireless/ti/wlcore/vendor_cmd.h | 9 +++ 5 files changed, 93 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ti/wl18xx/event.c b/drivers/net/wireless/ti/wl18xx/event.c index c9199d7804c6..eb1848e08424 100644 --- a/drivers/net/wireless/ti/wl18xx/event.c +++ b/drivers/net/wireless/ti/wl18xx/event.c @@ -19,10 +19,12 @@ * */ +#include #include "event.h" #include "scan.h" #include "../wlcore/cmd.h" #include "../wlcore/debug.h" +#include "../wlcore/vendor_cmd.h" int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event, bool *timeout) @@ -45,6 +47,58 @@ int wl18xx_wait_for_event(struct wl1271 *wl, enum wlcore_wait_event event, return wlcore_cmd_wait_for_event_or_timeout(wl, local_event, timeout); } +static int wlcore_smart_config_sync_event(struct wl1271 *wl, u8 sync_channel, + u8 sync_band) +{ + struct sk_buff *skb; + enum ieee80211_band band; + int freq; + + if (sync_band == WLCORE_BAND_5GHZ) + band = IEEE80211_BAND_5GHZ; + else + band = IEEE80211_BAND_2GHZ; + + freq = ieee80211_channel_to_frequency(sync_channel, band); + + wl1271_debug(DEBUG_EVENT, + "SMART_CONFIG_SYNC_EVENT_ID, freq: %d (chan: %d band %d)", + freq, sync_channel, sync_band); + skb = cfg80211_vendor_event_alloc(wl->hw->wiphy, 20, + WLCORE_VENDOR_EVENT_SC_SYNC, + GFP_KERNEL); + + if (nla_put_u32(skb, WLCORE_VENDOR_ATTR_FREQ, freq)) { + kfree_skb(skb); + return -EMSGSIZE; + } + cfg80211_vendor_event(skb, GFP_KERNEL); + return 0; +} + +static int wlcore_smart_config_decode_event(struct wl1271 *wl, + u8 ssid_len, u8 *ssid, + u8 pwd_len, u8 *pwd) +{ + struct sk_buff *skb; + + wl1271_debug(DEBUG_EVENT, "SMART_CONFIG_DECODE_EVENT_ID"); + wl1271_dump_ascii(DEBUG_EVENT, "SSID:", ssid, ssid_len); + + skb = cfg80211_vendor_event_alloc(wl->hw->wiphy, + ssid_len + pwd_len + 20, + WLCORE_VENDOR_EVENT_SC_DECODE, + GFP_KERNEL); + + if (nla_put(skb, WLCORE_VENDOR_ATTR_SSID, ssid_len, ssid) || + nla_put(skb, WLCORE_VENDOR_ATTR_PSK, pwd_len, pwd)) { + kfree_skb(skb); + return -EMSGSIZE; + } + cfg80211_vendor_event(skb, GFP_KERNEL); + return 0; +} + int wl18xx_process_mailbox_events(struct wl1271 *wl) { struct wl18xx_event_mailbox *mbox = wl->mbox; @@ -107,5 +161,16 @@ int wl18xx_process_mailbox_events(struct wl1271 *wl) if (vector & REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID) wlcore_event_roc_complete(wl); + if (vector & SMART_CONFIG_SYNC_EVENT_ID) + wlcore_smart_config_sync_event(wl, mbox->sc_sync_channel, + mbox->sc_sync_band); + + if (vector & SMART_CONFIG_DECODE_EVENT_ID) + wlcore_smart_config_decode_event(wl, + mbox->sc_ssid_len, + mbox->sc_ssid, + mbox->sc_pwd_len, + mbox->sc_pwd); + return 0; } diff --git a/drivers/net/wireless/ti/wl18xx/event.h b/drivers/net/wireless/ti/wl18xx/event.h index a76e98eb8372..0680312d4943 100644 --- a/drivers/net/wireless/ti/wl18xx/event.h +++ b/drivers/net/wireless/ti/wl18xx/event.h @@ -38,6 +38,8 @@ enum { REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID = BIT(18), DFS_CHANNELS_CONFIG_COMPLETE_EVENT = BIT(19), PERIODIC_SCAN_REPORT_EVENT_ID = BIT(20), + SMART_CONFIG_SYNC_EVENT_ID = BIT(22), + SMART_CONFIG_DECODE_EVENT_ID = BIT(23), }; struct wl18xx_event_mailbox { diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 2727ca38807c..4422ecf0e726 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -992,7 +992,10 @@ static int wl18xx_boot(struct wl1271 *wl) REMAIN_ON_CHANNEL_COMPLETE_EVENT_ID | INACTIVE_STA_EVENT_ID | CHANNEL_SWITCH_COMPLETE_EVENT_ID | - DFS_CHANNELS_CONFIG_COMPLETE_EVENT; + DFS_CHANNELS_CONFIG_COMPLETE_EVENT | + SMART_CONFIG_SYNC_EVENT_ID | + SMART_CONFIG_DECODE_EVENT_ID; +; wl->ap_event_mask = MAX_TX_FAILURE_EVENT_ID; diff --git a/drivers/net/wireless/ti/wlcore/vendor_cmd.c b/drivers/net/wireless/ti/wlcore/vendor_cmd.c index 98852b2ceb4d..ad86a48dcfcb 100644 --- a/drivers/net/wireless/ti/wlcore/vendor_cmd.c +++ b/drivers/net/wireless/ti/wlcore/vendor_cmd.c @@ -177,8 +177,21 @@ static const struct wiphy_vendor_command wlcore_vendor_commands[] = { }, }; +static const struct nl80211_vendor_cmd_info wlcore_vendor_events[] = { + { + .vendor_id = TI_OUI, + .subcmd = WLCORE_VENDOR_EVENT_SC_SYNC, + }, + { + .vendor_id = TI_OUI, + .subcmd = WLCORE_VENDOR_EVENT_SC_DECODE, + }, +}; + void wlcore_set_vendor_commands(struct wiphy *wiphy) { wiphy->vendor_commands = wlcore_vendor_commands; wiphy->n_vendor_commands = ARRAY_SIZE(wlcore_vendor_commands); + wiphy->vendor_events = wlcore_vendor_events; + wiphy->n_vendor_events = ARRAY_SIZE(wlcore_vendor_events); } diff --git a/drivers/net/wireless/ti/wlcore/vendor_cmd.h b/drivers/net/wireless/ti/wlcore/vendor_cmd.h index 7e8e92fad16c..6e0c15e30f03 100644 --- a/drivers/net/wireless/ti/wlcore/vendor_cmd.h +++ b/drivers/net/wireless/ti/wlcore/vendor_cmd.h @@ -11,6 +11,10 @@ #ifndef __WLCORE_VENDOR_H__ #define __WLCORE_VENDOR_H__ +#ifdef __KERNEL__ +void wlcore_set_vendor_commands(struct wiphy *wiphy); +#endif + #define TI_OUI 0x080028 enum wlcore_vendor_commands { @@ -33,4 +37,9 @@ enum wlcore_vendor_attributes { MAX_WLCORE_VENDOR_ATTR = NUM_WLCORE_VENDOR_ATTR - 1 }; +enum wlcore_vendor_events { + WLCORE_VENDOR_EVENT_SC_SYNC, + WLCORE_VENDOR_EVENT_SC_DECODE, +}; + #endif /* __WLCORE_VENDOR_H__ */ From fbddf587cbd1b75557267b674c8b42a677437c69 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Fri, 11 Jul 2014 03:01:34 +0300 Subject: [PATCH 308/378] wlcore: increase max roc duration to 30 seconds we don't have any actual limitation in the driver, so increase it arbitrarily to 30 seconds. The long ROC is needed for the smart config.flow. Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- drivers/net/wireless/ti/wlcore/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 4c16262afa16..01b4d9e9af69 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5806,7 +5806,7 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) wl->hw->wiphy->max_sched_scan_ie_len = WL1271_CMD_TEMPL_MAX_SIZE - sizeof(struct ieee80211_header); - wl->hw->wiphy->max_remain_on_channel_duration = 5000; + wl->hw->wiphy->max_remain_on_channel_duration = 30000; wl->hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD | WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | From d8c5a48d2751086de9ed0ae9ff97ab094e2d534d Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Fri, 11 Jul 2014 03:01:35 +0300 Subject: [PATCH 309/378] wlcore: register vendor commands All the smart config code is in place now, so register the relevant vendor commands. Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- drivers/net/wireless/ti/wlcore/main.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 01b4d9e9af69..d92f578dd95c 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -37,6 +37,7 @@ #include "init.h" #include "debugfs.h" #include "testmode.h" +#include "vendor_cmd.h" #include "scan.h" #include "hw_ops.h" #include "sysfs.h" @@ -5875,6 +5876,9 @@ static int wl1271_init_ieee80211(struct wl1271 *wl) wl->hw->wiphy->iface_combinations = wl->iface_combinations; wl->hw->wiphy->n_iface_combinations = wl->n_iface_combinations; + /* register vendor commands */ + wlcore_set_vendor_commands(wl->hw->wiphy); + SET_IEEE80211_DEV(wl->hw, wl->dev); wl->hw->sta_data_size = sizeof(struct wl1271_station); From e65628691f04f1a9ce57d8036c9b119c43031187 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Fri, 11 Jul 2014 03:01:36 +0300 Subject: [PATCH 310/378] wlcore: don't switch channels on disconnected STA vifs Sending the FW a channel switch command on a disconnected vif may result in a beacon loss event. Avoid this corner case. Signed-off-by: Arik Nemtsov Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- drivers/net/wireless/ti/wlcore/main.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index d92f578dd95c..ec211413ffe6 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -5192,6 +5192,10 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw, if (unlikely(wl->state == WLCORE_STATE_OFF)) { wl12xx_for_each_wlvif_sta(wl, wlvif) { struct ieee80211_vif *vif = wl12xx_wlvif_to_vif(wlvif); + + if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) + continue; + ieee80211_chswitch_done(vif, false); } goto out; @@ -5207,6 +5211,9 @@ static void wl12xx_op_channel_switch(struct ieee80211_hw *hw, wl12xx_for_each_wlvif_sta(wl, wlvif) { unsigned long delay_usec; + if (!test_bit(WLVIF_FLAG_STA_ASSOCIATED, &wlvif->flags)) + continue; + ret = wl->ops->channel_switch(wl, wlvif, ch_switch); if (ret) goto out_sleep; From 1631020226ee6a95d1d9ff64562c5da1113ff77f Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Fri, 11 Jul 2014 03:01:37 +0300 Subject: [PATCH 311/378] wl18xx: change the number of WLAN addrs per chip Each 18xx chip contains only 2 real MAC addresses usable for WLAN, forcing us to use the LAA bit approach to obtain a third MAC address. Signed-off-by: Arik Nemtsov Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- drivers/net/wireless/ti/wl18xx/wl18xx.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/ti/wl18xx/wl18xx.h b/drivers/net/wireless/ti/wl18xx/wl18xx.h index eb7cfe817010..6a2b88030c1d 100644 --- a/drivers/net/wireless/ti/wl18xx/wl18xx.h +++ b/drivers/net/wireless/ti/wl18xx/wl18xx.h @@ -38,7 +38,7 @@ #define WL18XX_NUM_TX_DESCRIPTORS 32 #define WL18XX_NUM_RX_DESCRIPTORS 32 -#define WL18XX_NUM_MAC_ADDRESSES 3 +#define WL18XX_NUM_MAC_ADDRESSES 2 #define WL18XX_RX_BA_MAX_SESSIONS 13 From 9bccb8ae054fda9ab51e3291eeee545ecc1f1854 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Fri, 11 Jul 2014 03:01:38 +0300 Subject: [PATCH 312/378] wl18xx: make sure fw_status->priv exists before deref In some corner cases with specific timings, we might try dequeueing tx before we got information about the link status (e.g. due to recovery during tx). Instead of NULL dereference, assume all the links in this case have low priorities. Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- drivers/net/wireless/ti/wl18xx/main.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index 4422ecf0e726..edc3e4d8966d 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1609,9 +1609,14 @@ static bool wl18xx_lnk_high_prio(struct wl1271 *wl, u8 hlid, u8 thold; struct wl18xx_fw_status_priv *status_priv = (struct wl18xx_fw_status_priv *)wl->fw_status->priv; - u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap); + u32 suspend_bitmap; + + /* if we don't have the link map yet, assume they all low prio */ + if (!status_priv) + return false; /* suspended links are never high priority */ + suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap); if (test_bit(hlid, (unsigned long *)&suspend_bitmap)) return false; @@ -1631,8 +1636,13 @@ static bool wl18xx_lnk_low_prio(struct wl1271 *wl, u8 hlid, u8 thold; struct wl18xx_fw_status_priv *status_priv = (struct wl18xx_fw_status_priv *)wl->fw_status->priv; - u32 suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap); + u32 suspend_bitmap; + /* if we don't have the link map yet, assume they all low prio */ + if (!status_priv) + return true; + + suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap); if (test_bit(hlid, (unsigned long *)&suspend_bitmap)) thold = status_priv->tx_suspend_threshold; else if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map) && From 5e74b3aa6ffd80128e3df605bf27d8a6a3c04997 Mon Sep 17 00:00:00 2001 From: Eliad Peller Date: Fri, 11 Jul 2014 03:01:39 +0300 Subject: [PATCH 313/378] wlcore/wl18xx/wl12xx: convert bitmaps to unsigned longs The *_bit operations expect unsigned longs. Instead of casting the pointers, simply define various bitmaps as unsigned long (instead of u32). Signed-off-by: Eliad Peller Signed-off-by: John W. Linville --- drivers/net/wireless/ti/wl12xx/main.c | 2 +- drivers/net/wireless/ti/wl18xx/main.c | 16 ++++++++-------- drivers/net/wireless/ti/wlcore/debugfs.c | 2 +- drivers/net/wireless/ti/wlcore/main.c | 8 ++++---- drivers/net/wireless/ti/wlcore/tx.c | 2 +- drivers/net/wireless/ti/wlcore/wlcore.h | 4 ++-- 6 files changed, 17 insertions(+), 17 deletions(-) diff --git a/drivers/net/wireless/ti/wl12xx/main.c b/drivers/net/wireless/ti/wl12xx/main.c index d50dfac91631..0bccf123831e 100644 --- a/drivers/net/wireless/ti/wl12xx/main.c +++ b/drivers/net/wireless/ti/wl12xx/main.c @@ -1668,7 +1668,7 @@ static bool wl12xx_lnk_high_prio(struct wl1271 *wl, u8 hlid, { u8 thold; - if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map)) + if (test_bit(hlid, &wl->fw_fast_lnk_map)) thold = wl->conf.tx.fast_link_thold; else thold = wl->conf.tx.slow_link_thold; diff --git a/drivers/net/wireless/ti/wl18xx/main.c b/drivers/net/wireless/ti/wl18xx/main.c index edc3e4d8966d..7af1936719eb 100644 --- a/drivers/net/wireless/ti/wl18xx/main.c +++ b/drivers/net/wireless/ti/wl18xx/main.c @@ -1609,7 +1609,7 @@ static bool wl18xx_lnk_high_prio(struct wl1271 *wl, u8 hlid, u8 thold; struct wl18xx_fw_status_priv *status_priv = (struct wl18xx_fw_status_priv *)wl->fw_status->priv; - u32 suspend_bitmap; + unsigned long suspend_bitmap; /* if we don't have the link map yet, assume they all low prio */ if (!status_priv) @@ -1617,12 +1617,12 @@ static bool wl18xx_lnk_high_prio(struct wl1271 *wl, u8 hlid, /* suspended links are never high priority */ suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap); - if (test_bit(hlid, (unsigned long *)&suspend_bitmap)) + if (test_bit(hlid, &suspend_bitmap)) return false; /* the priority thresholds are taken from FW */ - if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map) && - !test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map)) + if (test_bit(hlid, &wl->fw_fast_lnk_map) && + !test_bit(hlid, &wl->ap_fw_ps_map)) thold = status_priv->tx_fast_link_prio_threshold; else thold = status_priv->tx_slow_link_prio_threshold; @@ -1636,17 +1636,17 @@ static bool wl18xx_lnk_low_prio(struct wl1271 *wl, u8 hlid, u8 thold; struct wl18xx_fw_status_priv *status_priv = (struct wl18xx_fw_status_priv *)wl->fw_status->priv; - u32 suspend_bitmap; + unsigned long suspend_bitmap; /* if we don't have the link map yet, assume they all low prio */ if (!status_priv) return true; suspend_bitmap = le32_to_cpu(status_priv->link_suspend_bitmap); - if (test_bit(hlid, (unsigned long *)&suspend_bitmap)) + if (test_bit(hlid, &suspend_bitmap)) thold = status_priv->tx_suspend_threshold; - else if (test_bit(hlid, (unsigned long *)&wl->fw_fast_lnk_map) && - !test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map)) + else if (test_bit(hlid, &wl->fw_fast_lnk_map) && + !test_bit(hlid, &wl->ap_fw_ps_map)) thold = status_priv->tx_fast_stop_threshold; else thold = status_priv->tx_slow_stop_threshold; diff --git a/drivers/net/wireless/ti/wlcore/debugfs.c b/drivers/net/wireless/ti/wlcore/debugfs.c index 89893c717025..0be21f62fcb0 100644 --- a/drivers/net/wireless/ti/wlcore/debugfs.c +++ b/drivers/net/wireless/ti/wlcore/debugfs.c @@ -496,7 +496,7 @@ static ssize_t driver_state_read(struct file *file, char __user *user_buf, DRIVER_STATE_PRINT_INT(sg_enabled); DRIVER_STATE_PRINT_INT(enable_11a); DRIVER_STATE_PRINT_INT(noise); - DRIVER_STATE_PRINT_HEX(ap_fw_ps_map); + DRIVER_STATE_PRINT_LHEX(ap_fw_ps_map); DRIVER_STATE_PRINT_LHEX(ap_ps_map); DRIVER_STATE_PRINT_HEX(quirks); DRIVER_STATE_PRINT_HEX(irq); diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index ec211413ffe6..575c8f6d4009 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -333,7 +333,7 @@ static void wl12xx_irq_ps_regulate_link(struct wl1271 *wl, { bool fw_ps; - fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); + fw_ps = test_bit(hlid, &wl->ap_fw_ps_map); /* * Wake up from high level PS if the STA is asleep with too little @@ -360,13 +360,13 @@ static void wl12xx_irq_update_links_status(struct wl1271 *wl, struct wl12xx_vif *wlvif, struct wl_fw_status *status) { - u32 cur_fw_ps_map; + unsigned long cur_fw_ps_map; u8 hlid; cur_fw_ps_map = status->link_ps_bitmap; if (wl->ap_fw_ps_map != cur_fw_ps_map) { wl1271_debug(DEBUG_PSM, - "link ps prev 0x%x cur 0x%x changed 0x%x", + "link ps prev 0x%lx cur 0x%lx changed 0x%lx", wl->ap_fw_ps_map, cur_fw_ps_map, wl->ap_fw_ps_map ^ cur_fw_ps_map); @@ -4754,7 +4754,7 @@ void wl1271_free_sta(struct wl1271 *wl, struct wl12xx_vif *wlvif, u8 hlid) clear_bit(hlid, wlvif->ap.sta_hlid_map); __clear_bit(hlid, &wl->ap_ps_map); - __clear_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); + __clear_bit(hlid, &wl->ap_fw_ps_map); /* * save the last used PN in the private part of iee80211_sta, diff --git a/drivers/net/wireless/ti/wlcore/tx.c b/drivers/net/wireless/ti/wlcore/tx.c index 40b43115f835..f0ac36139bcc 100644 --- a/drivers/net/wireless/ti/wlcore/tx.c +++ b/drivers/net/wireless/ti/wlcore/tx.c @@ -126,7 +126,7 @@ static void wl1271_tx_regulate_link(struct wl1271 *wl, if (WARN_ON(!test_bit(hlid, wlvif->links_map))) return; - fw_ps = test_bit(hlid, (unsigned long *)&wl->ap_fw_ps_map); + fw_ps = test_bit(hlid, &wl->ap_fw_ps_map); tx_pkts = wl->links[hlid].allocated_pkts; /* diff --git a/drivers/net/wireless/ti/wlcore/wlcore.h b/drivers/net/wireless/ti/wlcore/wlcore.h index 13459c4f74d0..df78cf12ef15 100644 --- a/drivers/net/wireless/ti/wlcore/wlcore.h +++ b/drivers/net/wireless/ti/wlcore/wlcore.h @@ -388,10 +388,10 @@ struct wl1271 { int active_link_count; /* Fast/slow links bitmap according to FW */ - u32 fw_fast_lnk_map; + unsigned long fw_fast_lnk_map; /* AP-mode - a bitmap of links currently in PS mode according to FW */ - u32 ap_fw_ps_map; + unsigned long ap_fw_ps_map; /* AP-mode - a bitmap of links currently in PS mode in mac80211 */ unsigned long ap_ps_map; From 6a06e554daef86c4e8d290284927b081fedb249e Mon Sep 17 00:00:00 2001 From: Xose Vazquez Perez Date: Fri, 11 Jul 2014 21:46:57 +0200 Subject: [PATCH 314/378] wireless: rt2x00: add new rt2800usb devices 0x0b05 0x17e8 RT5372 USB 2.0 bgn 2x2 ASUS USB-N14 0x0411 0x0253 RT5572 USB 2.0 abgn 2x2 BUFFALO WLP-U2-300D 0x0df6 0x0078 RT???? Sitecom N300 Cc: Ivo van Doorn Cc: Helmut Schaa Cc: John W. Linville Cc: users@rt2x00.serialmonkey.com Cc: linux-wireless@vger.kernel.org Signed-off-by: Xose Vazquez Perez Signed-off-by: John W. Linville --- drivers/net/wireless/rt2x00/rt2800usb.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/net/wireless/rt2x00/rt2800usb.c b/drivers/net/wireless/rt2x00/rt2800usb.c index 832006b5aab1..573897b8e878 100644 --- a/drivers/net/wireless/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/rt2x00/rt2800usb.c @@ -1284,6 +1284,8 @@ static struct usb_device_id rt2800usb_device_table[] = { /* Arcadyan */ { USB_DEVICE(0x043e, 0x7a12) }, { USB_DEVICE(0x043e, 0x7a32) }, + /* ASUS */ + { USB_DEVICE(0x0b05, 0x17e8) }, /* Azurewave */ { USB_DEVICE(0x13d3, 0x3329) }, { USB_DEVICE(0x13d3, 0x3365) }, @@ -1320,6 +1322,7 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x057c, 0x8501) }, /* Buffalo */ { USB_DEVICE(0x0411, 0x0241) }, + { USB_DEVICE(0x0411, 0x0253) }, /* D-Link */ { USB_DEVICE(0x2001, 0x3c1a) }, { USB_DEVICE(0x2001, 0x3c21) }, @@ -1410,6 +1413,7 @@ static struct usb_device_id rt2800usb_device_table[] = { { USB_DEVICE(0x0df6, 0x0053) }, { USB_DEVICE(0x0df6, 0x0069) }, { USB_DEVICE(0x0df6, 0x006f) }, + { USB_DEVICE(0x0df6, 0x0078) }, /* SMC */ { USB_DEVICE(0x083a, 0xa512) }, { USB_DEVICE(0x083a, 0xc522) }, From 2d702830e004f17e62980869e72f79775140201a Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 11 Jul 2014 20:53:13 -0700 Subject: [PATCH 315/378] mwifiex: access rx_reorder_tbl_ptr only while holding lock This patch fixes a bug in which rx_reorder_tbl_ptr is accessed without holding spinlock at few places. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/11n_rxreorder.c | 27 +++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/drivers/net/wireless/mwifiex/11n_rxreorder.c b/drivers/net/wireless/mwifiex/11n_rxreorder.c index b22bae3d1205..06a2c215ef5e 100644 --- a/drivers/net/wireless/mwifiex/11n_rxreorder.c +++ b/drivers/net/wireless/mwifiex/11n_rxreorder.c @@ -249,13 +249,22 @@ void mwifiex_11n_del_rx_reorder_tbl_by_ta(struct mwifiex_private *priv, u8 *ta) * buffered in Rx reordering table. */ static int -mwifiex_11n_find_last_seq_num(struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr) +mwifiex_11n_find_last_seq_num(struct reorder_tmr_cnxt *ctx) { + struct mwifiex_rx_reorder_tbl *rx_reorder_tbl_ptr = ctx->ptr; + struct mwifiex_private *priv = ctx->priv; + unsigned long flags; int i; - for (i = (rx_reorder_tbl_ptr->win_size - 1); i >= 0; --i) - if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) + spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); + for (i = rx_reorder_tbl_ptr->win_size - 1; i >= 0; --i) { + if (rx_reorder_tbl_ptr->rx_reorder_ptr[i]) { + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, + flags); return i; + } + } + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); return -1; } @@ -274,7 +283,7 @@ mwifiex_flush_data(unsigned long context) (struct reorder_tmr_cnxt *) context; int start_win, seq_num; - seq_num = mwifiex_11n_find_last_seq_num(ctx->ptr); + seq_num = mwifiex_11n_find_last_seq_num(ctx); if (seq_num < 0) return; @@ -729,9 +738,9 @@ void mwifiex_11n_cleanup_reorder_tbl(struct mwifiex_private *priv) mwifiex_del_rx_reorder_entry(priv, del_tbl_ptr); spin_lock_irqsave(&priv->rx_reorder_tbl_lock, flags); } + INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, flags); - INIT_LIST_HEAD(&priv->rx_reorder_tbl_ptr); mwifiex_reset_11n_rx_seq_num(priv); } @@ -749,10 +758,14 @@ void mwifiex_update_rxreor_flags(struct mwifiex_adapter *adapter, u8 flags) priv = adapter->priv[i]; if (!priv) continue; - if (list_empty(&priv->rx_reorder_tbl_ptr)) - continue; spin_lock_irqsave(&priv->rx_reorder_tbl_lock, lock_flags); + if (list_empty(&priv->rx_reorder_tbl_ptr)) { + spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, + lock_flags); + continue; + } + list_for_each_entry(tbl, &priv->rx_reorder_tbl_ptr, list) tbl->flags = flags; spin_unlock_irqrestore(&priv->rx_reorder_tbl_lock, lock_flags); From d5343f06902bf6635734d1e72aa2100dd63a6f4b Mon Sep 17 00:00:00 2001 From: Amitkumar Karwar Date: Fri, 11 Jul 2014 20:53:14 -0700 Subject: [PATCH 316/378] mwifiex: fix corner case system hang issue Sometimes pending internal scan commands are delayed to give preference to Tx traffic. 'scan_processing' flag has been checked at the beginning of delay timer routine to know if in the meantime scan operation has been cancelled. There is a corner case where pending scan commands are emptied after scan_processing flag check is passed. In this case wrong pointer returned by list_first_entry() is passed to list_del() which causes system hang. This patch fixes the issue by adding list_empty() check. Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/main.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/mwifiex/main.c b/drivers/net/wireless/mwifiex/main.c index 3e5194fb0b0f..dfa37eadc4db 100644 --- a/drivers/net/wireless/mwifiex/main.c +++ b/drivers/net/wireless/mwifiex/main.c @@ -33,6 +33,7 @@ static void scan_delay_timer_fn(unsigned long data) struct mwifiex_private *priv = (struct mwifiex_private *)data; struct mwifiex_adapter *adapter = priv->adapter; struct cmd_ctrl_node *cmd_node, *tmp_node; + spinlock_t *scan_q_lock = &adapter->scan_pending_q_lock; unsigned long flags; if (adapter->surprise_removed) @@ -44,13 +45,13 @@ static void scan_delay_timer_fn(unsigned long data) * Abort scan operation by cancelling all pending scan * commands */ - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + spin_lock_irqsave(scan_q_lock, flags); list_for_each_entry_safe(cmd_node, tmp_node, &adapter->scan_pending_q, list) { list_del(&cmd_node->list); mwifiex_insert_cmd_to_free_q(adapter, cmd_node); } - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, flags); + spin_unlock_irqrestore(scan_q_lock, flags); spin_lock_irqsave(&adapter->mwifiex_cmd_lock, flags); adapter->scan_processing = false; @@ -79,12 +80,17 @@ static void scan_delay_timer_fn(unsigned long data) */ adapter->scan_delay_cnt = 0; adapter->empty_tx_q_cnt = 0; - spin_lock_irqsave(&adapter->scan_pending_q_lock, flags); + spin_lock_irqsave(scan_q_lock, flags); + + if (list_empty(&adapter->scan_pending_q)) { + spin_unlock_irqrestore(scan_q_lock, flags); + goto done; + } + cmd_node = list_first_entry(&adapter->scan_pending_q, struct cmd_ctrl_node, list); list_del(&cmd_node->list); - spin_unlock_irqrestore(&adapter->scan_pending_q_lock, - flags); + spin_unlock_irqrestore(scan_q_lock, flags); mwifiex_insert_cmd_to_pending_q(adapter, cmd_node, true); From 2a317ad806655dbb376a97a336c6dc30cb849275 Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Fri, 11 Jul 2014 20:54:32 -0700 Subject: [PATCH 317/378] mwifiex: fix a cut-n-paste error in adhoc-start The 'else if' branch never gets the chance as its condition matches 'if' branch's. Reported-by: David Binderman Signed-off-by: Amitkumar Karwar Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/join.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c index fc135649b85f..0b977ea1cddb 100644 --- a/drivers/net/wireless/mwifiex/join.c +++ b/drivers/net/wireless/mwifiex/join.c @@ -949,7 +949,7 @@ mwifiex_cmd_802_11_ad_hoc_start(struct mwifiex_private *priv, chan_tlv->chan_scan_param[0].radio_type |= (IEEE80211_HT_PARAM_CHA_SEC_ABOVE << 4); else if (adapter->sec_chan_offset == - IEEE80211_HT_PARAM_CHA_SEC_ABOVE) + IEEE80211_HT_PARAM_CHA_SEC_BELOW) chan_tlv->chan_scan_param[0].radio_type |= (IEEE80211_HT_PARAM_CHA_SEC_BELOW << 4); } From 30fa51c8889bfad475a6c7ec46a203092da5c162 Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Fri, 11 Jul 2014 20:57:13 -0700 Subject: [PATCH 318/378] mwifiex: define TDLS idle timeout macro with units The unit of this timeout is in seconds. Reported-by: Paul Stewart Signed-off-by: Bing Zhao Signed-off-by: Avinash Patil Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/fw.h | 2 +- drivers/net/wireless/mwifiex/sta_cmd.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/mwifiex/fw.h b/drivers/net/wireless/mwifiex/fw.h index 5561573452bb..49da2d53d294 100644 --- a/drivers/net/wireless/mwifiex/fw.h +++ b/drivers/net/wireless/mwifiex/fw.h @@ -713,7 +713,7 @@ struct mwifiex_ie_types_vendor_param_set { u8 ie[MWIFIEX_MAX_VSIE_LEN]; }; -#define MWIFIEX_TDLS_IDLE_TIMEOUT 60 +#define MWIFIEX_TDLS_IDLE_TIMEOUT_IN_SEC 60 struct mwifiex_ie_types_tdls_idle_timeout { struct mwifiex_ie_types_header header; diff --git a/drivers/net/wireless/mwifiex/sta_cmd.c b/drivers/net/wireless/mwifiex/sta_cmd.c index 0f077aaadab6..733de92a4c61 100644 --- a/drivers/net/wireless/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/mwifiex/sta_cmd.c @@ -1647,7 +1647,7 @@ mwifiex_cmd_tdls_oper(struct mwifiex_private *priv, timeout = (void *)(pos + config_len); timeout->header.type = cpu_to_le16(TLV_TYPE_TDLS_IDLE_TIMEOUT); timeout->header.len = cpu_to_le16(sizeof(timeout->value)); - timeout->value = cpu_to_le16(MWIFIEX_TDLS_IDLE_TIMEOUT); + timeout->value = cpu_to_le16(MWIFIEX_TDLS_IDLE_TIMEOUT_IN_SEC); config_len += sizeof(struct mwifiex_ie_types_tdls_idle_timeout); break; From ea4eb7fb0c6c5bb56fcbf2181f2d43cdfa95265a Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Fri, 11 Jul 2014 20:57:14 -0700 Subject: [PATCH 319/378] mwifiex: declare sta_ptr in smaller scope sta_ptr is used only in an 'if' branch in this function. Move it to the smaller scope where it is used. Reported-by: Paul Stewart Signed-off-by: Bing Zhao Signed-off-by: Avinash Patil Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/11n.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/mwifiex/11n.c b/drivers/net/wireless/mwifiex/11n.c index 9d6d8d9f01e3..62f5dbe602d3 100644 --- a/drivers/net/wireless/mwifiex/11n.c +++ b/drivers/net/wireless/mwifiex/11n.c @@ -541,7 +541,6 @@ void mwifiex_create_ba_tbl(struct mwifiex_private *priv, u8 *ra, int tid, int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac) { struct host_cmd_ds_11n_addba_req add_ba_req; - struct mwifiex_sta_node *sta_ptr; u32 tx_win_size = priv->add_ba_param.tx_win_size; static u8 dialog_tok; int ret; @@ -553,6 +552,8 @@ int mwifiex_send_addba(struct mwifiex_private *priv, int tid, u8 *peer_mac) ISSUPP_TDLS_ENABLED(priv->adapter->fw_cap_info) && priv->adapter->is_hw_11ac_capable && memcmp(priv->cfg_bssid, peer_mac, ETH_ALEN)) { + struct mwifiex_sta_node *sta_ptr; + sta_ptr = mwifiex_get_sta_entry(priv, peer_mac); if (!sta_ptr) { dev_warn(priv->adapter->dev, From b0535570869f0f4c66f41811d9d941f2e5866b47 Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Fri, 11 Jul 2014 20:57:15 -0700 Subject: [PATCH 320/378] mwifiex: correct a typo in mwifiex_ret_tdls_oper This patch fixes this typo. Reported-by: Paul Stewart Signed-off-by: Bing Zhao Signed-off-by: Avinash Patil Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/sta_cmdresp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/wireless/mwifiex/sta_cmdresp.c b/drivers/net/wireless/mwifiex/sta_cmdresp.c index 822357b7b0bb..08b78baeb846 100644 --- a/drivers/net/wireless/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/mwifiex/sta_cmdresp.c @@ -908,7 +908,7 @@ static int mwifiex_ret_tdls_oper(struct mwifiex_private *priv, break; default: dev_err(priv->adapter->dev, - "Unknown TDLS command action respnse %d", action); + "Unknown TDLS command action response %d", action); return -1; } From 5779ae6a5559252adabc5b5e807381dc7a16f1ff Mon Sep 17 00:00:00 2001 From: Hante Meuleman Date: Sat, 12 Jul 2014 08:49:34 +0200 Subject: [PATCH 321/378] brcmfmac: Cleanup used device IDs. This patch cleans up used broadcom IDs, device IDs for all the bus layers and uses consistent naming for all IDs. Reviewed-by: Arend Van Spriel Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Hante Meuleman Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/bcmsdh.c | 23 +++---- .../net/wireless/brcm80211/brcmfmac/chip.c | 28 ++++----- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 32 +++++----- drivers/net/wireless/brcm80211/brcmfmac/usb.c | 49 +++++++-------- .../wireless/brcm80211/include/brcm_hw_ids.h | 60 +++++++++++++------ 5 files changed, 107 insertions(+), 85 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c index a16e644e7c08..f467cafe3e8f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include #include @@ -979,18 +978,20 @@ out: return ret; } +#define BRCMF_SDIO_DEVICE(dev_id) \ + {SDIO_DEVICE(BRCM_SDIO_VENDOR_ID_BROADCOM, dev_id)} + /* devices we support, null terminated */ static const struct sdio_device_id brcmf_sdmmc_ids[] = { - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43143)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43241)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4330)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4334)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_43362)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, - SDIO_DEVICE_ID_BROADCOM_4335_4339)}, - {SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4354)}, - { /* end: all zeroes */ }, + BRCMF_SDIO_DEVICE(BRCM_SDIO_43143_DEVICE_ID), + BRCMF_SDIO_DEVICE(BRCM_SDIO_43241_DEVICE_ID), + BRCMF_SDIO_DEVICE(BRCM_SDIO_4329_DEVICE_ID), + BRCMF_SDIO_DEVICE(BRCM_SDIO_4330_DEVICE_ID), + BRCMF_SDIO_DEVICE(BRCM_SDIO_4334_DEVICE_ID), + BRCMF_SDIO_DEVICE(BRCM_SDIO_43362_DEVICE_ID), + BRCMF_SDIO_DEVICE(BRCM_SDIO_4335_4339_DEVICE_ID), + BRCMF_SDIO_DEVICE(BRCM_SDIO_4354_DEVICE_ID), + { /* end: all zeroes */ } }; MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/chip.c b/drivers/net/wireless/brcm80211/brcmfmac/chip.c index c7c9f15c0fe0..96800db0536b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/chip.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/chip.c @@ -482,30 +482,30 @@ static inline int brcmf_chip_cores_check(struct brcmf_chip_priv *ci) static void brcmf_chip_get_raminfo(struct brcmf_chip_priv *ci) { switch (ci->pub.chip) { - case BCM4329_CHIP_ID: + case BRCM_CC_4329_CHIP_ID: ci->pub.ramsize = BCM4329_RAMSIZE; break; - case BCM43143_CHIP_ID: + case BRCM_CC_43143_CHIP_ID: ci->pub.ramsize = BCM43143_RAMSIZE; break; - case BCM43241_CHIP_ID: + case BRCM_CC_43241_CHIP_ID: ci->pub.ramsize = 0x90000; break; - case BCM4330_CHIP_ID: + case BRCM_CC_4330_CHIP_ID: ci->pub.ramsize = 0x48000; break; - case BCM4334_CHIP_ID: + case BRCM_CC_4334_CHIP_ID: ci->pub.ramsize = 0x80000; break; - case BCM4335_CHIP_ID: + case BRCM_CC_4335_CHIP_ID: ci->pub.ramsize = 0xc0000; ci->pub.rambase = 0x180000; break; - case BCM43362_CHIP_ID: + case BRCM_CC_43362_CHIP_ID: ci->pub.ramsize = 0x3c000; break; - case BCM4339_CHIP_ID: - case BCM4354_CHIP_ID: + case BRCM_CC_4339_CHIP_ID: + case BRCM_CC_4354_CHIP_ID: ci->pub.ramsize = 0xc0000; ci->pub.rambase = 0x180000; break; @@ -682,7 +682,7 @@ static int brcmf_chip_recognition(struct brcmf_chip_priv *ci) ci->pub.chiprev); if (socitype == SOCI_SB) { - if (ci->pub.chip != BCM4329_CHIP_ID) { + if (ci->pub.chip != BRCM_CC_4329_CHIP_ID) { brcmf_err("SB chip is not supported\n"); return -ENODEV; } @@ -1008,13 +1008,13 @@ bool brcmf_chip_sr_capable(struct brcmf_chip *pub) chip = container_of(pub, struct brcmf_chip_priv, pub); switch (pub->chip) { - case BCM4354_CHIP_ID: + case BRCM_CC_4354_CHIP_ID: /* explicitly check SR engine enable bit */ pmu_cc3_mask = BIT(2); /* fall-through */ - case BCM43241_CHIP_ID: - case BCM4335_CHIP_ID: - case BCM4339_CHIP_ID: + case BRCM_CC_43241_CHIP_ID: + case BRCM_CC_4335_CHIP_ID: + case BRCM_CC_4339_CHIP_ID: /* read PMU chipcontrol register 3 */ addr = CORE_CC_REG(base, chipcontrol_addr); chip->ops->write32(chip->ctx, addr, 3); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 8fa0dbbbda72..bf9bc2d89230 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -620,16 +620,16 @@ enum brcmf_firmware_type { name ## _FIRMWARE_NAME, name ## _NVRAM_NAME static const struct brcmf_firmware_names brcmf_fwname_data[] = { - { BCM43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) }, - { BCM43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) }, - { BCM43241_CHIP_ID, 0xFFFFFFE0, BRCMF_FIRMWARE_NVRAM(BCM43241B4) }, - { BCM4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) }, - { BCM4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) }, - { BCM4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) }, - { BCM4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, - { BCM43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, - { BCM4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, - { BCM4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } + { BRCM_CC_43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) }, + { BRCM_CC_43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) }, + { BRCM_CC_43241_CHIP_ID, 0xFFFFFFE0, BRCMF_FIRMWARE_NVRAM(BCM43241B4) }, + { BRCM_CC_4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) }, + { BRCM_CC_4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) }, + { BRCM_CC_4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) }, + { BRCM_CC_4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, + { BRCM_CC_43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, + { BRCM_CC_4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, + { BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } }; static const char *brcmf_sdio_get_fwname(struct brcmf_chip *ci, @@ -3598,17 +3598,17 @@ brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, return; switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) { - case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12): + case SDIOD_DRVSTR_KEY(BRCM_CC_4330_CHIP_ID, 12): str_tab = sdiod_drvstr_tab1_1v8; str_mask = 0x00003800; str_shift = 11; break; - case SDIOD_DRVSTR_KEY(BCM4334_CHIP_ID, 17): + case SDIOD_DRVSTR_KEY(BRCM_CC_4334_CHIP_ID, 17): str_tab = sdiod_drvstr_tab6_1v8; str_mask = 0x00001800; str_shift = 11; break; - case SDIOD_DRVSTR_KEY(BCM43143_CHIP_ID, 17): + case SDIOD_DRVSTR_KEY(BRCM_CC_43143_CHIP_ID, 17): /* note: 43143 does not support tristate */ i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1; if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) { @@ -3619,7 +3619,7 @@ brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n", ci->name, drivestrength); break; - case SDIOD_DRVSTR_KEY(BCM43362_CHIP_ID, 13): + case SDIOD_DRVSTR_KEY(BRCM_CC_43362_CHIP_ID, 13): str_tab = sdiod_drive_strength_tab5_1v8; str_mask = 0x00003800; str_shift = 11; @@ -3720,12 +3720,12 @@ static u32 brcmf_sdio_buscore_read32(void *ctx, u32 addr) u32 val, rev; val = brcmf_sdiod_regrl(sdiodev, addr, NULL); - if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 && + if (sdiodev->func[0]->device == BRCM_SDIO_4335_4339_DEVICE_ID && addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) { rev = (val & CID_REV_MASK) >> CID_REV_SHIFT; if (rev >= 2) { val &= ~CID_ID_MASK; - val |= BCM4339_CHIP_ID; + val |= BRCM_CC_4339_CHIP_ID; } } return val; diff --git a/drivers/net/wireless/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/brcm80211/brcmfmac/usb.c index b732a99e402c..dc135915470d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/usb.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -913,16 +914,16 @@ static int brcmf_usb_dlrun(struct brcmf_usbdev_info *devinfo) static bool brcmf_usb_chip_support(int chipid, int chiprev) { switch(chipid) { - case 43143: + case BRCM_CC_43143_CHIP_ID: return true; - case 43235: - case 43236: - case 43238: + case BRCM_CC_43235_CHIP_ID: + case BRCM_CC_43236_CHIP_ID: + case BRCM_CC_43238_CHIP_ID: return (chiprev == 3); - case 43242: + case BRCM_CC_43242_CHIP_ID: return true; - case 43566: - case 43569: + case BRCM_CC_43566_CHIP_ID: + case BRCM_CC_43569_CHIP_ID: return true; default: break; @@ -1016,16 +1017,16 @@ static int check_file(const u8 *headers) static const char *brcmf_usb_get_fwname(struct brcmf_usbdev_info *devinfo) { switch (devinfo->bus_pub.devid) { - case 43143: + case BRCM_CC_43143_CHIP_ID: return BRCMF_USB_43143_FW_NAME; - case 43235: - case 43236: - case 43238: + case BRCM_CC_43235_CHIP_ID: + case BRCM_CC_43236_CHIP_ID: + case BRCM_CC_43238_CHIP_ID: return BRCMF_USB_43236_FW_NAME; - case 43242: + case BRCM_CC_43242_CHIP_ID: return BRCMF_USB_43242_FW_NAME; - case 43566: - case 43569: + case BRCM_CC_43566_CHIP_ID: + case BRCM_CC_43569_CHIP_ID: return BRCMF_USB_43569_FW_NAME; default: return NULL; @@ -1366,21 +1367,17 @@ static int brcmf_usb_reset_resume(struct usb_interface *intf) brcmf_usb_probe_phase2); } -#define BRCMF_USB_VENDOR_ID_BROADCOM 0x0a5c -#define BRCMF_USB_DEVICE_ID_43143 0xbd1e -#define BRCMF_USB_DEVICE_ID_43236 0xbd17 -#define BRCMF_USB_DEVICE_ID_43242 0xbd1f -#define BRCMF_USB_DEVICE_ID_43569 0xbd27 -#define BRCMF_USB_DEVICE_ID_BCMFW 0x0bdc +#define BRCMF_USB_DEVICE(dev_id) \ + { USB_DEVICE(BRCM_USB_VENDOR_ID_BROADCOM, dev_id) } static struct usb_device_id brcmf_usb_devid_table[] = { - { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43143) }, - { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43236) }, - { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43242) }, - { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_43569) }, + BRCMF_USB_DEVICE(BRCM_USB_43143_DEVICE_ID), + BRCMF_USB_DEVICE(BRCM_USB_43236_DEVICE_ID), + BRCMF_USB_DEVICE(BRCM_USB_43242_DEVICE_ID), + BRCMF_USB_DEVICE(BRCM_USB_43569_DEVICE_ID), /* special entry for device with firmware loaded and running */ - { USB_DEVICE(BRCMF_USB_VENDOR_ID_BROADCOM, BRCMF_USB_DEVICE_ID_BCMFW) }, - { } + BRCMF_USB_DEVICE(BRCM_USB_BCMFW_DEVICE_ID), + { /* end: all zeroes */ } }; MODULE_DEVICE_TABLE(usb, brcmf_usb_devid_table); diff --git a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h index d816270db3be..64d1a7ba040c 100644 --- a/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h +++ b/drivers/net/wireless/brcm80211/include/brcm_hw_ids.h @@ -17,32 +17,56 @@ #ifndef _BRCM_HW_IDS_H_ #define _BRCM_HW_IDS_H_ -#define BCM4313_D11N2G_ID 0x4727 /* 4313 802.11n 2.4G device */ +#include +#include +#define BRCM_USB_VENDOR_ID_BROADCOM 0x0a5c +#define BRCM_PCIE_VENDOR_ID_BROADCOM PCI_VENDOR_ID_BROADCOM +#define BRCM_SDIO_VENDOR_ID_BROADCOM SDIO_VENDOR_ID_BROADCOM + +/* Chipcommon Core Chip IDs */ +#define BRCM_CC_43143_CHIP_ID 43143 +#define BRCM_CC_43235_CHIP_ID 43235 +#define BRCM_CC_43236_CHIP_ID 43236 +#define BRCM_CC_43238_CHIP_ID 43238 +#define BRCM_CC_43241_CHIP_ID 0x4324 +#define BRCM_CC_43242_CHIP_ID 43242 +#define BRCM_CC_4329_CHIP_ID 0x4329 +#define BRCM_CC_4330_CHIP_ID 0x4330 +#define BRCM_CC_4334_CHIP_ID 0x4334 +#define BRCM_CC_43362_CHIP_ID 43362 +#define BRCM_CC_4335_CHIP_ID 0x4335 +#define BRCM_CC_4339_CHIP_ID 0x4339 +#define BRCM_CC_4354_CHIP_ID 0x4354 +#define BRCM_CC_43566_CHIP_ID 43566 +#define BRCM_CC_43569_CHIP_ID 43569 + +/* SDIO Device IDs */ +#define BRCM_SDIO_43143_DEVICE_ID BRCM_CC_43143_CHIP_ID +#define BRCM_SDIO_43241_DEVICE_ID BRCM_CC_43241_CHIP_ID +#define BRCM_SDIO_4329_DEVICE_ID BRCM_CC_4329_CHIP_ID +#define BRCM_SDIO_4330_DEVICE_ID BRCM_CC_4330_CHIP_ID +#define BRCM_SDIO_4334_DEVICE_ID BRCM_CC_4334_CHIP_ID +#define BRCM_SDIO_43362_DEVICE_ID BRCM_CC_43362_CHIP_ID +#define BRCM_SDIO_4335_4339_DEVICE_ID BRCM_CC_4335_CHIP_ID +#define BRCM_SDIO_4354_DEVICE_ID BRCM_CC_4354_CHIP_ID + +/* USB Device IDs */ +#define BRCM_USB_43143_DEVICE_ID 0xbd1e +#define BRCM_USB_43236_DEVICE_ID 0xbd17 +#define BRCM_USB_43242_DEVICE_ID 0xbd1f +#define BRCM_USB_43569_DEVICE_ID 0xbd27 +#define BRCM_USB_BCMFW_DEVICE_ID 0x0bdc + +/* brcmsmac IDs */ +#define BCM4313_D11N2G_ID 0x4727 /* 4313 802.11n 2.4G device */ #define BCM43224_D11N_ID 0x4353 /* 43224 802.11n dualband device */ #define BCM43224_D11N_ID_VEN1 0x0576 /* Vendor specific 43224 802.11n db */ - #define BCM43225_D11N2G_ID 0x4357 /* 43225 802.11n 2.4GHz device */ - #define BCM43236_D11N_ID 0x4346 /* 43236 802.11n dualband device */ #define BCM43236_D11N2G_ID 0x4347 /* 43236 802.11n 2.4GHz device */ -/* Chipcommon Core Chip IDs */ #define BCM4313_CHIP_ID 0x4313 -#define BCM43143_CHIP_ID 43143 #define BCM43224_CHIP_ID 43224 -#define BCM43225_CHIP_ID 43225 -#define BCM43235_CHIP_ID 43235 -#define BCM43236_CHIP_ID 43236 -#define BCM43238_CHIP_ID 43238 -#define BCM43241_CHIP_ID 0x4324 -#define BCM4329_CHIP_ID 0x4329 -#define BCM4330_CHIP_ID 0x4330 -#define BCM4331_CHIP_ID 0x4331 -#define BCM4334_CHIP_ID 0x4334 -#define BCM4335_CHIP_ID 0x4335 -#define BCM43362_CHIP_ID 43362 -#define BCM4339_CHIP_ID 0x4339 -#define BCM4354_CHIP_ID 0x4354 #endif /* _BRCM_HW_IDS_H_ */ From 1b1e4e9e3abca01b7be6400db879ca8c475a2c96 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 12 Jul 2014 08:49:35 +0200 Subject: [PATCH 322/378] brcmfmac: make use of seq_file API for debugfs entries The use of seq_file simplifies the debugfs code. Simpler is better. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/dhd_dbg.c | 237 +++++++++--------- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 120 ++++----- 2 files changed, 163 insertions(+), 194 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c index 03fe8aca4d32..e1ac932a3154 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c @@ -41,28 +41,27 @@ void brcmf_debugfs_exit(void) root_folder = NULL; } -static -ssize_t brcmf_debugfs_chipinfo_read(struct file *f, char __user *data, - size_t count, loff_t *ppos) +static int brcmf_debugfs_chipinfo_read(struct seq_file *seq, void *data) { - struct brcmf_pub *drvr = f->private_data; + struct brcmf_pub *drvr = seq->private; struct brcmf_bus *bus = drvr->bus_if; - char buf[40]; - int res; - /* only allow read from start */ - if (*ppos > 0) - return 0; + seq_printf(seq, "chip: %x(%u) rev %u\n", + bus->chip, bus->chip, bus->chiprev); + return 0; +} - res = scnprintf(buf, sizeof(buf), "chip: %x(%u) rev %u\n", - bus->chip, bus->chip, bus->chiprev); - return simple_read_from_buffer(data, count, ppos, buf, res); +static int brcmf_debugfs_chipinfo_open(struct inode *inode, struct file *f) +{ + return single_open(f, brcmf_debugfs_chipinfo_read, inode->i_private); } static const struct file_operations brcmf_debugfs_chipinfo_ops = { .owner = THIS_MODULE, - .open = simple_open, - .read = brcmf_debugfs_chipinfo_read + .open = brcmf_debugfs_chipinfo_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek }; static int brcmf_debugfs_create_chipinfo(struct brcmf_pub *drvr) @@ -98,55 +97,54 @@ struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr) return drvr->dbgfs_dir; } -static -ssize_t brcmf_debugfs_sdio_counter_read(struct file *f, char __user *data, - size_t count, loff_t *ppos) +static int brcmf_debugfs_sdio_count_read(struct seq_file *seq, void *data) { - struct brcmf_sdio_count *sdcnt = f->private_data; - char buf[750]; - int res; + struct brcmf_sdio_count *sdcnt = seq->private; - /* only allow read from start */ - if (*ppos > 0) - return 0; + seq_printf(seq, + "intrcount: %u\nlastintrs: %u\n" + "pollcnt: %u\nregfails: %u\n" + "tx_sderrs: %u\nfcqueued: %u\n" + "rxrtx: %u\nrx_toolong: %u\n" + "rxc_errors: %u\nrx_hdrfail: %u\n" + "rx_badhdr: %u\nrx_badseq: %u\n" + "fc_rcvd: %u\nfc_xoff: %u\n" + "fc_xon: %u\nrxglomfail: %u\n" + "rxglomframes: %u\nrxglompkts: %u\n" + "f2rxhdrs: %u\nf2rxdata: %u\n" + "f2txdata: %u\nf1regdata: %u\n" + "tickcnt: %u\ntx_ctlerrs: %lu\n" + "tx_ctlpkts: %lu\nrx_ctlerrs: %lu\n" + "rx_ctlpkts: %lu\nrx_readahead: %lu\n", + sdcnt->intrcount, sdcnt->lastintrs, + sdcnt->pollcnt, sdcnt->regfails, + sdcnt->tx_sderrs, sdcnt->fcqueued, + sdcnt->rxrtx, sdcnt->rx_toolong, + sdcnt->rxc_errors, sdcnt->rx_hdrfail, + sdcnt->rx_badhdr, sdcnt->rx_badseq, + sdcnt->fc_rcvd, sdcnt->fc_xoff, + sdcnt->fc_xon, sdcnt->rxglomfail, + sdcnt->rxglomframes, sdcnt->rxglompkts, + sdcnt->f2rxhdrs, sdcnt->f2rxdata, + sdcnt->f2txdata, sdcnt->f1regdata, + sdcnt->tickcnt, sdcnt->tx_ctlerrs, + sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs, + sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt); - res = scnprintf(buf, sizeof(buf), - "intrcount: %u\nlastintrs: %u\n" - "pollcnt: %u\nregfails: %u\n" - "tx_sderrs: %u\nfcqueued: %u\n" - "rxrtx: %u\nrx_toolong: %u\n" - "rxc_errors: %u\nrx_hdrfail: %u\n" - "rx_badhdr: %u\nrx_badseq: %u\n" - "fc_rcvd: %u\nfc_xoff: %u\n" - "fc_xon: %u\nrxglomfail: %u\n" - "rxglomframes: %u\nrxglompkts: %u\n" - "f2rxhdrs: %u\nf2rxdata: %u\n" - "f2txdata: %u\nf1regdata: %u\n" - "tickcnt: %u\ntx_ctlerrs: %lu\n" - "tx_ctlpkts: %lu\nrx_ctlerrs: %lu\n" - "rx_ctlpkts: %lu\nrx_readahead: %lu\n", - sdcnt->intrcount, sdcnt->lastintrs, - sdcnt->pollcnt, sdcnt->regfails, - sdcnt->tx_sderrs, sdcnt->fcqueued, - sdcnt->rxrtx, sdcnt->rx_toolong, - sdcnt->rxc_errors, sdcnt->rx_hdrfail, - sdcnt->rx_badhdr, sdcnt->rx_badseq, - sdcnt->fc_rcvd, sdcnt->fc_xoff, - sdcnt->fc_xon, sdcnt->rxglomfail, - sdcnt->rxglomframes, sdcnt->rxglompkts, - sdcnt->f2rxhdrs, sdcnt->f2rxdata, - sdcnt->f2txdata, sdcnt->f1regdata, - sdcnt->tickcnt, sdcnt->tx_ctlerrs, - sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs, - sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt); + return 0; +} - return simple_read_from_buffer(data, count, ppos, buf, res); +static int brcmf_debugfs_sdio_count_open(struct inode *inode, struct file *f) +{ + return single_open(f, brcmf_debugfs_sdio_count_read, inode->i_private); } static const struct file_operations brcmf_debugfs_sdio_counter_ops = { .owner = THIS_MODULE, - .open = simple_open, - .read = brcmf_debugfs_sdio_counter_read + .open = brcmf_debugfs_sdio_count_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek }; void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr, @@ -159,79 +157,78 @@ void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr, sdcnt, &brcmf_debugfs_sdio_counter_ops); } -static -ssize_t brcmf_debugfs_fws_stats_read(struct file *f, char __user *data, - size_t count, loff_t *ppos) +static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data) { - struct brcmf_fws_stats *fwstats = f->private_data; - char buf[650]; - int res; + struct brcmf_fws_stats *fwstats = seq->private; - /* only allow read from start */ - if (*ppos > 0) - return 0; + seq_printf(seq, + "header_pulls: %u\n" + "header_only_pkt: %u\n" + "tlv_parse_failed: %u\n" + "tlv_invalid_type: %u\n" + "mac_update_fails: %u\n" + "ps_update_fails: %u\n" + "if_update_fails: %u\n" + "pkt2bus: %u\n" + "generic_error: %u\n" + "rollback_success: %u\n" + "rollback_failed: %u\n" + "delayq_full: %u\n" + "supprq_full: %u\n" + "txs_indicate: %u\n" + "txs_discard: %u\n" + "txs_suppr_core: %u\n" + "txs_suppr_ps: %u\n" + "txs_tossed: %u\n" + "txs_host_tossed: %u\n" + "bus_flow_block: %u\n" + "fws_flow_block: %u\n" + "send_pkts: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n" + "requested_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n", + fwstats->header_pulls, + fwstats->header_only_pkt, + fwstats->tlv_parse_failed, + fwstats->tlv_invalid_type, + fwstats->mac_update_failed, + fwstats->mac_ps_update_failed, + fwstats->if_update_failed, + fwstats->pkt2bus, + fwstats->generic_error, + fwstats->rollback_success, + fwstats->rollback_failed, + fwstats->delayq_full_error, + fwstats->supprq_full_error, + fwstats->txs_indicate, + fwstats->txs_discard, + fwstats->txs_supp_core, + fwstats->txs_supp_ps, + fwstats->txs_tossed, + fwstats->txs_host_tossed, + fwstats->bus_flow_block, + fwstats->fws_flow_block, + fwstats->send_pkts[0], fwstats->send_pkts[1], + fwstats->send_pkts[2], fwstats->send_pkts[3], + fwstats->send_pkts[4], + fwstats->requested_sent[0], + fwstats->requested_sent[1], + fwstats->requested_sent[2], + fwstats->requested_sent[3], + fwstats->requested_sent[4]); - res = scnprintf(buf, sizeof(buf), - "header_pulls: %u\n" - "header_only_pkt: %u\n" - "tlv_parse_failed: %u\n" - "tlv_invalid_type: %u\n" - "mac_update_fails: %u\n" - "ps_update_fails: %u\n" - "if_update_fails: %u\n" - "pkt2bus: %u\n" - "generic_error: %u\n" - "rollback_success: %u\n" - "rollback_failed: %u\n" - "delayq_full: %u\n" - "supprq_full: %u\n" - "txs_indicate: %u\n" - "txs_discard: %u\n" - "txs_suppr_core: %u\n" - "txs_suppr_ps: %u\n" - "txs_tossed: %u\n" - "txs_host_tossed: %u\n" - "bus_flow_block: %u\n" - "fws_flow_block: %u\n" - "send_pkts: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n" - "requested_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n", - fwstats->header_pulls, - fwstats->header_only_pkt, - fwstats->tlv_parse_failed, - fwstats->tlv_invalid_type, - fwstats->mac_update_failed, - fwstats->mac_ps_update_failed, - fwstats->if_update_failed, - fwstats->pkt2bus, - fwstats->generic_error, - fwstats->rollback_success, - fwstats->rollback_failed, - fwstats->delayq_full_error, - fwstats->supprq_full_error, - fwstats->txs_indicate, - fwstats->txs_discard, - fwstats->txs_supp_core, - fwstats->txs_supp_ps, - fwstats->txs_tossed, - fwstats->txs_host_tossed, - fwstats->bus_flow_block, - fwstats->fws_flow_block, - fwstats->send_pkts[0], fwstats->send_pkts[1], - fwstats->send_pkts[2], fwstats->send_pkts[3], - fwstats->send_pkts[4], - fwstats->requested_sent[0], - fwstats->requested_sent[1], - fwstats->requested_sent[2], - fwstats->requested_sent[3], - fwstats->requested_sent[4]); + return 0; +} - return simple_read_from_buffer(data, count, ppos, buf, res); +static int brcmf_debugfs_fws_stats_open(struct inode *inode, struct file *f) +{ + return single_open(f, brcmf_debugfs_fws_stats_read, inode->i_private); } static const struct file_operations brcmf_debugfs_fws_stats_ops = { .owner = THIS_MODULE, - .open = simple_open, - .read = brcmf_debugfs_fws_stats_read + .open = brcmf_debugfs_fws_stats_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek }; void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index bf9bc2d89230..0c98a7942a0f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -2898,16 +2898,13 @@ brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) } #ifdef DEBUG -static int brcmf_sdio_dump_console(struct brcmf_sdio *bus, - struct sdpcm_shared *sh, char __user *data, - size_t count) +static int brcmf_sdio_dump_console(struct seq_file *seq, struct brcmf_sdio *bus, + struct sdpcm_shared *sh) { u32 addr, console_ptr, console_size, console_index; char *conbuf = NULL; __le32 sh_val; int rv; - loff_t pos = 0; - int nbytes = 0; /* obtain console information from device memory */ addr = sh->console_addr + offsetof(struct rte_console, log_le); @@ -2945,33 +2942,24 @@ static int brcmf_sdio_dump_console(struct brcmf_sdio *bus, if (rv < 0) goto done; - rv = simple_read_from_buffer(data, count, &pos, - conbuf + console_index, - console_size - console_index); + rv = seq_write(seq, conbuf + console_index, + console_size - console_index); if (rv < 0) goto done; - nbytes = rv; - if (console_index > 0) { - pos = 0; - rv = simple_read_from_buffer(data+nbytes, count, &pos, - conbuf, console_index - 1); - if (rv < 0) - goto done; - rv += nbytes; - } + if (console_index > 0) + rv = seq_write(seq, conbuf, console_index - 1); + done: vfree(conbuf); return rv; } -static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh, - char __user *data, size_t count) +static int brcmf_sdio_trap_info(struct seq_file *seq, struct brcmf_sdio *bus, + struct sdpcm_shared *sh) { - int error, res; - char buf[350]; + int error; struct brcmf_trap_info tr; - loff_t pos = 0; if ((sh->flags & SDPCM_SHARED_TRAP) == 0) { brcmf_dbg(INFO, "no trap in firmware\n"); @@ -2983,34 +2971,30 @@ static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh, if (error < 0) return error; - res = scnprintf(buf, sizeof(buf), - "dongle trap info: type 0x%x @ epc 0x%08x\n" - " cpsr 0x%08x spsr 0x%08x sp 0x%08x\n" - " lr 0x%08x pc 0x%08x offset 0x%x\n" - " r0 0x%08x r1 0x%08x r2 0x%08x r3 0x%08x\n" - " r4 0x%08x r5 0x%08x r6 0x%08x r7 0x%08x\n", - le32_to_cpu(tr.type), le32_to_cpu(tr.epc), - le32_to_cpu(tr.cpsr), le32_to_cpu(tr.spsr), - le32_to_cpu(tr.r13), le32_to_cpu(tr.r14), - le32_to_cpu(tr.pc), sh->trap_addr, - le32_to_cpu(tr.r0), le32_to_cpu(tr.r1), - le32_to_cpu(tr.r2), le32_to_cpu(tr.r3), - le32_to_cpu(tr.r4), le32_to_cpu(tr.r5), - le32_to_cpu(tr.r6), le32_to_cpu(tr.r7)); + seq_printf(seq, + "dongle trap info: type 0x%x @ epc 0x%08x\n" + " cpsr 0x%08x spsr 0x%08x sp 0x%08x\n" + " lr 0x%08x pc 0x%08x offset 0x%x\n" + " r0 0x%08x r1 0x%08x r2 0x%08x r3 0x%08x\n" + " r4 0x%08x r5 0x%08x r6 0x%08x r7 0x%08x\n", + le32_to_cpu(tr.type), le32_to_cpu(tr.epc), + le32_to_cpu(tr.cpsr), le32_to_cpu(tr.spsr), + le32_to_cpu(tr.r13), le32_to_cpu(tr.r14), + le32_to_cpu(tr.pc), sh->trap_addr, + le32_to_cpu(tr.r0), le32_to_cpu(tr.r1), + le32_to_cpu(tr.r2), le32_to_cpu(tr.r3), + le32_to_cpu(tr.r4), le32_to_cpu(tr.r5), + le32_to_cpu(tr.r6), le32_to_cpu(tr.r7)); - return simple_read_from_buffer(data, count, &pos, buf, res); + return 0; } -static int brcmf_sdio_assert_info(struct brcmf_sdio *bus, - struct sdpcm_shared *sh, char __user *data, - size_t count) +static int brcmf_sdio_assert_info(struct seq_file *seq, struct brcmf_sdio *bus, + struct sdpcm_shared *sh) { int error = 0; - char buf[200]; char file[80] = "?"; char expr[80] = ""; - int res; - loff_t pos = 0; if ((sh->flags & SDPCM_SHARED_ASSERT_BUILT) == 0) { brcmf_dbg(INFO, "firmware not built with -assert\n"); @@ -3035,10 +3019,9 @@ static int brcmf_sdio_assert_info(struct brcmf_sdio *bus, } sdio_release_host(bus->sdiodev->func[1]); - res = scnprintf(buf, sizeof(buf), - "dongle assert: %s:%d: assert(%s)\n", - file, sh->assert_line, expr); - return simple_read_from_buffer(data, count, &pos, buf, res); + seq_printf(seq, "dongle assert: %s:%d: assert(%s)\n", + file, sh->assert_line, expr); + return 0; } static int brcmf_sdio_checkdied(struct brcmf_sdio *bus) @@ -3062,58 +3045,47 @@ static int brcmf_sdio_checkdied(struct brcmf_sdio *bus) return 0; } -static int brcmf_sdio_died_dump(struct brcmf_sdio *bus, char __user *data, - size_t count, loff_t *ppos) +static int brcmf_sdio_died_dump(struct seq_file *seq, struct brcmf_sdio *bus) { int error = 0; struct sdpcm_shared sh; - int nbytes = 0; - loff_t pos = *ppos; - - if (pos != 0) - return 0; error = brcmf_sdio_readshared(bus, &sh); if (error < 0) goto done; - error = brcmf_sdio_assert_info(bus, &sh, data, count); + error = brcmf_sdio_assert_info(seq, bus, &sh); if (error < 0) goto done; - nbytes = error; - error = brcmf_sdio_trap_info(bus, &sh, data+nbytes, count); + error = brcmf_sdio_trap_info(seq, bus, &sh); if (error < 0) goto done; - nbytes += error; - error = brcmf_sdio_dump_console(bus, &sh, data+nbytes, count); - if (error < 0) - goto done; - nbytes += error; + error = brcmf_sdio_dump_console(seq, bus, &sh); - error = nbytes; - *ppos += nbytes; done: return error; } -static ssize_t brcmf_sdio_forensic_read(struct file *f, char __user *data, - size_t count, loff_t *ppos) +static int brcmf_sdio_forensic_read(struct seq_file *seq, void *data) { - struct brcmf_sdio *bus = f->private_data; - int res; + struct brcmf_sdio *bus = seq->private; - res = brcmf_sdio_died_dump(bus, data, count, ppos); - if (res > 0) - *ppos += res; - return (ssize_t)res; + return brcmf_sdio_died_dump(seq, bus); +} + +static int brcmf_sdio_forensic_open(struct inode *inode, struct file *f) +{ + return single_open(f, brcmf_sdio_forensic_read, inode->i_private); } static const struct file_operations brcmf_sdio_forensic_ops = { .owner = THIS_MODULE, - .open = simple_open, - .read = brcmf_sdio_forensic_read + .open = brcmf_sdio_forensic_open, + .release = single_release, + .read = seq_read, + .llseek = seq_lseek }; static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) From 82d957e09d4f0ff8091ca67e39b51ec6bdc672b1 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 12 Jul 2014 08:49:36 +0200 Subject: [PATCH 323/378] brcmfmac: rework debugfs functions in the driver Reworked the debugfs functions in the driver making it easier for other driver parts to add a debugfs entry and keeping the information they want to expose in debugfs private, ie. not in a header. This is accomplished by providing the function brcmf_debugfs_add_entry() in which the caller provides a read function in which they provide the content. The debugfs function will take care of creating the debugfs entry and cleaning up upon removal. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/dhd_dbg.c | 185 +++--------------- .../net/wireless/brcm80211/brcmfmac/dhd_dbg.h | 74 +------ .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 89 +++++++-- .../wireless/brcm80211/brcmfmac/fwsignal.c | 100 +++++++++- 4 files changed, 210 insertions(+), 238 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c index e1ac932a3154..be9f4f829192 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.c @@ -43,37 +43,13 @@ void brcmf_debugfs_exit(void) static int brcmf_debugfs_chipinfo_read(struct seq_file *seq, void *data) { - struct brcmf_pub *drvr = seq->private; - struct brcmf_bus *bus = drvr->bus_if; + struct brcmf_bus *bus = dev_get_drvdata(seq->private); seq_printf(seq, "chip: %x(%u) rev %u\n", bus->chip, bus->chip, bus->chiprev); return 0; } -static int brcmf_debugfs_chipinfo_open(struct inode *inode, struct file *f) -{ - return single_open(f, brcmf_debugfs_chipinfo_read, inode->i_private); -} - -static const struct file_operations brcmf_debugfs_chipinfo_ops = { - .owner = THIS_MODULE, - .open = brcmf_debugfs_chipinfo_open, - .release = single_release, - .read = seq_read, - .llseek = seq_lseek -}; - -static int brcmf_debugfs_create_chipinfo(struct brcmf_pub *drvr) -{ - struct dentry *dentry = drvr->dbgfs_dir; - - if (!IS_ERR_OR_NULL(dentry)) - debugfs_create_file("chipinfo", S_IRUGO, dentry, drvr, - &brcmf_debugfs_chipinfo_ops); - return 0; -} - int brcmf_debugfs_attach(struct brcmf_pub *drvr) { struct device *dev = drvr->bus_if->dev; @@ -82,7 +58,8 @@ int brcmf_debugfs_attach(struct brcmf_pub *drvr) return -ENODEV; drvr->dbgfs_dir = debugfs_create_dir(dev_name(dev), root_folder); - brcmf_debugfs_create_chipinfo(drvr); + brcmf_debugfs_add_entry(drvr, "chipinfo", brcmf_debugfs_chipinfo_read); + return PTR_ERR_OR_ZERO(drvr->dbgfs_dir); } @@ -97,146 +74,44 @@ struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr) return drvr->dbgfs_dir; } -static int brcmf_debugfs_sdio_count_read(struct seq_file *seq, void *data) +struct brcmf_debugfs_entry { + int (*read)(struct seq_file *seq, void *data); + struct brcmf_pub *drvr; +}; + +static int brcmf_debugfs_entry_open(struct inode *inode, struct file *f) { - struct brcmf_sdio_count *sdcnt = seq->private; + struct brcmf_debugfs_entry *entry = inode->i_private; - seq_printf(seq, - "intrcount: %u\nlastintrs: %u\n" - "pollcnt: %u\nregfails: %u\n" - "tx_sderrs: %u\nfcqueued: %u\n" - "rxrtx: %u\nrx_toolong: %u\n" - "rxc_errors: %u\nrx_hdrfail: %u\n" - "rx_badhdr: %u\nrx_badseq: %u\n" - "fc_rcvd: %u\nfc_xoff: %u\n" - "fc_xon: %u\nrxglomfail: %u\n" - "rxglomframes: %u\nrxglompkts: %u\n" - "f2rxhdrs: %u\nf2rxdata: %u\n" - "f2txdata: %u\nf1regdata: %u\n" - "tickcnt: %u\ntx_ctlerrs: %lu\n" - "tx_ctlpkts: %lu\nrx_ctlerrs: %lu\n" - "rx_ctlpkts: %lu\nrx_readahead: %lu\n", - sdcnt->intrcount, sdcnt->lastintrs, - sdcnt->pollcnt, sdcnt->regfails, - sdcnt->tx_sderrs, sdcnt->fcqueued, - sdcnt->rxrtx, sdcnt->rx_toolong, - sdcnt->rxc_errors, sdcnt->rx_hdrfail, - sdcnt->rx_badhdr, sdcnt->rx_badseq, - sdcnt->fc_rcvd, sdcnt->fc_xoff, - sdcnt->fc_xon, sdcnt->rxglomfail, - sdcnt->rxglomframes, sdcnt->rxglompkts, - sdcnt->f2rxhdrs, sdcnt->f2rxdata, - sdcnt->f2txdata, sdcnt->f1regdata, - sdcnt->tickcnt, sdcnt->tx_ctlerrs, - sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs, - sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt); - - return 0; + return single_open(f, entry->read, entry->drvr->bus_if->dev); } -static int brcmf_debugfs_sdio_count_open(struct inode *inode, struct file *f) -{ - return single_open(f, brcmf_debugfs_sdio_count_read, inode->i_private); -} - -static const struct file_operations brcmf_debugfs_sdio_counter_ops = { +static const struct file_operations brcmf_debugfs_def_ops = { .owner = THIS_MODULE, - .open = brcmf_debugfs_sdio_count_open, + .open = brcmf_debugfs_entry_open, .release = single_release, .read = seq_read, .llseek = seq_lseek }; -void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr, - struct brcmf_sdio_count *sdcnt) -{ - struct dentry *dentry = drvr->dbgfs_dir; - - if (!IS_ERR_OR_NULL(dentry)) - debugfs_create_file("counters", S_IRUGO, dentry, - sdcnt, &brcmf_debugfs_sdio_counter_ops); -} - -static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data) -{ - struct brcmf_fws_stats *fwstats = seq->private; - - seq_printf(seq, - "header_pulls: %u\n" - "header_only_pkt: %u\n" - "tlv_parse_failed: %u\n" - "tlv_invalid_type: %u\n" - "mac_update_fails: %u\n" - "ps_update_fails: %u\n" - "if_update_fails: %u\n" - "pkt2bus: %u\n" - "generic_error: %u\n" - "rollback_success: %u\n" - "rollback_failed: %u\n" - "delayq_full: %u\n" - "supprq_full: %u\n" - "txs_indicate: %u\n" - "txs_discard: %u\n" - "txs_suppr_core: %u\n" - "txs_suppr_ps: %u\n" - "txs_tossed: %u\n" - "txs_host_tossed: %u\n" - "bus_flow_block: %u\n" - "fws_flow_block: %u\n" - "send_pkts: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n" - "requested_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n", - fwstats->header_pulls, - fwstats->header_only_pkt, - fwstats->tlv_parse_failed, - fwstats->tlv_invalid_type, - fwstats->mac_update_failed, - fwstats->mac_ps_update_failed, - fwstats->if_update_failed, - fwstats->pkt2bus, - fwstats->generic_error, - fwstats->rollback_success, - fwstats->rollback_failed, - fwstats->delayq_full_error, - fwstats->supprq_full_error, - fwstats->txs_indicate, - fwstats->txs_discard, - fwstats->txs_supp_core, - fwstats->txs_supp_ps, - fwstats->txs_tossed, - fwstats->txs_host_tossed, - fwstats->bus_flow_block, - fwstats->fws_flow_block, - fwstats->send_pkts[0], fwstats->send_pkts[1], - fwstats->send_pkts[2], fwstats->send_pkts[3], - fwstats->send_pkts[4], - fwstats->requested_sent[0], - fwstats->requested_sent[1], - fwstats->requested_sent[2], - fwstats->requested_sent[3], - fwstats->requested_sent[4]); - - return 0; -} - -static int brcmf_debugfs_fws_stats_open(struct inode *inode, struct file *f) -{ - return single_open(f, brcmf_debugfs_fws_stats_read, inode->i_private); -} - -static const struct file_operations brcmf_debugfs_fws_stats_ops = { - .owner = THIS_MODULE, - .open = brcmf_debugfs_fws_stats_open, - .release = single_release, - .read = seq_read, - .llseek = seq_lseek -}; - -void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr, - struct brcmf_fws_stats *stats) +int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn, + int (*read_fn)(struct seq_file *seq, void *data)) { struct dentry *dentry = drvr->dbgfs_dir; + struct brcmf_debugfs_entry *entry; - if (!IS_ERR_OR_NULL(dentry)) - debugfs_create_file("fws_stats", S_IRUGO, dentry, - stats, &brcmf_debugfs_fws_stats_ops); + if (IS_ERR_OR_NULL(dentry)) + return -ENOENT; + + entry = devm_kzalloc(drvr->bus_if->dev, sizeof(*entry), GFP_KERNEL); + if (!entry) + return -ENOMEM; + + entry->read = read_fn; + entry->drvr = drvr; + + dentry = debugfs_create_file(fn, S_IRUGO, dentry, entry, + &brcmf_debugfs_def_ops); + + return PTR_ERR_OR_ZERO(dentry); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h index ef52ed7abc69..6eade7c60c63 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h @@ -100,68 +100,6 @@ do { \ extern int brcmf_msg_level; -/* - * hold counter variables used in brcmfmac sdio driver. - */ -struct brcmf_sdio_count { - uint intrcount; /* Count of device interrupt callbacks */ - uint lastintrs; /* Count as of last watchdog timer */ - uint pollcnt; /* Count of active polls */ - uint regfails; /* Count of R_REG failures */ - uint tx_sderrs; /* Count of tx attempts with sd errors */ - uint fcqueued; /* Tx packets that got queued */ - uint rxrtx; /* Count of rtx requests (NAK to dongle) */ - uint rx_toolong; /* Receive frames too long to receive */ - uint rxc_errors; /* SDIO errors when reading control frames */ - uint rx_hdrfail; /* SDIO errors on header reads */ - uint rx_badhdr; /* Bad received headers (roosync?) */ - uint rx_badseq; /* Mismatched rx sequence number */ - uint fc_rcvd; /* Number of flow-control events received */ - uint fc_xoff; /* Number which turned on flow-control */ - uint fc_xon; /* Number which turned off flow-control */ - uint rxglomfail; /* Failed deglom attempts */ - uint rxglomframes; /* Number of glom frames (superframes) */ - uint rxglompkts; /* Number of packets from glom frames */ - uint f2rxhdrs; /* Number of header reads */ - uint f2rxdata; /* Number of frame data reads */ - uint f2txdata; /* Number of f2 frame writes */ - uint f1regdata; /* Number of f1 register accesses */ - uint tickcnt; /* Number of watchdog been schedule */ - ulong tx_ctlerrs; /* Err of sending ctrl frames */ - ulong tx_ctlpkts; /* Ctrl frames sent to dongle */ - ulong rx_ctlerrs; /* Err of processing rx ctrl frames */ - ulong rx_ctlpkts; /* Ctrl frames processed from dongle */ - ulong rx_readahead_cnt; /* packets where header read-ahead was used */ -}; - -struct brcmf_fws_stats { - u32 tlv_parse_failed; - u32 tlv_invalid_type; - u32 header_only_pkt; - u32 header_pulls; - u32 pkt2bus; - u32 send_pkts[5]; - u32 requested_sent[5]; - u32 generic_error; - u32 mac_update_failed; - u32 mac_ps_update_failed; - u32 if_update_failed; - u32 packet_request_failed; - u32 credit_request_failed; - u32 rollback_success; - u32 rollback_failed; - u32 delayq_full_error; - u32 supprq_full_error; - u32 txs_indicate; - u32 txs_discard; - u32 txs_supp_core; - u32 txs_supp_ps; - u32 txs_tossed; - u32 txs_host_tossed; - u32 bus_flow_block; - u32 fws_flow_block; -}; - struct brcmf_pub; #ifdef DEBUG void brcmf_debugfs_init(void); @@ -169,10 +107,8 @@ void brcmf_debugfs_exit(void); int brcmf_debugfs_attach(struct brcmf_pub *drvr); void brcmf_debugfs_detach(struct brcmf_pub *drvr); struct dentry *brcmf_debugfs_get_devdir(struct brcmf_pub *drvr); -void brcmf_debugfs_create_sdio_count(struct brcmf_pub *drvr, - struct brcmf_sdio_count *sdcnt); -void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr, - struct brcmf_fws_stats *stats); +int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn, + int (*read_fn)(struct seq_file *seq, void *data)); #else static inline void brcmf_debugfs_init(void) { @@ -187,9 +123,11 @@ static inline int brcmf_debugfs_attach(struct brcmf_pub *drvr) static inline void brcmf_debugfs_detach(struct brcmf_pub *drvr) { } -static inline void brcmf_debugfs_create_fws_stats(struct brcmf_pub *drvr, - struct brcmf_fws_stats *stats) +static inline +int brcmf_debugfs_add_entry(struct brcmf_pub *drvr, const char *fn, + int (*read_fn)(struct seq_file *seq, void *data)) { + return 0; } #endif diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 0c98a7942a0f..c0486329331b 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -391,6 +391,40 @@ struct brcmf_sdio_hdrinfo { u16 tail_pad; }; +/* + * hold counter variables + */ +struct brcmf_sdio_count { + uint intrcount; /* Count of device interrupt callbacks */ + uint lastintrs; /* Count as of last watchdog timer */ + uint pollcnt; /* Count of active polls */ + uint regfails; /* Count of R_REG failures */ + uint tx_sderrs; /* Count of tx attempts with sd errors */ + uint fcqueued; /* Tx packets that got queued */ + uint rxrtx; /* Count of rtx requests (NAK to dongle) */ + uint rx_toolong; /* Receive frames too long to receive */ + uint rxc_errors; /* SDIO errors when reading control frames */ + uint rx_hdrfail; /* SDIO errors on header reads */ + uint rx_badhdr; /* Bad received headers (roosync?) */ + uint rx_badseq; /* Mismatched rx sequence number */ + uint fc_rcvd; /* Number of flow-control events received */ + uint fc_xoff; /* Number which turned on flow-control */ + uint fc_xon; /* Number which turned off flow-control */ + uint rxglomfail; /* Failed deglom attempts */ + uint rxglomframes; /* Number of glom frames (superframes) */ + uint rxglompkts; /* Number of packets from glom frames */ + uint f2rxhdrs; /* Number of header reads */ + uint f2rxdata; /* Number of frame data reads */ + uint f2txdata; /* Number of f2 frame writes */ + uint f1regdata; /* Number of f1 register accesses */ + uint tickcnt; /* Number of watchdog been schedule */ + ulong tx_ctlerrs; /* Err of sending ctrl frames */ + ulong tx_ctlpkts; /* Ctrl frames sent to dongle */ + ulong rx_ctlerrs; /* Err of processing rx ctrl frames */ + ulong rx_ctlpkts; /* Ctrl frames processed from dongle */ + ulong rx_readahead_cnt; /* packets where header read-ahead was used */ +}; + /* misc chip info needed by some of the routines */ /* Private data for SDIO bus interaction */ struct brcmf_sdio { @@ -3070,23 +3104,50 @@ done: static int brcmf_sdio_forensic_read(struct seq_file *seq, void *data) { - struct brcmf_sdio *bus = seq->private; + struct brcmf_bus *bus_if = dev_get_drvdata(seq->private); + struct brcmf_sdio *bus = bus_if->bus_priv.sdio->bus; return brcmf_sdio_died_dump(seq, bus); } -static int brcmf_sdio_forensic_open(struct inode *inode, struct file *f) +static int brcmf_debugfs_sdio_count_read(struct seq_file *seq, void *data) { - return single_open(f, brcmf_sdio_forensic_read, inode->i_private); -} + struct brcmf_bus *bus_if = dev_get_drvdata(seq->private); + struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; + struct brcmf_sdio_count *sdcnt = &sdiodev->bus->sdcnt; -static const struct file_operations brcmf_sdio_forensic_ops = { - .owner = THIS_MODULE, - .open = brcmf_sdio_forensic_open, - .release = single_release, - .read = seq_read, - .llseek = seq_lseek -}; + seq_printf(seq, + "intrcount: %u\nlastintrs: %u\n" + "pollcnt: %u\nregfails: %u\n" + "tx_sderrs: %u\nfcqueued: %u\n" + "rxrtx: %u\nrx_toolong: %u\n" + "rxc_errors: %u\nrx_hdrfail: %u\n" + "rx_badhdr: %u\nrx_badseq: %u\n" + "fc_rcvd: %u\nfc_xoff: %u\n" + "fc_xon: %u\nrxglomfail: %u\n" + "rxglomframes: %u\nrxglompkts: %u\n" + "f2rxhdrs: %u\nf2rxdata: %u\n" + "f2txdata: %u\nf1regdata: %u\n" + "tickcnt: %u\ntx_ctlerrs: %lu\n" + "tx_ctlpkts: %lu\nrx_ctlerrs: %lu\n" + "rx_ctlpkts: %lu\nrx_readahead: %lu\n", + sdcnt->intrcount, sdcnt->lastintrs, + sdcnt->pollcnt, sdcnt->regfails, + sdcnt->tx_sderrs, sdcnt->fcqueued, + sdcnt->rxrtx, sdcnt->rx_toolong, + sdcnt->rxc_errors, sdcnt->rx_hdrfail, + sdcnt->rx_badhdr, sdcnt->rx_badseq, + sdcnt->fc_rcvd, sdcnt->fc_xoff, + sdcnt->fc_xon, sdcnt->rxglomfail, + sdcnt->rxglomframes, sdcnt->rxglompkts, + sdcnt->f2rxhdrs, sdcnt->f2rxdata, + sdcnt->f2txdata, sdcnt->f1regdata, + sdcnt->tickcnt, sdcnt->tx_ctlerrs, + sdcnt->tx_ctlpkts, sdcnt->rx_ctlerrs, + sdcnt->rx_ctlpkts, sdcnt->rx_readahead_cnt); + + return 0; +} static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) { @@ -3096,9 +3157,9 @@ static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) if (IS_ERR_OR_NULL(dentry)) return; - debugfs_create_file("forensics", S_IRUGO, dentry, bus, - &brcmf_sdio_forensic_ops); - brcmf_debugfs_create_sdio_count(drvr, &bus->sdcnt); + brcmf_debugfs_add_entry(drvr, "forensics", brcmf_sdio_forensic_read); + brcmf_debugfs_add_entry(drvr, "counters", + brcmf_debugfs_sdio_count_read); debugfs_create_u32("console_interval", 0644, dentry, &bus->console_interval); } diff --git a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c index 699908de314a..d42f7d04b65f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/fwsignal.c @@ -454,6 +454,34 @@ struct brcmf_fws_macdesc_table { struct brcmf_fws_mac_descriptor other; }; +struct brcmf_fws_stats { + u32 tlv_parse_failed; + u32 tlv_invalid_type; + u32 header_only_pkt; + u32 header_pulls; + u32 pkt2bus; + u32 send_pkts[5]; + u32 requested_sent[5]; + u32 generic_error; + u32 mac_update_failed; + u32 mac_ps_update_failed; + u32 if_update_failed; + u32 packet_request_failed; + u32 credit_request_failed; + u32 rollback_success; + u32 rollback_failed; + u32 delayq_full_error; + u32 supprq_full_error; + u32 txs_indicate; + u32 txs_discard; + u32 txs_supp_core; + u32 txs_supp_ps; + u32 txs_tossed; + u32 txs_host_tossed; + u32 bus_flow_block; + u32 fws_flow_block; +}; + struct brcmf_fws_info { struct brcmf_pub *drvr; spinlock_t spinlock; @@ -2017,6 +2045,75 @@ static void brcmf_fws_dequeue_worker(struct work_struct *worker) brcmf_fws_unlock(fws); } +#ifdef DEBUG +static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(seq->private); + struct brcmf_fws_stats *fwstats = &bus_if->drvr->fws->stats; + + seq_printf(seq, + "header_pulls: %u\n" + "header_only_pkt: %u\n" + "tlv_parse_failed: %u\n" + "tlv_invalid_type: %u\n" + "mac_update_fails: %u\n" + "ps_update_fails: %u\n" + "if_update_fails: %u\n" + "pkt2bus: %u\n" + "generic_error: %u\n" + "rollback_success: %u\n" + "rollback_failed: %u\n" + "delayq_full: %u\n" + "supprq_full: %u\n" + "txs_indicate: %u\n" + "txs_discard: %u\n" + "txs_suppr_core: %u\n" + "txs_suppr_ps: %u\n" + "txs_tossed: %u\n" + "txs_host_tossed: %u\n" + "bus_flow_block: %u\n" + "fws_flow_block: %u\n" + "send_pkts: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n" + "requested_sent: BK:%u BE:%u VO:%u VI:%u BCMC:%u\n", + fwstats->header_pulls, + fwstats->header_only_pkt, + fwstats->tlv_parse_failed, + fwstats->tlv_invalid_type, + fwstats->mac_update_failed, + fwstats->mac_ps_update_failed, + fwstats->if_update_failed, + fwstats->pkt2bus, + fwstats->generic_error, + fwstats->rollback_success, + fwstats->rollback_failed, + fwstats->delayq_full_error, + fwstats->supprq_full_error, + fwstats->txs_indicate, + fwstats->txs_discard, + fwstats->txs_supp_core, + fwstats->txs_supp_ps, + fwstats->txs_tossed, + fwstats->txs_host_tossed, + fwstats->bus_flow_block, + fwstats->fws_flow_block, + fwstats->send_pkts[0], fwstats->send_pkts[1], + fwstats->send_pkts[2], fwstats->send_pkts[3], + fwstats->send_pkts[4], + fwstats->requested_sent[0], + fwstats->requested_sent[1], + fwstats->requested_sent[2], + fwstats->requested_sent[3], + fwstats->requested_sent[4]); + + return 0; +} +#else +static int brcmf_debugfs_fws_stats_read(struct seq_file *seq, void *data) +{ + return 0; +} +#endif + int brcmf_fws_init(struct brcmf_pub *drvr) { struct brcmf_fws_info *fws; @@ -2107,7 +2204,8 @@ int brcmf_fws_init(struct brcmf_pub *drvr) BRCMF_FWS_PSQ_LEN); /* create debugfs file for statistics */ - brcmf_debugfs_create_fws_stats(drvr, &fws->stats); + brcmf_debugfs_add_entry(drvr, "fws_stats", + brcmf_debugfs_fws_stats_read); brcmf_dbg(INFO, "%s bdcv2 tlv signaling [%x]\n", fws->fw_signals ? "enabled" : "disabled", tlv); From c1b20532ef395f23c2d24bc9c7f772a45e0420c7 Mon Sep 17 00:00:00 2001 From: Daniel Kim Date: Sat, 12 Jul 2014 08:49:37 +0200 Subject: [PATCH 324/378] brcmfmac: Make firmware path a module parameter This patch makes firmware path a module parameter so that firmware and nvram files can be loaded from the specified path. Signed-off-by: Daniel Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/dhd_sdio.c | 47 +++++++++++-------- .../wireless/brcm80211/brcmfmac/firmware.c | 5 ++ .../wireless/brcm80211/brcmfmac/firmware.h | 5 ++ .../wireless/brcm80211/brcmfmac/sdio_host.h | 4 ++ 4 files changed, 41 insertions(+), 20 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index c0486329331b..67d91d5cc13d 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -666,28 +666,34 @@ static const struct brcmf_firmware_names brcmf_fwname_data[] = { { BRCM_CC_4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } }; -static const char *brcmf_sdio_get_fwname(struct brcmf_chip *ci, - enum brcmf_firmware_type type) +static int brcmf_sdio_get_fwnames(struct brcmf_chip *ci, + struct brcmf_sdio_dev *sdiodev) { int i; for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) { if (brcmf_fwname_data[i].chipid == ci->chip && - brcmf_fwname_data[i].revmsk & BIT(ci->chiprev)) { - switch (type) { - case BRCMF_FIRMWARE_BIN: - return brcmf_fwname_data[i].bin; - case BRCMF_FIRMWARE_NVRAM: - return brcmf_fwname_data[i].nv; - default: - brcmf_err("invalid firmware type (%d)\n", type); - return NULL; - } - } + brcmf_fwname_data[i].revmsk & BIT(ci->chiprev)) + break; } - brcmf_err("Unknown chipid %d [%d]\n", - ci->chip, ci->chiprev); - return NULL; + + if (i == ARRAY_SIZE(brcmf_fwname_data)) { + brcmf_err("Unknown chipid %d [%d]\n", ci->chip, ci->chiprev); + return -ENODEV; + } + + /* check if firmware path is provided by module parameter */ + if (brcmf_firmware_path[0] != '\0') { + if (brcmf_firmware_path[strlen(brcmf_firmware_path) - 1] != '/') + strcat(brcmf_firmware_path, "/"); + + strcpy(sdiodev->fw_name, brcmf_firmware_path); + strcpy(sdiodev->nvram_name, brcmf_firmware_path); + } + strcat(sdiodev->fw_name, brcmf_fwname_data[i].bin); + strcat(sdiodev->nvram_name, brcmf_fwname_data[i].nv); + + return 0; } static void pkt_align(struct sk_buff *p, int len, int align) @@ -4160,11 +4166,12 @@ struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) brcmf_sdio_debugfs_create(bus); brcmf_dbg(INFO, "completed!!\n"); + ret = brcmf_sdio_get_fwnames(bus->ci, sdiodev); + if (ret) + goto fail; + ret = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM, - brcmf_sdio_get_fwname(bus->ci, - BRCMF_FIRMWARE_BIN), - brcmf_sdio_get_fwname(bus->ci, - BRCMF_FIRMWARE_NVRAM), + sdiodev->fw_name, sdiodev->nvram_name, brcmf_sdio_firmware_callback); if (ret != 0) { brcmf_err("async firmware request failed: %d\n", ret); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c index 7b7d237c1ddb..8ea9f283d2b8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.c @@ -18,10 +18,15 @@ #include #include #include +#include #include "dhd_dbg.h" #include "firmware.h" +char brcmf_firmware_path[BRCMF_FW_PATH_LEN]; +module_param_string(firmware_path, brcmf_firmware_path, + BRCMF_FW_PATH_LEN, 0440); + enum nvram_parser_state { IDLE, KEY, diff --git a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h index 6431bfd7afff..4d3482356b77 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/firmware.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/firmware.h @@ -21,6 +21,11 @@ #define BRCMF_FW_REQ_FLAGS 0x00F0 #define BRCMF_FW_REQ_NV_OPTIONAL 0x0010 +#define BRCMF_FW_PATH_LEN 256 +#define BRCMF_FW_NAME_LEN 32 + +extern char brcmf_firmware_path[]; + void brcmf_fw_nvram_free(void *nvram); /* * Request firmware(s) asynchronously. When the asynchronous request diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h index 3deab7959a0d..6c5e585ccda9 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h @@ -18,6 +18,8 @@ #define _BRCM_SDH_H_ #include +#include +#include "firmware.h" #define SDIO_FUNC_0 0 #define SDIO_FUNC_1 1 @@ -182,6 +184,8 @@ struct brcmf_sdio_dev { uint max_segment_size; uint txglomsz; struct sg_table sgtable; + char fw_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; + char nvram_name[BRCMF_FW_PATH_LEN + BRCMF_FW_NAME_LEN]; }; /* sdio core registers */ From ccfd1e810bb7f053e74bc796b84c8e1de37c21fa Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 12 Jul 2014 08:49:38 +0200 Subject: [PATCH 325/378] brcmfmac: move attach and detach functions in wl_cfg80211.c Preparing for another patch move the functions in separate commit. Reviewed-by: Hante Meuleman Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 215 +++++++++--------- 1 file changed, 107 insertions(+), 108 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index 9682cf213ec4..dd487e93141f 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -4967,114 +4967,6 @@ static int brcmf_enable_bw40_2g(struct brcmf_if *ifp) return err; } -struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, - struct device *busdev) -{ - struct net_device *ndev = drvr->iflist[0]->ndev; - struct brcmf_cfg80211_info *cfg; - struct wiphy *wiphy; - struct brcmf_cfg80211_vif *vif; - struct brcmf_if *ifp; - s32 err = 0; - s32 io_type; - - if (!ndev) { - brcmf_err("ndev is invalid\n"); - return NULL; - } - - ifp = netdev_priv(ndev); - wiphy = brcmf_setup_wiphy(busdev); - if (IS_ERR(wiphy)) - return NULL; - - cfg = wiphy_priv(wiphy); - cfg->wiphy = wiphy; - cfg->pub = drvr; - init_vif_event(&cfg->vif_event); - INIT_LIST_HEAD(&cfg->vif_list); - - vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false); - if (IS_ERR(vif)) { - wiphy_free(wiphy); - return NULL; - } - - vif->ifp = ifp; - vif->wdev.netdev = ndev; - ndev->ieee80211_ptr = &vif->wdev; - SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy)); - - err = wl_init_priv(cfg); - if (err) { - brcmf_err("Failed to init iwm_priv (%d)\n", err); - goto cfg80211_attach_out; - } - ifp->vif = vif; - - err = brcmf_p2p_attach(cfg); - if (err) { - brcmf_err("P2P initilisation failed (%d)\n", err); - goto cfg80211_p2p_attach_out; - } - err = brcmf_btcoex_attach(cfg); - if (err) { - brcmf_err("BT-coex initialisation failed (%d)\n", err); - brcmf_p2p_detach(&cfg->p2p); - goto cfg80211_p2p_attach_out; - } - - /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(), - * setup 40MHz in 2GHz band and enable OBSS scanning. - */ - if (wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap & - IEEE80211_HT_CAP_SUP_WIDTH_20_40) { - err = brcmf_enable_bw40_2g(ifp); - if (!err) - err = brcmf_fil_iovar_int_set(ifp, "obss_coex", - BRCMF_OBSS_COEX_AUTO); - } - /* clear for now and rely on update later */ - wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.ht_supported = false; - wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap = 0; - - err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1); - if (err) { - brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err); - wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS; - } - - err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, - &io_type); - if (err) { - brcmf_err("Failed to get D11 version (%d)\n", err); - goto cfg80211_p2p_attach_out; - } - cfg->d11inf.io_type = (u8)io_type; - brcmu_d11_attach(&cfg->d11inf); - - return cfg; - -cfg80211_p2p_attach_out: - wl_deinit_priv(cfg); - -cfg80211_attach_out: - brcmf_free_vif(vif); - return NULL; -} - -void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg) -{ - if (!cfg) - return; - - WARN_ON(!list_empty(&cfg->vif_list)); - wiphy_unregister(cfg->wiphy); - brcmf_btcoex_detach(cfg); - wl_deinit_priv(cfg); - wiphy_free(cfg->wiphy); -} - static s32 brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout) { @@ -5658,3 +5550,110 @@ int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg, vif_event_equals(event, action), timeout); } +struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, + struct device *busdev) +{ + struct net_device *ndev = drvr->iflist[0]->ndev; + struct brcmf_cfg80211_info *cfg; + struct wiphy *wiphy; + struct brcmf_cfg80211_vif *vif; + struct brcmf_if *ifp; + s32 err = 0; + s32 io_type; + + if (!ndev) { + brcmf_err("ndev is invalid\n"); + return NULL; + } + + ifp = netdev_priv(ndev); + wiphy = brcmf_setup_wiphy(busdev); + if (IS_ERR(wiphy)) + return NULL; + + cfg = wiphy_priv(wiphy); + cfg->wiphy = wiphy; + cfg->pub = drvr; + init_vif_event(&cfg->vif_event); + INIT_LIST_HEAD(&cfg->vif_list); + + vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false); + if (IS_ERR(vif)) { + wiphy_free(wiphy); + return NULL; + } + + vif->ifp = ifp; + vif->wdev.netdev = ndev; + ndev->ieee80211_ptr = &vif->wdev; + SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy)); + + err = wl_init_priv(cfg); + if (err) { + brcmf_err("Failed to init iwm_priv (%d)\n", err); + goto cfg80211_attach_out; + } + ifp->vif = vif; + + err = brcmf_p2p_attach(cfg); + if (err) { + brcmf_err("P2P initilisation failed (%d)\n", err); + goto cfg80211_p2p_attach_out; + } + err = brcmf_btcoex_attach(cfg); + if (err) { + brcmf_err("BT-coex initialisation failed (%d)\n", err); + brcmf_p2p_detach(&cfg->p2p); + goto cfg80211_p2p_attach_out; + } + + /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(), + * setup 40MHz in 2GHz band and enable OBSS scanning. + */ + if (wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap & + IEEE80211_HT_CAP_SUP_WIDTH_20_40) { + err = brcmf_enable_bw40_2g(ifp); + if (!err) + err = brcmf_fil_iovar_int_set(ifp, "obss_coex", + BRCMF_OBSS_COEX_AUTO); + } + /* clear for now and rely on update later */ + wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.ht_supported = false; + wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap = 0; + + err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1); + if (err) { + brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err); + wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS; + } + + err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, + &io_type); + if (err) { + brcmf_err("Failed to get D11 version (%d)\n", err); + goto cfg80211_p2p_attach_out; + } + cfg->d11inf.io_type = (u8)io_type; + brcmu_d11_attach(&cfg->d11inf); + + return cfg; + +cfg80211_p2p_attach_out: + wl_deinit_priv(cfg); + +cfg80211_attach_out: + brcmf_free_vif(vif); + return NULL; +} + +void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg) +{ + if (!cfg) + return; + + WARN_ON(!list_empty(&cfg->vif_list)); + wiphy_unregister(cfg->wiphy); + brcmf_btcoex_detach(cfg); + wl_deinit_priv(cfg); + wiphy_free(cfg->wiphy); +} From c08437b4b5c89677a81b543644b2d0a99484d947 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 12 Jul 2014 08:49:39 +0200 Subject: [PATCH 326/378] brcmfmac: introduce feature and quirk handling Introducing a new source module that will be responsible for identifying features and quirks related to the device being handled. Reviewed-by: Hante Meuleman Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Daniel (Deognyoun) Kim Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../net/wireless/brcm80211/brcmfmac/Makefile | 1 + drivers/net/wireless/brcm80211/brcmfmac/dhd.h | 5 +- .../wireless/brcm80211/brcmfmac/dhd_linux.c | 13 +- .../net/wireless/brcm80211/brcmfmac/feature.c | 136 ++++++++++++++++++ .../net/wireless/brcm80211/brcmfmac/feature.h | 86 +++++++++++ .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 30 ++-- 6 files changed, 244 insertions(+), 27 deletions(-) create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/feature.c create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/feature.h diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile index 4cffb2ee3673..de0cff3df389 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/Makefile +++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile @@ -34,6 +34,7 @@ brcmfmac-objs += \ dhd_common.o \ dhd_linux.o \ firmware.o \ + feature.o \ btcoex.o \ vendor.o brcmfmac-$(CONFIG_BRCMFMAC_SDIO) += \ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h index a8998eb60d22..7da6441bcfa8 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h @@ -103,6 +103,10 @@ struct brcmf_pub { struct brcmf_ampdu_rx_reorder *reorder_flows[BRCMF_AMPDU_RX_REORDER_MAXFLOWS]; + + u32 feat_flags; + u32 chip_quirks; + #ifdef DEBUG struct dentry *dbgfs_dir; #endif @@ -175,7 +179,6 @@ struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, s32 bssidx, s32 ifidx, void brcmf_del_if(struct brcmf_pub *drvr, s32 bssidx); void brcmf_txflowblock_if(struct brcmf_if *ifp, enum brcmf_netif_stop_reason reason, bool state); -u32 brcmf_get_chip_info(struct brcmf_if *ifp); void brcmf_txfinalize(struct brcmf_pub *drvr, struct sk_buff *txp, u8 ifidx, bool success); diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c index 09dd8c13d844..bf448081d6fb 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c @@ -30,6 +30,7 @@ #include "wl_cfg80211.h" #include "fwil.h" #include "fwsignal.h" +#include "feature.h" #include "proto.h" MODULE_AUTHOR("Broadcom Corporation"); @@ -936,6 +937,8 @@ int brcmf_bus_start(struct device *dev) if (ret < 0) goto fail; + brcmf_feat_attach(drvr); + ret = brcmf_fws_init(drvr); if (ret < 0) goto fail; @@ -1073,16 +1076,6 @@ int brcmf_netdev_wait_pend8021x(struct net_device *ndev) return !err; } -/* - * return chip id and rev of the device encoded in u32. - */ -u32 brcmf_get_chip_info(struct brcmf_if *ifp) -{ - struct brcmf_bus *bus = ifp->drvr->bus_if; - - return bus->chip << 4 | bus->chiprev; -} - static void brcmf_driver_register(struct work_struct *work) { #ifdef CONFIG_BRCMFMAC_SDIO diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/brcm80211/brcmfmac/feature.c new file mode 100644 index 000000000000..50877e3c5d2f --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.c @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, 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. + */ + +#include + +#include +#include "dhd.h" +#include "dhd_bus.h" +#include "dhd_dbg.h" +#include "fwil.h" +#include "feature.h" + +/* + * firmware error code received if iovar is unsupported. + */ +#define EBRCMF_FEAT_UNSUPPORTED 23 + +/* + * expand feature list to array of feature strings. + */ +#define BRCMF_FEAT_DEF(_f) \ + #_f, +static const char *brcmf_feat_names[] = { + BRCMF_FEAT_LIST +}; +#undef BRCMF_FEAT_DEF + +#ifdef DEBUG +/* + * expand quirk list to array of quirk strings. + */ +#define BRCMF_QUIRK_DEF(_q) \ + #_q, +static const char * const brcmf_quirk_names[] = { + BRCMF_QUIRK_LIST +}; +#undef BRCMF_QUIRK_DEF + +/** + * brcmf_feat_debugfs_read() - expose feature info to debugfs. + * + * @seq: sequence for debugfs entry. + * @data: raw data pointer. + */ +static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data) +{ + struct brcmf_bus *bus_if = dev_get_drvdata(seq->private); + u32 feats = bus_if->drvr->feat_flags; + u32 quirks = bus_if->drvr->chip_quirks; + int id; + + seq_printf(seq, "Features: %08x\n", feats); + for (id = 0; id < BRCMF_FEAT_LAST; id++) + if (feats & BIT(id)) + seq_printf(seq, "\t%s\n", brcmf_feat_names[id]); + seq_printf(seq, "\nQuirks: %08x\n", quirks); + for (id = 0; id < BRCMF_FEAT_QUIRK_LAST; id++) + if (quirks & BIT(id)) + seq_printf(seq, "\t%s\n", brcmf_quirk_names[id]); + return 0; +} +#else +static int brcmf_feat_debugfs_read(struct seq_file *seq, void *data) +{ + return 0; +} +#endif /* DEBUG */ + +/** + * brcmf_feat_iovar_int_get() - determine feature through iovar query. + * + * @ifp: interface to query. + * @id: feature id. + * @name: iovar name. + */ +static void brcmf_feat_iovar_int_get(struct brcmf_if *ifp, + enum brcmf_feat_id id, char *name) +{ + u32 data; + int err; + + err = brcmf_fil_iovar_int_get(ifp, name, &data); + if (err == 0) { + brcmf_dbg(INFO, "enabling feature: %s\n", brcmf_feat_names[id]); + ifp->drvr->feat_flags |= BIT(id); + } else { + brcmf_dbg(TRACE, "%s feature check failed: %d\n", + brcmf_feat_names[id], err); + } +} + +void brcmf_feat_attach(struct brcmf_pub *drvr) +{ + struct brcmf_if *ifp = drvr->iflist[0]; + + brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_MCHAN, "mchan"); + + /* set chip related quirks */ + switch (drvr->bus_if->chip) { + case BRCM_CC_43236_CHIP_ID: + drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_AUTO_AUTH); + break; + case BRCM_CC_4329_CHIP_ID: + drvr->chip_quirks |= BIT(BRCMF_FEAT_QUIRK_NEED_MPC); + break; + default: + /* no quirks */ + break; + } + + brcmf_debugfs_add_entry(drvr, "features", brcmf_feat_debugfs_read); +} + +bool brcmf_feat_is_enabled(struct brcmf_if *ifp, enum brcmf_feat_id id) +{ + return (ifp->drvr->feat_flags & BIT(id)); +} + +bool brcmf_feat_is_quirk_enabled(struct brcmf_if *ifp, + enum brcmf_feat_quirk quirk) +{ + return (ifp->drvr->chip_quirks & BIT(quirk)); +} diff --git a/drivers/net/wireless/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/brcm80211/brcmfmac/feature.h new file mode 100644 index 000000000000..961d175f8afb --- /dev/null +++ b/drivers/net/wireless/brcm80211/brcmfmac/feature.h @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2014 Broadcom Corporation + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, 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. + */ +#ifndef _BRCMF_FEATURE_H +#define _BRCMF_FEATURE_H + +/* + * Features: + * + * MCHAN: multi-channel for concurrent P2P. + */ +#define BRCMF_FEAT_LIST \ + BRCMF_FEAT_DEF(MCHAN) +/* + * Quirks: + * + * AUTO_AUTH: workaround needed for automatic authentication type. + * NEED_MPC: driver needs to disable MPC during scanning operation. + */ +#define BRCMF_QUIRK_LIST \ + BRCMF_QUIRK_DEF(AUTO_AUTH) \ + BRCMF_QUIRK_DEF(NEED_MPC) + +#define BRCMF_FEAT_DEF(_f) \ + BRCMF_FEAT_ ## _f, +/* + * expand feature list to enumeration. + */ +enum brcmf_feat_id { + BRCMF_FEAT_LIST + BRCMF_FEAT_LAST +}; +#undef BRCMF_FEAT_DEF + +#define BRCMF_QUIRK_DEF(_q) \ + BRCMF_FEAT_QUIRK_ ## _q, +/* + * expand quirk list to enumeration. + */ +enum brcmf_feat_quirk { + BRCMF_QUIRK_LIST + BRCMF_FEAT_QUIRK_LAST +}; +#undef BRCMF_QUIRK_DEF + +/** + * brcmf_feat_attach() - determine features and quirks. + * + * @drvr: driver instance. + */ +void brcmf_feat_attach(struct brcmf_pub *drvr); + +/** + * brcmf_feat_is_enabled() - query feature. + * + * @ifp: interface instance. + * @id: feature id to check. + * + * Return: true is feature is enabled; otherwise false. + */ +bool brcmf_feat_is_enabled(struct brcmf_if *ifp, enum brcmf_feat_id id); + +/** + * brcmf_feat_is_quirk_enabled() - query chip quirk. + * + * @ifp: interface instance. + * @quirk: quirk id to check. + * + * Return: true is quirk is enabled; otherwise false. + */ +bool brcmf_feat_is_quirk_enabled(struct brcmf_if *ifp, + enum brcmf_feat_quirk quirk); + +#endif /* _BRCMF_FEATURE_H */ diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index dd487e93141f..e3c1c71c5d2e 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -33,6 +33,7 @@ #include "p2p.h" #include "btcoex.h" #include "wl_cfg80211.h" +#include "feature.h" #include "fwil.h" #include "vendor.h" @@ -592,7 +593,7 @@ static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc) { - if ((brcmf_get_chip_info(ifp) >> 4) == 0x4329) + if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC)) brcmf_set_mpc(ifp, mpc); } @@ -1619,17 +1620,10 @@ static enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp, enum nl80211_auth_type type) { - u32 ci; - if (type == NL80211_AUTHTYPE_AUTOMATIC) { - /* shift to ignore chip revision */ - ci = brcmf_get_chip_info(ifp) >> 4; - switch (ci) { - case 43236: - brcmf_dbg(CONN, "43236 WAR: use OPEN instead of AUTO\n"); - return NL80211_AUTHTYPE_OPEN_SYSTEM; - default: - break; - } + if (type == NL80211_AUTHTYPE_AUTOMATIC && + brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) { + brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n"); + type = NL80211_AUTHTYPE_OPEN_SYSTEM; } return type; } @@ -4310,10 +4304,10 @@ static const struct ieee80211_iface_limit brcmf_iface_limits[] = { .types = BIT(NL80211_IFTYPE_P2P_DEVICE) } }; -static const struct ieee80211_iface_combination brcmf_iface_combos[] = { +static struct ieee80211_iface_combination brcmf_iface_combos[] = { { .max_interfaces = BRCMF_IFACE_MAX_CNT, - .num_different_channels = 2, + .num_different_channels = 1, .n_limits = ARRAY_SIZE(brcmf_iface_limits), .limits = brcmf_iface_limits } @@ -4348,7 +4342,8 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = { } }; -static struct wiphy *brcmf_setup_wiphy(struct device *phydev) +static +struct wiphy *brcmf_setup_wiphy(struct brcmf_if *ifp, struct device *phydev) { struct wiphy *wiphy; s32 err = 0; @@ -4368,6 +4363,9 @@ static struct wiphy *brcmf_setup_wiphy(struct device *phydev) BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_DEVICE); + /* need VSDB firmware feature for concurrent channels */ + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN)) + brcmf_iface_combos[0].num_different_channels = 2; wiphy->iface_combinations = brcmf_iface_combos; wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos); wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; @@ -5567,7 +5565,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, } ifp = netdev_priv(ndev); - wiphy = brcmf_setup_wiphy(busdev); + wiphy = brcmf_setup_wiphy(ifp, busdev); if (IS_ERR(wiphy)) return NULL; From aa70b4fa43dfe5dc3df2161d814ccaa7897d73c9 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 12 Jul 2014 08:49:40 +0200 Subject: [PATCH 327/378] brcmfmac: moving some functions around Just reordering the functions in preparation of subsequent changes. Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 288 +++++++++--------- 1 file changed, 144 insertions(+), 144 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index e3c1c71c5d2e..d0b48dd4f154 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -4278,126 +4278,6 @@ static struct cfg80211_ops wl_cfg80211_ops = { .tdls_oper = brcmf_cfg80211_tdls_oper, }; -static void brcmf_wiphy_pno_params(struct wiphy *wiphy) -{ - /* scheduled scan settings */ - wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT; - wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT; - wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; - wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; -} - -static const struct ieee80211_iface_limit brcmf_iface_limits[] = { - { - .max = 2, - .types = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_AP) - }, - { - .max = 1, - .types = BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_P2P_GO) - }, - { - .max = 1, - .types = BIT(NL80211_IFTYPE_P2P_DEVICE) - } -}; -static struct ieee80211_iface_combination brcmf_iface_combos[] = { - { - .max_interfaces = BRCMF_IFACE_MAX_CNT, - .num_different_channels = 1, - .n_limits = ARRAY_SIZE(brcmf_iface_limits), - .limits = brcmf_iface_limits - } -}; - -static const struct ieee80211_txrx_stypes -brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = { - [NL80211_IFTYPE_STATION] = { - .tx = 0xffff, - .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4) - }, - [NL80211_IFTYPE_P2P_CLIENT] = { - .tx = 0xffff, - .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4) - }, - [NL80211_IFTYPE_P2P_GO] = { - .tx = 0xffff, - .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | - BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | - BIT(IEEE80211_STYPE_DISASSOC >> 4) | - BIT(IEEE80211_STYPE_AUTH >> 4) | - BIT(IEEE80211_STYPE_DEAUTH >> 4) | - BIT(IEEE80211_STYPE_ACTION >> 4) - }, - [NL80211_IFTYPE_P2P_DEVICE] = { - .tx = 0xffff, - .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | - BIT(IEEE80211_STYPE_PROBE_REQ >> 4) - } -}; - -static -struct wiphy *brcmf_setup_wiphy(struct brcmf_if *ifp, struct device *phydev) -{ - struct wiphy *wiphy; - s32 err = 0; - - wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info)); - if (!wiphy) { - brcmf_err("Could not allocate wiphy device\n"); - return ERR_PTR(-ENOMEM); - } - set_wiphy_dev(wiphy, phydev); - wiphy->max_scan_ssids = WL_NUM_SCAN_MAX; - wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; - wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX; - wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | - BIT(NL80211_IFTYPE_ADHOC) | - BIT(NL80211_IFTYPE_AP) | - BIT(NL80211_IFTYPE_P2P_CLIENT) | - BIT(NL80211_IFTYPE_P2P_GO) | - BIT(NL80211_IFTYPE_P2P_DEVICE); - /* need VSDB firmware feature for concurrent channels */ - if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN)) - brcmf_iface_combos[0].num_different_channels = 2; - wiphy->iface_combinations = brcmf_iface_combos; - wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos); - wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; - wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; - wiphy->cipher_suites = __wl_cipher_suites; - wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites); - wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT | - WIPHY_FLAG_OFFCHAN_TX | - WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | - WIPHY_FLAG_SUPPORTS_TDLS; - if (!brcmf_roamoff) - wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; - wiphy->mgmt_stypes = brcmf_txrx_stypes; - wiphy->max_remain_on_channel_duration = 5000; - brcmf_wiphy_pno_params(wiphy); - brcmf_dbg(INFO, "Registering custom regulatory\n"); - wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; - wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); - - /* vendor commands/events support */ - wiphy->vendor_commands = brcmf_vendor_cmds; - wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1; - - err = wiphy_register(wiphy); - if (err < 0) { - brcmf_err("Could not register wiphy device (%d)\n", err); - wiphy_free(wiphy); - return ERR_PTR(err); - } - return wiphy; -} - struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, enum nl80211_iftype type, bool pm_block) @@ -4941,30 +4821,6 @@ static void init_vif_event(struct brcmf_cfg80211_vif_event *event) mutex_init(&event->vif_event_lock); } -static int brcmf_enable_bw40_2g(struct brcmf_if *ifp) -{ - struct brcmf_fil_bwcap_le band_bwcap; - u32 val; - int err; - - /* verify support for bw_cap command */ - val = WLC_BAND_5G; - err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val); - - if (!err) { - /* only set 2G bandwidth using bw_cap command */ - band_bwcap.band = cpu_to_le32(WLC_BAND_2G); - band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ); - err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap, - sizeof(band_bwcap)); - } else { - brcmf_dbg(INFO, "fallback to mimo_bw_cap\n"); - val = WLC_N_BW_40ALL; - err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val); - } - return err; -} - static s32 brcmf_dongle_roam(struct brcmf_if *ifp, u32 bcn_timeout) { @@ -5194,6 +5050,30 @@ exit: return err; } +static int brcmf_enable_bw40_2g(struct brcmf_if *ifp) +{ + struct brcmf_fil_bwcap_le band_bwcap; + u32 val; + int err; + + /* verify support for bw_cap command */ + val = WLC_BAND_5G; + err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val); + + if (!err) { + /* only set 2G bandwidth using bw_cap command */ + band_bwcap.band = cpu_to_le32(WLC_BAND_2G); + band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ); + err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap, + sizeof(band_bwcap)); + } else { + brcmf_dbg(INFO, "fallback to mimo_bw_cap\n"); + val = WLC_N_BW_40ALL; + err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val); + } + return err; +} + static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[]) { u32 band, mimo_bwcap; @@ -5377,6 +5257,126 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg) return err; } +static const struct ieee80211_iface_limit brcmf_iface_limits[] = { + { + .max = 2, + .types = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP) + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) + }, + { + .max = 1, + .types = BIT(NL80211_IFTYPE_P2P_DEVICE) + } +}; +static struct ieee80211_iface_combination brcmf_iface_combos[] = { + { + .max_interfaces = BRCMF_IFACE_MAX_CNT, + .num_different_channels = 1, + .n_limits = ARRAY_SIZE(brcmf_iface_limits), + .limits = brcmf_iface_limits + } +}; + +static const struct ieee80211_txrx_stypes +brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = { + [NL80211_IFTYPE_STATION] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_P2P_CLIENT] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + }, + [NL80211_IFTYPE_P2P_GO] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | + BIT(IEEE80211_STYPE_DISASSOC >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4) | + BIT(IEEE80211_STYPE_DEAUTH >> 4) | + BIT(IEEE80211_STYPE_ACTION >> 4) + }, + [NL80211_IFTYPE_P2P_DEVICE] = { + .tx = 0xffff, + .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_PROBE_REQ >> 4) + } +}; + +static void brcmf_wiphy_pno_params(struct wiphy *wiphy) +{ + /* scheduled scan settings */ + wiphy->max_sched_scan_ssids = BRCMF_PNO_MAX_PFN_COUNT; + wiphy->max_match_sets = BRCMF_PNO_MAX_PFN_COUNT; + wiphy->max_sched_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; + wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; +} + +static struct wiphy *brcmf_setup_wiphy(struct brcmf_if *ifp, + struct device *phydev) +{ + struct wiphy *wiphy; + s32 err = 0; + + wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info)); + if (!wiphy) { + brcmf_err("Could not allocate wiphy device\n"); + return ERR_PTR(-ENOMEM); + } + set_wiphy_dev(wiphy, phydev); + wiphy->max_scan_ssids = WL_NUM_SCAN_MAX; + wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; + wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX; + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_ADHOC) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_DEVICE); + /* need VSDB firmware feature for concurrent channels */ + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN)) + brcmf_iface_combos[0].num_different_channels = 2; + wiphy->iface_combinations = brcmf_iface_combos; + wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos); + wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; + wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; + wiphy->cipher_suites = __wl_cipher_suites; + wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites); + wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT | + WIPHY_FLAG_OFFCHAN_TX | + WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL | + WIPHY_FLAG_SUPPORTS_TDLS; + if (!brcmf_roamoff) + wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; + wiphy->mgmt_stypes = brcmf_txrx_stypes; + wiphy->max_remain_on_channel_duration = 5000; + brcmf_wiphy_pno_params(wiphy); + brcmf_dbg(INFO, "Registering custom regulatory\n"); + wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; + wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); + + /* vendor commands/events support */ + wiphy->vendor_commands = brcmf_vendor_cmds; + wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1; + + err = wiphy_register(wiphy); + if (err < 0) { + brcmf_err("Could not register wiphy device (%d)\n", err); + wiphy_free(wiphy); + return ERR_PTR(err); + } + return wiphy; +} + static s32 brcmf_dongle_probecap(struct brcmf_cfg80211_info *cfg) { From b48d891676f756d48b4d0ee131e4a7a5d43ca417 Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 12 Jul 2014 08:49:41 +0200 Subject: [PATCH 328/378] brcmfmac: rework wiphy structure setup Instead of waiting for IFF_UP of the primary net device to determine the band and channel information of the wiphy structure, this is now done during driver initialization in brcmf_cfg80211_attach(). The channel information is obtained from the device and the 2G band is updated when 40MHz bandwidth is enabled for that band. Before this change the band and channel objects were common between multiple brcmfmac devices in the system, which make that information rather unreliable. That is also fixed with this reworked implementation. Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Reviewed-by: Franky (Zhenhui) Lin Reviewed-by: Daniel (Deognyoun) Kim Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- .../wireless/brcm80211/brcmfmac/wl_cfg80211.c | 564 +++++++++--------- 1 file changed, 297 insertions(+), 267 deletions(-) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index d0b48dd4f154..d3bb4e0f4480 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -103,24 +103,6 @@ static bool check_vif_up(struct brcmf_cfg80211_vif *vif) return true; } -#define CHAN2G(_channel, _freq, _flags) { \ - .band = IEEE80211_BAND_2GHZ, \ - .center_freq = (_freq), \ - .hw_value = (_channel), \ - .flags = (_flags), \ - .max_antenna_gain = 0, \ - .max_power = 30, \ -} - -#define CHAN5G(_channel, _flags) { \ - .band = IEEE80211_BAND_5GHZ, \ - .center_freq = 5000 + (5 * (_channel)), \ - .hw_value = (_channel), \ - .flags = (_flags), \ - .max_antenna_gain = 0, \ - .max_power = 30, \ -} - #define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2) #define RATETAB_ENT(_rateid, _flags) \ { \ @@ -149,58 +131,17 @@ static struct ieee80211_rate __wl_rates[] = { #define wl_g_rates (__wl_rates + 0) #define wl_g_rates_size 12 -static struct ieee80211_channel __wl_2ghz_channels[] = { - CHAN2G(1, 2412, 0), - CHAN2G(2, 2417, 0), - CHAN2G(3, 2422, 0), - CHAN2G(4, 2427, 0), - CHAN2G(5, 2432, 0), - CHAN2G(6, 2437, 0), - CHAN2G(7, 2442, 0), - CHAN2G(8, 2447, 0), - CHAN2G(9, 2452, 0), - CHAN2G(10, 2457, 0), - CHAN2G(11, 2462, 0), - CHAN2G(12, 2467, 0), - CHAN2G(13, 2472, 0), - CHAN2G(14, 2484, 0), -}; - -static struct ieee80211_channel __wl_5ghz_a_channels[] = { - CHAN5G(34, 0), CHAN5G(36, 0), - CHAN5G(38, 0), CHAN5G(40, 0), - CHAN5G(42, 0), CHAN5G(44, 0), - CHAN5G(46, 0), CHAN5G(48, 0), - CHAN5G(52, 0), CHAN5G(56, 0), - CHAN5G(60, 0), CHAN5G(64, 0), - CHAN5G(100, 0), CHAN5G(104, 0), - CHAN5G(108, 0), CHAN5G(112, 0), - CHAN5G(116, 0), CHAN5G(120, 0), - CHAN5G(124, 0), CHAN5G(128, 0), - CHAN5G(132, 0), CHAN5G(136, 0), - CHAN5G(140, 0), CHAN5G(149, 0), - CHAN5G(153, 0), CHAN5G(157, 0), - CHAN5G(161, 0), CHAN5G(165, 0), - CHAN5G(184, 0), CHAN5G(188, 0), - CHAN5G(192, 0), CHAN5G(196, 0), - CHAN5G(200, 0), CHAN5G(204, 0), - CHAN5G(208, 0), CHAN5G(212, 0), - CHAN5G(216, 0), -}; - -static struct ieee80211_supported_band __wl_band_2ghz = { +/* Band templates duplicated per wiphy. The channel info + * is filled in after querying the device. + */ +static const struct ieee80211_supported_band __wl_band_2ghz = { .band = IEEE80211_BAND_2GHZ, - .channels = __wl_2ghz_channels, - .n_channels = ARRAY_SIZE(__wl_2ghz_channels), .bitrates = wl_g_rates, .n_bitrates = wl_g_rates_size, - .ht_cap = {IEEE80211_HT_CAP_SUP_WIDTH_20_40, true}, }; -static struct ieee80211_supported_band __wl_band_5ghz_a = { +static const struct ieee80211_supported_band __wl_band_5ghz_a = { .band = IEEE80211_BAND_5GHZ, - .channels = __wl_5ghz_a_channels, - .n_channels = ARRAY_SIZE(__wl_5ghz_a_channels), .bitrates = wl_a_rates, .n_bitrates = wl_a_rates_size, }; @@ -4913,25 +4854,77 @@ dongle_scantime_out: return err; } +/* Filter the list of channels received from firmware counting only + * the 20MHz channels. The wiphy band data only needs those which get + * flagged to indicate if they can take part in higher bandwidth. + */ +static void brcmf_count_20mhz_channels(struct brcmf_cfg80211_info *cfg, + struct brcmf_chanspec_list *chlist, + u32 chcnt[]) +{ + u32 total = le32_to_cpu(chlist->count); + struct brcmu_chan ch; + int i; -static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, - u32 bw_cap[]) + for (i = 0; i <= total; i++) { + ch.chspec = (u16)le32_to_cpu(chlist->element[i]); + cfg->d11inf.decchspec(&ch); + + /* Firmware gives a ordered list. We skip non-20MHz + * channels is 2G. For 5G we can abort upon reaching + * a non-20MHz channel in the list. + */ + if (ch.bw != BRCMU_CHAN_BW_20) { + if (ch.band == BRCMU_CHAN_BAND_5G) + break; + else + continue; + } + + if (ch.band == BRCMU_CHAN_BAND_2G) + chcnt[0] += 1; + else if (ch.band == BRCMU_CHAN_BAND_5G) + chcnt[1] += 1; + } +} + +static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel, + struct brcmu_chan *ch) +{ + u32 ht40_flag; + + ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40; + if (ch->sb == BRCMU_CHAN_SB_U) { + if (ht40_flag == IEEE80211_CHAN_NO_HT40) + channel->flags &= ~IEEE80211_CHAN_NO_HT40; + channel->flags |= IEEE80211_CHAN_NO_HT40PLUS; + } else { + /* It should be one of + * IEEE80211_CHAN_NO_HT40 or + * IEEE80211_CHAN_NO_HT40PLUS + */ + channel->flags &= ~IEEE80211_CHAN_NO_HT40; + if (ht40_flag == IEEE80211_CHAN_NO_HT40) + channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; + } +} + +static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, + u32 bw_cap[]) { struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); - struct ieee80211_channel *band_chan_arr; + struct ieee80211_supported_band *band; + struct ieee80211_channel *channel; + struct wiphy *wiphy; struct brcmf_chanspec_list *list; struct brcmu_chan ch; - s32 err; + int err; u8 *pbuf; u32 i, j; u32 total; - enum ieee80211_band band; - u32 channel; - u32 *n_cnt; + u32 chaninfo; + u32 chcnt[2] = { 0, 0 }; u32 index; - u32 ht40_flag; - bool update; - u32 array_size; pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); @@ -4944,11 +4937,45 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, BRCMF_DCMD_MEDLEN); if (err) { brcmf_err("get chanspecs error (%d)\n", err); - goto exit; + goto fail_pbuf; } - __wl_band_2ghz.n_channels = 0; - __wl_band_5ghz_a.n_channels = 0; + brcmf_count_20mhz_channels(cfg, list, chcnt); + wiphy = cfg_to_wiphy(cfg); + if (chcnt[0]) { + band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz), + GFP_KERNEL); + if (band == NULL) { + err = -ENOMEM; + goto fail_pbuf; + } + band->channels = kcalloc(chcnt[0], sizeof(*channel), + GFP_KERNEL); + if (band->channels == NULL) { + kfree(band); + err = -ENOMEM; + goto fail_pbuf; + } + band->n_channels = 0; + wiphy->bands[IEEE80211_BAND_2GHZ] = band; + } + if (chcnt[1]) { + band = kmemdup(&__wl_band_5ghz_a, sizeof(__wl_band_5ghz_a), + GFP_KERNEL); + if (band == NULL) { + err = -ENOMEM; + goto fail_band2g; + } + band->channels = kcalloc(chcnt[1], sizeof(*channel), + GFP_KERNEL); + if (band->channels == NULL) { + kfree(band); + err = -ENOMEM; + goto fail_band2g; + } + band->n_channels = 0; + wiphy->bands[IEEE80211_BAND_5GHZ] = band; + } total = le32_to_cpu(list->count); for (i = 0; i < total; i++) { @@ -4956,105 +4983,88 @@ static s32 brcmf_construct_reginfo(struct brcmf_cfg80211_info *cfg, cfg->d11inf.decchspec(&ch); if (ch.band == BRCMU_CHAN_BAND_2G) { - band_chan_arr = __wl_2ghz_channels; - array_size = ARRAY_SIZE(__wl_2ghz_channels); - n_cnt = &__wl_band_2ghz.n_channels; - band = IEEE80211_BAND_2GHZ; + band = wiphy->bands[IEEE80211_BAND_2GHZ]; } else if (ch.band == BRCMU_CHAN_BAND_5G) { - band_chan_arr = __wl_5ghz_a_channels; - array_size = ARRAY_SIZE(__wl_5ghz_a_channels); - n_cnt = &__wl_band_5ghz_a.n_channels; - band = IEEE80211_BAND_5GHZ; + band = wiphy->bands[IEEE80211_BAND_5GHZ]; } else { brcmf_err("Invalid channel Spec. 0x%x.\n", ch.chspec); continue; } - if (!(bw_cap[band] & WLC_BW_40MHZ_BIT) && + if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) && ch.bw == BRCMU_CHAN_BW_40) continue; - if (!(bw_cap[band] & WLC_BW_80MHZ_BIT) && + if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) && ch.bw == BRCMU_CHAN_BW_80) continue; - update = false; - for (j = 0; (j < *n_cnt && (*n_cnt < array_size)); j++) { - if (band_chan_arr[j].hw_value == ch.chnum) { - update = true; + + channel = band->channels; + index = band->n_channels; + for (j = 0; j < band->n_channels; j++) { + if (channel[j].hw_value == ch.chnum) { + index = j; break; } } - if (update) - index = j; - else - index = *n_cnt; - if (index < array_size) { - band_chan_arr[index].center_freq = - ieee80211_channel_to_frequency(ch.chnum, band); - band_chan_arr[index].hw_value = ch.chnum; + channel[index].center_freq = + ieee80211_channel_to_frequency(ch.chnum, band->band); + channel[index].hw_value = ch.chnum; - /* assuming the chanspecs order is HT20, - * HT40 upper, HT40 lower, and VHT80. + /* assuming the chanspecs order is HT20, + * HT40 upper, HT40 lower, and VHT80. + */ + if (ch.bw == BRCMU_CHAN_BW_80) { + channel[index].flags &= ~IEEE80211_CHAN_NO_80MHZ; + } else if (ch.bw == BRCMU_CHAN_BW_40) { + brcmf_update_bw40_channel_flag(&channel[index], &ch); + } else { + /* disable other bandwidths for now as mentioned + * order assure they are enabled for subsequent + * chanspecs. */ - if (ch.bw == BRCMU_CHAN_BW_80) { - band_chan_arr[index].flags &= - ~IEEE80211_CHAN_NO_80MHZ; - } else if (ch.bw == BRCMU_CHAN_BW_40) { - ht40_flag = band_chan_arr[index].flags & - IEEE80211_CHAN_NO_HT40; - if (ch.sb == BRCMU_CHAN_SB_U) { - if (ht40_flag == IEEE80211_CHAN_NO_HT40) - band_chan_arr[index].flags &= - ~IEEE80211_CHAN_NO_HT40; - band_chan_arr[index].flags |= - IEEE80211_CHAN_NO_HT40PLUS; - } else { - /* It should be one of - * IEEE80211_CHAN_NO_HT40 or - * IEEE80211_CHAN_NO_HT40PLUS - */ - band_chan_arr[index].flags &= - ~IEEE80211_CHAN_NO_HT40; - if (ht40_flag == IEEE80211_CHAN_NO_HT40) - band_chan_arr[index].flags |= - IEEE80211_CHAN_NO_HT40MINUS; - } - } else { - /* disable other bandwidths for now as mentioned - * order assure they are enabled for subsequent - * chanspecs. - */ - band_chan_arr[index].flags = - IEEE80211_CHAN_NO_HT40 | - IEEE80211_CHAN_NO_80MHZ; - ch.bw = BRCMU_CHAN_BW_20; - cfg->d11inf.encchspec(&ch); - channel = ch.chspec; - err = brcmf_fil_bsscfg_int_get(ifp, - "per_chan_info", - &channel); - if (!err) { - if (channel & WL_CHAN_RADAR) - band_chan_arr[index].flags |= - (IEEE80211_CHAN_RADAR | - IEEE80211_CHAN_NO_IR); - if (channel & WL_CHAN_PASSIVE) - band_chan_arr[index].flags |= - IEEE80211_CHAN_NO_IR; - } + channel[index].flags = IEEE80211_CHAN_NO_HT40 | + IEEE80211_CHAN_NO_80MHZ; + ch.bw = BRCMU_CHAN_BW_20; + cfg->d11inf.encchspec(&ch); + chaninfo = ch.chspec; + err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info", + &chaninfo); + if (!err) { + if (chaninfo & WL_CHAN_RADAR) + channel[index].flags |= + (IEEE80211_CHAN_RADAR | + IEEE80211_CHAN_NO_IR); + if (chaninfo & WL_CHAN_PASSIVE) + channel[index].flags |= + IEEE80211_CHAN_NO_IR; } - if (!update) - (*n_cnt)++; } + if (index == band->n_channels) + band->n_channels++; } -exit: + kfree(pbuf); + return 0; + +fail_band2g: + kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels); + kfree(wiphy->bands[IEEE80211_BAND_2GHZ]); + wiphy->bands[IEEE80211_BAND_2GHZ] = NULL; +fail_pbuf: kfree(pbuf); return err; } -static int brcmf_enable_bw40_2g(struct brcmf_if *ifp) +static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg) { + struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); + struct ieee80211_supported_band *band; struct brcmf_fil_bwcap_le band_bwcap; + struct brcmf_chanspec_list *list; + u8 *pbuf; u32 val; int err; + struct brcmu_chan ch; + u32 num_chan; + int i, j; /* verify support for bw_cap command */ val = WLC_BAND_5G; @@ -5071,6 +5081,50 @@ static int brcmf_enable_bw40_2g(struct brcmf_if *ifp) val = WLC_N_BW_40ALL; err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val); } + + if (!err) { + /* update channel info in 2G band */ + pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); + + if (pbuf == NULL) + return -ENOMEM; + + ch.band = BRCMU_CHAN_BAND_2G; + ch.bw = BRCMU_CHAN_BW_40; + ch.chnum = 0; + cfg->d11inf.encchspec(&ch); + + /* pass encoded chanspec in query */ + *(__le16 *)pbuf = cpu_to_le16(ch.chspec); + + err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf, + BRCMF_DCMD_MEDLEN); + if (err) { + brcmf_err("get chanspecs error (%d)\n", err); + kfree(pbuf); + return err; + } + + band = cfg_to_wiphy(cfg)->bands[IEEE80211_BAND_2GHZ]; + list = (struct brcmf_chanspec_list *)pbuf; + num_chan = le32_to_cpu(list->count); + for (i = 0; i < num_chan; i++) { + ch.chspec = (u16)le32_to_cpu(list->element[i]); + cfg->d11inf.decchspec(&ch); + if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G)) + continue; + if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40)) + continue; + for (j = 0; j < band->n_channels; j++) { + if (band->channels[j].hw_value == ch.chnum) + break; + } + if (WARN_ON(j == band->n_channels)) + continue; + + brcmf_update_bw40_channel_flag(&band->channels[j], &ch); + } + } return err; } @@ -5164,44 +5218,19 @@ static void brcmf_update_vht_cap(struct ieee80211_supported_band *band, band->vht_cap.vht_mcs.tx_mcs_map = mcs_map; } -static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg) +static int brcmf_setup_wiphybands(struct wiphy *wiphy) { + struct brcmf_cfg80211_info *cfg = wiphy_priv(wiphy); struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); - struct wiphy *wiphy; - s32 phy_list; - u32 band_list[3]; u32 nmode = 0; u32 vhtmode = 0; - u32 bw_cap[2] = { 0, 0 }; + u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT }; u32 rxchain; u32 nchain; - s8 phy; - s32 err; - u32 nband; + int err; s32 i; - struct ieee80211_supported_band *bands[2] = { NULL, NULL }; struct ieee80211_supported_band *band; - err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_PHYLIST, - &phy_list, sizeof(phy_list)); - if (err) { - brcmf_err("BRCMF_C_GET_PHYLIST error (%d)\n", err); - return err; - } - - phy = ((char *)&phy_list)[0]; - brcmf_dbg(INFO, "BRCMF_C_GET_PHYLIST reported: %c phy\n", phy); - - - err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, - &band_list, sizeof(band_list)); - if (err) { - brcmf_err("BRCMF_C_GET_BANDLIST error (%d)\n", err); - return err; - } - brcmf_dbg(INFO, "BRCMF_C_GET_BANDLIST reported: 0x%08x 0x%08x 0x%08x phy\n", - band_list[0], band_list[1], band_list[2]); - (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode); err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode); if (err) { @@ -5223,38 +5252,25 @@ static s32 brcmf_update_wiphybands(struct brcmf_cfg80211_info *cfg) } brcmf_dbg(INFO, "nchain=%d\n", nchain); - err = brcmf_construct_reginfo(cfg, bw_cap); + err = brcmf_construct_chaninfo(cfg, bw_cap); if (err) { - brcmf_err("brcmf_construct_reginfo failed (%d)\n", err); + brcmf_err("brcmf_construct_chaninfo failed (%d)\n", err); return err; } - nband = band_list[0]; - - for (i = 1; i <= nband && i < ARRAY_SIZE(band_list); i++) { - band = NULL; - if ((band_list[i] == WLC_BAND_5G) && - (__wl_band_5ghz_a.n_channels > 0)) - band = &__wl_band_5ghz_a; - else if ((band_list[i] == WLC_BAND_2G) && - (__wl_band_2ghz.n_channels > 0)) - band = &__wl_band_2ghz; - else + wiphy = cfg_to_wiphy(cfg); + for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) { + band = wiphy->bands[i]; + if (band == NULL) continue; if (nmode) brcmf_update_ht_cap(band, bw_cap, nchain); if (vhtmode) brcmf_update_vht_cap(band, bw_cap, nchain); - bands[band->band] = band; } - wiphy = cfg_to_wiphy(cfg); - wiphy->bands[IEEE80211_BAND_2GHZ] = bands[IEEE80211_BAND_2GHZ]; - wiphy->bands[IEEE80211_BAND_5GHZ] = bands[IEEE80211_BAND_5GHZ]; - wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); - - return err; + return 0; } static const struct ieee80211_iface_limit brcmf_iface_limits[] = { @@ -5321,18 +5337,9 @@ static void brcmf_wiphy_pno_params(struct wiphy *wiphy) wiphy->flags |= WIPHY_FLAG_SUPPORTS_SCHED_SCAN; } -static struct wiphy *brcmf_setup_wiphy(struct brcmf_if *ifp, - struct device *phydev) +static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) { - struct wiphy *wiphy; - s32 err = 0; - - wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info)); - if (!wiphy) { - brcmf_err("Could not allocate wiphy device\n"); - return ERR_PTR(-ENOMEM); - } - set_wiphy_dev(wiphy, phydev); + struct ieee80211_iface_combination ifc_combo; wiphy->max_scan_ssids = WL_NUM_SCAN_MAX; wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX; @@ -5343,11 +5350,13 @@ static struct wiphy *brcmf_setup_wiphy(struct brcmf_if *ifp, BIT(NL80211_IFTYPE_P2P_GO) | BIT(NL80211_IFTYPE_P2P_DEVICE); /* need VSDB firmware feature for concurrent channels */ + ifc_combo = brcmf_iface_combos[0]; if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN)) - brcmf_iface_combos[0].num_different_channels = 2; - wiphy->iface_combinations = brcmf_iface_combos; + ifc_combo.num_different_channels = 2; + wiphy->iface_combinations = kmemdup(&ifc_combo, + sizeof(ifc_combo), + GFP_KERNEL); wiphy->n_iface_combinations = ARRAY_SIZE(brcmf_iface_combos); - wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz; wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; wiphy->cipher_suites = __wl_cipher_suites; wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites); @@ -5360,27 +5369,12 @@ static struct wiphy *brcmf_setup_wiphy(struct brcmf_if *ifp, wiphy->mgmt_stypes = brcmf_txrx_stypes; wiphy->max_remain_on_channel_duration = 5000; brcmf_wiphy_pno_params(wiphy); - brcmf_dbg(INFO, "Registering custom regulatory\n"); - wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; - wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); /* vendor commands/events support */ wiphy->vendor_commands = brcmf_vendor_cmds; wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1; - err = wiphy_register(wiphy); - if (err < 0) { - brcmf_err("Could not register wiphy device (%d)\n", err); - wiphy_free(wiphy); - return ERR_PTR(err); - } - return wiphy; -} - - -static s32 brcmf_dongle_probecap(struct brcmf_cfg80211_info *cfg) -{ - return brcmf_update_wiphybands(cfg); + return brcmf_setup_wiphybands(wiphy); } static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) @@ -5418,9 +5412,6 @@ static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) NULL, NULL); if (err) goto default_conf_out; - err = brcmf_dongle_probecap(cfg); - if (err) - goto default_conf_out; brcmf_configure_arp_offload(ifp, true); @@ -5548,6 +5539,20 @@ int brcmf_cfg80211_wait_vif_event_timeout(struct brcmf_cfg80211_info *cfg, vif_event_equals(event, action), timeout); } +static void brcmf_free_wiphy(struct wiphy *wiphy) +{ + kfree(wiphy->iface_combinations); + if (wiphy->bands[IEEE80211_BAND_2GHZ]) { + kfree(wiphy->bands[IEEE80211_BAND_2GHZ]->channels); + kfree(wiphy->bands[IEEE80211_BAND_2GHZ]); + } + if (wiphy->bands[IEEE80211_BAND_5GHZ]) { + kfree(wiphy->bands[IEEE80211_BAND_5GHZ]->channels); + kfree(wiphy->bands[IEEE80211_BAND_5GHZ]); + } + wiphy_free(wiphy); +} + struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, struct device *busdev) { @@ -5558,6 +5563,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, struct brcmf_if *ifp; s32 err = 0; s32 io_type; + u16 *cap = NULL; if (!ndev) { brcmf_err("ndev is invalid\n"); @@ -5565,9 +5571,12 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, } ifp = netdev_priv(ndev); - wiphy = brcmf_setup_wiphy(ifp, busdev); - if (IS_ERR(wiphy)) + wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info)); + if (!wiphy) { + brcmf_err("Could not allocate wiphy device\n"); return NULL; + } + set_wiphy_dev(wiphy, busdev); cfg = wiphy_priv(wiphy); cfg->wiphy = wiphy; @@ -5576,10 +5585,8 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, INIT_LIST_HEAD(&cfg->vif_list); vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION, false); - if (IS_ERR(vif)) { - wiphy_free(wiphy); - return NULL; - } + if (IS_ERR(vif)) + goto wiphy_out; vif->ifp = ifp; vif->wdev.netdev = ndev; @@ -5589,35 +5596,65 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, err = wl_init_priv(cfg); if (err) { brcmf_err("Failed to init iwm_priv (%d)\n", err); - goto cfg80211_attach_out; + brcmf_free_vif(vif); + goto wiphy_out; } ifp->vif = vif; - err = brcmf_p2p_attach(cfg); + /* determine d11 io type before wiphy setup */ + err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type); if (err) { - brcmf_err("P2P initilisation failed (%d)\n", err); - goto cfg80211_p2p_attach_out; + brcmf_err("Failed to get D11 version (%d)\n", err); + goto priv_out; } - err = brcmf_btcoex_attach(cfg); - if (err) { - brcmf_err("BT-coex initialisation failed (%d)\n", err); - brcmf_p2p_detach(&cfg->p2p); - goto cfg80211_p2p_attach_out; + cfg->d11inf.io_type = (u8)io_type; + brcmu_d11_attach(&cfg->d11inf); + + err = brcmf_setup_wiphy(wiphy, ifp); + if (err < 0) + goto priv_out; + + brcmf_dbg(INFO, "Registering custom regulatory\n"); + wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; + wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); + + /* firmware defaults to 40MHz disabled in 2G band. We signal + * cfg80211 here that we do and have it decide we can enable + * it. But first check if device does support 2G operation. + */ + if (wiphy->bands[IEEE80211_BAND_2GHZ]) { + cap = &wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap; + *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + } + err = wiphy_register(wiphy); + if (err < 0) { + brcmf_err("Could not register wiphy device (%d)\n", err); + goto priv_out; } /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(), * setup 40MHz in 2GHz band and enable OBSS scanning. */ - if (wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap & - IEEE80211_HT_CAP_SUP_WIDTH_20_40) { - err = brcmf_enable_bw40_2g(ifp); + if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) { + err = brcmf_enable_bw40_2g(cfg); if (!err) err = brcmf_fil_iovar_int_set(ifp, "obss_coex", BRCMF_OBSS_COEX_AUTO); + else + *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + } + + err = brcmf_p2p_attach(cfg); + if (err) { + brcmf_err("P2P initilisation failed (%d)\n", err); + goto wiphy_unreg_out; + } + err = brcmf_btcoex_attach(cfg); + if (err) { + brcmf_err("BT-coex initialisation failed (%d)\n", err); + brcmf_p2p_detach(&cfg->p2p); + goto wiphy_unreg_out; } - /* clear for now and rely on update later */ - wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.ht_supported = false; - wiphy->bands[IEEE80211_BAND_2GHZ]->ht_cap.cap = 0; err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1); if (err) { @@ -5625,22 +5662,15 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS; } - err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, - &io_type); - if (err) { - brcmf_err("Failed to get D11 version (%d)\n", err); - goto cfg80211_p2p_attach_out; - } - cfg->d11inf.io_type = (u8)io_type; - brcmu_d11_attach(&cfg->d11inf); - return cfg; -cfg80211_p2p_attach_out: +wiphy_unreg_out: + wiphy_unregister(cfg->wiphy); +priv_out: wl_deinit_priv(cfg); - -cfg80211_attach_out: brcmf_free_vif(vif); +wiphy_out: + brcmf_free_wiphy(wiphy); return NULL; } @@ -5653,5 +5683,5 @@ void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg) wiphy_unregister(cfg->wiphy); brcmf_btcoex_detach(cfg); wl_deinit_priv(cfg); - wiphy_free(cfg->wiphy); + brcmf_free_wiphy(cfg->wiphy); } From c3da74bbbb8b230ad8bb84ca324c4e07607c9a7f Mon Sep 17 00:00:00 2001 From: Arend van Spriel Date: Sat, 12 Jul 2014 08:49:42 +0200 Subject: [PATCH 329/378] brcmfmac: add brcmf_p2p_detach() call in brcmf_cfg80211_detach() The function brcmf_p2p_detach() was only called in error flow of the brcmf_cfg80211_attach() routine, but it also needs to be called upon brcmf_cfg80211_detach(). Reviewed-by: Hante Meuleman Reviewed-by: Pieter-Paul Giesberts Signed-off-by: Arend van Spriel Signed-off-by: John W. Linville --- drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c index d3bb4e0f4480..48078a321716 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c @@ -5682,6 +5682,7 @@ void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg) WARN_ON(!list_empty(&cfg->vif_list)); wiphy_unregister(cfg->wiphy); brcmf_btcoex_detach(cfg); + brcmf_p2p_detach(&cfg->p2p); wl_deinit_priv(cfg); brcmf_free_wiphy(cfg->wiphy); } From c9ff78b4ff6aee37155bf8bc1df3d6156a46656d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sat, 12 Jul 2014 12:34:46 +0200 Subject: [PATCH 330/378] b43: N-PHY: add missing TX gain table for radio 0x2057 rev 5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/tables_nphy.c | 58 ++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/b43/tables_nphy.c b/drivers/net/wireless/b43/tables_nphy.c index 3a5cd902ff1f..b39a8dd375e9 100644 --- a/drivers/net/wireless/b43/tables_nphy.c +++ b/drivers/net/wireless/b43/tables_nphy.c @@ -2692,6 +2692,42 @@ static const u32 b43_ntab_tx_gain_ipa_rev6_2g[] = { 0x00f70028, 0x00f70027, 0x00f70026, 0x00f70025, }; +/* Copied from brcmsmac (5.75.11): nphy_tpc_txgain_ipa_2g_2057rev5 */ +static const u32 b43_ntab_tx_gain_ipa_2057_rev5_2g[] = { + 0x30ff0031, 0x30e70031, 0x30e7002e, 0x30cf002e, + 0x30bf002e, 0x30af002e, 0x309f002f, 0x307f0033, + 0x307f0031, 0x307f002e, 0x3077002e, 0x306f002e, + 0x3067002e, 0x305f002f, 0x30570030, 0x3057002d, + 0x304f002e, 0x30470031, 0x3047002e, 0x3047002c, + 0x30470029, 0x303f002c, 0x303f0029, 0x3037002d, + 0x3037002a, 0x30370028, 0x302f002c, 0x302f002a, + 0x302f0028, 0x302f0026, 0x3027002c, 0x30270029, + 0x30270027, 0x30270025, 0x30270023, 0x301f002c, + 0x301f002a, 0x301f0028, 0x301f0025, 0x301f0024, + 0x301f0022, 0x301f001f, 0x3017002d, 0x3017002b, + 0x30170028, 0x30170026, 0x30170024, 0x30170022, + 0x30170020, 0x3017001e, 0x3017001d, 0x3017001b, + 0x3017001a, 0x30170018, 0x30170017, 0x30170015, + 0x300f002c, 0x300f0029, 0x300f0027, 0x300f0024, + 0x300f0022, 0x300f0021, 0x300f001f, 0x300f001d, + 0x300f001b, 0x300f001a, 0x300f0018, 0x300f0017, + 0x300f0016, 0x300f0015, 0x300f0115, 0x300f0215, + 0x300f0315, 0x300f0415, 0x300f0515, 0x300f0615, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, + 0x300f0715, 0x300f0715, 0x300f0715, 0x300f0715, +}; + /* Extracted from MMIO dump of 6.30.223.141 */ static const u32 b43_ntab_tx_gain_ipa_2057_rev9_2g[] = { 0x60ff0031, 0x60e7002c, 0x60cf002a, 0x60c70029, @@ -3550,6 +3586,11 @@ static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev) case 16: if (phy->radio_rev == 9) return b43_ntab_tx_gain_ipa_2057_rev9_2g; + break; + case 8: + if (phy->radio_rev == 5) + return b43_ntab_tx_gain_ipa_2057_rev5_2g; + break; case 6: if (dev->dev->chip_id == BCMA_CHIP_ID_BCM47162) return b43_ntab_tx_gain_ipa_rev5_2g; @@ -3559,23 +3600,24 @@ static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev) case 4: case 3: return b43_ntab_tx_gain_ipa_rev3_2g; - default: - b43err(dev->wl, - "No 2GHz IPA gain table available for this device\n"); - return NULL; } + + b43err(dev->wl, + "No 2GHz IPA gain table available for this device\n"); + return NULL; } else { switch (phy->rev) { case 16: if (phy->radio_rev == 9) return b43_ntab_tx_gain_ipa_2057_rev9_5g; + break; case 3 ... 6: return b43_ntab_tx_gain_ipa_rev3_5g; - default: - b43err(dev->wl, - "No 5GHz IPA gain table available for this device\n"); - return NULL; } + + b43err(dev->wl, + "No 5GHz IPA gain table available for this device\n"); + return NULL; } } From b3bbf842670def767ec8ebdd424f09358afeb5c6 Mon Sep 17 00:00:00 2001 From: Xose Vazquez Perez Date: Sat, 12 Jul 2014 14:57:38 +0200 Subject: [PATCH 331/378] wireless: zd1211rw: new url for fw, remove experimental Cc: Daniel Drake Cc: Ulrich Kunitz Cc: John W. Linville Cc: linux-wireless@vger.kernel.org Signed-off-by: Xose Vazquez Perez Signed-off-by: John W. Linville --- drivers/net/wireless/zd1211rw/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/zd1211rw/Kconfig b/drivers/net/wireless/zd1211rw/Kconfig index 96c8e1de0879..95920581860a 100644 --- a/drivers/net/wireless/zd1211rw/Kconfig +++ b/drivers/net/wireless/zd1211rw/Kconfig @@ -3,11 +3,11 @@ config ZD1211RW depends on USB && MAC80211 select FW_LOADER ---help--- - This is an experimental driver for the ZyDAS ZD1211/ZD1211B wireless + This is a driver for the ZyDAS ZD1211/ZD1211B wireless chip, present in many USB-wireless adapters. Device firmware is required alongside this driver. You can download - the firmware distribution from http://zd1211.ath.cx/get-firmware + the firmware distribution from http://sf.net/projects/zd1211/files/ config ZD1211RW_DEBUG bool "ZyDAS ZD1211 debugging" From 67d392c0e900bbf885ee08b8942c3204a2cab697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sat, 12 Jul 2014 16:42:09 +0200 Subject: [PATCH 332/378] ssb: make code for antenna gain extraction more generic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/ssb/pci.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c index a8dc95ebf2d6..fc0bb4923ac3 100644 --- a/drivers/ssb/pci.c +++ b/drivers/ssb/pci.c @@ -326,13 +326,13 @@ err_ctlreg: return err; } -static s8 r123_extract_antgain(u8 sprom_revision, const u16 *in, - u16 mask, u16 shift) +static s8 sprom_extract_antgain(u8 sprom_revision, const u16 *in, u16 offset, + u16 mask, u16 shift) { u16 v; u8 gain; - v = in[SPOFF(SSB_SPROM1_AGAIN)]; + v = in[SPOFF(offset)]; gain = (v & mask) >> shift; if (gain == 0xFF) gain = 2; /* If unset use 2dBm */ @@ -416,12 +416,14 @@ static void sprom_extract_r123(struct ssb_sprom *out, const u16 *in) SPEX(alpha2[1], SSB_SPROM1_CCODE, 0x00ff, 0); /* Extract the antenna gain values. */ - out->antenna_gain.a0 = r123_extract_antgain(out->revision, in, - SSB_SPROM1_AGAIN_BG, - SSB_SPROM1_AGAIN_BG_SHIFT); - out->antenna_gain.a1 = r123_extract_antgain(out->revision, in, - SSB_SPROM1_AGAIN_A, - SSB_SPROM1_AGAIN_A_SHIFT); + out->antenna_gain.a0 = sprom_extract_antgain(out->revision, in, + SSB_SPROM1_AGAIN, + SSB_SPROM1_AGAIN_BG, + SSB_SPROM1_AGAIN_BG_SHIFT); + out->antenna_gain.a1 = sprom_extract_antgain(out->revision, in, + SSB_SPROM1_AGAIN, + SSB_SPROM1_AGAIN_A, + SSB_SPROM1_AGAIN_A_SHIFT); if (out->revision >= 2) sprom_extract_r23(out, in); } From 6daf43216fd5171f0f44504226efeb5563538f66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Sat, 12 Jul 2014 16:42:10 +0200 Subject: [PATCH 333/378] ssb: extract antenna gains from SPROMs revs 4+ properly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They are encoded the same way as in older SPROMs. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/ssb/pci.c | 48 +++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c index fc0bb4923ac3..6318364be590 100644 --- a/drivers/ssb/pci.c +++ b/drivers/ssb/pci.c @@ -526,14 +526,22 @@ static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in) } /* Extract the antenna gain values. */ - SPEX(antenna_gain.a0, SSB_SPROM4_AGAIN01, - SSB_SPROM4_AGAIN0, SSB_SPROM4_AGAIN0_SHIFT); - SPEX(antenna_gain.a1, SSB_SPROM4_AGAIN01, - SSB_SPROM4_AGAIN1, SSB_SPROM4_AGAIN1_SHIFT); - SPEX(antenna_gain.a2, SSB_SPROM4_AGAIN23, - SSB_SPROM4_AGAIN2, SSB_SPROM4_AGAIN2_SHIFT); - SPEX(antenna_gain.a3, SSB_SPROM4_AGAIN23, - SSB_SPROM4_AGAIN3, SSB_SPROM4_AGAIN3_SHIFT); + out->antenna_gain.a0 = sprom_extract_antgain(out->revision, in, + SSB_SPROM4_AGAIN01, + SSB_SPROM4_AGAIN0, + SSB_SPROM4_AGAIN0_SHIFT); + out->antenna_gain.a1 = sprom_extract_antgain(out->revision, in, + SSB_SPROM4_AGAIN01, + SSB_SPROM4_AGAIN1, + SSB_SPROM4_AGAIN1_SHIFT); + out->antenna_gain.a2 = sprom_extract_antgain(out->revision, in, + SSB_SPROM4_AGAIN23, + SSB_SPROM4_AGAIN2, + SSB_SPROM4_AGAIN2_SHIFT); + out->antenna_gain.a3 = sprom_extract_antgain(out->revision, in, + SSB_SPROM4_AGAIN23, + SSB_SPROM4_AGAIN3, + SSB_SPROM4_AGAIN3_SHIFT); sprom_extract_r458(out, in); @@ -623,14 +631,22 @@ static void sprom_extract_r8(struct ssb_sprom *out, const u16 *in) SPEX32(ofdm5ghpo, SSB_SPROM8_OFDM5GHPO, 0xFFFFFFFF, 0); /* Extract the antenna gain values. */ - SPEX(antenna_gain.a0, SSB_SPROM8_AGAIN01, - SSB_SPROM8_AGAIN0, SSB_SPROM8_AGAIN0_SHIFT); - SPEX(antenna_gain.a1, SSB_SPROM8_AGAIN01, - SSB_SPROM8_AGAIN1, SSB_SPROM8_AGAIN1_SHIFT); - SPEX(antenna_gain.a2, SSB_SPROM8_AGAIN23, - SSB_SPROM8_AGAIN2, SSB_SPROM8_AGAIN2_SHIFT); - SPEX(antenna_gain.a3, SSB_SPROM8_AGAIN23, - SSB_SPROM8_AGAIN3, SSB_SPROM8_AGAIN3_SHIFT); + out->antenna_gain.a0 = sprom_extract_antgain(out->revision, in, + SSB_SPROM8_AGAIN01, + SSB_SPROM8_AGAIN0, + SSB_SPROM8_AGAIN0_SHIFT); + out->antenna_gain.a1 = sprom_extract_antgain(out->revision, in, + SSB_SPROM8_AGAIN01, + SSB_SPROM8_AGAIN1, + SSB_SPROM8_AGAIN1_SHIFT); + out->antenna_gain.a2 = sprom_extract_antgain(out->revision, in, + SSB_SPROM8_AGAIN23, + SSB_SPROM8_AGAIN2, + SSB_SPROM8_AGAIN2_SHIFT); + out->antenna_gain.a3 = sprom_extract_antgain(out->revision, in, + SSB_SPROM8_AGAIN23, + SSB_SPROM8_AGAIN3, + SSB_SPROM8_AGAIN3_SHIFT); /* Extract cores power info info */ for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) { From f95f59fe5d2b05a0333214eeba78690081ee5f70 Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Mon, 14 Jul 2014 20:53:34 -0700 Subject: [PATCH 334/378] mwifiex: remove redundant TDLS setup action frame check and avoid leaks The unwanted frame types are already handled in 'default' case of the switch/case below. The str_ptr is allocated but it can be leaked if the length check fails in the REQUEST/RESP cases. Fix it by allocating sta_ptr after the length checks. Reported-by: Paul Stewart Signed-off-by: Bing Zhao Signed-off-by: Avinash Patil Signed-off-by: Paul Stewart Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/tdls.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/mwifiex/tdls.c b/drivers/net/wireless/mwifiex/tdls.c index a414161c6064..4c5fd953893d 100644 --- a/drivers/net/wireless/mwifiex/tdls.c +++ b/drivers/net/wireless/mwifiex/tdls.c @@ -781,6 +781,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, struct mwifiex_sta_node *sta_ptr; u8 *peer, *pos, *end; u8 i, action, basic; + __le16 cap = 0; int ie_len = 0; if (len < (sizeof(struct ethhdr) + 3)) @@ -792,18 +793,9 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, peer = buf + ETH_ALEN; action = *(buf + sizeof(struct ethhdr) + 2); - - /* just handle TDLS setup request/response/confirm */ - if (action > WLAN_TDLS_SETUP_CONFIRM) - return; - dev_dbg(priv->adapter->dev, "rx:tdls action: peer=%pM, action=%d\n", peer, action); - sta_ptr = mwifiex_add_sta_entry(priv, peer); - if (!sta_ptr) - return; - switch (action) { case WLAN_TDLS_SETUP_REQUEST: if (len < (sizeof(struct ethhdr) + TDLS_REQ_FIX_LEN)) @@ -811,7 +803,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, pos = buf + sizeof(struct ethhdr) + 4; /* payload 1+ category 1 + action 1 + dialog 1 */ - sta_ptr->tdls_cap.capab = cpu_to_le16(*(u16 *)pos); + cap = cpu_to_le16(*(u16 *)pos); ie_len = len - sizeof(struct ethhdr) - TDLS_REQ_FIX_LEN; pos += 2; break; @@ -821,7 +813,7 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, return; /* payload 1+ category 1 + action 1 + dialog 1 + status code 2*/ pos = buf + sizeof(struct ethhdr) + 6; - sta_ptr->tdls_cap.capab = cpu_to_le16(*(u16 *)pos); + cap = cpu_to_le16(*(u16 *)pos); ie_len = len - sizeof(struct ethhdr) - TDLS_RESP_FIX_LEN; pos += 2; break; @@ -833,10 +825,16 @@ void mwifiex_process_tdls_action_frame(struct mwifiex_private *priv, ie_len = len - sizeof(struct ethhdr) - TDLS_CONFIRM_FIX_LEN; break; default: - dev_warn(priv->adapter->dev, "Unknown TDLS frame type.\n"); + dev_dbg(priv->adapter->dev, "Unknown TDLS frame type.\n"); return; } + sta_ptr = mwifiex_add_sta_entry(priv, peer); + if (!sta_ptr) + return; + + sta_ptr->tdls_cap.capab = cap; + for (end = pos + ie_len; pos + 1 < end; pos += 2 + pos[1]) { if (pos + 2 + pos[1] > end) break; From 1d4cc30c86301543a09ff4118a36044546c7cfa1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 18 Jul 2014 09:47:26 +0200 Subject: [PATCH 335/378] mac80211: suppress unused variable warning without lockdep When lockdep isn't compiled, a local variable isn't used (it's only in a macro argument), annotate it to suppress the compiler warning. Signed-off-by: Johannes Berg --- net/mac80211/chan.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/chan.c b/net/mac80211/chan.c index c3fd4d275bf4..6d537f03c0ba 100644 --- a/net/mac80211/chan.c +++ b/net/mac80211/chan.c @@ -66,7 +66,7 @@ static bool ieee80211_can_create_new_chanctx(struct ieee80211_local *local) static struct ieee80211_chanctx * ieee80211_vif_get_chanctx(struct ieee80211_sub_if_data *sdata) { - struct ieee80211_local *local = sdata->local; + struct ieee80211_local *local __maybe_unused = sdata->local; struct ieee80211_chanctx_conf *conf; conf = rcu_dereference_protected(sdata->vif.chanctx_conf, From 6508281b0b15c469a940ffa46bb9215c9e18eaf3 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 14 Jul 2014 09:49:37 +0300 Subject: [PATCH 336/378] wil6210: support for "sparrow" hardware New hardware release appears; it require some changes to properly support it. Introduce struct wil_board and "board" attribute in wil6210_priv; keep hardware variant information in this structure. fill it on probe(). Used in the reset flow. Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/main.c | 42 +++++++++++++++++---- drivers/net/wireless/ath/wil6210/pcie_bus.c | 22 +++++++++-- drivers/net/wireless/ath/wil6210/wil6210.h | 10 +++++ 3 files changed, 63 insertions(+), 11 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 53a689ed7c7d..3704d2a434f3 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -314,8 +314,9 @@ static void wil_target_reset(struct wil6210_priv *wil) int delay = 0; u32 hw_state; u32 rev_id; + bool is_sparrow = (wil->board->board == WIL_BOARD_SPARROW); - wil_dbg_misc(wil, "Resetting...\n"); + wil_dbg_misc(wil, "Resetting \"%s\"...\n", wil->board->name); /* register read */ #define R(a) ioread32(wil->csr + HOSTADDR(a)) @@ -328,35 +329,59 @@ static void wil_target_reset(struct wil6210_priv *wil) wil->hw_version = R(RGF_USER_FW_REV_ID); rev_id = wil->hw_version & 0xff; + + /* Clear MAC link up */ + S(RGF_HP_CTRL, BIT(15)); /* hpal_perst_from_pad_src_n_mask */ S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(6)); /* car_perst_rst_src_n_mask */ S(RGF_USER_CLKS_CTL_SW_RST_MASK_0, BIT(7)); wmb(); /* order is important here */ + if (is_sparrow) { + W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x3ff81f); + wmb(); /* order is important here */ + } + W(RGF_USER_MAC_CPU_0, BIT(1)); /* mac_cpu_man_rst */ W(RGF_USER_USER_CPU_0, BIT(1)); /* user_cpu_man_rst */ wmb(); /* order is important here */ W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0xFE000000); W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0x0000003F); - W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000170); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, is_sparrow ? 0x000000B0 : 0x00000170); W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0xFFE7FC00); wmb(); /* order is important here */ + if (is_sparrow) { + W(RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0, 0x0); + wmb(); /* order is important here */ + } + W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0); W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0); W(RGF_USER_CLKS_CTL_SW_RST_VEC_1, 0); W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); wmb(); /* order is important here */ - W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001); - if (rev_id == 1) { - W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080); - } else { - W(RGF_PCIE_LOS_COUNTER_CTL, BIT(6) | BIT(8)); + if (is_sparrow) { + W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000003); + /* reset A2 PCIE AHB */ W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000); + + } else { + W(RGF_USER_CLKS_CTL_SW_RST_VEC_3, 0x00000001); + if (rev_id == 1) { + /* reset A1 BOTH PCIE AHB & PCIE RGF */ + W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00000080); + } else { + W(RGF_PCIE_LOS_COUNTER_CTL, BIT(6) | BIT(8)); + W(RGF_USER_CLKS_CTL_SW_RST_VEC_2, 0x00008000); + } + } + + /* TODO: check order here!!! Erez code is different */ W(RGF_USER_CLKS_CTL_SW_RST_VEC_0, 0); wmb(); /* order is important here */ @@ -371,7 +396,8 @@ static void wil_target_reset(struct wil6210_priv *wil) } } while (hw_state != HW_MACHINE_BOOT_DONE); - if (rev_id == 2) + /* TODO: Erez check rev_id != 1 */ + if (!is_sparrow && (rev_id != 1)) W(RGF_PCIE_LOS_COUNTER_CTL, BIT(8)); C(RGF_USER_CLKS_CTL_0, BIT_USER_CLKS_RST_PWGD); diff --git a/drivers/net/wireless/ath/wil6210/pcie_bus.c b/drivers/net/wireless/ath/wil6210/pcie_bus.c index 77b6272d93fb..d3fbfa28db62 100644 --- a/drivers/net/wireless/ath/wil6210/pcie_bus.c +++ b/drivers/net/wireless/ath/wil6210/pcie_bus.c @@ -122,10 +122,12 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) struct wil6210_priv *wil; struct device *dev = &pdev->dev; void __iomem *csr; + struct wil_board *board = (struct wil_board *)id->driver_data; int rc; /* check HW */ - dev_info(&pdev->dev, WIL_NAME " device found [%04x:%04x] (rev %x)\n", + dev_info(&pdev->dev, WIL_NAME + " \"%s\" device found [%04x:%04x] (rev %x)\n", board->name, (int)pdev->vendor, (int)pdev->device, (int)pdev->revision); if (pci_resource_len(pdev, 0) != WIL6210_MEM_SIZE) { @@ -175,6 +177,7 @@ static int wil_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) pci_set_drvdata(pdev, wil); wil->pdev = pdev; + wil->board = board; wil6210_clear_irq(wil); /* FW should raise IRQ when ready */ @@ -225,8 +228,21 @@ static void wil_pcie_remove(struct pci_dev *pdev) pci_disable_device(pdev); } -static DEFINE_PCI_DEVICE_TABLE(wil6210_pcie_ids) = { - { PCI_DEVICE(0x1ae9, 0x0301) }, +static const struct wil_board wil_board_marlon = { + .board = WIL_BOARD_MARLON, + .name = "marlon", +}; + +static const struct wil_board wil_board_sparrow = { + .board = WIL_BOARD_SPARROW, + .name = "sparrow", +}; + +static const struct pci_device_id wil6210_pcie_ids[] = { + { PCI_DEVICE(0x1ae9, 0x0301), + .driver_data = (kernel_ulong_t)&wil_board_marlon }, + { PCI_DEVICE(0x1ae9, 0x0310), + .driver_data = (kernel_ulong_t)&wil_board_sparrow }, { /* end: all zeroes */ }, }; MODULE_DEVICE_TABLE(pci, wil6210_pcie_ids); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 424906635f05..09c36a7a32e0 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -24,6 +24,13 @@ #define WIL_NAME "wil6210" +struct wil_board { + int board; +#define WIL_BOARD_MARLON (1) +#define WIL_BOARD_SPARROW (2) + const char * const name; +}; + /** * extract bits [@b0:@b1] (inclusive) from the value @x * it should be @b0 <= @b1, or result is incorrect @@ -93,6 +100,7 @@ struct RGF_ICR { #define RGF_USER_CLKS_CTL_SW_RST_MASK_0 (0x880b14) #define RGF_USER_USER_ICR (0x880b4c) /* struct RGF_ICR */ #define BIT_USER_USER_ICR_SW_INT_2 BIT(18) +#define RGF_USER_CLKS_CTL_EXT_SW_RST_VEC_0 (0x880c18) #define RGF_DMA_EP_TX_ICR (0x881bb4) /* struct RGF_ICR */ #define BIT_DMA_EP_TX_ICR_TX_DONE BIT(0) @@ -121,6 +129,7 @@ struct RGF_ICR { #define BIT_DMA_PSEUDO_CAUSE_TX BIT(1) #define BIT_DMA_PSEUDO_CAUSE_MISC BIT(2) +#define RGF_HP_CTRL (0x88265c) #define RGF_PCIE_LOS_COUNTER_CTL (0x882dc4) /* popular locations */ @@ -365,6 +374,7 @@ struct wil6210_priv { ulong status; u32 fw_version; u32 hw_version; + struct wil_board *board; u8 n_mids; /* number of additional MIDs as reported by FW */ int recovery_count; /* num of FW recovery attempts in a short time */ unsigned long last_fw_recovery; /* jiffies of last fw recovery */ From 6f55007d0e9d179d5893a518a9466c0abbcfa1ca Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 14 Jul 2014 09:49:38 +0300 Subject: [PATCH 337/378] wil6210: export FW/HW versions through debugfs Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/debugfs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index a868c5eebe37..7435b5a256ab 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -986,6 +986,8 @@ int wil6210_debugfs_init(struct wil6210_priv *wil) &wil->secure_pcp); wil_debugfs_create_ulong("status", S_IRUGO | S_IWUSR, dbg, &wil->status); + debugfs_create_u32("fw_version", S_IRUGO, dbg, &wil->fw_version); + debugfs_create_x32("hw_version", S_IRUGO, dbg, &wil->hw_version); wil6210_debugfs_create_ISR(wil, "USER_ICR", dbg, HOSTADDR(RGF_USER_USER_ICR)); From 76dfa4b7715679b8f90499745c071d44472c200c Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 14 Jul 2014 09:49:39 +0300 Subject: [PATCH 338/378] wil6210: fix double definition of 'ctx' Variable 'ctx' declarad again in the inner loop. Should use one from outer loop instead. Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/txrx.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/txrx.c b/drivers/net/wireless/ath/wil6210/txrx.c index af4b93e4beb5..d3467943d39d 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.c +++ b/drivers/net/wireless/ath/wil6210/txrx.c @@ -1108,8 +1108,10 @@ int wil_tx_complete(struct wil6210_priv *wil, int ringid) while (vring->swtail != new_swtail) { struct vring_tx_desc dd, *d = ⅆ u16 dmalen; - struct wil_ctx *ctx = &vring->ctx[vring->swtail]; - struct sk_buff *skb = ctx->skb; + struct sk_buff *skb; + + ctx = &vring->ctx[vring->swtail]; + skb = ctx->skb; _d = &vring->va[vring->swtail].tx; *d = *_d; From 359ee6275368c6fc8c6143f706e1b0075a244070 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 14 Jul 2014 09:49:40 +0300 Subject: [PATCH 339/378] wil6210: fix memory leak on error path in wil_write_file_rxon() If copy_from_user() fails, buffer allocated for parameters would leak Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/debugfs.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index 7435b5a256ab..b6400680850a 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -448,8 +448,10 @@ static ssize_t wil_write_file_rxon(struct file *file, const char __user *buf, char *kbuf = kmalloc(len + 1, GFP_KERNEL); if (!kbuf) return -ENOMEM; - if (copy_from_user(kbuf, buf, len)) + if (copy_from_user(kbuf, buf, len)) { + kfree(kbuf); return -EIO; + } kbuf[len] = '\0'; rc = kstrtol(kbuf, 0, &channel); From b541d0a0266ddcb6560cf4192ce26f05ec716386 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 14 Jul 2014 09:49:41 +0300 Subject: [PATCH 340/378] wil6210: use same mapping table for FW addr translation and debugfs Use single data source for all information regarding the firmware memory map. With this change "ucode_xxx" regions disappears since they are in fact part of larger "upper area" region Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/debugfs.c | 49 ++++++++++------------ drivers/net/wireless/ath/wil6210/wil6210.h | 15 ++++--- drivers/net/wireless/ath/wil6210/wmi.c | 20 ++++----- 3 files changed, 39 insertions(+), 45 deletions(-) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index b6400680850a..d5b095d404e1 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -965,6 +965,26 @@ static const struct file_operations fops_sta = { }; /*----------------*/ +static void wil6210_debugfs_init_blobs(struct wil6210_priv *wil, + struct dentry *dbg) +{ + int i; + char name[32]; + + for (i = 0; i < ARRAY_SIZE(fw_mapping); i++) { + struct debugfs_blob_wrapper *blob = &wil->blobs[i]; + const struct fw_map *map = &fw_mapping[i]; + + if (!map->name) + continue; + + blob->data = (void * __force)wil->csr + HOSTADDR(map->host); + blob->size = map->to - map->from; + snprintf(name, sizeof(name), "blob_%s", map->name); + wil_debugfs_create_ioblob(name, S_IRUGO, dbg, blob); + } +} + int wil6210_debugfs_init(struct wil6210_priv *wil) { struct dentry *dbg = wil->debug = debugfs_create_dir(WIL_NAME, @@ -1014,34 +1034,7 @@ int wil6210_debugfs_init(struct wil6210_priv *wil) debugfs_create_file("link", S_IRUGO, dbg, wil, &fops_link); debugfs_create_file("info", S_IRUGO, dbg, wil, &fops_info); - wil->rgf_blob.data = (void * __force)wil->csr + 0; - wil->rgf_blob.size = 0xa000; - wil_debugfs_create_ioblob("blob_rgf", S_IRUGO, dbg, &wil->rgf_blob); - - wil->fw_code_blob.data = (void * __force)wil->csr + 0x40000; - wil->fw_code_blob.size = 0x40000; - wil_debugfs_create_ioblob("blob_fw_code", S_IRUGO, dbg, - &wil->fw_code_blob); - - wil->fw_data_blob.data = (void * __force)wil->csr + 0x80000; - wil->fw_data_blob.size = 0x8000; - wil_debugfs_create_ioblob("blob_fw_data", S_IRUGO, dbg, - &wil->fw_data_blob); - - wil->fw_peri_blob.data = (void * __force)wil->csr + 0x88000; - wil->fw_peri_blob.size = 0x18000; - wil_debugfs_create_ioblob("blob_fw_peri", S_IRUGO, dbg, - &wil->fw_peri_blob); - - wil->uc_code_blob.data = (void * __force)wil->csr + 0xa0000; - wil->uc_code_blob.size = 0x10000; - wil_debugfs_create_ioblob("blob_uc_code", S_IRUGO, dbg, - &wil->uc_code_blob); - - wil->uc_data_blob.data = (void * __force)wil->csr + 0xb0000; - wil->uc_data_blob.size = 0x4000; - wil_debugfs_create_ioblob("blob_uc_data", S_IRUGO, dbg, - &wil->uc_data_blob); + wil6210_debugfs_init_blobs(wil, dbg); return 0; } diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 09c36a7a32e0..02205b09fb50 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -144,6 +144,14 @@ struct RGF_ICR { #define ISR_MISC_FW_ERROR BIT_DMA_EP_MISC_ICR_FW_INT(3) /* Hardware definitions end */ +struct fw_map { + u32 from; /* linker address - from, inclusive */ + u32 to; /* linker address - to, exclusive */ + u32 host; /* PCI/Host address - BAR0 + 0x880000 */ + const char *name; /* for debugfs */ +}; +/* array size should be in sync with actual definition in the wmi.c */ +extern const struct fw_map fw_mapping[6]; /** * mk_cidxtid - construct @cidxtid field @@ -425,12 +433,7 @@ struct wil6210_priv { atomic_t isr_count_rx, isr_count_tx; /* debugfs */ struct dentry *debug; - struct debugfs_blob_wrapper fw_code_blob; - struct debugfs_blob_wrapper fw_data_blob; - struct debugfs_blob_wrapper fw_peri_blob; - struct debugfs_blob_wrapper uc_code_blob; - struct debugfs_blob_wrapper uc_data_blob; - struct debugfs_blob_wrapper rgf_blob; + struct debugfs_blob_wrapper blobs[ARRAY_SIZE(fw_mapping)]; }; #define wil_to_wiphy(i) (i->wdev->wiphy) diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index a136dab560e2..084c3de21c56 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -65,18 +65,16 @@ /** * @fw_mapping provides memory remapping table + * + * array size should be in sync with the declaration in the wil6210.h */ -static const struct { - u32 from; /* linker address - from, inclusive */ - u32 to; /* linker address - to, exclusive */ - u32 host; /* PCI/Host address - BAR0 + 0x880000 */ -} fw_mapping[] = { - {0x000000, 0x040000, 0x8c0000}, /* FW code RAM 256k */ - {0x800000, 0x808000, 0x900000}, /* FW data RAM 32k */ - {0x840000, 0x860000, 0x908000}, /* peripheral data RAM 128k/96k used */ - {0x880000, 0x88a000, 0x880000}, /* various RGF */ - {0x88b000, 0x88c000, 0x88b000}, /* Pcie_ext_rgf */ - {0x8c0000, 0x949000, 0x8c0000}, /* trivial mapping for upper area */ +const struct fw_map fw_mapping[] = { + {0x000000, 0x040000, 0x8c0000, "fw_code"}, /* FW code RAM 256k */ + {0x800000, 0x808000, 0x900000, "fw_data"}, /* FW data RAM 32k */ + {0x840000, 0x860000, 0x908000, "fw_peri"}, /* periph. data RAM 128k */ + {0x880000, 0x88a000, 0x880000, "rgf"}, /* various RGF 40k */ + {0x88b000, 0x88c000, 0x88b000, "rgf_ext"}, /* Pcie_ext_rgf 4k */ + {0x8c0000, 0x949000, 0x8c0000, "upper"}, /* upper area 548k */ /* * 920000..930000 ucode code RAM * 930000..932000 ucode data RAM From b373de72c6795e79bd66f046ced9925b08806df9 Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 14 Jul 2014 09:49:42 +0300 Subject: [PATCH 341/378] wil6210: map RGF_USER_USAGE_1 on the debugfs Firmware sets this register with the offset of the firmware trace area within the peripheral memory region. Critical for the firmware trace to work Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/debugfs.c | 3 +++ drivers/net/wireless/ath/wil6210/wil6210.h | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c index d5b095d404e1..8f66186adb8c 100644 --- a/drivers/net/wireless/ath/wil6210/debugfs.c +++ b/drivers/net/wireless/ath/wil6210/debugfs.c @@ -1022,6 +1022,9 @@ int wil6210_debugfs_init(struct wil6210_priv *wil) wil6210_debugfs_create_pseudo_ISR(wil, dbg); wil6210_debugfs_create_ITR_CNT(wil, dbg); + wil_debugfs_create_iomem_x32("RGF_USER_USAGE_1", S_IRUGO, dbg, + wil->csr + + HOSTADDR(RGF_USER_USAGE_1)); debugfs_create_u32("mem_addr", S_IRUGO | S_IWUSR, dbg, &mem_addr); debugfs_create_file("mem_val", S_IRUGO, dbg, wil, &fops_memread); diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index 02205b09fb50..a78aaaba59bd 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -85,6 +85,7 @@ struct RGF_ICR { } __packed; /* registers - FW addresses */ +#define RGF_USER_USAGE_1 (0x880004) #define RGF_USER_HW_MACHINE_STATE (0x8801dc) #define HW_MACHINE_BOOT_DONE (0x3fffffd) #define RGF_USER_USER_CPU_0 (0x8801e0) From 72269146afabf1656f867f715f40a7dd470499fb Mon Sep 17 00:00:00 2001 From: Vladimir Kondratiev Date: Mon, 14 Jul 2014 09:49:43 +0300 Subject: [PATCH 342/378] wil6210: add new register region for AGC table New register area defined in the firmware Signed-off-by: Vladimir Kondratiev Signed-off-by: John W. Linville --- drivers/net/wireless/ath/wil6210/wil6210.h | 2 +- drivers/net/wireless/ath/wil6210/wmi.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h index a78aaaba59bd..67e9624f7111 100644 --- a/drivers/net/wireless/ath/wil6210/wil6210.h +++ b/drivers/net/wireless/ath/wil6210/wil6210.h @@ -152,7 +152,7 @@ struct fw_map { const char *name; /* for debugfs */ }; /* array size should be in sync with actual definition in the wmi.c */ -extern const struct fw_map fw_mapping[6]; +extern const struct fw_map fw_mapping[7]; /** * mk_cidxtid - construct @cidxtid field diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 084c3de21c56..1d1d0afdd2e1 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -73,6 +73,7 @@ const struct fw_map fw_mapping[] = { {0x800000, 0x808000, 0x900000, "fw_data"}, /* FW data RAM 32k */ {0x840000, 0x860000, 0x908000, "fw_peri"}, /* periph. data RAM 128k */ {0x880000, 0x88a000, 0x880000, "rgf"}, /* various RGF 40k */ + {0x88a000, 0x88b000, 0x88a000, "AGC_tbl"}, /* AGC table 4k */ {0x88b000, 0x88c000, 0x88b000, "rgf_ext"}, /* Pcie_ext_rgf 4k */ {0x8c0000, 0x949000, 0x8c0000, "upper"}, /* upper area 548k */ /* From 6ad59343ecd72dd3f83c4db3bcddbb0beabb4c4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 15 Jul 2014 16:18:57 +0200 Subject: [PATCH 343/378] ssb: extract power info from SPROM revs 4 and 5 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is needed to properly handle early 802.11n devices like BCM4321. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/ssb/pci.c | 45 ++++++++++++++++++++++++++++++++++++ include/linux/ssb/ssb_regs.h | 37 +++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c index 6318364be590..0f28c08fcb3c 100644 --- a/drivers/ssb/pci.c +++ b/drivers/ssb/pci.c @@ -470,7 +470,15 @@ static void sprom_extract_r458(struct ssb_sprom *out, const u16 *in) static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in) { + static const u16 pwr_info_offset[] = { + SSB_SPROM4_PWR_INFO_CORE0, SSB_SPROM4_PWR_INFO_CORE1, + SSB_SPROM4_PWR_INFO_CORE2, SSB_SPROM4_PWR_INFO_CORE3 + }; u16 il0mac_offset; + int i; + + BUILD_BUG_ON(ARRAY_SIZE(pwr_info_offset) != + ARRAY_SIZE(out->core_pwr_info)); if (out->revision == 4) il0mac_offset = SSB_SPROM4_IL0MAC; @@ -543,6 +551,43 @@ static void sprom_extract_r45(struct ssb_sprom *out, const u16 *in) SSB_SPROM4_AGAIN3, SSB_SPROM4_AGAIN3_SHIFT); + /* Extract cores power info info */ + for (i = 0; i < ARRAY_SIZE(pwr_info_offset); i++) { + u16 o = pwr_info_offset[i]; + + SPEX(core_pwr_info[i].itssi_2g, o + SSB_SPROM4_2G_MAXP_ITSSI, + SSB_SPROM4_2G_ITSSI, SSB_SPROM4_2G_ITSSI_SHIFT); + SPEX(core_pwr_info[i].maxpwr_2g, o + SSB_SPROM4_2G_MAXP_ITSSI, + SSB_SPROM4_2G_MAXP, 0); + + SPEX(core_pwr_info[i].pa_2g[0], o + SSB_SPROM4_2G_PA_0, ~0, 0); + SPEX(core_pwr_info[i].pa_2g[1], o + SSB_SPROM4_2G_PA_1, ~0, 0); + SPEX(core_pwr_info[i].pa_2g[2], o + SSB_SPROM4_2G_PA_2, ~0, 0); + SPEX(core_pwr_info[i].pa_2g[3], o + SSB_SPROM4_2G_PA_3, ~0, 0); + + SPEX(core_pwr_info[i].itssi_5g, o + SSB_SPROM4_5G_MAXP_ITSSI, + SSB_SPROM4_5G_ITSSI, SSB_SPROM4_5G_ITSSI_SHIFT); + SPEX(core_pwr_info[i].maxpwr_5g, o + SSB_SPROM4_5G_MAXP_ITSSI, + SSB_SPROM4_5G_MAXP, 0); + SPEX(core_pwr_info[i].maxpwr_5gh, o + SSB_SPROM4_5GHL_MAXP, + SSB_SPROM4_5GH_MAXP, 0); + SPEX(core_pwr_info[i].maxpwr_5gl, o + SSB_SPROM4_5GHL_MAXP, + SSB_SPROM4_5GL_MAXP, SSB_SPROM4_5GL_MAXP_SHIFT); + + SPEX(core_pwr_info[i].pa_5gl[0], o + SSB_SPROM4_5GL_PA_0, ~0, 0); + SPEX(core_pwr_info[i].pa_5gl[1], o + SSB_SPROM4_5GL_PA_1, ~0, 0); + SPEX(core_pwr_info[i].pa_5gl[2], o + SSB_SPROM4_5GL_PA_2, ~0, 0); + SPEX(core_pwr_info[i].pa_5gl[3], o + SSB_SPROM4_5GL_PA_3, ~0, 0); + SPEX(core_pwr_info[i].pa_5g[0], o + SSB_SPROM4_5G_PA_0, ~0, 0); + SPEX(core_pwr_info[i].pa_5g[1], o + SSB_SPROM4_5G_PA_1, ~0, 0); + SPEX(core_pwr_info[i].pa_5g[2], o + SSB_SPROM4_5G_PA_2, ~0, 0); + SPEX(core_pwr_info[i].pa_5g[3], o + SSB_SPROM4_5G_PA_3, ~0, 0); + SPEX(core_pwr_info[i].pa_5gh[0], o + SSB_SPROM4_5GH_PA_0, ~0, 0); + SPEX(core_pwr_info[i].pa_5gh[1], o + SSB_SPROM4_5GH_PA_1, ~0, 0); + SPEX(core_pwr_info[i].pa_5gh[2], o + SSB_SPROM4_5GH_PA_2, ~0, 0); + SPEX(core_pwr_info[i].pa_5gh[3], o + SSB_SPROM4_5GH_PA_3, ~0, 0); + } + sprom_extract_r458(out, in); /* TODO - get remaining rev 4 stuff needed */ diff --git a/include/linux/ssb/ssb_regs.h b/include/linux/ssb/ssb_regs.h index f9f931c89e3e..f7b9100686c3 100644 --- a/include/linux/ssb/ssb_regs.h +++ b/include/linux/ssb/ssb_regs.h @@ -345,6 +345,43 @@ #define SSB_SPROM4_TXPID5GH2_SHIFT 0 #define SSB_SPROM4_TXPID5GH3 0xFF00 #define SSB_SPROM4_TXPID5GH3_SHIFT 8 + +/* There are 4 blocks with power info sharing the same layout */ +#define SSB_SPROM4_PWR_INFO_CORE0 0x0080 +#define SSB_SPROM4_PWR_INFO_CORE1 0x00AE +#define SSB_SPROM4_PWR_INFO_CORE2 0x00DC +#define SSB_SPROM4_PWR_INFO_CORE3 0x010A + +#define SSB_SPROM4_2G_MAXP_ITSSI 0x00 /* 2 GHz ITSSI and 2 GHz Max Power */ +#define SSB_SPROM4_2G_MAXP 0x00FF +#define SSB_SPROM4_2G_ITSSI 0xFF00 +#define SSB_SPROM4_2G_ITSSI_SHIFT 8 +#define SSB_SPROM4_2G_PA_0 0x02 /* 2 GHz power amp */ +#define SSB_SPROM4_2G_PA_1 0x04 +#define SSB_SPROM4_2G_PA_2 0x06 +#define SSB_SPROM4_2G_PA_3 0x08 +#define SSB_SPROM4_5G_MAXP_ITSSI 0x0A /* 5 GHz ITSSI and 5.3 GHz Max Power */ +#define SSB_SPROM4_5G_MAXP 0x00FF +#define SSB_SPROM4_5G_ITSSI 0xFF00 +#define SSB_SPROM4_5G_ITSSI_SHIFT 8 +#define SSB_SPROM4_5GHL_MAXP 0x0C /* 5.2 GHz and 5.8 GHz Max Power */ +#define SSB_SPROM4_5GH_MAXP 0x00FF +#define SSB_SPROM4_5GL_MAXP 0xFF00 +#define SSB_SPROM4_5GL_MAXP_SHIFT 8 +#define SSB_SPROM4_5G_PA_0 0x0E /* 5.3 GHz power amp */ +#define SSB_SPROM4_5G_PA_1 0x10 +#define SSB_SPROM4_5G_PA_2 0x12 +#define SSB_SPROM4_5G_PA_3 0x14 +#define SSB_SPROM4_5GL_PA_0 0x16 /* 5.2 GHz power amp */ +#define SSB_SPROM4_5GL_PA_1 0x18 +#define SSB_SPROM4_5GL_PA_2 0x1A +#define SSB_SPROM4_5GL_PA_3 0x1C +#define SSB_SPROM4_5GH_PA_0 0x1E /* 5.8 GHz power amp */ +#define SSB_SPROM4_5GH_PA_1 0x20 +#define SSB_SPROM4_5GH_PA_2 0x22 +#define SSB_SPROM4_5GH_PA_3 0x24 + +/* TODO: Make it deprecated */ #define SSB_SPROM4_MAXP_BG 0x0080 /* Max Power BG in path 1 */ #define SSB_SPROM4_MAXP_BG_MASK 0x00FF /* Mask for Max Power BG */ #define SSB_SPROM4_ITSSI_BG 0xFF00 /* Mask for path 1 itssi_bg */ From d8aef3239e7d6a1bd550014ac766e5ec11c63ea9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 15 Jul 2014 16:54:47 +0200 Subject: [PATCH 344/378] bcma: extract antenna gains from SPROM correctly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just like in case of SSB SPROMs they are encoded in a bit tricky way. SPROM struct already uses s8 type and it's supposed to store decoded values. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/bcma/sprom.c | 41 +++++++++++++++---- .../net/wireless/brcm80211/brcmsmac/main.c | 37 ----------------- 2 files changed, 33 insertions(+), 45 deletions(-) diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c index 72bf4540f565..a9dfb1ac138d 100644 --- a/drivers/bcma/sprom.c +++ b/drivers/bcma/sprom.c @@ -201,6 +201,23 @@ static int bcma_sprom_valid(struct bcma_bus *bus, const u16 *sprom, SPEX(_field[7], _offset + 14, _mask, _shift); \ } while (0) +static s8 sprom_extract_antgain(const u16 *in, u16 offset, u16 mask, u16 shift) +{ + u16 v; + u8 gain; + + v = in[SPOFF(offset)]; + gain = (v & mask) >> shift; + if (gain == 0xFF) { + gain = 8; /* If unset use 2dBm */ + } else { + /* Q5.2 Fractional part is stored in 0xC0 */ + gain = ((gain & 0xC0) >> 6) | ((gain & 0x3F) << 2); + } + + return (s8)gain; +} + static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom) { u16 v, o; @@ -381,14 +398,22 @@ static void bcma_sprom_extract_r8(struct bcma_bus *bus, const u16 *sprom) SPEX32(ofdm5ghpo, SSB_SPROM8_OFDM5GHPO, ~0, 0); /* Extract the antenna gain values. */ - SPEX(antenna_gain.a0, SSB_SPROM8_AGAIN01, - SSB_SPROM8_AGAIN0, SSB_SPROM8_AGAIN0_SHIFT); - SPEX(antenna_gain.a1, SSB_SPROM8_AGAIN01, - SSB_SPROM8_AGAIN1, SSB_SPROM8_AGAIN1_SHIFT); - SPEX(antenna_gain.a2, SSB_SPROM8_AGAIN23, - SSB_SPROM8_AGAIN2, SSB_SPROM8_AGAIN2_SHIFT); - SPEX(antenna_gain.a3, SSB_SPROM8_AGAIN23, - SSB_SPROM8_AGAIN3, SSB_SPROM8_AGAIN3_SHIFT); + bus->sprom.antenna_gain.a0 = sprom_extract_antgain(sprom, + SSB_SPROM8_AGAIN01, + SSB_SPROM8_AGAIN0, + SSB_SPROM8_AGAIN0_SHIFT); + bus->sprom.antenna_gain.a1 = sprom_extract_antgain(sprom, + SSB_SPROM8_AGAIN01, + SSB_SPROM8_AGAIN1, + SSB_SPROM8_AGAIN1_SHIFT); + bus->sprom.antenna_gain.a2 = sprom_extract_antgain(sprom, + SSB_SPROM8_AGAIN23, + SSB_SPROM8_AGAIN2, + SSB_SPROM8_AGAIN2_SHIFT); + bus->sprom.antenna_gain.a3 = sprom_extract_antgain(sprom, + SSB_SPROM8_AGAIN23, + SSB_SPROM8_AGAIN3, + SSB_SPROM8_AGAIN3_SHIFT); SPEX(leddc_on_time, SSB_SPROM8_LEDDC, SSB_SPROM8_LEDDC_ON, SSB_SPROM8_LEDDC_ON_SHIFT); diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c index af8ba64ace39..1b474828d5b8 100644 --- a/drivers/net/wireless/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c @@ -4707,41 +4707,6 @@ static int brcms_b_attach(struct brcms_c_info *wlc, struct bcma_device *core, return err; } -static void brcms_c_attach_antgain_init(struct brcms_c_info *wlc) -{ - uint unit; - unit = wlc->pub->unit; - - if ((wlc->band->antgain == -1) && (wlc->pub->sromrev == 1)) { - /* default antenna gain for srom rev 1 is 2 dBm (8 qdbm) */ - wlc->band->antgain = 8; - } else if (wlc->band->antgain == -1) { - wiphy_err(wlc->wiphy, "wl%d: %s: Invalid antennas available in" - " srom, using 2dB\n", unit, __func__); - wlc->band->antgain = 8; - } else { - s8 gain, fract; - /* Older sroms specified gain in whole dbm only. In order - * be able to specify qdbm granularity and remain backward - * compatible the whole dbms are now encoded in only - * low 6 bits and remaining qdbms are encoded in the hi 2 bits. - * 6 bit signed number ranges from -32 - 31. - * - * Examples: - * 0x1 = 1 db, - * 0xc1 = 1.75 db (1 + 3 quarters), - * 0x3f = -1 (-1 + 0 quarters), - * 0x7f = -.75 (-1 + 1 quarters) = -3 qdbm. - * 0xbf = -.50 (-1 + 2 quarters) = -2 qdbm. - */ - gain = wlc->band->antgain & 0x3f; - gain <<= 2; /* Sign extend */ - gain >>= 2; - fract = (wlc->band->antgain & 0xc0) >> 6; - wlc->band->antgain = 4 * gain + fract; - } -} - static bool brcms_c_attach_stf_ant_init(struct brcms_c_info *wlc) { int aa; @@ -4780,8 +4745,6 @@ static bool brcms_c_attach_stf_ant_init(struct brcms_c_info *wlc) else wlc->band->antgain = sprom->antenna_gain.a0; - brcms_c_attach_antgain_init(wlc); - return true; } From d1d3799fcb1037357b54be44e796a6253484268e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 15 Jul 2014 19:44:28 +0200 Subject: [PATCH 345/378] bcma: add support for BCM43217 found in Tenda W322E (14e4:43a9) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/bcma/driver_chipcommon_pmu.c | 1 + drivers/bcma/host_pci.c | 1 + drivers/bcma/sprom.c | 1 + include/linux/bcma/bcma.h | 1 + 4 files changed, 4 insertions(+) diff --git a/drivers/bcma/driver_chipcommon_pmu.c b/drivers/bcma/driver_chipcommon_pmu.c index 5081a8c439cc..bb694e2e9f32 100644 --- a/drivers/bcma/driver_chipcommon_pmu.c +++ b/drivers/bcma/driver_chipcommon_pmu.c @@ -603,6 +603,7 @@ void bcma_pmu_spuravoid_pllupdate(struct bcma_drv_cc *cc, int spuravoid) tmp = BCMA_CC_PMU_CTL_PLL_UPD | BCMA_CC_PMU_CTL_NOILPONW; break; + case BCMA_CHIP_ID_BCM43217: case BCMA_CHIP_ID_BCM43227: case BCMA_CHIP_ID_BCM43228: case BCMA_CHIP_ID_BCM43428: diff --git a/drivers/bcma/host_pci.c b/drivers/bcma/host_pci.c index e333305363aa..3cf725a49dc1 100644 --- a/drivers/bcma/host_pci.c +++ b/drivers/bcma/host_pci.c @@ -279,6 +279,7 @@ static const struct pci_device_id bcma_pci_bridge_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4358) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4359) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4365) }, + { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x43a9) }, { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, 0x4727) }, { 0, }, }; diff --git a/drivers/bcma/sprom.c b/drivers/bcma/sprom.c index a9dfb1ac138d..97bb38e9ed65 100644 --- a/drivers/bcma/sprom.c +++ b/drivers/bcma/sprom.c @@ -534,6 +534,7 @@ static bool bcma_sprom_onchip_available(struct bcma_bus *bus) /* for these chips OTP is always available */ present = true; break; + case BCMA_CHIP_ID_BCM43217: case BCMA_CHIP_ID_BCM43227: case BCMA_CHIP_ID_BCM43228: case BCMA_CHIP_ID_BCM43428: diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 452286a38b2b..7cb2344741cf 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -158,6 +158,7 @@ struct bcma_host_ops { /* Chip IDs of PCIe devices */ #define BCMA_CHIP_ID_BCM4313 0x4313 #define BCMA_CHIP_ID_BCM43142 43142 +#define BCMA_CHIP_ID_BCM43217 43217 #define BCMA_CHIP_ID_BCM43224 43224 #define BCMA_PKG_ID_BCM43224_FAB_CSM 0x8 #define BCMA_PKG_ID_BCM43224_FAB_SMIC 0xa From d954cd770051cde27a9da8f2627acd5bc709ffbb Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Wed, 16 Jul 2014 20:26:05 +0200 Subject: [PATCH 346/378] ath9k: fix pending tx frames accounting Packets originally buffered for the regular hardware tx queues can end up being transmitted through the U-APSD queue (via PS-Poll or U-APSD). When packets are dropped due to retransmit failures, the pending frames counter is not always updated properly. Fix this by keeping track of the queue that a frame was accounted for in the ath_frame_info struct, and using that on completion to decide whether the counter should be updated. This fixes some spurious transmit queue hangs. Cc: stable@vger.kernel.org Signed-off-by: Felix Fietkau Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ath9k.h | 3 ++- drivers/net/wireless/ath/ath9k/xmit.c | 25 ++++++++++++++----------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index 11b5e4dd6294..7fc13a8da675 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -182,7 +182,8 @@ struct ath_atx_ac { struct ath_frame_info { struct ath_buf *bf; - int framelen; + u16 framelen; + s8 txq; enum ath9k_key_type keytype; u8 keyix; u8 rtscts_rate; diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index d4927c9a6bae..36115298f64e 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -157,15 +157,14 @@ static void ath_txq_skb_done(struct ath_softc *sc, struct ath_txq *txq, struct sk_buff *skb) { struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); - int q, hw_queue; + struct ath_frame_info *fi = get_frame_info(skb); + int hw_queue; + int q = fi->txq; - q = skb_get_queue_mapping(skb); - if (txq == sc->tx.uapsdq) - txq = sc->tx.txq_map[q]; - - if (txq != sc->tx.txq_map[q]) + if (q < 0) return; + txq = sc->tx.txq_map[q]; if (WARN_ON(--txq->pending_frames < 0)) txq->pending_frames = 0; @@ -2036,6 +2035,7 @@ static void setup_frame_info(struct ieee80211_hw *hw, an = (struct ath_node *) sta->drv_priv; memset(fi, 0, sizeof(*fi)); + fi->txq = -1; if (hw_key) fi->keyix = hw_key->hw_key_idx; else if (an && ieee80211_is_data(hdr->frame_control) && an->ps_key > 0) @@ -2187,6 +2187,7 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_sta *sta = txctl->sta; struct ieee80211_vif *vif = info->control.vif; + struct ath_frame_info *fi = get_frame_info(skb); struct ath_vif *avp = NULL; struct ath_softc *sc = hw->priv; struct ath_txq *txq = txctl->txq; @@ -2216,11 +2217,13 @@ int ath_tx_start(struct ieee80211_hw *hw, struct sk_buff *skb, hw_queue = (info->hw_queue >= sc->hw->queues - 2) ? q : info->hw_queue; ath_txq_lock(sc, txq); - if (txq == sc->tx.txq_map[q] && - ++txq->pending_frames > sc->tx.txq_max_pending[q] && - !txq->stopped) { - ieee80211_stop_queue(sc->hw, hw_queue); - txq->stopped = true; + if (txq == sc->tx.txq_map[q]) { + fi->txq = q; + if (++txq->pending_frames > sc->tx.txq_max_pending[q] && + !txq->stopped) { + ieee80211_stop_queue(sc->hw, hw_queue); + txq->stopped = true; + } } queue = ieee80211_is_data_present(hdr->frame_control); From 3ae351abf15f984c02feb9aab5394443e9efb00a Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Wed, 16 Jul 2014 23:20:10 +0200 Subject: [PATCH 347/378] ath9k: set up tx power into the MRR Set up tx power for each MRR segment in the tx descriptor Signed-off-by: Lorenzo Bianconi Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/ar9002_mac.c | 6 +++++- drivers/net/wireless/ath/ath9k/ar9003_mac.c | 6 +++++- drivers/net/wireless/ath/ath9k/mac.h | 10 ++++++++-- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/ar9002_mac.c b/drivers/net/wireless/ath/ath9k/ar9002_mac.c index 741b38ddcb37..59af9f9712da 100644 --- a/drivers/net/wireless/ath/ath9k/ar9002_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9002_mac.c @@ -281,7 +281,7 @@ ar9002_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i) ACCESS_ONCE(ads->ds_ctl0) = (i->pkt_len & AR_FrameLen) | (i->flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0) - | SM(i->txpower, AR_XmitPower) + | SM(i->txpower, AR_XmitPower0) | (i->flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0) | (i->flags & ATH9K_TXDESC_INTREQ ? AR_TxIntrReq : 0) | (i->keyix != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0) @@ -306,6 +306,10 @@ ar9002_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i) | set11nRateFlags(i->rates, 2) | set11nRateFlags(i->rates, 3) | SM(i->rtscts_rate, AR_RTSCTSRate); + + ACCESS_ONCE(ads->ds_ctl9) = SM(i->txpower, AR_XmitPower1); + ACCESS_ONCE(ads->ds_ctl10) = SM(i->txpower, AR_XmitPower2); + ACCESS_ONCE(ads->ds_ctl11) = SM(i->txpower, AR_XmitPower3); } static int ar9002_hw_proc_txdesc(struct ath_hw *ah, void *ds, diff --git a/drivers/net/wireless/ath/ath9k/ar9003_mac.c b/drivers/net/wireless/ath/ath9k/ar9003_mac.c index 729ffbf07343..71e38e85aa99 100644 --- a/drivers/net/wireless/ath/ath9k/ar9003_mac.c +++ b/drivers/net/wireless/ath/ath9k/ar9003_mac.c @@ -101,7 +101,7 @@ ar9003_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i) ACCESS_ONCE(ads->ctl11) = (i->pkt_len & AR_FrameLen) | (i->flags & ATH9K_TXDESC_VMF ? AR_VirtMoreFrag : 0) - | SM(i->txpower, AR_XmitPower) + | SM(i->txpower, AR_XmitPower0) | (i->flags & ATH9K_TXDESC_VEOL ? AR_VEOL : 0) | (i->keyix != ATH9K_TXKEYIX_INVALID ? AR_DestIdxValid : 0) | (i->flags & ATH9K_TXDESC_LOWRXCHAIN ? AR_LowRxChain : 0) @@ -151,6 +151,10 @@ ar9003_set_txdesc(struct ath_hw *ah, void *ds, struct ath_tx_info *i) | SM(i->rtscts_rate, AR_RTSCTSRate); ACCESS_ONCE(ads->ctl19) = AR_Not_Sounding; + + ACCESS_ONCE(ads->ctl20) = SM(i->txpower, AR_XmitPower1); + ACCESS_ONCE(ads->ctl21) = SM(i->txpower, AR_XmitPower2); + ACCESS_ONCE(ads->ctl22) = SM(i->txpower, AR_XmitPower3); } static u16 ar9003_calc_ptr_chksum(struct ar9003_txc *ads) diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index da7686757535..6c56cafa5ca4 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h @@ -346,8 +346,14 @@ struct ar5416_desc { #define AR_FrameLen 0x00000fff #define AR_VirtMoreFrag 0x00001000 #define AR_TxCtlRsvd00 0x0000e000 -#define AR_XmitPower 0x003f0000 -#define AR_XmitPower_S 16 +#define AR_XmitPower0 0x003f0000 +#define AR_XmitPower0_S 16 +#define AR_XmitPower1 0x3f000000 +#define AR_XmitPower1_S 24 +#define AR_XmitPower2 0x3f000000 +#define AR_XmitPower2_S 24 +#define AR_XmitPower3 0x3f000000 +#define AR_XmitPower3_S 24 #define AR_RTSEnable 0x00400000 #define AR_VEOL 0x00800000 #define AR_ClrDestMask 0x01000000 From 3f5572020c4829e749716f2c31b5107417d5a96d Mon Sep 17 00:00:00 2001 From: Andrey Utkin Date: Thu, 17 Jul 2014 16:56:37 +0300 Subject: [PATCH 348/378] ath9k: drop negativity checks for unsigned values coming from kstrtoul() Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=80471 Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=80481 Reported-by: David Binderman Signed-off-by: Andrey Utkin Signed-off-by: John W. Linville --- drivers/net/wireless/ath/ath9k/debug.c | 2 +- drivers/net/wireless/ath/ath9k/spectral.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/net/wireless/ath/ath9k/debug.c b/drivers/net/wireless/ath/ath9k/debug.c index ce073e995dfe..d2279365be6f 100644 --- a/drivers/net/wireless/ath/ath9k/debug.c +++ b/drivers/net/wireless/ath/ath9k/debug.c @@ -202,7 +202,7 @@ static ssize_t write_file_ani(struct file *file, if (kstrtoul(buf, 0, &ani)) return -EINVAL; - if (ani < 0 || ani > 1) + if (ani > 1) return -EINVAL; common->disable_ani = !ani; diff --git a/drivers/net/wireless/ath/ath9k/spectral.c b/drivers/net/wireless/ath/ath9k/spectral.c index 99f4de95c264..5fe29b9f8fa2 100644 --- a/drivers/net/wireless/ath/ath9k/spectral.c +++ b/drivers/net/wireless/ath/ath9k/spectral.c @@ -313,7 +313,7 @@ static ssize_t write_file_spectral_short_repeat(struct file *file, if (kstrtoul(buf, 0, &val)) return -EINVAL; - if (val < 0 || val > 1) + if (val > 1) return -EINVAL; sc->spec_config.short_repeat = val; @@ -361,7 +361,7 @@ static ssize_t write_file_spectral_count(struct file *file, if (kstrtoul(buf, 0, &val)) return -EINVAL; - if (val < 0 || val > 255) + if (val > 255) return -EINVAL; sc->spec_config.count = val; @@ -409,7 +409,7 @@ static ssize_t write_file_spectral_period(struct file *file, if (kstrtoul(buf, 0, &val)) return -EINVAL; - if (val < 0 || val > 255) + if (val > 255) return -EINVAL; sc->spec_config.period = val; @@ -457,7 +457,7 @@ static ssize_t write_file_spectral_fft_period(struct file *file, if (kstrtoul(buf, 0, &val)) return -EINVAL; - if (val < 0 || val > 15) + if (val > 15) return -EINVAL; sc->spec_config.fft_period = val; From 701fa113805f5523201fafa1385829a092ff8416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Thu, 17 Jul 2014 19:31:01 +0200 Subject: [PATCH 349/378] b43: N-PHY: fix channel switching with 2 GHz radio 0x2057 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Values were written to wrong registers. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/radio_2057.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/net/wireless/b43/radio_2057.c b/drivers/net/wireless/b43/radio_2057.c index ca22faa41d28..c9c24b615cc5 100644 --- a/drivers/net/wireless/b43/radio_2057.c +++ b/drivers/net/wireless/b43/radio_2057.c @@ -163,12 +163,12 @@ static u16 r2057_rev9_init[][2] = { .radio_vcobuf_tune = r09, \ .radio_logen_mx2g_tune = r10, \ .radio_logen_indbuf2g_tune = r11, \ - .radio_lna2g_tune_core0 = r12, \ - .radio_txmix2g_tune_boost_pu_core0 = r13, \ - .radio_pad2g_tune_pus_core0 = r14, \ - .radio_lna2g_tune_core1 = r15, \ - .radio_txmix2g_tune_boost_pu_core1 = r16, \ - .radio_pad2g_tune_pus_core1 = r17 + .radio_txmix2g_tune_boost_pu_core0 = r12, \ + .radio_pad2g_tune_pus_core0 = r13, \ + .radio_lna2g_tune_core0 = r14, \ + .radio_txmix2g_tune_boost_pu_core1 = r15, \ + .radio_pad2g_tune_pus_core1 = r16, \ + .radio_lna2g_tune_core1 = r17 #define PHYREGS(r0, r1, r2, r3, r4, r5) \ .phy_regs.phy_bw1a = r0, \ From 3b7caa29272961c0205aff41316b56d4b0b588f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Thu, 17 Jul 2014 19:31:02 +0200 Subject: [PATCH 350/378] b43: N-PHY: add tables for radio 0x2057 rev 14 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/phy_n.c | 12 ++- drivers/net/wireless/b43/radio_2057.c | 103 ++++++++++++++++++++++++- drivers/net/wireless/b43/tables_nphy.c | 40 ++++++++++ 3 files changed, 152 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index 479cda88ca5e..3da02ff59cf1 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -765,7 +765,12 @@ static void b43_radio_2057_setup(struct b43_wldev *dev, } } break; - /* TODO */ + case 14: /* 2 GHz only */ + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_R1, 0x1b); + b43_radio_write(dev, R2057_CP_KPD_IDAC, 0x3f); + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C1, 0x1f); + b43_radio_write(dev, R2057_RFPLL_LOOPFILTER_C2, 0x1f); + break; } if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { @@ -778,8 +783,11 @@ static void b43_radio_2057_setup(struct b43_wldev *dev, txmix2g_tune_boost_pu = 0x0041; /* TODO */ break; + case 14: + txmix2g_tune_boost_pu = 0x21; + pad2g_tune_pus = 0x23; + break; } - /* TODO */ } if (txmix2g_tune_boost_pu) diff --git a/drivers/net/wireless/b43/radio_2057.c b/drivers/net/wireless/b43/radio_2057.c index c9c24b615cc5..ff1e026a61a1 100644 --- a/drivers/net/wireless/b43/radio_2057.c +++ b/drivers/net/wireless/b43/radio_2057.c @@ -117,6 +117,15 @@ static u16 r2057_rev9_init[][2] = { { 0x14e, 0x01 }, { 0x1b7, 0x05 }, { 0x1c2, 0xa0 }, }; +/* Extracted from MMIO dump of 6.30.223.248 */ +static u16 r2057_rev14_init[][2] = { + { 0x011, 0xfc }, { 0x030, 0x24 }, { 0x040, 0x1c }, { 0x082, 0x08 }, + { 0x0b4, 0x44 }, { 0x0c8, 0x01 }, { 0x0c9, 0x01 }, { 0x107, 0x08 }, + { 0x14d, 0x01 }, { 0x14e, 0x01 }, { 0x1af, 0x40 }, { 0x1b0, 0x40 }, + { 0x1cc, 0x01 }, { 0x1cf, 0x10 }, { 0x1d0, 0x0f }, { 0x1d3, 0x10 }, + { 0x1d4, 0x0f }, +}; + #define RADIOREGS7(r00, r01, r02, r03, r04, r05, r06, r07, r08, r09, \ r10, r11, r12, r13, r14, r15, r16, r17, r18, r19, \ r20, r21, r22, r23, r24, r25, r26, r27) \ @@ -280,6 +289,87 @@ static const struct b43_nphy_chantabent_rev7_2g b43_nphy_chantab_phy_rev8_radio_ } }; +/* Extracted from MMIO dump of 6.30.223.248 */ +static const struct b43_nphy_chantabent_rev7_2g b43_nphy_chantab_phy_rev17_radio_rev14[] = { + { + .freq = 2412, + RADIOREGS7_2G(0x48, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x6c, + 0x09, 0x0d, 0x09, 0x03, 0x21, 0x53, 0xff, 0x21, + 0x53, 0xff), + PHYREGS(0x03c9, 0x03c5, 0x03c1, 0x043a, 0x043f, 0x0443), + }, + { + .freq = 2417, + RADIOREGS7_2G(0x4b, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x71, + 0x09, 0x0d, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21, + 0x53, 0xff), + PHYREGS(0x03cb, 0x03c7, 0x03c3, 0x0438, 0x043d, 0x0441), + }, + { + .freq = 2422, + RADIOREGS7_2G(0x4e, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x76, + 0x09, 0x0d, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21, + 0x53, 0xff), + PHYREGS(0x03cd, 0x03c9, 0x03c5, 0x0436, 0x043a, 0x043f), + }, + { + .freq = 2427, + RADIOREGS7_2G(0x52, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x7b, + 0x09, 0x0c, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21, + 0x53, 0xff), + PHYREGS(0x03cf, 0x03cb, 0x03c7, 0x0434, 0x0438, 0x043d), + }, + { + .freq = 2432, + RADIOREGS7_2G(0x55, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x80, + 0x09, 0x0c, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21, + 0x53, 0xff), + PHYREGS(0x03d1, 0x03cd, 0x03c9, 0x0431, 0x0436, 0x043a), + }, + { + .freq = 2437, + RADIOREGS7_2G(0x58, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x85, + 0x09, 0x0c, 0x08, 0x03, 0x21, 0x53, 0xff, 0x21, + 0x53, 0xff), + PHYREGS(0x03d3, 0x03cf, 0x03cb, 0x042f, 0x0434, 0x0438), + }, + { + .freq = 2442, + RADIOREGS7_2G(0x5c, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x8a, + 0x09, 0x0c, 0x08, 0x03, 0x21, 0x43, 0xff, 0x21, + 0x43, 0xff), + PHYREGS(0x03d5, 0x03d1, 0x03cd, 0x042d, 0x0431, 0x0436), + }, + { + .freq = 2447, + RADIOREGS7_2G(0x5f, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x8f, + 0x09, 0x0c, 0x08, 0x03, 0x21, 0x43, 0xff, 0x21, + 0x43, 0xff), + PHYREGS(0x03d7, 0x03d3, 0x03cf, 0x042b, 0x042f, 0x0434), + }, + { + .freq = 2452, + RADIOREGS7_2G(0x62, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x94, + 0x09, 0x0c, 0x08, 0x03, 0x21, 0x43, 0xff, 0x21, + 0x43, 0xff), + PHYREGS(0x03d9, 0x03d5, 0x03d1, 0x0429, 0x042d, 0x0431), + }, + { + .freq = 2457, + RADIOREGS7_2G(0x66, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x99, + 0x09, 0x0b, 0x07, 0x03, 0x21, 0x43, 0xff, 0x21, + 0x43, 0xff), + PHYREGS(0x03db, 0x03d7, 0x03d3, 0x0427, 0x042b, 0x042f), + }, + { + .freq = 2462, + RADIOREGS7_2G(0x69, 0x16, 0x30, 0x2b, 0x1f, 0x1f, 0x30, 0x9e, + 0x09, 0x0b, 0x07, 0x03, 0x01, 0x43, 0xff, 0x01, + 0x43, 0xff), + PHYREGS(0x03dd, 0x03d9, 0x03d5, 0x0424, 0x0429, 0x042d), + }, +}; + /* Extracted from MMIO dump of 6.30.223.141 */ static const struct b43_nphy_chantabent_rev7 b43_nphy_chantab_phy_rev16_radio_rev9[] = { { @@ -476,6 +566,12 @@ void r2057_upload_inittabs(struct b43_wldev *dev) size = ARRAY_SIZE(r2057_rev9_init); } break; + case 17: + if (phy->radio_rev == 14) { + table = r2057_rev14_init[0]; + size = ARRAY_SIZE(r2057_rev14_init); + } + break; } B43_WARN_ON(!table); @@ -498,7 +594,6 @@ void r2057_get_chantabent_rev7(struct b43_wldev *dev, u16 freq, *tabent_r7 = NULL; *tabent_r7_2g = NULL; - /* TODO */ switch (phy->rev) { case 8: if (phy->radio_rev == 5) { @@ -512,6 +607,12 @@ void r2057_get_chantabent_rev7(struct b43_wldev *dev, u16 freq, len = ARRAY_SIZE(b43_nphy_chantab_phy_rev16_radio_rev9); } break; + case 17: + if (phy->radio_rev == 14) { + e_r7_2g = b43_nphy_chantab_phy_rev17_radio_rev14; + len = ARRAY_SIZE(b43_nphy_chantab_phy_rev17_radio_rev14); + } + break; default: break; } diff --git a/drivers/net/wireless/b43/tables_nphy.c b/drivers/net/wireless/b43/tables_nphy.c index b39a8dd375e9..ab27c2de2f43 100644 --- a/drivers/net/wireless/b43/tables_nphy.c +++ b/drivers/net/wireless/b43/tables_nphy.c @@ -2764,6 +2764,42 @@ static const u32 b43_ntab_tx_gain_ipa_2057_rev9_2g[] = { 0x600f0715, 0x600f0715, 0x600f0715, 0x600f0715, }; +/* Extracted from MMIO dump of 6.30.223.248 */ +static const u32 b43_ntab_tx_gain_ipa_2057_rev14_2g[] = { + 0x50df002e, 0x50cf002d, 0x50bf002c, 0x50b7002b, + 0x50af002a, 0x50a70029, 0x509f0029, 0x50970028, + 0x508f0027, 0x50870027, 0x507f0027, 0x50770027, + 0x506f0027, 0x50670027, 0x505f0028, 0x50570029, + 0x504f002b, 0x5047002e, 0x5047002b, 0x50470029, + 0x503f002c, 0x503f0029, 0x5037002c, 0x5037002a, + 0x50370028, 0x502f002d, 0x502f002b, 0x502f0028, + 0x502f0026, 0x5027002d, 0x5027002a, 0x50270028, + 0x50270026, 0x50270024, 0x501f002e, 0x501f002b, + 0x501f0029, 0x501f0027, 0x501f0024, 0x501f0022, + 0x501f0020, 0x501f001f, 0x5017002c, 0x50170029, + 0x50170027, 0x50170024, 0x50170022, 0x50170021, + 0x5017001f, 0x5017001d, 0x5017001b, 0x5017001a, + 0x50170018, 0x50170017, 0x50170015, 0x500f002c, + 0x500f002a, 0x500f0027, 0x500f0025, 0x500f0023, + 0x500f0022, 0x500f001f, 0x500f001e, 0x500f001c, + 0x500f001a, 0x500f0019, 0x500f0018, 0x500f0016, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, + 0x500f0015, 0x500f0015, 0x500f0015, 0x500f0015, +}; + /* IPA 2 5Hz */ static const u32 b43_ntab_tx_gain_ipa_rev3_5g[] = { @@ -3583,6 +3619,10 @@ static const u32 *b43_nphy_get_ipa_gain_table(struct b43_wldev *dev) if (b43_current_band(dev->wl) == IEEE80211_BAND_2GHZ) { switch (phy->rev) { + case 17: + if (phy->radio_rev == 14) + return b43_ntab_tx_gain_ipa_2057_rev14_2g; + break; case 16: if (phy->radio_rev == 9) return b43_ntab_tx_gain_ipa_2057_rev9_2g; From ef0d635ed147047afd97bb86510fb7a889fab709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Thu, 17 Jul 2014 19:31:03 +0200 Subject: [PATCH 351/378] b43: N-PHY: complete 0x2057 radio init calibration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/phy_n.c | 86 ++++++++++++++++++++++++++++---- drivers/net/wireless/b43/phy_n.h | 2 + 2 files changed, 79 insertions(+), 9 deletions(-) diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index 3da02ff59cf1..2c5583231d2f 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -820,13 +820,62 @@ static void b43_radio_2057_setup(struct b43_wldev *dev, static u8 b43_radio_2057_rcal(struct b43_wldev *dev) { struct b43_phy *phy = &dev->phy; + u16 saved_regs_phy[12]; + u16 saved_regs_phy_rf[6]; + u16 saved_regs_radio[2] = { }; + static const u16 phy_to_store[] = { + B43_NPHY_RFCTL_RSSIO1, B43_NPHY_RFCTL_RSSIO2, + B43_NPHY_RFCTL_LUT_TRSW_LO1, B43_NPHY_RFCTL_LUT_TRSW_LO2, + B43_NPHY_RFCTL_RXG1, B43_NPHY_RFCTL_RXG2, + B43_NPHY_RFCTL_TXG1, B43_NPHY_RFCTL_TXG2, + B43_NPHY_REV7_RF_CTL_MISC_REG3, B43_NPHY_REV7_RF_CTL_MISC_REG4, + B43_NPHY_REV7_RF_CTL_MISC_REG5, B43_NPHY_REV7_RF_CTL_MISC_REG6, + }; + static const u16 phy_to_store_rf[] = { + B43_NPHY_REV3_RFCTL_OVER0, B43_NPHY_REV3_RFCTL_OVER1, + B43_NPHY_REV7_RF_CTL_OVER3, B43_NPHY_REV7_RF_CTL_OVER4, + B43_NPHY_REV7_RF_CTL_OVER5, B43_NPHY_REV7_RF_CTL_OVER6, + }; u16 tmp; + int i; - if (phy->radio_rev == 5) { - b43_phy_mask(dev, 0x342, ~0x2); + /* Save */ + for (i = 0; i < ARRAY_SIZE(phy_to_store); i++) + saved_regs_phy[i] = b43_phy_read(dev, phy_to_store[i]); + for (i = 0; i < ARRAY_SIZE(phy_to_store_rf); i++) + saved_regs_phy_rf[i] = b43_phy_read(dev, phy_to_store_rf[i]); + + /* Set */ + for (i = 0; i < ARRAY_SIZE(phy_to_store); i++) + b43_phy_write(dev, phy_to_store[i], 0); + b43_phy_write(dev, B43_NPHY_REV3_RFCTL_OVER0, 0x07ff); + b43_phy_write(dev, B43_NPHY_REV3_RFCTL_OVER1, 0x07ff); + b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER3, 0x07ff); + b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER4, 0x07ff); + b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER5, 0x007f); + b43_phy_write(dev, B43_NPHY_REV7_RF_CTL_OVER6, 0x007f); + + switch (phy->radio_rev) { + case 5: + b43_phy_mask(dev, B43_NPHY_REV7_RF_CTL_OVER3, ~0x2); udelay(10); b43_radio_set(dev, R2057_IQTEST_SEL_PU, 0x1); - b43_radio_maskset(dev, 0x1ca, ~0x2, 0x1); + b43_radio_maskset(dev, R2057v7_IQTEST_SEL_PU2, ~0x2, 0x1); + break; + case 9: + b43_phy_set(dev, B43_NPHY_REV7_RF_CTL_OVER3, 0x2); + b43_phy_set(dev, B43_NPHY_REV7_RF_CTL_MISC_REG3, 0x2); + saved_regs_radio[0] = b43_radio_read(dev, R2057_IQTEST_SEL_PU); + b43_radio_write(dev, R2057_IQTEST_SEL_PU, 0x11); + break; + case 14: + saved_regs_radio[0] = b43_radio_read(dev, R2057_IQTEST_SEL_PU); + saved_regs_radio[1] = b43_radio_read(dev, R2057v7_IQTEST_SEL_PU2); + b43_phy_set(dev, B43_NPHY_REV7_RF_CTL_MISC_REG3, 0x2); + b43_phy_set(dev, B43_NPHY_REV7_RF_CTL_OVER3, 0x2); + b43_radio_write(dev, R2057v7_IQTEST_SEL_PU2, 0x2); + b43_radio_write(dev, R2057_IQTEST_SEL_PU, 0x1); + break; } /* Enable */ @@ -850,14 +899,30 @@ static u8 b43_radio_2057_rcal(struct b43_wldev *dev) /* Disable */ b43_radio_mask(dev, R2057_RCAL_CONFIG, ~0x1); - if (phy->radio_rev == 5) { - b43_radio_mask(dev, R2057_IPA2G_CASCONV_CORE0, ~0x1); - b43_radio_mask(dev, 0x1ca, ~0x2); - } - if (phy->radio_rev <= 4 || phy->radio_rev == 6) { + /* Restore */ + for (i = 0; i < ARRAY_SIZE(phy_to_store_rf); i++) + b43_phy_write(dev, phy_to_store_rf[i], saved_regs_phy_rf[i]); + for (i = 0; i < ARRAY_SIZE(phy_to_store); i++) + b43_phy_write(dev, phy_to_store[i], saved_regs_phy[i]); + + switch (phy->radio_rev) { + case 0 ... 4: + case 6: b43_radio_maskset(dev, R2057_TEMPSENSE_CONFIG, ~0x3C, tmp); b43_radio_maskset(dev, R2057_BANDGAP_RCAL_TRIM, ~0xF0, tmp << 2); + break; + case 5: + b43_radio_mask(dev, R2057_IPA2G_CASCONV_CORE0, ~0x1); + b43_radio_mask(dev, R2057v7_IQTEST_SEL_PU2, ~0x2); + break; + case 9: + b43_radio_write(dev, R2057_IQTEST_SEL_PU, saved_regs_radio[0]); + break; + case 14: + b43_radio_write(dev, R2057_IQTEST_SEL_PU, saved_regs_radio[0]); + b43_radio_write(dev, R2057v7_IQTEST_SEL_PU2, saved_regs_radio[1]); + break; } return tmp & 0x3e; @@ -879,7 +944,7 @@ static u16 b43_radio_2057_rccal(struct b43_wldev *dev) b43_radio_write(dev, R2057_RCCAL_TRC0, 0xC0); } else { b43_radio_write(dev, R2057v7_RCCAL_MASTER, 0x61); - b43_radio_write(dev, R2057_RCCAL_TRC0, 0xE1); + b43_radio_write(dev, R2057_RCCAL_TRC0, 0xE9); } b43_radio_write(dev, R2057_RCCAL_X1, 0x6E); @@ -959,6 +1024,9 @@ static void b43_radio_2057_init_post(struct b43_wldev *dev) { b43_radio_set(dev, R2057_XTALPUOVR_PINCTRL, 0x1); + if (0) /* FIXME: Is this BCM43217 specific? */ + b43_radio_set(dev, R2057_XTALPUOVR_PINCTRL, 0x2); + b43_radio_set(dev, R2057_RFPLL_MISC_CAL_RESETN, 0x78); b43_radio_set(dev, R2057_XTAL_CONFIG2, 0x80); mdelay(2); diff --git a/drivers/net/wireless/b43/phy_n.h b/drivers/net/wireless/b43/phy_n.h index 7fd5d5f9161d..474bf758295d 100644 --- a/drivers/net/wireless/b43/phy_n.h +++ b/drivers/net/wireless/b43/phy_n.h @@ -366,11 +366,13 @@ #define B43_NPHY_TXF_40CO_B1S0 B43_PHY_N(0x0E5) /* TX filter 40 coeff B1 stage 0 */ #define B43_NPHY_TXF_40CO_B32S1 B43_PHY_N(0x0E6) /* TX filter 40 coeff B32 stage 1 */ #define B43_NPHY_TXF_40CO_B1S1 B43_PHY_N(0x0E7) /* TX filter 40 coeff B1 stage 1 */ +#define B43_NPHY_REV3_RFCTL_OVER0 B43_PHY_N(0x0E7) #define B43_NPHY_TXF_40CO_B32S2 B43_PHY_N(0x0E8) /* TX filter 40 coeff B32 stage 2 */ #define B43_NPHY_TXF_40CO_B1S2 B43_PHY_N(0x0E9) /* TX filter 40 coeff B1 stage 2 */ #define B43_NPHY_BIST_STAT2 B43_PHY_N(0x0EA) /* BIST status 2 */ #define B43_NPHY_BIST_STAT3 B43_PHY_N(0x0EB) /* BIST status 3 */ #define B43_NPHY_RFCTL_OVER B43_PHY_N(0x0EC) /* RF control override */ +#define B43_NPHY_REV3_RFCTL_OVER1 B43_PHY_N(0x0EC) #define B43_NPHY_MIMOCFG B43_PHY_N(0x0ED) /* MIMO config */ #define B43_NPHY_MIMOCFG_GFMIX 0x0004 /* Greenfield or mixed mode */ #define B43_NPHY_MIMOCFG_AUTO 0x0100 /* Greenfield/mixed mode auto */ From c9325e2f2435d93117e9336d72754b68abda26d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Thu, 17 Jul 2014 19:31:04 +0200 Subject: [PATCH 352/378] b43: N-PHY: set band on every channel switch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Seems to be required by some hardware, wl does it every time. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/phy_n.c | 16 ++++++++-------- drivers/net/wireless/b43/phy_n.h | 2 ++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index 2c5583231d2f..ef1acaec7027 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -6058,23 +6058,23 @@ static void b43_nphy_channel_setup(struct b43_wldev *dev, struct b43_phy *phy = &dev->phy; struct b43_phy_n *nphy = dev->phy.n; int ch = new_channel->hw_value; - - u16 old_band_5ghz; u16 tmp16; - old_band_5ghz = - b43_phy_read(dev, B43_NPHY_BANDCTL) & B43_NPHY_BANDCTL_5GHZ; - if (new_channel->band == IEEE80211_BAND_5GHZ && !old_band_5ghz) { + if (new_channel->band == IEEE80211_BAND_5GHZ) { tmp16 = b43_read16(dev, B43_MMIO_PSM_PHY_HDR); b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16 | 4); - b43_phy_set(dev, B43_PHY_B_BBCFG, 0xC000); + /* Put BPHY in the reset */ + b43_phy_set(dev, B43_PHY_B_BBCFG, + B43_PHY_B_BBCFG_RSTCCA | B43_PHY_B_BBCFG_RSTRX); b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16); b43_phy_set(dev, B43_NPHY_BANDCTL, B43_NPHY_BANDCTL_5GHZ); - } else if (new_channel->band == IEEE80211_BAND_2GHZ && old_band_5ghz) { + } else if (new_channel->band == IEEE80211_BAND_2GHZ) { b43_phy_mask(dev, B43_NPHY_BANDCTL, ~B43_NPHY_BANDCTL_5GHZ); tmp16 = b43_read16(dev, B43_MMIO_PSM_PHY_HDR); b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16 | 4); - b43_phy_mask(dev, B43_PHY_B_BBCFG, 0x3FFF); + /* Take BPHY out of the reset */ + b43_phy_mask(dev, B43_PHY_B_BBCFG, + (u16)~(B43_PHY_B_BBCFG_RSTCCA | B43_PHY_B_BBCFG_RSTRX)); b43_write16(dev, B43_MMIO_PSM_PHY_HDR, tmp16); } diff --git a/drivers/net/wireless/b43/phy_n.h b/drivers/net/wireless/b43/phy_n.h index 474bf758295d..30bec815b969 100644 --- a/drivers/net/wireless/b43/phy_n.h +++ b/drivers/net/wireless/b43/phy_n.h @@ -869,6 +869,8 @@ #define B43_NPHY_REV7_RF_CTL_OVER6 B43_PHY_N(0x347) #define B43_PHY_B_BBCFG B43_PHY_N_BMODE(0x001) /* BB config */ +#define B43_PHY_B_BBCFG_RSTCCA 0x4000 /* Reset CCA */ +#define B43_PHY_B_BBCFG_RSTRX 0x8000 /* Reset RX */ #define B43_PHY_B_TEST B43_PHY_N_BMODE(0x00A) struct b43_wldev; From c2cb2c4cf1a089501242a1701b589d2ad5eb0448 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Thu, 17 Jul 2014 19:31:05 +0200 Subject: [PATCH 353/378] b43: use one shared function for setting MAC frequency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By the way add few chipsets that were tracked with "wl" dumps. Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/main.c | 39 ++++++++++++++++++++++++++++++ drivers/net/wireless/b43/main.h | 1 + drivers/net/wireless/b43/phy_lcn.c | 35 +-------------------------- drivers/net/wireless/b43/phy_n.c | 7 +----- include/linux/bcma/bcma.h | 1 + 5 files changed, 43 insertions(+), 40 deletions(-) diff --git a/drivers/net/wireless/b43/main.c b/drivers/net/wireless/b43/main.c index 3dcd3aa38608..3e127be06bfb 100644 --- a/drivers/net/wireless/b43/main.c +++ b/drivers/net/wireless/b43/main.c @@ -2964,6 +2964,45 @@ void b43_mac_phy_clock_set(struct b43_wldev *dev, bool on) } } +/* brcms_b_switch_macfreq */ +void b43_mac_switch_freq(struct b43_wldev *dev, u8 spurmode) +{ + u16 chip_id = dev->dev->chip_id; + + if (chip_id == BCMA_CHIP_ID_BCM43217 || + chip_id == BCMA_CHIP_ID_BCM43222 || + chip_id == BCMA_CHIP_ID_BCM43224 || + chip_id == BCMA_CHIP_ID_BCM43225 || + chip_id == BCMA_CHIP_ID_BCM43227 || + chip_id == BCMA_CHIP_ID_BCM43228) { + switch (spurmode) { + case 2: /* 126 Mhz */ + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x2082); + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); + break; + case 1: /* 123 Mhz */ + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x5341); + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); + break; + default: /* 120 Mhz */ + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x8889); + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); + break; + } + } else if (dev->phy.type == B43_PHYTYPE_LCN) { + switch (spurmode) { + case 1: /* 82 Mhz */ + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x7CE0); + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC); + break; + default: /* 80 Mhz */ + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0xCCCD); + b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC); + break; + } + } +} + static void b43_adjust_opmode(struct b43_wldev *dev) { struct b43_wl *wl = dev->wl; diff --git a/drivers/net/wireless/b43/main.h b/drivers/net/wireless/b43/main.h index f476fc337d64..9f22e4b4c132 100644 --- a/drivers/net/wireless/b43/main.h +++ b/drivers/net/wireless/b43/main.h @@ -99,6 +99,7 @@ void b43_power_saving_ctl_bits(struct b43_wldev *dev, unsigned int ps_flags); void b43_mac_suspend(struct b43_wldev *dev); void b43_mac_enable(struct b43_wldev *dev); void b43_mac_phy_clock_set(struct b43_wldev *dev, bool on); +void b43_mac_switch_freq(struct b43_wldev *dev, u8 spurmode); struct b43_request_fw_context; diff --git a/drivers/net/wireless/b43/phy_lcn.c b/drivers/net/wireless/b43/phy_lcn.c index 0bafa3b17035..e76bbdf3247e 100644 --- a/drivers/net/wireless/b43/phy_lcn.c +++ b/drivers/net/wireless/b43/phy_lcn.c @@ -54,39 +54,6 @@ enum lcn_sense_type { B43_SENSE_VBAT, }; -/* In theory it's PHY common function, move if needed */ -/* brcms_b_switch_macfreq */ -static void b43_phy_switch_macfreq(struct b43_wldev *dev, u8 spurmode) -{ - if (dev->dev->chip_id == 43224 || dev->dev->chip_id == 43225) { - switch (spurmode) { - case 2: /* 126 Mhz */ - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x2082); - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); - break; - case 1: /* 123 Mhz */ - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x5341); - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); - break; - default: /* 120 Mhz */ - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x8889); - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); - break; - } - } else if (dev->phy.type == B43_PHYTYPE_LCN) { - switch (spurmode) { - case 1: /* 82 Mhz */ - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0x7CE0); - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC); - break; - default: /* 80 Mhz */ - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, 0xCCCD); - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0xC); - break; - } - } -} - /************************************************** * Radio 2064. **************************************************/ @@ -609,7 +576,7 @@ static void b43_phy_lcn_txrx_spur_avoidance_mode(struct b43_wldev *dev, b43_phy_write(dev, 0x93b, ((0 << 13) + 23)); b43_phy_write(dev, 0x93c, ((0 << 13) + 1989)); } - b43_phy_switch_macfreq(dev, enable); + b43_mac_switch_freq(dev, enable); } /************************************************** diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index ef1acaec7027..0f0c1306b0ad 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -6113,12 +6113,7 @@ static void b43_nphy_channel_setup(struct b43_wldev *dev, b43_nphy_pmu_spur_avoid(dev, avoid); - if (dev->dev->chip_id == 43222 || dev->dev->chip_id == 43224 || - dev->dev->chip_id == 43225) { - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_LOW, - avoid ? 0x5341 : 0x8889); - b43_write16(dev, B43_MMIO_TSF_CLK_FRAC_HIGH, 0x8); - } + b43_mac_switch_freq(dev, avoid); if (dev->phy.rev == 3 || dev->phy.rev == 4) ; /* TODO: reset PLL */ diff --git a/include/linux/bcma/bcma.h b/include/linux/bcma/bcma.h index 7cb2344741cf..969af0f2bdf9 100644 --- a/include/linux/bcma/bcma.h +++ b/include/linux/bcma/bcma.h @@ -159,6 +159,7 @@ struct bcma_host_ops { #define BCMA_CHIP_ID_BCM4313 0x4313 #define BCMA_CHIP_ID_BCM43142 43142 #define BCMA_CHIP_ID_BCM43217 43217 +#define BCMA_CHIP_ID_BCM43222 43222 #define BCMA_CHIP_ID_BCM43224 43224 #define BCMA_PKG_ID_BCM43224_FAB_CSM 0x8 #define BCMA_PKG_ID_BCM43224_FAB_SMIC 0xa From e2f04f7256b3da8411bb9422ccf2ac17d7c51be8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Thu, 17 Jul 2014 19:31:06 +0200 Subject: [PATCH 354/378] b43: N-PHY: update spur avoidance to support newer devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki Signed-off-by: John W. Linville --- drivers/net/wireless/b43/phy_n.c | 43 +++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/drivers/net/wireless/b43/phy_n.c b/drivers/net/wireless/b43/phy_n.c index 0f0c1306b0ad..92bfe352ba08 100644 --- a/drivers/net/wireless/b43/phy_n.c +++ b/drivers/net/wireless/b43/phy_n.c @@ -6099,26 +6099,45 @@ static void b43_nphy_channel_setup(struct b43_wldev *dev, if (dev->phy.rev >= 3 && dev->phy.n->spur_avoid != B43_SPUR_AVOID_DISABLE) { - bool avoid = false; + u8 spuravoid = 0; + if (dev->phy.n->spur_avoid == B43_SPUR_AVOID_FORCE) { - avoid = true; - } else if (!b43_is_40mhz(dev)) { - if ((ch >= 5 && ch <= 8) || ch == 13 || ch == 14) - avoid = true; - } else { /* 40MHz */ - if (nphy->aband_spurwar_en && - (ch == 38 || ch == 102 || ch == 118)) - avoid = dev->dev->chip_id == 0x4716; + spuravoid = 1; + } else if (phy->rev >= 19) { + /* TODO */ + } else if (phy->rev >= 18) { + /* TODO */ + } else if (phy->rev >= 17) { + /* TODO: Off for channels 1-11, but check 12-14! */ + } else if (phy->rev >= 16) { + /* TODO: Off for 2 GHz, but check 5 GHz! */ + } else if (phy->rev >= 7) { + if (!b43_is_40mhz(dev)) { /* 20MHz */ + if (ch == 13 || ch == 14 || ch == 153) + spuravoid = 1; + } else { /* 40 MHz */ + if (ch == 54) + spuravoid = 1; + } + } else { + if (!b43_is_40mhz(dev)) { /* 20MHz */ + if ((ch >= 5 && ch <= 8) || ch == 13 || ch == 14) + spuravoid = 1; + } else { /* 40MHz */ + if (nphy->aband_spurwar_en && + (ch == 38 || ch == 102 || ch == 118)) + spuravoid = dev->dev->chip_id == 0x4716; + } } - b43_nphy_pmu_spur_avoid(dev, avoid); + b43_nphy_pmu_spur_avoid(dev, spuravoid); - b43_mac_switch_freq(dev, avoid); + b43_mac_switch_freq(dev, spuravoid); if (dev->phy.rev == 3 || dev->phy.rev == 4) ; /* TODO: reset PLL */ - if (avoid) + if (spuravoid) b43_phy_set(dev, B43_NPHY_BBCFG, B43_NPHY_BBCFG_RSTRX); else b43_phy_mask(dev, B43_NPHY_BBCFG, From 9435d09146b3ab31e399e6c6ff2cf225dd0955d8 Mon Sep 17 00:00:00 2001 From: Avinash Patil Date: Thu, 17 Jul 2014 11:48:47 -0700 Subject: [PATCH 355/378] mwifiex: remove needless current_bssid variable This local variable is not used anywhere in function. Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/join.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/drivers/net/wireless/mwifiex/join.c b/drivers/net/wireless/mwifiex/join.c index 0b977ea1cddb..8d6c25908b6d 100644 --- a/drivers/net/wireless/mwifiex/join.c +++ b/drivers/net/wireless/mwifiex/join.c @@ -1288,8 +1288,6 @@ done: int mwifiex_associate(struct mwifiex_private *priv, struct mwifiex_bssdescriptor *bss_desc) { - u8 current_bssid[ETH_ALEN]; - /* Return error if the adapter is not STA role or table entry * is not marked as infra. */ @@ -1304,10 +1302,6 @@ int mwifiex_associate(struct mwifiex_private *priv, else mwifiex_set_ba_params(priv); - memcpy(¤t_bssid, - &priv->curr_bss_params.bss_descriptor.mac_address, - sizeof(current_bssid)); - /* Clear any past association response stored for application retrieval */ priv->assoc_rsp_size = 0; From 71954f24c93fd569314985e9a7319b68e0b918e6 Mon Sep 17 00:00:00 2001 From: Ujjal Roy Date: Thu, 17 Jul 2014 11:49:55 -0700 Subject: [PATCH 356/378] mwifiex: do not re-associate when already connected In managed mode if the driver is getting a re-associate command from cfg80211, driver deauthenticates with the AP internally and sends a disconnected event to cfg80211 before completion of its association process. The disconnected event then modifies the SSID length as wdev->ssid_len = 0. So, upon receiving the connect result event from driver, cfg80211 is unable to get that BSS from the device's BSS list and generates the following WARN_ON message. WARNING: CPU: 0 PID: 857 at net/wireless/sme.c:658 __cfg80211_connect_result+0x3a6/0x3e0 [cfg80211]() Avoid re-association while the device is already associated to a network. Also remove the internal deauthentication from the association path. Signed-off-by: Ujjal Roy Signed-off-by: Avinash Patil Signed-off-by: Bing Zhao Signed-off-by: Amitkumar Karwar Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cfg80211.c | 8 +++++--- drivers/net/wireless/mwifiex/sta_ioctl.c | 10 ---------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/drivers/net/wireless/mwifiex/cfg80211.c b/drivers/net/wireless/mwifiex/cfg80211.c index 6af135fa99f7..a738cc959a47 100644 --- a/drivers/net/wireless/mwifiex/cfg80211.c +++ b/drivers/net/wireless/mwifiex/cfg80211.c @@ -1604,9 +1604,6 @@ mwifiex_cfg80211_assoc(struct mwifiex_private *priv, size_t ssid_len, return -EINVAL; } - /* disconnect before try to associate */ - mwifiex_deauthenticate(priv, NULL); - /* As this is new association, clear locally stored * keys and security related flags */ priv->sec_info.wpa_enabled = false; @@ -1744,6 +1741,11 @@ mwifiex_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev, return -EINVAL; } + if (priv->wdev && priv->wdev->current_bss) { + wiphy_warn(wiphy, "%s: already connected\n", dev->name); + return -EALREADY; + } + wiphy_dbg(wiphy, "info: Trying to associate to %s and bssid %pM\n", (char *) sme->ssid, sme->bssid); diff --git a/drivers/net/wireless/mwifiex/sta_ioctl.c b/drivers/net/wireless/mwifiex/sta_ioctl.c index 1a03d4d8b418..caae9738100a 100644 --- a/drivers/net/wireless/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/mwifiex/sta_ioctl.c @@ -283,10 +283,6 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, priv->bss_mode == NL80211_IFTYPE_P2P_CLIENT) { u8 config_bands; - ret = mwifiex_deauthenticate(priv, NULL); - if (ret) - goto done; - if (!bss_desc) return -1; @@ -345,12 +341,6 @@ int mwifiex_bss_start(struct mwifiex_private *priv, struct cfg80211_bss *bss, goto done; } - /* Exit Adhoc mode first */ - dev_dbg(adapter->dev, "info: Sending Adhoc Stop\n"); - ret = mwifiex_deauthenticate(priv, NULL); - if (ret) - goto done; - priv->adhoc_is_link_sensed = false; ret = mwifiex_check_network_compatibility(priv, bss_desc); From 1d9e954e8b522ae37c7c0fdd791b5736321684a0 Mon Sep 17 00:00:00 2001 From: Bing Zhao Date: Thu, 17 Jul 2014 15:55:09 -0700 Subject: [PATCH 357/378] mwifiex: remove redundant timestamps in debug prints We don't need wall-clock time here, and in most configurations that care, there are already timestamps in the kernel using CONFIG_PRINTK_TIME=y. Reported-by: Paul Stewart Signed-off-by: Bing Zhao Signed-off-by: John W. Linville --- drivers/net/wireless/mwifiex/cmdevt.c | 40 ++++++++------------------- drivers/net/wireless/mwifiex/pcie.c | 14 +++------- drivers/net/wireless/mwifiex/sdio.c | 14 +++------- 3 files changed, 20 insertions(+), 48 deletions(-) diff --git a/drivers/net/wireless/mwifiex/cmdevt.c b/drivers/net/wireless/mwifiex/cmdevt.c index 5899eee87fb1..baf0aab63c04 100644 --- a/drivers/net/wireless/mwifiex/cmdevt.c +++ b/drivers/net/wireless/mwifiex/cmdevt.c @@ -137,7 +137,6 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv, struct host_cmd_ds_command *host_cmd; uint16_t cmd_code; uint16_t cmd_size; - struct timeval tstamp; unsigned long flags; __le32 tmp; @@ -198,10 +197,8 @@ static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv, */ skb_put(cmd_node->cmd_skb, cmd_size - cmd_node->cmd_skb->len); - do_gettimeofday(&tstamp); - dev_dbg(adapter->dev, "cmd: DNLD_CMD: (%lu.%lu): %#x, act %#x, len %d," - " seqno %#x\n", - tstamp.tv_sec, tstamp.tv_usec, cmd_code, + dev_dbg(adapter->dev, + "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n", cmd_code, le16_to_cpu(*(__le16 *) ((u8 *) host_cmd + S_DS_GEN)), cmd_size, le16_to_cpu(host_cmd->seq_num)); @@ -273,7 +270,6 @@ static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter) (struct mwifiex_opt_sleep_confirm *) adapter->sleep_cfm->data; struct sk_buff *sleep_cfm_tmp; - struct timeval ts; __le32 tmp; priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); @@ -284,10 +280,9 @@ static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter) (adapter->seq_num, priv->bss_num, priv->bss_type))); - do_gettimeofday(&ts); dev_dbg(adapter->dev, - "cmd: DNLD_CMD: (%lu.%lu): %#x, act %#x, len %d, seqno %#x\n", - ts.tv_sec, ts.tv_usec, le16_to_cpu(sleep_cfm_buf->command), + "cmd: DNLD_CMD: %#x, act %#x, len %d, seqno %#x\n", + le16_to_cpu(sleep_cfm_buf->command), le16_to_cpu(sleep_cfm_buf->action), le16_to_cpu(sleep_cfm_buf->size), le16_to_cpu(sleep_cfm_buf->seq_num)); @@ -442,7 +437,6 @@ int mwifiex_process_event(struct mwifiex_adapter *adapter) mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); struct sk_buff *skb = adapter->event_skb; u32 eventcause = adapter->event_cause; - struct timeval tstamp; struct mwifiex_rxinfo *rx_info; /* Save the last event to debug log */ @@ -467,9 +461,7 @@ int mwifiex_process_event(struct mwifiex_adapter *adapter) rx_info->bss_type = priv->bss_type; } - do_gettimeofday(&tstamp); - dev_dbg(adapter->dev, "EVENT: %lu.%lu: cause: %#x\n", - tstamp.tv_sec, tstamp.tv_usec, eventcause); + dev_dbg(adapter->dev, "EVENT: cause: %#x\n", eventcause); if (eventcause == EVENT_PS_SLEEP || eventcause == EVENT_PS_AWAKE) { /* Handle PS_SLEEP/AWAKE events on STA */ priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_STA); @@ -781,7 +773,6 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter) uint16_t orig_cmdresp_no; uint16_t cmdresp_no; uint16_t cmdresp_result; - struct timeval tstamp; unsigned long flags; /* Now we got response from FW, cancel the command timer */ @@ -839,11 +830,10 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter) adapter->dbg.last_cmd_resp_id[adapter->dbg.last_cmd_resp_index] = orig_cmdresp_no; - do_gettimeofday(&tstamp); - dev_dbg(adapter->dev, "cmd: CMD_RESP: (%lu.%lu): 0x%x, result %d," - " len %d, seqno 0x%x\n", - tstamp.tv_sec, tstamp.tv_usec, orig_cmdresp_no, cmdresp_result, - le16_to_cpu(resp->size), le16_to_cpu(resp->seq_num)); + dev_dbg(adapter->dev, + "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n", + orig_cmdresp_no, cmdresp_result, + le16_to_cpu(resp->size), le16_to_cpu(resp->seq_num)); if (!(orig_cmdresp_no & HostCmd_RET_BIT)) { dev_err(adapter->dev, "CMD_RESP: invalid cmd resp\n"); @@ -903,7 +893,6 @@ mwifiex_cmd_timeout_func(unsigned long function_context) struct mwifiex_adapter *adapter = (struct mwifiex_adapter *) function_context; struct cmd_ctrl_node *cmd_node; - struct timeval tstamp; adapter->is_cmd_timedout = 1; if (!adapter->curr_cmd) { @@ -916,10 +905,8 @@ mwifiex_cmd_timeout_func(unsigned long function_context) adapter->dbg.last_cmd_id[adapter->dbg.last_cmd_index]; adapter->dbg.timeout_cmd_act = adapter->dbg.last_cmd_act[adapter->dbg.last_cmd_index]; - do_gettimeofday(&tstamp); dev_err(adapter->dev, - "%s: Timeout cmd id (%lu.%lu) = %#x, act = %#x\n", - __func__, tstamp.tv_sec, tstamp.tv_usec, + "%s: Timeout cmd id = %#x, act = %#x\n", __func__, adapter->dbg.timeout_cmd_id, adapter->dbg.timeout_cmd_act); @@ -1237,18 +1224,15 @@ mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *adapter, uint16_t result = le16_to_cpu(cmd->result); uint16_t command = le16_to_cpu(cmd->command); uint16_t seq_num = le16_to_cpu(cmd->seq_num); - struct timeval ts; if (!upld_len) { dev_err(adapter->dev, "%s: cmd size is 0\n", __func__); return; } - do_gettimeofday(&ts); dev_dbg(adapter->dev, - "cmd: CMD_RESP: (%lu.%lu): 0x%x, result %d, len %d, seqno 0x%x\n", - ts.tv_sec, ts.tv_usec, command, result, le16_to_cpu(cmd->size), - seq_num); + "cmd: CMD_RESP: 0x%x, result %d, len %d, seqno 0x%x\n", + command, result, le16_to_cpu(cmd->size), seq_num); /* Get BSS number and corresponding priv */ priv = mwifiex_get_priv_by_id(adapter, HostCmd_GET_BSS_NO(seq_num), diff --git a/drivers/net/wireless/mwifiex/pcie.c b/drivers/net/wireless/mwifiex/pcie.c index 5f7afffdd34e..c16dd2cc8198 100644 --- a/drivers/net/wireless/mwifiex/pcie.c +++ b/drivers/net/wireless/mwifiex/pcie.c @@ -2238,7 +2238,6 @@ static void mwifiex_pcie_fw_dump_work(struct mwifiex_adapter *adapter) struct pcie_service_card *card = adapter->card; const struct mwifiex_pcie_card_reg *creg = card->pcie.reg; unsigned int reg, reg_start, reg_end; - struct timeval t; u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0; enum rdwr_status stat; u32 memory_size; @@ -2257,9 +2256,7 @@ static void mwifiex_pcie_fw_dump_work(struct mwifiex_adapter *adapter) entry->mem_size = 0; } - do_gettimeofday(&t); - dev_info(adapter->dev, "== mwifiex firmware dump start: %u.%06u ==\n", - (u32)t.tv_sec, (u32)t.tv_usec); + dev_info(adapter->dev, "== mwifiex firmware dump start ==\n"); /* Read the number of the memories which will dump */ stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); @@ -2303,9 +2300,8 @@ static void mwifiex_pcie_fw_dump_work(struct mwifiex_adapter *adapter) end_ptr = dbg_ptr + memory_size; doneflag = entry->done_flag; - do_gettimeofday(&t); - dev_info(adapter->dev, "Start %s output %u.%06u, please wait...\n", - entry->mem_name, (u32)t.tv_sec, (u32)t.tv_usec); + dev_info(adapter->dev, "Start %s output, please wait...\n", + entry->mem_name); do { stat = mwifiex_pcie_rdwr_firmware(adapter, doneflag); @@ -2331,9 +2327,7 @@ static void mwifiex_pcie_fw_dump_work(struct mwifiex_adapter *adapter) break; } while (true); } - do_gettimeofday(&t); - dev_info(adapter->dev, "== mwifiex firmware dump end: %u.%06u ==\n", - (u32)t.tv_sec, (u32)t.tv_usec); + dev_info(adapter->dev, "== mwifiex firmware dump end ==\n"); kobject_uevent_env(&adapter->wiphy->dev.kobj, KOBJ_CHANGE, env); diff --git a/drivers/net/wireless/mwifiex/sdio.c b/drivers/net/wireless/mwifiex/sdio.c index 1da04a086bd9..3e48ef5ca53c 100644 --- a/drivers/net/wireless/mwifiex/sdio.c +++ b/drivers/net/wireless/mwifiex/sdio.c @@ -2012,7 +2012,6 @@ static void mwifiex_sdio_fw_dump_work(struct work_struct *work) int ret = 0; unsigned int reg, reg_start, reg_end; u8 *dbg_ptr, *end_ptr, dump_num, idx, i, read_reg, doneflag = 0; - struct timeval t; enum rdwr_status stat; u32 memory_size; static char *env[] = { "DRIVER=mwifiex_sdio", "EVENT=fw_dump", NULL }; @@ -2033,9 +2032,7 @@ static void mwifiex_sdio_fw_dump_work(struct work_struct *work) mwifiex_pm_wakeup_card(adapter); sdio_claim_host(card->func); - do_gettimeofday(&t); - dev_info(adapter->dev, "== mwifiex firmware dump start: %u.%06u ==\n", - (u32)t.tv_sec, (u32)t.tv_usec); + dev_info(adapter->dev, "== mwifiex firmware dump start ==\n"); stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag); if (stat == RDWR_STATUS_FAILURE) @@ -2087,9 +2084,8 @@ static void mwifiex_sdio_fw_dump_work(struct work_struct *work) end_ptr = dbg_ptr + memory_size; doneflag = entry->done_flag; - do_gettimeofday(&t); - dev_info(adapter->dev, "Start %s output %u.%06u, please wait...\n", - entry->mem_name, (u32)t.tv_sec, (u32)t.tv_usec); + dev_info(adapter->dev, "Start %s output, please wait...\n", + entry->mem_name); do { stat = mwifiex_sdio_rdwr_firmware(adapter, doneflag); @@ -2120,9 +2116,7 @@ static void mwifiex_sdio_fw_dump_work(struct work_struct *work) break; } while (1); } - do_gettimeofday(&t); - dev_info(adapter->dev, "== mwifiex firmware dump end: %u.%06u ==\n", - (u32)t.tv_sec, (u32)t.tv_usec); + dev_info(adapter->dev, "== mwifiex firmware dump end ==\n"); kobject_uevent_env(&adapter->wiphy->dev.kobj, KOBJ_CHANGE, env); From 626911cc60d873b38f7ca4c5c537fcb918c658d7 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 17 Jul 2014 17:14:17 +0300 Subject: [PATCH 358/378] mac80211: track TDLS initiator internally Infer the TDLS initiator and track it in mac80211 via a STA flag. This avoids breaking old userspace that doesn't pass it via nl80211 APIs. The only case where userspace will need to pass the initiator is when the STA is removed due to unreachability before a teardown packet is sent. Support for unreachability was only recently added to wpa_supplicant, so it won't be a problem in practice. Signed-off-by: Arik Nemtsov Reviewed-by: Johannes Berg Signed-off-by: Johannes Berg --- net/mac80211/sta_info.h | 3 +++ net/mac80211/tdls.c | 35 ++++++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index 2a04361b2162..e37f00969526 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -47,6 +47,8 @@ * @WLAN_STA_TDLS_PEER: Station is a TDLS peer. * @WLAN_STA_TDLS_PEER_AUTH: This TDLS peer is authorized to send direct * packets. This means the link is enabled. + * @WLAN_STA_TDLS_INITIATOR: We are the initiator of the TDLS link with this + * station. * @WLAN_STA_UAPSD: Station requested unscheduled SP while driver was * keeping station in power-save mode, reply when the driver * unblocks the station. @@ -76,6 +78,7 @@ enum ieee80211_sta_info_flags { WLAN_STA_PSPOLL, WLAN_STA_TDLS_PEER, WLAN_STA_TDLS_PEER_AUTH, + WLAN_STA_TDLS_INITIATOR, WLAN_STA_UAPSD, WLAN_STA_SP, WLAN_STA_4ADDR_EVENT, diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index f7185338a0fa..b01b3104b445 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -203,6 +203,7 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, struct sk_buff *skb = NULL; bool send_direct; const u8 *init_addr, *rsp_addr; + struct sta_info *sta; int ret; skb = dev_alloc_skb(local->hw.extra_tx_headroom + @@ -245,32 +246,40 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, if (extra_ies_len) memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); - /* sanity check for initiator */ + rcu_read_lock(); + sta = sta_info_get(sdata, peer); + + /* infer the initiator if we can, to support old userspace */ switch (action_code) { case WLAN_TDLS_SETUP_REQUEST: + if (sta) + set_sta_flag(sta, WLAN_STA_TDLS_INITIATOR); + /* fall-through */ case WLAN_TDLS_SETUP_CONFIRM: case WLAN_TDLS_DISCOVERY_REQUEST: - if (!initiator) { - ret = -EINVAL; - goto fail; - } + initiator = true; break; case WLAN_TDLS_SETUP_RESPONSE: + /* + * In some testing scenarios, we send a request and response. + * Make the last packet sent take effect for the initiator + * value. + */ + if (sta) + clear_sta_flag(sta, WLAN_STA_TDLS_INITIATOR); + /* fall-through */ case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: - if (initiator) { - ret = -EINVAL; - goto fail; - } + initiator = false; break; case WLAN_TDLS_TEARDOWN: /* any value is ok */ break; default: ret = -ENOTSUPP; - goto fail; + break; } - if (initiator) { + if (initiator || (sta && test_sta_flag(sta, WLAN_STA_TDLS_INITIATOR))) { init_addr = sdata->vif.addr; rsp_addr = peer; } else { @@ -278,6 +287,10 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, rsp_addr = sdata->vif.addr; } + rcu_read_unlock(); + if (ret < 0) + goto fail; + ieee80211_tdls_add_link_ie(skb, init_addr, rsp_addr, sdata->u.mgd.bssid); From 6ae32e5d284a5db589bfa63561932ad3306f538a Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 17 Jul 2014 17:14:18 +0300 Subject: [PATCH 359/378] mac80211: fix error path for TDLS setup The patch "8f02e6b mac80211: make sure TDLS peer STA exists during setup" broke TDLS error paths where the STA doesn't exist when sending the error. Fix it by only testing for STA existence during a non-error flow. Signed-off-by: Arik Nemtsov Reviewed-by: Johannes Berg Signed-off-by: Johannes Berg --- net/mac80211/tdls.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index b01b3104b445..53c235de5d1c 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -349,15 +349,19 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev, /* * make sure we have a STA representing the peer so we drop or buffer * non-TDLS-setup frames to the peer. We can't send other packets - * during setup through the AP path + * during setup through the AP path. + * Allow error packets to be sent - sometimes we don't even add a STA + * before failing the setup. */ - rcu_read_lock(); - if (!sta_info_get(sdata, peer)) { + if (status_code == 0) { + rcu_read_lock(); + if (!sta_info_get(sdata, peer)) { + rcu_read_unlock(); + ret = -ENOLINK; + goto exit; + } rcu_read_unlock(); - ret = -ENOLINK; - goto exit; } - rcu_read_unlock(); ieee80211_flush_queues(local, sdata); From 46792a2dfcc7e000e6927088fbf06a135aaaa3eb Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 17 Jul 2014 17:14:19 +0300 Subject: [PATCH 360/378] mac80211: consolidate TDLS IE treatment Add all information elements for TDLS discovery and setup in the same function. Signed-off-by: Arik Nemtsov Reviewed-by: Liad Kaufman Reviewed-by: Johannes Berg Signed-off-by: Johannes Berg --- net/mac80211/tdls.c | 69 +++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 28 deletions(-) diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 53c235de5d1c..b61448acc438 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -78,13 +78,49 @@ static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr, memcpy(lnkid->resp_sta, peer, ETH_ALEN); } +static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, const u8 *peer, + u8 action_code, bool initiator, + const u8 *extra_ies, size_t extra_ies_len) +{ + const u8 *init_addr, *rsp_addr; + enum ieee80211_band band = ieee80211_get_sdata_band(sdata); + + switch (action_code) { + case WLAN_TDLS_SETUP_REQUEST: + case WLAN_TDLS_SETUP_RESPONSE: + case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: + ieee80211_add_srates_ie(sdata, skb, false, band); + ieee80211_add_ext_srates_ie(sdata, skb, false, band); + ieee80211_tdls_add_ext_capab(skb); + break; + case WLAN_TDLS_SETUP_CONFIRM: + case WLAN_TDLS_TEARDOWN: + case WLAN_TDLS_DISCOVERY_REQUEST: + break; + } + + if (initiator) { + init_addr = sdata->vif.addr; + rsp_addr = peer; + } else { + init_addr = peer; + rsp_addr = sdata->vif.addr; + } + + if (extra_ies_len) + memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); + + ieee80211_tdls_add_link_ie(skb, init_addr, rsp_addr, + sdata->u.mgd.bssid); +} + static int ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code, struct sk_buff *skb) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - enum ieee80211_band band = ieee80211_get_sdata_band(sdata); struct ieee80211_tdls_data *tf; tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u)); @@ -103,10 +139,6 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, tf->u.setup_req.dialog_token = dialog_token; tf->u.setup_req.capability = cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); - - ieee80211_add_srates_ie(sdata, skb, false, band); - ieee80211_add_ext_srates_ie(sdata, skb, false, band); - ieee80211_tdls_add_ext_capab(skb); break; case WLAN_TDLS_SETUP_RESPONSE: tf->category = WLAN_CATEGORY_TDLS; @@ -117,10 +149,6 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, tf->u.setup_resp.dialog_token = dialog_token; tf->u.setup_resp.capability = cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); - - ieee80211_add_srates_ie(sdata, skb, false, band); - ieee80211_add_ext_srates_ie(sdata, skb, false, band); - ieee80211_tdls_add_ext_capab(skb); break; case WLAN_TDLS_SETUP_CONFIRM: tf->category = WLAN_CATEGORY_TDLS; @@ -157,7 +185,6 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, u16 status_code, struct sk_buff *skb) { struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); - enum ieee80211_band band = ieee80211_get_sdata_band(sdata); struct ieee80211_mgmt *mgmt; mgmt = (void *)skb_put(skb, 24); @@ -179,10 +206,6 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, dialog_token; mgmt->u.action.u.tdls_discover_resp.capability = cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); - - ieee80211_add_srates_ie(sdata, skb, false, band); - ieee80211_add_ext_srates_ie(sdata, skb, false, band); - ieee80211_tdls_add_ext_capab(skb); break; default: return -EINVAL; @@ -202,7 +225,6 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, struct ieee80211_local *local = sdata->local; struct sk_buff *skb = NULL; bool send_direct; - const u8 *init_addr, *rsp_addr; struct sta_info *sta; int ret; @@ -243,9 +265,6 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, if (ret < 0) goto fail; - if (extra_ies_len) - memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); - rcu_read_lock(); sta = sta_info_get(sdata, peer); @@ -279,21 +298,15 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, break; } - if (initiator || (sta && test_sta_flag(sta, WLAN_STA_TDLS_INITIATOR))) { - init_addr = sdata->vif.addr; - rsp_addr = peer; - } else { - init_addr = peer; - rsp_addr = sdata->vif.addr; - } + if (sta && test_sta_flag(sta, WLAN_STA_TDLS_INITIATOR)) + initiator = true; rcu_read_unlock(); if (ret < 0) goto fail; - ieee80211_tdls_add_link_ie(skb, init_addr, rsp_addr, - sdata->u.mgd.bssid); - + ieee80211_tdls_add_ies(sdata, skb, peer, action_code, initiator, + extra_ies, extra_ies_len); if (send_direct) { ieee80211_tx_skb(sdata, skb); return 0; From f09a87d274942bf619f5081ac6e9e9441f3eabc4 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 17 Jul 2014 17:14:20 +0300 Subject: [PATCH 361/378] mac80211: split extra TDLS IEs in setup frames When building TDLS setup frames, use the IE order mandates in the specification, splitting extra IEs coming from usermode. Signed-off-by: Arik Nemtsov Reviewed-by: Johannes Berg Signed-off-by: Johannes Berg --- net/mac80211/tdls.c | 77 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 70 insertions(+), 7 deletions(-) diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index b61448acc438..8d6c9285752f 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -78,25 +78,91 @@ static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr, memcpy(lnkid->resp_sta, peer, ETH_ALEN); } +static void +ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, const u8 *peer, + u8 action_code, const u8 *extra_ies, + size_t extra_ies_len) +{ + enum ieee80211_band band = ieee80211_get_sdata_band(sdata); + size_t offset = 0, noffset; + u8 *pos; + + ieee80211_add_srates_ie(sdata, skb, false, band); + ieee80211_add_ext_srates_ie(sdata, skb, false, band); + + /* add any custom IEs that go before Extended Capabilities */ + if (extra_ies_len) { + static const u8 before_ext_cap[] = { + WLAN_EID_SUPP_RATES, + WLAN_EID_COUNTRY, + WLAN_EID_EXT_SUPP_RATES, + WLAN_EID_SUPPORTED_CHANNELS, + WLAN_EID_RSN, + }; + noffset = ieee80211_ie_split(extra_ies, extra_ies_len, + before_ext_cap, + ARRAY_SIZE(before_ext_cap), + offset); + pos = skb_put(skb, noffset - offset); + memcpy(pos, extra_ies + offset, noffset - offset); + offset = noffset; + } + + ieee80211_tdls_add_ext_capab(skb); + + /* add any custom IEs that go before HT capabilities */ + if (extra_ies_len) { + static const u8 before_ht_cap[] = { + WLAN_EID_SUPP_RATES, + WLAN_EID_COUNTRY, + WLAN_EID_EXT_SUPP_RATES, + WLAN_EID_SUPPORTED_CHANNELS, + WLAN_EID_RSN, + WLAN_EID_EXT_CAPABILITY, + WLAN_EID_QOS_CAPA, + WLAN_EID_FAST_BSS_TRANSITION, + WLAN_EID_TIMEOUT_INTERVAL, + WLAN_EID_SUPPORTED_REGULATORY_CLASSES, + }; + noffset = ieee80211_ie_split(extra_ies, extra_ies_len, + before_ht_cap, + ARRAY_SIZE(before_ht_cap), + offset); + pos = skb_put(skb, noffset - offset); + memcpy(pos, extra_ies + offset, noffset - offset); + offset = noffset; + } + + /* add any remaining IEs */ + if (extra_ies_len) { + noffset = extra_ies_len; + pos = skb_put(skb, noffset - offset); + memcpy(pos, extra_ies + offset, noffset - offset); + } +} + static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, const u8 *peer, u8 action_code, bool initiator, const u8 *extra_ies, size_t extra_ies_len) { const u8 *init_addr, *rsp_addr; - enum ieee80211_band band = ieee80211_get_sdata_band(sdata); switch (action_code) { case WLAN_TDLS_SETUP_REQUEST: case WLAN_TDLS_SETUP_RESPONSE: case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: - ieee80211_add_srates_ie(sdata, skb, false, band); - ieee80211_add_ext_srates_ie(sdata, skb, false, band); - ieee80211_tdls_add_ext_capab(skb); + ieee80211_tdls_add_setup_start_ies(sdata, skb, peer, + action_code, extra_ies, + extra_ies_len); break; case WLAN_TDLS_SETUP_CONFIRM: case WLAN_TDLS_TEARDOWN: case WLAN_TDLS_DISCOVERY_REQUEST: + if (extra_ies_len) + memcpy(skb_put(skb, extra_ies_len), extra_ies, + extra_ies_len); break; } @@ -108,9 +174,6 @@ static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata, rsp_addr = sdata->vif.addr; } - if (extra_ies_len) - memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); - ieee80211_tdls_add_link_ie(skb, init_addr, rsp_addr, sdata->u.mgd.bssid); } From 1606ef4a9d294dde98a2185e3645468b08925a6f Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 17 Jul 2014 17:14:21 +0300 Subject: [PATCH 362/378] mac80211: avoid adding some IEs on TDLS setup failure packets Most setup-specific information elements are not to be added when a setup frame is sent with an error status code. Signed-off-by: Arik Nemtsov Reviewed-by: Liad Kaufman Reviewed-by: Johannes Berg Signed-off-by: Johannes Berg --- net/mac80211/tdls.c | 58 +++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 26 deletions(-) diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 8d6c9285752f..99d5ed3aa474 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -63,26 +63,36 @@ static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata) return capab; } -static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, const u8 *src_addr, - const u8 *peer, const u8 *bssid) +static void ieee80211_tdls_add_link_ie(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, const u8 *peer, + bool initiator) { struct ieee80211_tdls_lnkie *lnkid; + const u8 *init_addr, *rsp_addr; + + if (initiator) { + init_addr = sdata->vif.addr; + rsp_addr = peer; + } else { + init_addr = peer; + rsp_addr = sdata->vif.addr; + } lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie)); lnkid->ie_type = WLAN_EID_LINK_ID; lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2; - memcpy(lnkid->bssid, bssid, ETH_ALEN); - memcpy(lnkid->init_sta, src_addr, ETH_ALEN); - memcpy(lnkid->resp_sta, peer, ETH_ALEN); + memcpy(lnkid->bssid, sdata->u.mgd.bssid, ETH_ALEN); + memcpy(lnkid->init_sta, init_addr, ETH_ALEN); + memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN); } static void ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, const u8 *peer, - u8 action_code, const u8 *extra_ies, - size_t extra_ies_len) + u8 action_code, bool initiator, + const u8 *extra_ies, size_t extra_ies_len) { enum ieee80211_band band = ieee80211_get_sdata_band(sdata); size_t offset = 0, noffset; @@ -140,22 +150,26 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, pos = skb_put(skb, noffset - offset); memcpy(pos, extra_ies + offset, noffset - offset); } + + ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); } static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, const u8 *peer, - u8 action_code, bool initiator, - const u8 *extra_ies, size_t extra_ies_len) + u8 action_code, u16 status_code, + bool initiator, const u8 *extra_ies, + size_t extra_ies_len) { - const u8 *init_addr, *rsp_addr; - switch (action_code) { case WLAN_TDLS_SETUP_REQUEST: case WLAN_TDLS_SETUP_RESPONSE: case WLAN_PUB_ACTION_TDLS_DISCOVER_RES: - ieee80211_tdls_add_setup_start_ies(sdata, skb, peer, - action_code, extra_ies, - extra_ies_len); + if (status_code == 0) + ieee80211_tdls_add_setup_start_ies(sdata, skb, peer, + action_code, + initiator, + extra_ies, + extra_ies_len); break; case WLAN_TDLS_SETUP_CONFIRM: case WLAN_TDLS_TEARDOWN: @@ -163,19 +177,11 @@ static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata, if (extra_ies_len) memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len); + if (status_code == 0 || action_code == WLAN_TDLS_TEARDOWN) + ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); break; } - if (initiator) { - init_addr = sdata->vif.addr; - rsp_addr = peer; - } else { - init_addr = peer; - rsp_addr = sdata->vif.addr; - } - - ieee80211_tdls_add_link_ie(skb, init_addr, rsp_addr, - sdata->u.mgd.bssid); } static int @@ -368,8 +374,8 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, if (ret < 0) goto fail; - ieee80211_tdls_add_ies(sdata, skb, peer, action_code, initiator, - extra_ies, extra_ies_len); + ieee80211_tdls_add_ies(sdata, skb, peer, action_code, status_code, + initiator, extra_ies, extra_ies_len); if (send_direct) { ieee80211_tx_skb(sdata, skb); return 0; From dd8c0b03d35be7effe20c9e5fda7e231e2c88e19 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 17 Jul 2014 17:14:22 +0300 Subject: [PATCH 363/378] mac80211: set TDLS capab to zero on failure frames When sending setup-failure frames, set the capability field to zero, as mandated by the specification (IEEE802.11-2012 8.5.13). Signed-off-by: Arik Nemtsov Reviewed-by: Liad Kaufman Reviewed-by: Johannes Berg Signed-off-by: Johannes Berg --- net/mac80211/tdls.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 99d5ed3aa474..398a41302972 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -46,11 +46,16 @@ static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb) *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED; } -static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata) +static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata, + u16 status_code) { struct ieee80211_local *local = sdata->local; u16 capab; + /* The capability will be 0 when sending a failure code */ + if (status_code != 0) + return 0; + capab = 0; if (ieee80211_get_sdata_band(sdata) != IEEE80211_BAND_2GHZ) return capab; @@ -207,7 +212,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, skb_put(skb, sizeof(tf->u.setup_req)); tf->u.setup_req.dialog_token = dialog_token; tf->u.setup_req.capability = - cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); + cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata, + status_code)); break; case WLAN_TDLS_SETUP_RESPONSE: tf->category = WLAN_CATEGORY_TDLS; @@ -217,7 +223,8 @@ ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev, tf->u.setup_resp.status_code = cpu_to_le16(status_code); tf->u.setup_resp.dialog_token = dialog_token; tf->u.setup_resp.capability = - cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); + cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata, + status_code)); break; case WLAN_TDLS_SETUP_CONFIRM: tf->category = WLAN_CATEGORY_TDLS; @@ -274,7 +281,8 @@ ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev, mgmt->u.action.u.tdls_discover_resp.dialog_token = dialog_token; mgmt->u.action.u.tdls_discover_resp.capability = - cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata)); + cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata, + status_code)); break; default: return -EINVAL; From 40b861a0eeb06bbfa472b456482ebf89b6886926 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 17 Jul 2014 17:14:23 +0300 Subject: [PATCH 364/378] mac80211: add QoS IE during TDLS setup start If QoS is supported by the card, add an appropriate IE to TDLS setup- request and setup-response frames. Consolidate the setting of the WMM info IE across mac80211. Signed-off-by: Arik Nemtsov Reviewed-by: Liad Kaufman Reviewed-by: Johannes Berg Signed-off-by: Johannes Berg --- net/mac80211/ibss.c | 13 ++----------- net/mac80211/ieee80211_i.h | 1 + net/mac80211/mlme.c | 11 +---------- net/mac80211/tdls.c | 7 +++++++ net/mac80211/util.c | 15 +++++++++++++++ 5 files changed, 26 insertions(+), 21 deletions(-) diff --git a/net/mac80211/ibss.c b/net/mac80211/ibss.c index 713485f9effc..9713dc54ea4b 100644 --- a/net/mac80211/ibss.c +++ b/net/mac80211/ibss.c @@ -189,17 +189,8 @@ ieee80211_ibss_build_presp(struct ieee80211_sub_if_data *sdata, chandef, 0); } - if (local->hw.queues >= IEEE80211_NUM_ACS) { - *pos++ = WLAN_EID_VENDOR_SPECIFIC; - *pos++ = 7; /* len */ - *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */ - *pos++ = 0x50; - *pos++ = 0xf2; - *pos++ = 2; /* WME */ - *pos++ = 0; /* WME info */ - *pos++ = 1; /* WME ver */ - *pos++ = 0; /* U-APSD no in use */ - } + if (local->hw.queues >= IEEE80211_NUM_ACS) + pos = ieee80211_add_wmm_info_ie(pos, 0); /* U-APSD not in use */ presp->head_len = pos - presp->head; if (WARN_ON(presp->head_len > frame_len)) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 9e025e1184cc..cb874760e99f 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1824,6 +1824,7 @@ int ieee80211_add_srates_ie(struct ieee80211_sub_if_data *sdata, int ieee80211_add_ext_srates_ie(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, bool need_basic, enum ieee80211_band band); +u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo); /* channel management */ void ieee80211_ht_oper_to_chandef(struct ieee80211_channel *control_chan, diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 931330bbe00c..d863ff8b6e41 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -830,16 +830,7 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata) qos_info = 0; } - pos = skb_put(skb, 9); - *pos++ = WLAN_EID_VENDOR_SPECIFIC; - *pos++ = 7; /* len */ - *pos++ = 0x00; /* Microsoft OUI 00:50:F2 */ - *pos++ = 0x50; - *pos++ = 0xf2; - *pos++ = 2; /* WME */ - *pos++ = 0; /* WME info */ - *pos++ = 1; /* WME ver */ - *pos++ = qos_info; + pos = ieee80211_add_wmm_info_ie(skb_put(skb, 9), qos_info); } /* add any remaining custom (i.e. vendor specific here) IEs */ diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 398a41302972..bfd8fc4a6b2f 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -100,6 +100,7 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, const u8 *extra_ies, size_t extra_ies_len) { enum ieee80211_band band = ieee80211_get_sdata_band(sdata); + struct ieee80211_local *local = sdata->local; size_t offset = 0, noffset; u8 *pos; @@ -126,6 +127,11 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, ieee80211_tdls_add_ext_capab(skb); + /* add the QoS element if we support it */ + if (local->hw.queues >= IEEE80211_NUM_ACS && + action_code != WLAN_PUB_ACTION_TDLS_DISCOVER_RES) + ieee80211_add_wmm_info_ie(skb_put(skb, 9), 0); /* no U-APSD */ + /* add any custom IEs that go before HT capabilities */ if (extra_ies_len) { static const u8 before_ht_cap[] = { @@ -310,6 +316,7 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, sizeof(struct ieee80211_tdls_data)) + 50 + /* supported rates */ 7 + /* ext capab */ + 26 + /* max(WMM-info, WMM-param) */ extra_ies_len + sizeof(struct ieee80211_tdls_lnkie)); if (!skb) diff --git a/net/mac80211/util.c b/net/mac80211/util.c index ea79668c2e5f..08ce77664082 100644 --- a/net/mac80211/util.c +++ b/net/mac80211/util.c @@ -3082,3 +3082,18 @@ int ieee80211_max_num_channels(struct ieee80211_local *local) return max_num_different_channels; } + +u8 *ieee80211_add_wmm_info_ie(u8 *buf, u8 qosinfo) +{ + *buf++ = WLAN_EID_VENDOR_SPECIFIC; + *buf++ = 7; /* len */ + *buf++ = 0x00; /* Microsoft OUI 00:50:F2 */ + *buf++ = 0x50; + *buf++ = 0xf2; + *buf++ = 2; /* WME */ + *buf++ = 0; /* WME info */ + *buf++ = 1; /* WME ver */ + *buf++ = qosinfo; /* U-APSD no in use */ + + return buf; +} From 6f7eaa47e1de30159277f91f1145a6687f13ffd9 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 17 Jul 2014 17:14:24 +0300 Subject: [PATCH 365/378] mac80211: add TDLS QoS param IE on setup-confirm When TDLS QoS is supported by the the peer and the local card, add the WMM parameter IE to the setup-confirm frame. Take the QoS settings from the current AP, or if unsupported, use the default values from the specification. This behavior is mandated by IEEE802.11-2012 section 10.22.4. Signed-off-by: Arik Nemtsov Reviewed-by: Liad Kaufman Reviewed-by: Johannes Berg Signed-off-by: Johannes Berg --- include/linux/ieee80211.h | 20 ++++++ net/mac80211/tdls.c | 124 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 144 insertions(+) diff --git a/include/linux/ieee80211.h b/include/linux/ieee80211.h index 75d17e15da33..63ab3873c5ed 100644 --- a/include/linux/ieee80211.h +++ b/include/linux/ieee80211.h @@ -1001,6 +1001,26 @@ struct ieee80211_vendor_ie { u8 oui_type; } __packed; +struct ieee80211_wmm_ac_param { + u8 aci_aifsn; /* AIFSN, ACM, ACI */ + u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */ + __le16 txop_limit; +} __packed; + +struct ieee80211_wmm_param_ie { + u8 element_id; /* Element ID: 221 (0xdd); */ + u8 len; /* Length: 24 */ + /* required fields for WMM version 1 */ + u8 oui[3]; /* 00:50:f2 */ + u8 oui_type; /* 2 */ + u8 oui_subtype; /* 1 */ + u8 version; /* 1 for WMM version 1.0 */ + u8 qos_info; /* AP/STA specific QoS info */ + u8 reserved; /* 0 */ + /* AC_BE, AC_BK, AC_VI, AC_VO */ + struct ieee80211_wmm_ac_param ac[4]; +} __packed; + /* Control frames */ struct ieee80211_rts { __le16 frame_control; diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index bfd8fc4a6b2f..72eebea7e60a 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -8,6 +8,7 @@ */ #include +#include #include #include "ieee80211_i.h" #include "driver-ops.h" @@ -93,6 +94,74 @@ static void ieee80211_tdls_add_link_ie(struct ieee80211_sub_if_data *sdata, memcpy(lnkid->resp_sta, rsp_addr, ETH_ALEN); } +/* translate numbering in the WMM parameter IE to the mac80211 notation */ +static enum ieee80211_ac_numbers ieee80211_ac_from_wmm(int ac) +{ + switch (ac) { + default: + WARN_ON_ONCE(1); + case 0: + return IEEE80211_AC_BE; + case 1: + return IEEE80211_AC_BK; + case 2: + return IEEE80211_AC_VI; + case 3: + return IEEE80211_AC_VO; + } +} + +static u8 ieee80211_wmm_aci_aifsn(int aifsn, bool acm, int aci) +{ + u8 ret; + + ret = aifsn & 0x0f; + if (acm) + ret |= 0x10; + ret |= (aci << 5) & 0x60; + return ret; +} + +static u8 ieee80211_wmm_ecw(u16 cw_min, u16 cw_max) +{ + return ((ilog2(cw_min + 1) << 0x0) & 0x0f) | + ((ilog2(cw_max + 1) << 0x4) & 0xf0); +} + +static void ieee80211_tdls_add_wmm_param_ie(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) +{ + struct ieee80211_wmm_param_ie *wmm; + struct ieee80211_tx_queue_params *txq; + int i; + + wmm = (void *)skb_put(skb, sizeof(*wmm)); + memset(wmm, 0, sizeof(*wmm)); + + wmm->element_id = WLAN_EID_VENDOR_SPECIFIC; + wmm->len = sizeof(*wmm) - 2; + + wmm->oui[0] = 0x00; /* Microsoft OUI 00:50:F2 */ + wmm->oui[1] = 0x50; + wmm->oui[2] = 0xf2; + wmm->oui_type = 2; /* WME */ + wmm->oui_subtype = 1; /* WME param */ + wmm->version = 1; /* WME ver */ + wmm->qos_info = 0; /* U-APSD not in use */ + + /* + * Use the EDCA parameters defined for the BSS, or default if the AP + * doesn't support it, as mandated by 802.11-2012 section 10.22.4 + */ + for (i = 0; i < IEEE80211_NUM_ACS; i++) { + txq = &sdata->tx_conf[ieee80211_ac_from_wmm(i)]; + wmm->ac[i].aci_aifsn = ieee80211_wmm_aci_aifsn(txq->aifs, + txq->acm, i); + wmm->ac[i].cw = ieee80211_wmm_ecw(txq->cw_min, txq->cw_max); + wmm->ac[i].txop_limit = cpu_to_le16(txq->txop); + } +} + static void ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, const u8 *peer, @@ -165,6 +234,56 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); } +static void +ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb, const u8 *peer, + bool initiator, const u8 *extra_ies, + size_t extra_ies_len) +{ + struct ieee80211_local *local = sdata->local; + size_t offset = 0, noffset; + struct sta_info *sta; + u8 *pos; + + rcu_read_lock(); + + sta = sta_info_get(sdata, peer); + if (WARN_ON_ONCE(!sta)) { + rcu_read_unlock(); + return; + } + + /* add any custom IEs that go before the QoS IE */ + if (extra_ies_len) { + static const u8 before_qos[] = { + WLAN_EID_RSN, + }; + noffset = ieee80211_ie_split(extra_ies, extra_ies_len, + before_qos, + ARRAY_SIZE(before_qos), + offset); + pos = skb_put(skb, noffset - offset); + memcpy(pos, extra_ies + offset, noffset - offset); + offset = noffset; + } + + /* add the QoS param IE if both the peer and we support it */ + if (local->hw.queues >= IEEE80211_NUM_ACS && + test_sta_flag(sta, WLAN_STA_WME)) + ieee80211_tdls_add_wmm_param_ie(sdata, skb); + + /* add any remaining IEs */ + if (extra_ies_len) { + noffset = extra_ies_len; + pos = skb_put(skb, noffset - offset); + memcpy(pos, extra_ies + offset, noffset - offset); + } + + ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); + + rcu_read_unlock(); +} + static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata, struct sk_buff *skb, const u8 *peer, u8 action_code, u16 status_code, @@ -183,6 +302,11 @@ static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata, extra_ies_len); break; case WLAN_TDLS_SETUP_CONFIRM: + if (status_code == 0) + ieee80211_tdls_add_setup_cfm_ies(sdata, skb, peer, + initiator, extra_ies, + extra_ies_len); + break; case WLAN_TDLS_TEARDOWN: case WLAN_TDLS_DISCOVERY_REQUEST: if (extra_ies_len) From 81dd2b8822410e56048b927be779d95a2b6dc186 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 17 Jul 2014 17:14:25 +0300 Subject: [PATCH 366/378] mac80211: move TDLS data to mgd private part We can only be a station for TDLS connections. Also fix a bug where a delayed work could be left scheduled if the station interface was brought down during TDLS setup. Signed-off-by: Arik Nemtsov Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 6 +++--- net/mac80211/iface.c | 2 -- net/mac80211/mlme.c | 3 +++ net/mac80211/tdls.c | 28 ++++++++++++++-------------- 4 files changed, 20 insertions(+), 19 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index cb874760e99f..0d8539caebca 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -503,6 +503,9 @@ struct ieee80211_if_managed { struct ieee80211_ht_cap ht_capa_mask; /* Valid parts of ht_capa */ struct ieee80211_vht_cap vht_capa; /* configured VHT overrides */ struct ieee80211_vht_cap vht_capa_mask; /* Valid parts of vht_capa */ + + u8 tdls_peer[ETH_ALEN] __aligned(2); + struct delayed_work tdls_peer_del_work; }; struct ieee80211_if_ibss { @@ -815,9 +818,6 @@ struct ieee80211_sub_if_data { bool radar_required; struct delayed_work dfs_cac_timer_work; - u8 tdls_peer[ETH_ALEN] __aligned(2); - struct delayed_work tdls_peer_del_work; - /* * AP this belongs to: self in AP mode and * corresponding AP in VLAN mode, NULL for diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index bbf51b2f0651..2a12b8aa6aad 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1672,8 +1672,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, ieee80211_dfs_cac_timer_work); INIT_DELAYED_WORK(&sdata->dec_tailroom_needed_wk, ieee80211_delayed_tailroom_dec); - INIT_DELAYED_WORK(&sdata->tdls_peer_del_work, - ieee80211_tdls_peer_del_work); for (i = 0; i < IEEE80211_NUM_BANDS; i++) { struct ieee80211_supported_band *sband; diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index d863ff8b6e41..fcc074871d51 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -3713,6 +3713,8 @@ void ieee80211_sta_setup_sdata(struct ieee80211_sub_if_data *sdata) INIT_WORK(&ifmgd->csa_connection_drop_work, ieee80211_csa_connection_drop_work); INIT_WORK(&ifmgd->request_smps_work, ieee80211_request_smps_mgd_work); + INIT_DELAYED_WORK(&ifmgd->tdls_peer_del_work, + ieee80211_tdls_peer_del_work); setup_timer(&ifmgd->timer, ieee80211_sta_timer, (unsigned long) sdata); setup_timer(&ifmgd->bcn_mon_timer, ieee80211_sta_bcn_mon_timer, @@ -4576,6 +4578,7 @@ void ieee80211_mgd_stop(struct ieee80211_sub_if_data *sdata) cancel_work_sync(&ifmgd->request_smps_work); cancel_work_sync(&ifmgd->csa_connection_drop_work); cancel_work_sync(&ifmgd->chswitch_work); + cancel_delayed_work_sync(&ifmgd->tdls_peer_del_work); sdata_lock(sdata); if (ifmgd->assoc_data) { diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 72eebea7e60a..c59b8f460eb9 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -22,14 +22,14 @@ void ieee80211_tdls_peer_del_work(struct work_struct *wk) struct ieee80211_local *local; sdata = container_of(wk, struct ieee80211_sub_if_data, - tdls_peer_del_work.work); + u.mgd.tdls_peer_del_work.work); local = sdata->local; mutex_lock(&local->mtx); - if (!is_zero_ether_addr(sdata->tdls_peer)) { - tdls_dbg(sdata, "TDLS del peer %pM\n", sdata->tdls_peer); - sta_info_destroy_addr(sdata, sdata->tdls_peer); - eth_zero_addr(sdata->tdls_peer); + if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer)) { + tdls_dbg(sdata, "TDLS del peer %pM\n", sdata->u.mgd.tdls_peer); + sta_info_destroy_addr(sdata, sdata->u.mgd.tdls_peer); + eth_zero_addr(sdata->u.mgd.tdls_peer); } mutex_unlock(&local->mtx); } @@ -561,8 +561,8 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev, mutex_lock(&local->mtx); /* we don't support concurrent TDLS peer setups */ - if (!is_zero_ether_addr(sdata->tdls_peer) && - !ether_addr_equal(sdata->tdls_peer, peer)) { + if (!is_zero_ether_addr(sdata->u.mgd.tdls_peer) && + !ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) { ret = -EBUSY; goto exit; } @@ -593,9 +593,9 @@ ieee80211_tdls_mgmt_setup(struct wiphy *wiphy, struct net_device *dev, if (ret < 0) goto exit; - memcpy(sdata->tdls_peer, peer, ETH_ALEN); + memcpy(sdata->u.mgd.tdls_peer, peer, ETH_ALEN); ieee80211_queue_delayed_work(&sdata->local->hw, - &sdata->tdls_peer_del_work, + &sdata->u.mgd.tdls_peer_del_work, TDLS_PEER_SETUP_TIMEOUT); exit: @@ -751,8 +751,8 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, set_sta_flag(sta, WLAN_STA_TDLS_PEER_AUTH); rcu_read_unlock(); - WARN_ON_ONCE(is_zero_ether_addr(sdata->tdls_peer) || - !ether_addr_equal(sdata->tdls_peer, peer)); + WARN_ON_ONCE(is_zero_ether_addr(sdata->u.mgd.tdls_peer) || + !ether_addr_equal(sdata->u.mgd.tdls_peer, peer)); ret = 0; break; case NL80211_TDLS_DISABLE_LINK: @@ -766,9 +766,9 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, break; } - if (ret == 0 && ether_addr_equal(sdata->tdls_peer, peer)) { - cancel_delayed_work(&sdata->tdls_peer_del_work); - eth_zero_addr(sdata->tdls_peer); + if (ret == 0 && ether_addr_equal(sdata->u.mgd.tdls_peer, peer)) { + cancel_delayed_work(&sdata->u.mgd.tdls_peer_del_work); + eth_zero_addr(sdata->u.mgd.tdls_peer); } mutex_unlock(&local->mtx); From 13cc8a4a1d24ff1f3b8b6de16779ef925371b18b Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 17 Jul 2014 17:14:26 +0300 Subject: [PATCH 367/378] mac80211: support HT for TDLS stations Add the HT capabilities and HT operation information elements to TDLS setup packets where appropriate. Signed-off-by: Arik Nemtsov Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/ht.c | 7 ++-- net/mac80211/tdls.c | 88 ++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 87 insertions(+), 8 deletions(-) diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 15702ff64a4c..568055c02a98 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -150,13 +150,12 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, /* * If user has specified capability over-rides, take care - * of that if the station we're setting up is the AP that + * of that if the station we're setting up is the AP or TDLS peer that * we advertised a restricted capability set to. Override * our own capabilities and then use those below. */ - if ((sdata->vif.type == NL80211_IFTYPE_STATION || - sdata->vif.type == NL80211_IFTYPE_ADHOC) && - !test_sta_flag(sta, WLAN_STA_TDLS_PEER)) + if (sdata->vif.type == NL80211_IFTYPE_STATION || + sdata->vif.type == NL80211_IFTYPE_ADHOC) ieee80211_apply_htcap_overrides(sdata, &own_cap); /* diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index c59b8f460eb9..50d0e0660cc4 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -170,9 +170,23 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, { enum ieee80211_band band = ieee80211_get_sdata_band(sdata); struct ieee80211_local *local = sdata->local; + struct ieee80211_supported_band *sband; + struct ieee80211_sta_ht_cap ht_cap; + struct sta_info *sta = NULL; size_t offset = 0, noffset; u8 *pos; + rcu_read_lock(); + + /* we should have the peer STA if we're already responding */ + if (action_code == WLAN_TDLS_SETUP_RESPONSE) { + sta = sta_info_get(sdata, peer); + if (WARN_ON_ONCE(!sta)) { + rcu_read_unlock(); + return; + } + } + ieee80211_add_srates_ie(sdata, skb, false, band); ieee80211_add_ext_srates_ie(sdata, skb, false, band); @@ -224,6 +238,38 @@ ieee80211_tdls_add_setup_start_ies(struct ieee80211_sub_if_data *sdata, offset = noffset; } + /* + * with TDLS we can switch channels, and HT-caps are not necessarily + * the same on all bands. The specification limits the setup to a + * single HT-cap, so use the current band for now. + */ + sband = local->hw.wiphy->bands[band]; + memcpy(&ht_cap, &sband->ht_cap, sizeof(ht_cap)); + if ((action_code == WLAN_TDLS_SETUP_REQUEST || + action_code == WLAN_TDLS_SETUP_RESPONSE) && + ht_cap.ht_supported && (!sta || sta->sta.ht_cap.ht_supported)) { + if (action_code == WLAN_TDLS_SETUP_REQUEST) { + ieee80211_apply_htcap_overrides(sdata, &ht_cap); + + /* disable SMPS in TDLS initiator */ + ht_cap.cap |= (WLAN_HT_CAP_SM_PS_DISABLED + << IEEE80211_HT_CAP_SM_PS_SHIFT); + } else { + /* disable SMPS in TDLS responder */ + sta->sta.ht_cap.cap |= + (WLAN_HT_CAP_SM_PS_DISABLED + << IEEE80211_HT_CAP_SM_PS_SHIFT); + + /* the peer caps are already intersected with our own */ + memcpy(&ht_cap, &sta->sta.ht_cap, sizeof(ht_cap)); + } + + pos = skb_put(skb, sizeof(struct ieee80211_ht_cap) + 2); + ieee80211_ie_build_ht_cap(pos, &ht_cap, ht_cap.cap); + } + + rcu_read_unlock(); + /* add any remaining IEs */ if (extra_ies_len) { noffset = extra_ies_len; @@ -241,14 +287,16 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata, size_t extra_ies_len) { struct ieee80211_local *local = sdata->local; + struct ieee80211_if_managed *ifmgd = &sdata->u.mgd; size_t offset = 0, noffset; - struct sta_info *sta; + struct sta_info *sta, *ap_sta; u8 *pos; rcu_read_lock(); sta = sta_info_get(sdata, peer); - if (WARN_ON_ONCE(!sta)) { + ap_sta = sta_info_get(sdata, ifmgd->bssid); + if (WARN_ON_ONCE(!sta || !ap_sta)) { rcu_read_unlock(); return; } @@ -272,6 +320,38 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata, test_sta_flag(sta, WLAN_STA_WME)) ieee80211_tdls_add_wmm_param_ie(sdata, skb); + /* add any custom IEs that go before HT operation */ + if (extra_ies_len) { + static const u8 before_ht_op[] = { + WLAN_EID_RSN, + WLAN_EID_QOS_CAPA, + WLAN_EID_FAST_BSS_TRANSITION, + WLAN_EID_TIMEOUT_INTERVAL, + }; + noffset = ieee80211_ie_split(extra_ies, extra_ies_len, + before_ht_op, + ARRAY_SIZE(before_ht_op), + offset); + pos = skb_put(skb, noffset - offset); + memcpy(pos, extra_ies + offset, noffset - offset); + offset = noffset; + } + + /* if HT support is only added in TDLS, we need an HT-operation IE */ + if (!ap_sta->sta.ht_cap.ht_supported && sta->sta.ht_cap.ht_supported) { + struct ieee80211_chanctx_conf *chanctx_conf = + rcu_dereference(sdata->vif.chanctx_conf); + if (!WARN_ON(!chanctx_conf)) { + pos = skb_put(skb, 2 + + sizeof(struct ieee80211_ht_operation)); + /* send an empty HT operation IE */ + ieee80211_ie_build_ht_oper(pos, &sta->sta.ht_cap, + &chanctx_conf->def, 0); + } + } + + rcu_read_unlock(); + /* add any remaining IEs */ if (extra_ies_len) { noffset = extra_ies_len; @@ -280,8 +360,6 @@ ieee80211_tdls_add_setup_cfm_ies(struct ieee80211_sub_if_data *sdata, } ieee80211_tdls_add_link_ie(sdata, skb, peer, initiator); - - rcu_read_unlock(); } static void ieee80211_tdls_add_ies(struct ieee80211_sub_if_data *sdata, @@ -441,6 +519,8 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev, 50 + /* supported rates */ 7 + /* ext capab */ 26 + /* max(WMM-info, WMM-param) */ + 2 + max(sizeof(struct ieee80211_ht_cap), + sizeof(struct ieee80211_ht_operation)) + extra_ies_len + sizeof(struct ieee80211_tdls_lnkie)); if (!skb) From dc5943d54092467b7b56ff6adaeb63165f692fa2 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 17 Jul 2014 17:14:27 +0300 Subject: [PATCH 368/378] mac80211: set Rx highest rate in ht_cap Set for completeness mostly, currently unused in the code. Signed-off-by: Arik Nemtsov Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/ht.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c index 568055c02a98..ff630be2ca75 100644 --- a/net/mac80211/ht.c +++ b/net/mac80211/ht.c @@ -227,6 +227,9 @@ bool ieee80211_ht_cap_ie_to_sta_ht_cap(struct ieee80211_sub_if_data *sdata, if (own_cap.mcs.rx_mask[32/8] & ht_cap_ie->mcs.rx_mask[32/8] & 1) ht_cap.mcs.rx_mask[32/8] |= 1; + /* set Rx highest rate */ + ht_cap.mcs.rx_highest = ht_cap_ie->mcs.rx_highest; + apply: changed = memcmp(&sta->sta.ht_cap, &ht_cap, sizeof(ht_cap)); From bed766bd4cea6413df73e8a314ebf40dd6a920f8 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 17 Jul 2014 17:14:28 +0300 Subject: [PATCH 369/378] mac80211: disable VHT for TDLS TDLS VHT support requires some more information elements during setup. While these are not there, mask out the peer's VHT capabilities so that VHT rates are not mistakenly used. Signed-off-by: Arik Nemtsov Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/vht.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/net/mac80211/vht.c b/net/mac80211/vht.c index 9265adfdabfc..671ce0d27a80 100644 --- a/net/mac80211/vht.c +++ b/net/mac80211/vht.c @@ -129,6 +129,10 @@ ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata, if (!vht_cap_ie || !sband->vht_cap.vht_supported) return; + /* don't support VHT for TDLS peers for now */ + if (test_sta_flag(sta, WLAN_STA_TDLS_PEER)) + return; + /* * A VHT STA must support 40 MHz, but if we verify that here * then we break a few things - some APs (e.g. Netgear R6300v2 From c72e1140463a643579c3f9e09f990e71e95671ac Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 17 Jul 2014 17:14:29 +0300 Subject: [PATCH 370/378] cfg80211: fix TDLS setup with VHT peers Some VHT TDLS peers (Google Nexus 5) include the VHT-AID IE in their TDLS setup request/response. Usermode passes this aid as the station aid, causing it to fail verifiction, since this happens in the "set_station" stage. Make an exception for the TDLS use-case. Signed-off-by: Arik Nemtsov Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/wireless/nl80211.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index c10295138eb5..13997c954249 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c @@ -3815,7 +3815,8 @@ int cfg80211_check_station_change(struct wiphy *wiphy, { if (params->listen_interval != -1) return -EINVAL; - if (params->aid) + if (params->aid && + !(params->sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))) return -EINVAL; /* When you run into this, adjust the code below for the new flag */ From db8e173245535e7e91603e3e69bc63722a82ed81 Mon Sep 17 00:00:00 2001 From: Arik Nemtsov Date: Thu, 17 Jul 2014 17:14:30 +0300 Subject: [PATCH 371/378] mac80211: ignore frames between TDLS peers when operating as AP If the AP receives actions frames destined for other peers, it may mistakenly toggle BA-sessions from itself to a peer. Ignore TDLS data packets as well - the AP should not handle them. Signed-off-by: Arik Nemtsov Reviewed-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/rx.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 5f572bed1761..5a786d489f7e 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -3129,6 +3129,14 @@ static bool prepare_for_handlers(struct ieee80211_rx_data *rx, if (!ieee80211_is_beacon(hdr->frame_control)) return false; status->rx_flags &= ~IEEE80211_RX_RA_MATCH; + } else if (!ieee80211_has_tods(hdr->frame_control)) { + /* ignore data frames to TDLS-peers */ + if (ieee80211_is_data(hdr->frame_control)) + return false; + /* ignore action frames to TDLS-peers */ + if (ieee80211_is_action(hdr->frame_control) && + !ether_addr_equal(bssid, hdr->addr1)) + return false; } break; case NL80211_IFTYPE_WDS: From bb3f848608f070a6e3f6c477ba7ff46cf1fb0f02 Mon Sep 17 00:00:00 2001 From: Liad Kaufman Date: Thu, 17 Jul 2014 17:14:31 +0300 Subject: [PATCH 372/378] mac80211: make sure TDLS teardown packet is sent on time Since the teardown packet is created while the queues are stopped, it isn't sent immediately, but rather is pending. To be sure that when we flush the queues prior to destroying the station we also send this packet - the tasklet handling pending packets is invoked to flush the packets. Signed-off-by: Liad Kaufman Reviewed-by: ArikX Nemtsov Reviewed-by: Johannes Berg Signed-off-by: Johannes Berg --- net/mac80211/tdls.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c index 50d0e0660cc4..1b21050be174 100644 --- a/net/mac80211/tdls.c +++ b/net/mac80211/tdls.c @@ -836,6 +836,17 @@ int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev, ret = 0; break; case NL80211_TDLS_DISABLE_LINK: + /* + * The teardown message in ieee80211_tdls_mgmt_teardown() was + * created while the queues were stopped, so it might still be + * pending. Before flushing the queues we need to be sure the + * message is handled by the tasklet handling pending messages, + * otherwise we might start destroying the station before + * sending the teardown packet. + * Note that this only forces the tasklet to flush pendings - + * not to stop the tasklet from rescheduling itself. + */ + tasklet_kill(&local->tx_pending_tasklet); /* flush a potentially queued teardown packet */ ieee80211_flush_queues(local, sdata); From 3e2a0226c624066943259eaa5e1261da9d8a25fc Mon Sep 17 00:00:00 2001 From: Luciano Coelho Date: Sat, 5 Jul 2014 11:43:01 +0300 Subject: [PATCH 373/378] mac80211: remove redundant IEEE80211_STA_CSA_RECEIVED flag The csa_active flag was added in sdata a while ago and made IEEE80211_STA_CSA_RECEIVED redundant. The new flag is also used to mark when CSA is ongoing on other iftypes and took over the old one as the preferred method for checking whether we're in the middle of a channel switch. Remove the old, redundant flag. Signed-off-by: Luciano Coelho Signed-off-by: Johannes Berg --- net/mac80211/ieee80211_i.h | 1 - net/mac80211/mlme.c | 8 +------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 0d8539caebca..49731dd044bb 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -345,7 +345,6 @@ enum ieee80211_sta_flags { IEEE80211_STA_CONNECTION_POLL = BIT(1), IEEE80211_STA_CONTROL_PORT = BIT(2), IEEE80211_STA_DISABLE_HT = BIT(4), - IEEE80211_STA_CSA_RECEIVED = BIT(5), IEEE80211_STA_MFP_ENABLED = BIT(6), IEEE80211_STA_UAPSD_ENABLED = BIT(7), IEEE80211_STA_NULLFUNC_ACKED = BIT(8), diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index fcc074871d51..31a8afaf7332 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -996,8 +996,6 @@ static void ieee80211_chswitch_work(struct work_struct *work) sdata->csa_block_tx = false; } - ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; - ieee80211_sta_reset_beacon_monitor(sdata); ieee80211_sta_reset_conn_monitor(sdata); @@ -1055,7 +1053,7 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, return; /* disregard subsequent announcements if we are already processing */ - if (ifmgd->flags & IEEE80211_STA_CSA_RECEIVED) + if (sdata->vif.csa_active) return; current_band = cbss->channel->band; @@ -1082,8 +1080,6 @@ ieee80211_sta_process_chanswitch(struct ieee80211_sub_if_data *sdata, return; } - ifmgd->flags |= IEEE80211_STA_CSA_RECEIVED; - mutex_lock(&local->mtx); mutex_lock(&local->chanctx_mtx); conf = rcu_dereference_protected(sdata->vif.chanctx_conf, @@ -2099,8 +2095,6 @@ static void __ieee80211_disconnect(struct ieee80211_sub_if_data *sdata) ieee80211_set_disassoc(sdata, IEEE80211_STYPE_DEAUTH, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY, true, frame_buf); - ifmgd->flags &= ~IEEE80211_STA_CSA_RECEIVED; - mutex_lock(&local->mtx); sdata->vif.csa_active = false; if (sdata->csa_block_tx) { From fa96aabb6a34eeb86ce6a5e1a3914fe9f106cfcc Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 14 Jul 2014 14:19:49 -0700 Subject: [PATCH 374/378] wireless: fixup genregdb.awk for remove of antenna gain from wireless-regd Since "wireless-regdb: remove antenna gain" was merged in the wireless-regdb tree, the awk script parser has been incompatible with the 'official' regulatory database. This fixes that up. Without this change the max EIRP is set to 0 making 802.11 devices useless. The fragile nature of the awk parser must be replaced, but ideas over how to do that in the most scalable way are being reviewed. In the meantime update the documentation for CFG80211_INTERNAL_REGDB so folks are aware of expectations for now. Reported-by: John Walker Reported-by: Krishna Chaitanya Signed-off-by: Luis R. Rodriguez Signed-off-by: Johannes Berg --- net/wireless/Kconfig | 6 ++++++ net/wireless/genregdb.awk | 35 ++++++++++++++++++++++------------- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig index 405f3c4cf70c..29c8675f9a11 100644 --- a/net/wireless/Kconfig +++ b/net/wireless/Kconfig @@ -162,6 +162,12 @@ config CFG80211_INTERNAL_REGDB and includes code to query that database. This is an alternative to using CRDA for defining regulatory rules for the kernel. + Using this option requires some parsing of the db.txt at build time, + the parser will be upkept with the latest wireless-regdb updates but + older wireless-regdb formats will be ignored. The parser may later + be replaced to avoid issues with conflicts on versions of + wireless-regdb. + For details see: http://wireless.kernel.org/en/developers/Regulatory diff --git a/net/wireless/genregdb.awk b/net/wireless/genregdb.awk index 40c37fc5b67c..baf2426b555a 100644 --- a/net/wireless/genregdb.awk +++ b/net/wireless/genregdb.awk @@ -51,32 +51,41 @@ function parse_country_head() { function parse_reg_rule() { + flag_starts_at = 7 + start = $1 sub(/\(/, "", start) end = $3 bw = $5 sub(/\),/, "", bw) - gain = $6 - sub(/\(/, "", gain) - sub(/,/, "", gain) - power = $7 - sub(/\)/, "", power) - sub(/,/, "", power) + gain = 0 + power = $6 # power might be in mW... - units = $8 + units = $7 + dfs_cac = 0 + + sub(/\(/, "", power) + sub(/\),/, "", power) + sub(/\),/, "", units) sub(/\)/, "", units) - sub(/,/, "", units) - dfs_cac = $9 + if (units == "mW") { + flag_starts_at = 8 power = 10 * log(power)/log(10) + if ($8 ~ /[[:digit:]]/) { + flag_starts_at = 9 + dfs_cac = $8 + } } else { - dfs_cac = $8 + if ($7 ~ /[[:digit:]]/) { + flag_starts_at = 8 + dfs_cac = $7 + } } - sub(/,/, "", dfs_cac) sub(/\(/, "", dfs_cac) - sub(/\)/, "", dfs_cac) + sub(/\),/, "", dfs_cac) flagstr = "" - for (i=8; i<=NF; i++) + for (i=flag_starts_at; i<=NF; i++) flagstr = flagstr $i split(flagstr, flagarray, ",") flags = "" From aeb136c5b433377324f030b1a50b96eb7a99193b Mon Sep 17 00:00:00 2001 From: Max Stepanov Date: Wed, 9 Jul 2014 16:55:32 +0300 Subject: [PATCH 375/378] mac80211: fix a potential NULL access in ieee80211_crypto_hw_decrypt The NULL pointer access could happen when ieee80211_crypto_hw_decrypt is called from ieee80211_rx_h_decrypt with the following condition: 1. rx->key->conf.cipher is not WEP, CCMP, TKIP or AES_CMAC 2. rx->sta is NULL When ieee80211_crypto_hw_decrypt is called, it verifies rx->sta->cipher_scheme and it will cause Oops if rx->sta is NULL. This path adds an addirional rx->sta == NULL verification in ieee80211_crypto_hw_decrypt for this case. Signed-off-by: Max Stepanov Signed-off-by: Emmanuel Grumbach Signed-off-by: Johannes Berg --- net/mac80211/wpa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c index 9b3dcc201145..f7d4ca4c46e0 100644 --- a/net/mac80211/wpa.c +++ b/net/mac80211/wpa.c @@ -811,7 +811,7 @@ ieee80211_crypto_hw_encrypt(struct ieee80211_tx_data *tx) ieee80211_rx_result ieee80211_crypto_hw_decrypt(struct ieee80211_rx_data *rx) { - if (rx->sta->cipher_scheme) + if (rx->sta && rx->sta->cipher_scheme) return ieee80211_crypto_cs_decrypt(rx); return RX_DROP_UNUSABLE; From 60e83deb4c1e7e8b6ab78e7331288bf4211bdeb6 Mon Sep 17 00:00:00 2001 From: Eytan Lifshitz Date: Mon, 21 Jul 2014 15:18:41 +0300 Subject: [PATCH 376/378] mac80211: remove useless NULL checks sdata can't be NULL, and key being NULL is really not possible unless the code is modified. The sdata check made a static analyze (klocwork) unhappy because we would get pointer to local (sdata->local) and only then check if sdata is non-NULL. Signed-off-by: Eytan Lifshitz Signed-off-by: Emmanuel Grumbach [remove !key check as well] Signed-off-by: Johannes Berg --- net/mac80211/key.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/net/mac80211/key.c b/net/mac80211/key.c index 16d97f044a20..d808cff80153 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -482,9 +482,6 @@ int ieee80211_key_link(struct ieee80211_key *key, int idx, ret; bool pairwise; - if (WARN_ON(!sdata || !key)) - return -EINVAL; - pairwise = key->conf.flags & IEEE80211_KEY_FLAG_PAIRWISE; idx = key->conf.keyidx; key->local = sdata->local; From 83eb935ec74a91468776cd86415abcb6ee23cca8 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 16 Jul 2014 12:09:31 +0200 Subject: [PATCH 377/378] mac80211: fix Rx reordering with RX_FLAG_AMSDU_MORE Some drivers (e.g. ath10k) report A-MSDU subframes individually with identical seqno. The A-MPDU Rx reorder code did not account for that which made it practically unusable with drivers using RX_FLAG_AMSDU_MORE because it would end up dropping a lot of frames resulting in confusion in upper network transport layers. Signed-off-by: Michal Kazior Signed-off-by: Johannes Berg --- net/mac80211/agg-rx.c | 9 ++++-- net/mac80211/ieee80211_i.h | 15 ++++++++++ net/mac80211/rx.c | 57 +++++++++++++++++++++++++------------- net/mac80211/sta_info.h | 5 ++-- 4 files changed, 62 insertions(+), 24 deletions(-) diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index 31bf2586fb84..d38c49b644cd 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -52,7 +52,7 @@ static void ieee80211_free_tid_rx(struct rcu_head *h) del_timer_sync(&tid_rx->reorder_timer); for (i = 0; i < tid_rx->buf_size; i++) - dev_kfree_skb(tid_rx->reorder_buf[i]); + __skb_queue_purge(&tid_rx->reorder_buf[i]); kfree(tid_rx->reorder_buf); kfree(tid_rx->reorder_time); kfree(tid_rx); @@ -232,7 +232,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, struct tid_ampdu_rx *tid_agg_rx; u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num, status; u8 dialog_token; - int ret = -EOPNOTSUPP; + int i, ret = -EOPNOTSUPP; /* extract session parameters from addba request frame */ dialog_token = mgmt->u.action.u.addba_req.dialog_token; @@ -308,7 +308,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, /* prepare reordering buffer */ tid_agg_rx->reorder_buf = - kcalloc(buf_size, sizeof(struct sk_buff *), GFP_KERNEL); + kcalloc(buf_size, sizeof(struct sk_buff_head), GFP_KERNEL); tid_agg_rx->reorder_time = kcalloc(buf_size, sizeof(unsigned long), GFP_KERNEL); if (!tid_agg_rx->reorder_buf || !tid_agg_rx->reorder_time) { @@ -318,6 +318,9 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, goto end; } + for (i = 0; i < buf_size; i++) + __skb_queue_head_init(&tid_agg_rx->reorder_buf[i]); + ret = drv_ampdu_action(local, sta->sdata, IEEE80211_AMPDU_RX_START, &sta->sta, tid, &start_seq_num, 0); ht_dbg(sta->sdata, "Rx A-MPDU request on %pM tid %d result %d\n", diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 49731dd044bb..c504e99a5404 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -1729,6 +1729,21 @@ static inline void ieee802_11_parse_elems(const u8 *start, size_t len, ieee802_11_parse_elems_crc(start, len, action, elems, 0, 0); } +static inline bool ieee80211_rx_reorder_ready(struct sk_buff_head *frames) +{ + struct sk_buff *tail = skb_peek_tail(frames); + struct ieee80211_rx_status *status; + + if (!tail) + return false; + + status = IEEE80211_SKB_RXCB(tail); + if (status->flag & RX_FLAG_AMSDU_MORE) + return false; + + return true; +} + void ieee80211_dynamic_ps_enable_work(struct work_struct *work); void ieee80211_dynamic_ps_disable_work(struct work_struct *work); void ieee80211_dynamic_ps_timer(unsigned long data); diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index 5a786d489f7e..bd2c9b22c945 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -688,20 +688,27 @@ static void ieee80211_release_reorder_frame(struct ieee80211_sub_if_data *sdata, int index, struct sk_buff_head *frames) { - struct sk_buff *skb = tid_agg_rx->reorder_buf[index]; + struct sk_buff_head *skb_list = &tid_agg_rx->reorder_buf[index]; + struct sk_buff *skb; struct ieee80211_rx_status *status; lockdep_assert_held(&tid_agg_rx->reorder_lock); - if (!skb) + if (skb_queue_empty(skb_list)) goto no_frame; - /* release the frame from the reorder ring buffer */ + if (!ieee80211_rx_reorder_ready(skb_list)) { + __skb_queue_purge(skb_list); + goto no_frame; + } + + /* release frames from the reorder ring buffer */ tid_agg_rx->stored_mpdu_num--; - tid_agg_rx->reorder_buf[index] = NULL; - status = IEEE80211_SKB_RXCB(skb); - status->rx_flags |= IEEE80211_RX_DEFERRED_RELEASE; - __skb_queue_tail(frames, skb); + while ((skb = __skb_dequeue(skb_list))) { + status = IEEE80211_SKB_RXCB(skb); + status->rx_flags |= IEEE80211_RX_DEFERRED_RELEASE; + __skb_queue_tail(frames, skb); + } no_frame: tid_agg_rx->head_seq_num = ieee80211_sn_inc(tid_agg_rx->head_seq_num); @@ -738,13 +745,13 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata, struct tid_ampdu_rx *tid_agg_rx, struct sk_buff_head *frames) { - int index, j; + int index, i, j; lockdep_assert_held(&tid_agg_rx->reorder_lock); /* release the buffer until next missing frame */ index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size; - if (!tid_agg_rx->reorder_buf[index] && + if (!ieee80211_rx_reorder_ready(&tid_agg_rx->reorder_buf[index]) && tid_agg_rx->stored_mpdu_num) { /* * No buffers ready to be released, but check whether any @@ -753,7 +760,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata, int skipped = 1; for (j = (index + 1) % tid_agg_rx->buf_size; j != index; j = (j + 1) % tid_agg_rx->buf_size) { - if (!tid_agg_rx->reorder_buf[j]) { + if (!ieee80211_rx_reorder_ready( + &tid_agg_rx->reorder_buf[j])) { skipped++; continue; } @@ -762,6 +770,11 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata, HT_RX_REORDER_BUF_TIMEOUT)) goto set_release_timer; + /* don't leave incomplete A-MSDUs around */ + for (i = (index + 1) % tid_agg_rx->buf_size; i != j; + i = (i + 1) % tid_agg_rx->buf_size) + __skb_queue_purge(&tid_agg_rx->reorder_buf[i]); + ht_dbg_ratelimited(sdata, "release an RX reorder frame due to timeout on earlier frames\n"); ieee80211_release_reorder_frame(sdata, tid_agg_rx, j, @@ -775,7 +788,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata, skipped) & IEEE80211_SN_MASK; skipped = 0; } - } else while (tid_agg_rx->reorder_buf[index]) { + } else while (ieee80211_rx_reorder_ready( + &tid_agg_rx->reorder_buf[index])) { ieee80211_release_reorder_frame(sdata, tid_agg_rx, index, frames); index = tid_agg_rx->head_seq_num % tid_agg_rx->buf_size; @@ -786,7 +800,8 @@ static void ieee80211_sta_reorder_release(struct ieee80211_sub_if_data *sdata, for (; j != (index - 1) % tid_agg_rx->buf_size; j = (j + 1) % tid_agg_rx->buf_size) { - if (tid_agg_rx->reorder_buf[j]) + if (ieee80211_rx_reorder_ready( + &tid_agg_rx->reorder_buf[j])) break; } @@ -811,6 +826,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata struct sk_buff_head *frames) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data; + struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb); u16 sc = le16_to_cpu(hdr->seq_ctrl); u16 mpdu_seq_num = (sc & IEEE80211_SCTL_SEQ) >> 4; u16 head_seq_num, buf_size; @@ -845,7 +861,7 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata index = mpdu_seq_num % tid_agg_rx->buf_size; /* check if we already stored this frame */ - if (tid_agg_rx->reorder_buf[index]) { + if (ieee80211_rx_reorder_ready(&tid_agg_rx->reorder_buf[index])) { dev_kfree_skb(skb); goto out; } @@ -858,17 +874,20 @@ static bool ieee80211_sta_manage_reorder_buf(struct ieee80211_sub_if_data *sdata */ if (mpdu_seq_num == tid_agg_rx->head_seq_num && tid_agg_rx->stored_mpdu_num == 0) { - tid_agg_rx->head_seq_num = - ieee80211_sn_inc(tid_agg_rx->head_seq_num); + if (!(status->flag & RX_FLAG_AMSDU_MORE)) + tid_agg_rx->head_seq_num = + ieee80211_sn_inc(tid_agg_rx->head_seq_num); ret = false; goto out; } /* put the frame in the reordering buffer */ - tid_agg_rx->reorder_buf[index] = skb; - tid_agg_rx->reorder_time[index] = jiffies; - tid_agg_rx->stored_mpdu_num++; - ieee80211_sta_reorder_release(sdata, tid_agg_rx, frames); + __skb_queue_tail(&tid_agg_rx->reorder_buf[index], skb); + if (!(status->flag & RX_FLAG_AMSDU_MORE)) { + tid_agg_rx->reorder_time[index] = jiffies; + tid_agg_rx->stored_mpdu_num++; + ieee80211_sta_reorder_release(sdata, tid_agg_rx, frames); + } out: spin_unlock(&tid_agg_rx->reorder_lock); diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index e37f00969526..d411bcc8ef08 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -155,7 +155,8 @@ struct tid_ampdu_tx { /** * struct tid_ampdu_rx - TID aggregation information (Rx). * - * @reorder_buf: buffer to reorder incoming aggregated MPDUs + * @reorder_buf: buffer to reorder incoming aggregated MPDUs. An MPDU may be an + * A-MSDU with individually reported subframes. * @reorder_time: jiffies when skb was added * @session_timer: check if peer keeps Tx-ing on the TID (by timeout value) * @reorder_timer: releases expired frames from the reorder buffer. @@ -180,7 +181,7 @@ struct tid_ampdu_tx { struct tid_ampdu_rx { struct rcu_head rcu_head; spinlock_t reorder_lock; - struct sk_buff **reorder_buf; + struct sk_buff_head *reorder_buf; unsigned long *reorder_time; struct timer_list session_timer; struct timer_list reorder_timer; From 08cf42e843f9a7e253502011c81677f61f7e5c42 Mon Sep 17 00:00:00 2001 From: Michal Kazior Date: Wed, 16 Jul 2014 12:12:15 +0200 Subject: [PATCH 378/378] mac80211: add support for Rx reordering offloading Some drivers may be performing most of Tx/Rx aggregation on their own (e.g. in firmware) including AddBa/DelBa negotiations but may otherwise require Rx reordering assistance. The patch exports 2 new functions for establishing Rx aggregation sessions in assumption device driver has taken care of the necessary negotiations. Signed-off-by: Michal Kazior [fix endian bug] Signed-off-by: Johannes Berg --- include/net/mac80211.h | 34 +++++++++++++ net/mac80211/agg-rx.c | 101 ++++++++++++++++++++++++++++--------- net/mac80211/ieee80211_i.h | 11 ++++ net/mac80211/iface.c | 29 +++++++++++ 4 files changed, 152 insertions(+), 23 deletions(-) diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 9ce5cb17ed82..dae2e24616e1 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -4552,6 +4552,40 @@ void ieee80211_stop_rx_ba_session(struct ieee80211_vif *vif, u16 ba_rx_bitmap, */ void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn); +/** + * ieee80211_start_rx_ba_session_offl - start a Rx BA session + * + * Some device drivers may offload part of the Rx aggregation flow including + * AddBa/DelBa negotiation but may otherwise be incapable of full Rx + * reordering. + * + * Create structures responsible for reordering so device drivers may call here + * when they complete AddBa negotiation. + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback + * @addr: station mac address + * @tid: the rx tid + */ +void ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif, + const u8 *addr, u16 tid); + +/** + * ieee80211_stop_rx_ba_session_offl - stop a Rx BA session + * + * Some device drivers may offload part of the Rx aggregation flow including + * AddBa/DelBa negotiation but may otherwise be incapable of full Rx + * reordering. + * + * Destroy structures responsible for reordering so device drivers may call here + * when they complete DelBa negotiation. + * + * @vif: &struct ieee80211_vif pointer from the add_interface callback + * @addr: station mac address + * @tid: the rx tid + */ +void ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif, + const u8 *addr, u16 tid); + /* Rate control API */ /** diff --git a/net/mac80211/agg-rx.c b/net/mac80211/agg-rx.c index d38c49b644cd..f0e84bc48038 100644 --- a/net/mac80211/agg-rx.c +++ b/net/mac80211/agg-rx.c @@ -224,28 +224,15 @@ static void ieee80211_send_addba_resp(struct ieee80211_sub_if_data *sdata, u8 *d ieee80211_tx_skb(sdata, skb); } -void ieee80211_process_addba_request(struct ieee80211_local *local, - struct sta_info *sta, - struct ieee80211_mgmt *mgmt, - size_t len) +void __ieee80211_start_rx_ba_session(struct sta_info *sta, + u8 dialog_token, u16 timeout, + u16 start_seq_num, u16 ba_policy, u16 tid, + u16 buf_size, bool tx) { + struct ieee80211_local *local = sta->sdata->local; struct tid_ampdu_rx *tid_agg_rx; - u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num, status; - u8 dialog_token; int i, ret = -EOPNOTSUPP; - - /* extract session parameters from addba request frame */ - dialog_token = mgmt->u.action.u.addba_req.dialog_token; - timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout); - start_seq_num = - le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4; - - capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab); - ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1; - tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; - buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6; - - status = WLAN_STATUS_REQUEST_DECLINED; + u16 status = WLAN_STATUS_REQUEST_DECLINED; if (test_sta_flag(sta, WLAN_STA_BLOCK_BA)) { ht_dbg(sta->sdata, @@ -264,7 +251,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, status = WLAN_STATUS_INVALID_QOS_PARAM; ht_dbg_ratelimited(sta->sdata, "AddBA Req with bad params from %pM on tid %u. policy %d, buffer size %d\n", - mgmt->sa, tid, ba_policy, buf_size); + sta->sta.addr, tid, ba_policy, buf_size); goto end_no_lock; } /* determine default buffer size */ @@ -281,7 +268,7 @@ void ieee80211_process_addba_request(struct ieee80211_local *local, if (sta->ampdu_mlme.tid_rx[tid]) { ht_dbg_ratelimited(sta->sdata, "unexpected AddBA Req from %pM on tid %u\n", - mgmt->sa, tid); + sta->sta.addr, tid); /* delete existing Rx BA session on the same tid */ ___ieee80211_stop_rx_ba_session(sta, tid, WLAN_BACK_RECIPIENT, @@ -353,6 +340,74 @@ end: mutex_unlock(&sta->ampdu_mlme.mtx); end_no_lock: - ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid, - dialog_token, status, 1, buf_size, timeout); + if (tx) + ieee80211_send_addba_resp(sta->sdata, sta->sta.addr, tid, + dialog_token, status, 1, buf_size, + timeout); } + +void ieee80211_process_addba_request(struct ieee80211_local *local, + struct sta_info *sta, + struct ieee80211_mgmt *mgmt, + size_t len) +{ + u16 capab, tid, timeout, ba_policy, buf_size, start_seq_num; + u8 dialog_token; + + /* extract session parameters from addba request frame */ + dialog_token = mgmt->u.action.u.addba_req.dialog_token; + timeout = le16_to_cpu(mgmt->u.action.u.addba_req.timeout); + start_seq_num = + le16_to_cpu(mgmt->u.action.u.addba_req.start_seq_num) >> 4; + + capab = le16_to_cpu(mgmt->u.action.u.addba_req.capab); + ba_policy = (capab & IEEE80211_ADDBA_PARAM_POLICY_MASK) >> 1; + tid = (capab & IEEE80211_ADDBA_PARAM_TID_MASK) >> 2; + buf_size = (capab & IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK) >> 6; + + __ieee80211_start_rx_ba_session(sta, dialog_token, timeout, + start_seq_num, ba_policy, tid, + buf_size, true); +} + +void ieee80211_start_rx_ba_session_offl(struct ieee80211_vif *vif, + const u8 *addr, u16 tid) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_local *local = sdata->local; + struct ieee80211_rx_agg *rx_agg; + struct sk_buff *skb = dev_alloc_skb(0); + + if (unlikely(!skb)) + return; + + rx_agg = (struct ieee80211_rx_agg *) &skb->cb; + memcpy(&rx_agg->addr, addr, ETH_ALEN); + rx_agg->tid = tid; + + skb->pkt_type = IEEE80211_SDATA_QUEUE_RX_AGG_START; + skb_queue_tail(&sdata->skb_queue, skb); + ieee80211_queue_work(&local->hw, &sdata->work); +} +EXPORT_SYMBOL(ieee80211_start_rx_ba_session_offl); + +void ieee80211_stop_rx_ba_session_offl(struct ieee80211_vif *vif, + const u8 *addr, u16 tid) +{ + struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif); + struct ieee80211_local *local = sdata->local; + struct ieee80211_rx_agg *rx_agg; + struct sk_buff *skb = dev_alloc_skb(0); + + if (unlikely(!skb)) + return; + + rx_agg = (struct ieee80211_rx_agg *) &skb->cb; + memcpy(&rx_agg->addr, addr, ETH_ALEN); + rx_agg->tid = tid; + + skb->pkt_type = IEEE80211_SDATA_QUEUE_RX_AGG_STOP; + skb_queue_tail(&sdata->skb_queue, skb); + ieee80211_queue_work(&local->hw, &sdata->work); +} +EXPORT_SYMBOL(ieee80211_stop_rx_ba_session_offl); diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index c504e99a5404..ef7a089ac546 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -925,10 +925,17 @@ ieee80211_vif_get_shift(struct ieee80211_vif *vif) return shift; } +struct ieee80211_rx_agg { + u8 addr[ETH_ALEN]; + u16 tid; +}; + enum sdata_queue_type { IEEE80211_SDATA_QUEUE_TYPE_FRAME = 0, IEEE80211_SDATA_QUEUE_AGG_START = 1, IEEE80211_SDATA_QUEUE_AGG_STOP = 2, + IEEE80211_SDATA_QUEUE_RX_AGG_START = 3, + IEEE80211_SDATA_QUEUE_RX_AGG_STOP = 4, }; enum { @@ -1577,6 +1584,10 @@ void ___ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, u16 initiator, u16 reason, bool stop); void __ieee80211_stop_rx_ba_session(struct sta_info *sta, u16 tid, u16 initiator, u16 reason, bool stop); +void __ieee80211_start_rx_ba_session(struct sta_info *sta, + u8 dialog_token, u16 timeout, + u16 start_seq_num, u16 ba_policy, u16 tid, + u16 buf_size, bool tx); void ieee80211_sta_tear_down_BA_sessions(struct sta_info *sta, enum ieee80211_agg_stop_reason reason); void ieee80211_process_delba(struct ieee80211_sub_if_data *sdata, diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 2a12b8aa6aad..29be8854a027 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -1140,6 +1140,7 @@ static void ieee80211_iface_work(struct work_struct *work) struct sk_buff *skb; struct sta_info *sta; struct ieee80211_ra_tid *ra_tid; + struct ieee80211_rx_agg *rx_agg; if (!ieee80211_sdata_running(sdata)) return; @@ -1167,6 +1168,34 @@ static void ieee80211_iface_work(struct work_struct *work) ra_tid = (void *)&skb->cb; ieee80211_stop_tx_ba_cb(&sdata->vif, ra_tid->ra, ra_tid->tid); + } else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_RX_AGG_START) { + rx_agg = (void *)&skb->cb; + mutex_lock(&local->sta_mtx); + sta = sta_info_get_bss(sdata, rx_agg->addr); + if (sta) { + u16 last_seq; + + last_seq = le16_to_cpu( + sta->last_seq_ctrl[rx_agg->tid]); + + __ieee80211_start_rx_ba_session(sta, + 0, 0, + ieee80211_sn_inc(last_seq), + 1, rx_agg->tid, + IEEE80211_MAX_AMPDU_BUF, + false); + } + mutex_unlock(&local->sta_mtx); + } else if (skb->pkt_type == IEEE80211_SDATA_QUEUE_RX_AGG_STOP) { + rx_agg = (void *)&skb->cb; + mutex_lock(&local->sta_mtx); + sta = sta_info_get_bss(sdata, rx_agg->addr); + if (sta) + __ieee80211_stop_rx_ba_session(sta, + rx_agg->tid, + WLAN_BACK_RECIPIENT, 0, + false); + mutex_unlock(&local->sta_mtx); } else if (ieee80211_is_action(mgmt->frame_control) && mgmt->u.action.category == WLAN_CATEGORY_BACK) { int len = skb->len;