Bluetooth: Introduce hci_req helper to abort a connection
There are several different places needing to make sure that a
connection gets disconnected or canceled. The exact action needed
depends on the connection state, so centralizing this logic can save
quite a lot of code duplication.
Signed-off-by: Johan Hedberg <johan.hedberg@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c
index b736922..739f966 100644
--- a/net/bluetooth/hci_request.c
+++ b/net/bluetooth/hci_request.c
@@ -564,3 +564,96 @@
if (err && err != -ENODATA)
BT_ERR("Failed to run HCI request: err %d", err);
}
+
+void __hci_abort_conn(struct hci_request *req, struct hci_conn *conn,
+ u8 reason)
+{
+ switch (conn->state) {
+ case BT_CONNECTED:
+ case BT_CONFIG:
+ if (conn->type == AMP_LINK) {
+ struct hci_cp_disconn_phy_link cp;
+
+ cp.phy_handle = HCI_PHY_HANDLE(conn->handle);
+ cp.reason = reason;
+ hci_req_add(req, HCI_OP_DISCONN_PHY_LINK, sizeof(cp),
+ &cp);
+ } else {
+ struct hci_cp_disconnect dc;
+
+ dc.handle = cpu_to_le16(conn->handle);
+ dc.reason = reason;
+ hci_req_add(req, HCI_OP_DISCONNECT, sizeof(dc), &dc);
+ }
+
+ conn->state = BT_DISCONN;
+
+ break;
+ case BT_CONNECT:
+ if (conn->type == LE_LINK) {
+ if (test_bit(HCI_CONN_SCANNING, &conn->flags))
+ break;
+ hci_req_add(req, HCI_OP_LE_CREATE_CONN_CANCEL,
+ 0, NULL);
+ } else if (conn->type == ACL_LINK) {
+ if (req->hdev->hci_ver < BLUETOOTH_VER_1_2)
+ break;
+ hci_req_add(req, HCI_OP_CREATE_CONN_CANCEL,
+ 6, &conn->dst);
+ }
+ break;
+ case BT_CONNECT2:
+ if (conn->type == ACL_LINK) {
+ struct hci_cp_reject_conn_req rej;
+
+ bacpy(&rej.bdaddr, &conn->dst);
+ rej.reason = reason;
+
+ hci_req_add(req, HCI_OP_REJECT_CONN_REQ,
+ sizeof(rej), &rej);
+ } else if (conn->type == SCO_LINK || conn->type == ESCO_LINK) {
+ struct hci_cp_reject_sync_conn_req rej;
+
+ bacpy(&rej.bdaddr, &conn->dst);
+
+ /* SCO rejection has its own limited set of
+ * allowed error values (0x0D-0x0F) which isn't
+ * compatible with most values passed to this
+ * function. To be safe hard-code one of the
+ * values that's suitable for SCO.
+ */
+ rej.reason = HCI_ERROR_REMOTE_LOW_RESOURCES;
+
+ hci_req_add(req, HCI_OP_REJECT_SYNC_CONN_REQ,
+ sizeof(rej), &rej);
+ }
+ break;
+ default:
+ conn->state = BT_CLOSED;
+ break;
+ }
+}
+
+static void abort_conn_complete(struct hci_dev *hdev, u8 status, u16 opcode)
+{
+ if (status)
+ BT_DBG("Failed to abort connection: status 0x%2.2x", status);
+}
+
+int hci_abort_conn(struct hci_conn *conn, u8 reason)
+{
+ struct hci_request req;
+ int err;
+
+ hci_req_init(&req, conn->hdev);
+
+ __hci_abort_conn(&req, conn, reason);
+
+ err = hci_req_run(&req, abort_conn_complete);
+ if (err && err != -ENODATA) {
+ BT_ERR("Failed to run HCI request: err %d", err);
+ return err;
+ }
+
+ return 0;
+}