18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci Copyright (c) 2010,2011 Code Aurora Forum. All rights reserved. 48c2ecf20Sopenharmony_ci Copyright (c) 2011,2012 Intel Corp. 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci*/ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <net/bluetooth/bluetooth.h> 98c2ecf20Sopenharmony_ci#include <net/bluetooth/hci_core.h> 108c2ecf20Sopenharmony_ci#include <net/bluetooth/l2cap.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include "hci_request.h" 138c2ecf20Sopenharmony_ci#include "a2mp.h" 148c2ecf20Sopenharmony_ci#include "amp.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define A2MP_FEAT_EXT 0x8000 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci/* Global AMP Manager list */ 198c2ecf20Sopenharmony_cistatic LIST_HEAD(amp_mgr_list); 208c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(amp_mgr_list_lock); 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* A2MP build & send command helper functions */ 238c2ecf20Sopenharmony_cistatic struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci struct a2mp_cmd *cmd; 268c2ecf20Sopenharmony_ci int plen; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci plen = sizeof(*cmd) + len; 298c2ecf20Sopenharmony_ci cmd = kzalloc(plen, GFP_KERNEL); 308c2ecf20Sopenharmony_ci if (!cmd) 318c2ecf20Sopenharmony_ci return NULL; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci cmd->code = code; 348c2ecf20Sopenharmony_ci cmd->ident = ident; 358c2ecf20Sopenharmony_ci cmd->len = cpu_to_le16(len); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci memcpy(cmd->data, data, len); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci return cmd; 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct l2cap_chan *chan = mgr->a2mp_chan; 458c2ecf20Sopenharmony_ci struct a2mp_cmd *cmd; 468c2ecf20Sopenharmony_ci u16 total_len = len + sizeof(*cmd); 478c2ecf20Sopenharmony_ci struct kvec iv; 488c2ecf20Sopenharmony_ci struct msghdr msg; 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci cmd = __a2mp_build(code, ident, len, data); 518c2ecf20Sopenharmony_ci if (!cmd) 528c2ecf20Sopenharmony_ci return; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci iv.iov_base = cmd; 558c2ecf20Sopenharmony_ci iv.iov_len = total_len; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci memset(&msg, 0, sizeof(msg)); 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci iov_iter_kvec(&msg.msg_iter, WRITE, &iv, 1, total_len); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci l2cap_chan_send(chan, &msg, total_len); 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci kfree(cmd); 648c2ecf20Sopenharmony_ci} 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_cistatic u8 __next_ident(struct amp_mgr *mgr) 678c2ecf20Sopenharmony_ci{ 688c2ecf20Sopenharmony_ci if (++mgr->ident == 0) 698c2ecf20Sopenharmony_ci mgr->ident = 1; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci return mgr->ident; 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic struct amp_mgr *amp_mgr_lookup_by_state(u8 state) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci struct amp_mgr *mgr; 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci mutex_lock(&_mgr_list_lock); 798c2ecf20Sopenharmony_ci list_for_each_entry(mgr, &_mgr_list, list) { 808c2ecf20Sopenharmony_ci if (test_and_clear_bit(state, &mgr->state)) { 818c2ecf20Sopenharmony_ci amp_mgr_get(mgr); 828c2ecf20Sopenharmony_ci mutex_unlock(&_mgr_list_lock); 838c2ecf20Sopenharmony_ci return mgr; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci } 868c2ecf20Sopenharmony_ci mutex_unlock(&_mgr_list_lock); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci return NULL; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/* hci_dev_list shall be locked */ 928c2ecf20Sopenharmony_cistatic void __a2mp_add_cl(struct amp_mgr *mgr, struct a2mp_cl *cl) 938c2ecf20Sopenharmony_ci{ 948c2ecf20Sopenharmony_ci struct hci_dev *hdev; 958c2ecf20Sopenharmony_ci int i = 1; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci cl[0].id = AMP_ID_BREDR; 988c2ecf20Sopenharmony_ci cl[0].type = AMP_TYPE_BREDR; 998c2ecf20Sopenharmony_ci cl[0].status = AMP_STATUS_BLUETOOTH_ONLY; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci list_for_each_entry(hdev, &hci_dev_list, list) { 1028c2ecf20Sopenharmony_ci if (hdev->dev_type == HCI_AMP) { 1038c2ecf20Sopenharmony_ci cl[i].id = hdev->id; 1048c2ecf20Sopenharmony_ci cl[i].type = hdev->amp_type; 1058c2ecf20Sopenharmony_ci if (test_bit(HCI_UP, &hdev->flags)) 1068c2ecf20Sopenharmony_ci cl[i].status = hdev->amp_status; 1078c2ecf20Sopenharmony_ci else 1088c2ecf20Sopenharmony_ci cl[i].status = AMP_STATUS_POWERED_DOWN; 1098c2ecf20Sopenharmony_ci i++; 1108c2ecf20Sopenharmony_ci } 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci/* Processing A2MP messages */ 1158c2ecf20Sopenharmony_cistatic int a2mp_command_rej(struct amp_mgr *mgr, struct sk_buff *skb, 1168c2ecf20Sopenharmony_ci struct a2mp_cmd *hdr) 1178c2ecf20Sopenharmony_ci{ 1188c2ecf20Sopenharmony_ci struct a2mp_cmd_rej *rej = (void *) skb->data; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (le16_to_cpu(hdr->len) < sizeof(*rej)) 1218c2ecf20Sopenharmony_ci return -EINVAL; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci BT_DBG("ident %d reason %d", hdr->ident, le16_to_cpu(rej->reason)); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(*rej)); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return 0; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_cistatic int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb, 1318c2ecf20Sopenharmony_ci struct a2mp_cmd *hdr) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct a2mp_discov_req *req = (void *) skb->data; 1348c2ecf20Sopenharmony_ci u16 len = le16_to_cpu(hdr->len); 1358c2ecf20Sopenharmony_ci struct a2mp_discov_rsp *rsp; 1368c2ecf20Sopenharmony_ci u16 ext_feat; 1378c2ecf20Sopenharmony_ci u8 num_ctrl; 1388c2ecf20Sopenharmony_ci struct hci_dev *hdev; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci if (len < sizeof(*req)) 1418c2ecf20Sopenharmony_ci return -EINVAL; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(*req)); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci ext_feat = le16_to_cpu(req->ext_feat); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci BT_DBG("mtu %d efm 0x%4.4x", le16_to_cpu(req->mtu), ext_feat); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* check that packet is not broken for now */ 1508c2ecf20Sopenharmony_ci while (ext_feat & A2MP_FEAT_EXT) { 1518c2ecf20Sopenharmony_ci if (len < sizeof(ext_feat)) 1528c2ecf20Sopenharmony_ci return -EINVAL; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci ext_feat = get_unaligned_le16(skb->data); 1558c2ecf20Sopenharmony_ci BT_DBG("efm 0x%4.4x", ext_feat); 1568c2ecf20Sopenharmony_ci len -= sizeof(ext_feat); 1578c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(ext_feat)); 1588c2ecf20Sopenharmony_ci } 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci read_lock(&hci_dev_list_lock); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* at minimum the BR/EDR needs to be listed */ 1638c2ecf20Sopenharmony_ci num_ctrl = 1; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci list_for_each_entry(hdev, &hci_dev_list, list) { 1668c2ecf20Sopenharmony_ci if (hdev->dev_type == HCI_AMP) 1678c2ecf20Sopenharmony_ci num_ctrl++; 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci len = struct_size(rsp, cl, num_ctrl); 1718c2ecf20Sopenharmony_ci rsp = kmalloc(len, GFP_ATOMIC); 1728c2ecf20Sopenharmony_ci if (!rsp) { 1738c2ecf20Sopenharmony_ci read_unlock(&hci_dev_list_lock); 1748c2ecf20Sopenharmony_ci return -ENOMEM; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci rsp->mtu = cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU); 1788c2ecf20Sopenharmony_ci rsp->ext_feat = 0; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci __a2mp_add_cl(mgr, rsp->cl); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci read_unlock(&hci_dev_list_lock); 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci a2mp_send(mgr, A2MP_DISCOVER_RSP, hdr->ident, len, rsp); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci kfree(rsp); 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic int a2mp_discover_rsp(struct amp_mgr *mgr, struct sk_buff *skb, 1918c2ecf20Sopenharmony_ci struct a2mp_cmd *hdr) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct a2mp_discov_rsp *rsp = (void *) skb->data; 1948c2ecf20Sopenharmony_ci u16 len = le16_to_cpu(hdr->len); 1958c2ecf20Sopenharmony_ci struct a2mp_cl *cl; 1968c2ecf20Sopenharmony_ci u16 ext_feat; 1978c2ecf20Sopenharmony_ci bool found = false; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (len < sizeof(*rsp)) 2008c2ecf20Sopenharmony_ci return -EINVAL; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci len -= sizeof(*rsp); 2038c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(*rsp)); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci ext_feat = le16_to_cpu(rsp->ext_feat); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci BT_DBG("mtu %d efm 0x%4.4x", le16_to_cpu(rsp->mtu), ext_feat); 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci /* check that packet is not broken for now */ 2108c2ecf20Sopenharmony_ci while (ext_feat & A2MP_FEAT_EXT) { 2118c2ecf20Sopenharmony_ci if (len < sizeof(ext_feat)) 2128c2ecf20Sopenharmony_ci return -EINVAL; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci ext_feat = get_unaligned_le16(skb->data); 2158c2ecf20Sopenharmony_ci BT_DBG("efm 0x%4.4x", ext_feat); 2168c2ecf20Sopenharmony_ci len -= sizeof(ext_feat); 2178c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(ext_feat)); 2188c2ecf20Sopenharmony_ci } 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci cl = (void *) skb->data; 2218c2ecf20Sopenharmony_ci while (len >= sizeof(*cl)) { 2228c2ecf20Sopenharmony_ci BT_DBG("Remote AMP id %d type %d status %d", cl->id, cl->type, 2238c2ecf20Sopenharmony_ci cl->status); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci if (cl->id != AMP_ID_BREDR && cl->type != AMP_TYPE_BREDR) { 2268c2ecf20Sopenharmony_ci struct a2mp_info_req req; 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci found = true; 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_ci memset(&req, 0, sizeof(req)); 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci req.id = cl->id; 2338c2ecf20Sopenharmony_ci a2mp_send(mgr, A2MP_GETINFO_REQ, __next_ident(mgr), 2348c2ecf20Sopenharmony_ci sizeof(req), &req); 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci len -= sizeof(*cl); 2388c2ecf20Sopenharmony_ci cl = skb_pull(skb, sizeof(*cl)); 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* Fall back to L2CAP init sequence */ 2428c2ecf20Sopenharmony_ci if (!found) { 2438c2ecf20Sopenharmony_ci struct l2cap_conn *conn = mgr->l2cap_conn; 2448c2ecf20Sopenharmony_ci struct l2cap_chan *chan; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci mutex_lock(&conn->chan_lock); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci list_for_each_entry(chan, &conn->chan_l, list) { 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci BT_DBG("chan %p state %s", chan, 2518c2ecf20Sopenharmony_ci state_to_string(chan->state)); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci if (chan->scid == L2CAP_CID_A2MP) 2548c2ecf20Sopenharmony_ci continue; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci l2cap_chan_lock(chan); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (chan->state == BT_CONNECT) 2598c2ecf20Sopenharmony_ci l2cap_send_conn_req(chan); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci l2cap_chan_unlock(chan); 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci mutex_unlock(&conn->chan_lock); 2658c2ecf20Sopenharmony_ci } 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci return 0; 2688c2ecf20Sopenharmony_ci} 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_cistatic int a2mp_change_notify(struct amp_mgr *mgr, struct sk_buff *skb, 2718c2ecf20Sopenharmony_ci struct a2mp_cmd *hdr) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct a2mp_cl *cl = (void *) skb->data; 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci while (skb->len >= sizeof(*cl)) { 2768c2ecf20Sopenharmony_ci BT_DBG("Controller id %d type %d status %d", cl->id, cl->type, 2778c2ecf20Sopenharmony_ci cl->status); 2788c2ecf20Sopenharmony_ci cl = skb_pull(skb, sizeof(*cl)); 2798c2ecf20Sopenharmony_ci } 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci /* TODO send A2MP_CHANGE_RSP */ 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci return 0; 2848c2ecf20Sopenharmony_ci} 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic void read_local_amp_info_complete(struct hci_dev *hdev, u8 status, 2878c2ecf20Sopenharmony_ci u16 opcode) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci BT_DBG("%s status 0x%2.2x", hdev->name, status); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci a2mp_send_getinfo_rsp(hdev); 2928c2ecf20Sopenharmony_ci} 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_cistatic int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb, 2958c2ecf20Sopenharmony_ci struct a2mp_cmd *hdr) 2968c2ecf20Sopenharmony_ci{ 2978c2ecf20Sopenharmony_ci struct a2mp_info_req *req = (void *) skb->data; 2988c2ecf20Sopenharmony_ci struct hci_dev *hdev; 2998c2ecf20Sopenharmony_ci struct hci_request hreq; 3008c2ecf20Sopenharmony_ci int err = 0; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (le16_to_cpu(hdr->len) < sizeof(*req)) 3038c2ecf20Sopenharmony_ci return -EINVAL; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci BT_DBG("id %d", req->id); 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci hdev = hci_dev_get(req->id); 3088c2ecf20Sopenharmony_ci if (!hdev || hdev->dev_type != HCI_AMP) { 3098c2ecf20Sopenharmony_ci struct a2mp_info_rsp rsp; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci memset(&rsp, 0, sizeof(rsp)); 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_ci rsp.id = req->id; 3148c2ecf20Sopenharmony_ci rsp.status = A2MP_STATUS_INVALID_CTRL_ID; 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci a2mp_send(mgr, A2MP_GETINFO_RSP, hdr->ident, sizeof(rsp), 3178c2ecf20Sopenharmony_ci &rsp); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci goto done; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci set_bit(READ_LOC_AMP_INFO, &mgr->state); 3238c2ecf20Sopenharmony_ci hci_req_init(&hreq, hdev); 3248c2ecf20Sopenharmony_ci hci_req_add(&hreq, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL); 3258c2ecf20Sopenharmony_ci err = hci_req_run(&hreq, read_local_amp_info_complete); 3268c2ecf20Sopenharmony_ci if (err < 0) 3278c2ecf20Sopenharmony_ci a2mp_send_getinfo_rsp(hdev); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cidone: 3308c2ecf20Sopenharmony_ci if (hdev) 3318c2ecf20Sopenharmony_ci hci_dev_put(hdev); 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(*req)); 3348c2ecf20Sopenharmony_ci return 0; 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_cistatic int a2mp_getinfo_rsp(struct amp_mgr *mgr, struct sk_buff *skb, 3388c2ecf20Sopenharmony_ci struct a2mp_cmd *hdr) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct a2mp_info_rsp *rsp = (struct a2mp_info_rsp *) skb->data; 3418c2ecf20Sopenharmony_ci struct a2mp_amp_assoc_req req; 3428c2ecf20Sopenharmony_ci struct amp_ctrl *ctrl; 3438c2ecf20Sopenharmony_ci 3448c2ecf20Sopenharmony_ci if (le16_to_cpu(hdr->len) < sizeof(*rsp)) 3458c2ecf20Sopenharmony_ci return -EINVAL; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci BT_DBG("id %d status 0x%2.2x", rsp->id, rsp->status); 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci if (rsp->status) 3508c2ecf20Sopenharmony_ci return -EINVAL; 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci ctrl = amp_ctrl_add(mgr, rsp->id); 3538c2ecf20Sopenharmony_ci if (!ctrl) 3548c2ecf20Sopenharmony_ci return -ENOMEM; 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci memset(&req, 0, sizeof(req)); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci req.id = rsp->id; 3598c2ecf20Sopenharmony_ci a2mp_send(mgr, A2MP_GETAMPASSOC_REQ, __next_ident(mgr), sizeof(req), 3608c2ecf20Sopenharmony_ci &req); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(*rsp)); 3638c2ecf20Sopenharmony_ci return 0; 3648c2ecf20Sopenharmony_ci} 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_cistatic int a2mp_getampassoc_req(struct amp_mgr *mgr, struct sk_buff *skb, 3678c2ecf20Sopenharmony_ci struct a2mp_cmd *hdr) 3688c2ecf20Sopenharmony_ci{ 3698c2ecf20Sopenharmony_ci struct a2mp_amp_assoc_req *req = (void *) skb->data; 3708c2ecf20Sopenharmony_ci struct hci_dev *hdev; 3718c2ecf20Sopenharmony_ci struct amp_mgr *tmp; 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci if (le16_to_cpu(hdr->len) < sizeof(*req)) 3748c2ecf20Sopenharmony_ci return -EINVAL; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci BT_DBG("id %d", req->id); 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci /* Make sure that other request is not processed */ 3798c2ecf20Sopenharmony_ci tmp = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC); 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci hdev = hci_dev_get(req->id); 3828c2ecf20Sopenharmony_ci if (!hdev || hdev->amp_type == AMP_TYPE_BREDR || tmp) { 3838c2ecf20Sopenharmony_ci struct a2mp_amp_assoc_rsp rsp; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci memset(&rsp, 0, sizeof(rsp)); 3868c2ecf20Sopenharmony_ci rsp.id = req->id; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci if (tmp) { 3898c2ecf20Sopenharmony_ci rsp.status = A2MP_STATUS_COLLISION_OCCURED; 3908c2ecf20Sopenharmony_ci amp_mgr_put(tmp); 3918c2ecf20Sopenharmony_ci } else { 3928c2ecf20Sopenharmony_ci rsp.status = A2MP_STATUS_INVALID_CTRL_ID; 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci 3958c2ecf20Sopenharmony_ci a2mp_send(mgr, A2MP_GETAMPASSOC_RSP, hdr->ident, sizeof(rsp), 3968c2ecf20Sopenharmony_ci &rsp); 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci goto done; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci amp_read_loc_assoc(hdev, mgr); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_cidone: 4048c2ecf20Sopenharmony_ci if (hdev) 4058c2ecf20Sopenharmony_ci hci_dev_put(hdev); 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(*req)); 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_cistatic int a2mp_getampassoc_rsp(struct amp_mgr *mgr, struct sk_buff *skb, 4128c2ecf20Sopenharmony_ci struct a2mp_cmd *hdr) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct a2mp_amp_assoc_rsp *rsp = (void *) skb->data; 4158c2ecf20Sopenharmony_ci u16 len = le16_to_cpu(hdr->len); 4168c2ecf20Sopenharmony_ci struct hci_dev *hdev; 4178c2ecf20Sopenharmony_ci struct amp_ctrl *ctrl; 4188c2ecf20Sopenharmony_ci struct hci_conn *hcon; 4198c2ecf20Sopenharmony_ci size_t assoc_len; 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (len < sizeof(*rsp)) 4228c2ecf20Sopenharmony_ci return -EINVAL; 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci assoc_len = len - sizeof(*rsp); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci BT_DBG("id %d status 0x%2.2x assoc len %zu", rsp->id, rsp->status, 4278c2ecf20Sopenharmony_ci assoc_len); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci if (rsp->status) 4308c2ecf20Sopenharmony_ci return -EINVAL; 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci /* Save remote ASSOC data */ 4338c2ecf20Sopenharmony_ci ctrl = amp_ctrl_lookup(mgr, rsp->id); 4348c2ecf20Sopenharmony_ci if (ctrl) { 4358c2ecf20Sopenharmony_ci u8 *assoc; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci assoc = kmemdup(rsp->amp_assoc, assoc_len, GFP_KERNEL); 4388c2ecf20Sopenharmony_ci if (!assoc) { 4398c2ecf20Sopenharmony_ci amp_ctrl_put(ctrl); 4408c2ecf20Sopenharmony_ci return -ENOMEM; 4418c2ecf20Sopenharmony_ci } 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci ctrl->assoc = assoc; 4448c2ecf20Sopenharmony_ci ctrl->assoc_len = assoc_len; 4458c2ecf20Sopenharmony_ci ctrl->assoc_rem_len = assoc_len; 4468c2ecf20Sopenharmony_ci ctrl->assoc_len_so_far = 0; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci amp_ctrl_put(ctrl); 4498c2ecf20Sopenharmony_ci } 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci /* Create Phys Link */ 4528c2ecf20Sopenharmony_ci hdev = hci_dev_get(rsp->id); 4538c2ecf20Sopenharmony_ci if (!hdev) 4548c2ecf20Sopenharmony_ci return -EINVAL; 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci hcon = phylink_add(hdev, mgr, rsp->id, true); 4578c2ecf20Sopenharmony_ci if (!hcon) 4588c2ecf20Sopenharmony_ci goto done; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci BT_DBG("Created hcon %p: loc:%d -> rem:%d", hcon, hdev->id, rsp->id); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci mgr->bredr_chan->remote_amp_id = rsp->id; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci amp_create_phylink(hdev, mgr, hcon); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_cidone: 4678c2ecf20Sopenharmony_ci hci_dev_put(hdev); 4688c2ecf20Sopenharmony_ci skb_pull(skb, len); 4698c2ecf20Sopenharmony_ci return 0; 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, 4738c2ecf20Sopenharmony_ci struct a2mp_cmd *hdr) 4748c2ecf20Sopenharmony_ci{ 4758c2ecf20Sopenharmony_ci struct a2mp_physlink_req *req = (void *) skb->data; 4768c2ecf20Sopenharmony_ci struct a2mp_physlink_rsp rsp; 4778c2ecf20Sopenharmony_ci struct hci_dev *hdev; 4788c2ecf20Sopenharmony_ci struct hci_conn *hcon; 4798c2ecf20Sopenharmony_ci struct amp_ctrl *ctrl; 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_ci if (le16_to_cpu(hdr->len) < sizeof(*req)) 4828c2ecf20Sopenharmony_ci return -EINVAL; 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci BT_DBG("local_id %d, remote_id %d", req->local_id, req->remote_id); 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci memset(&rsp, 0, sizeof(rsp)); 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_ci rsp.local_id = req->remote_id; 4898c2ecf20Sopenharmony_ci rsp.remote_id = req->local_id; 4908c2ecf20Sopenharmony_ci 4918c2ecf20Sopenharmony_ci hdev = hci_dev_get(req->remote_id); 4928c2ecf20Sopenharmony_ci if (!hdev || hdev->amp_type == AMP_TYPE_BREDR) { 4938c2ecf20Sopenharmony_ci rsp.status = A2MP_STATUS_INVALID_CTRL_ID; 4948c2ecf20Sopenharmony_ci goto send_rsp; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci ctrl = amp_ctrl_lookup(mgr, rsp.remote_id); 4988c2ecf20Sopenharmony_ci if (!ctrl) { 4998c2ecf20Sopenharmony_ci ctrl = amp_ctrl_add(mgr, rsp.remote_id); 5008c2ecf20Sopenharmony_ci if (ctrl) { 5018c2ecf20Sopenharmony_ci amp_ctrl_get(ctrl); 5028c2ecf20Sopenharmony_ci } else { 5038c2ecf20Sopenharmony_ci rsp.status = A2MP_STATUS_UNABLE_START_LINK_CREATION; 5048c2ecf20Sopenharmony_ci goto send_rsp; 5058c2ecf20Sopenharmony_ci } 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci if (ctrl) { 5098c2ecf20Sopenharmony_ci size_t assoc_len = le16_to_cpu(hdr->len) - sizeof(*req); 5108c2ecf20Sopenharmony_ci u8 *assoc; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci assoc = kmemdup(req->amp_assoc, assoc_len, GFP_KERNEL); 5138c2ecf20Sopenharmony_ci if (!assoc) { 5148c2ecf20Sopenharmony_ci amp_ctrl_put(ctrl); 5158c2ecf20Sopenharmony_ci hci_dev_put(hdev); 5168c2ecf20Sopenharmony_ci return -ENOMEM; 5178c2ecf20Sopenharmony_ci } 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_ci ctrl->assoc = assoc; 5208c2ecf20Sopenharmony_ci ctrl->assoc_len = assoc_len; 5218c2ecf20Sopenharmony_ci ctrl->assoc_rem_len = assoc_len; 5228c2ecf20Sopenharmony_ci ctrl->assoc_len_so_far = 0; 5238c2ecf20Sopenharmony_ci 5248c2ecf20Sopenharmony_ci amp_ctrl_put(ctrl); 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci hcon = phylink_add(hdev, mgr, req->local_id, false); 5288c2ecf20Sopenharmony_ci if (hcon) { 5298c2ecf20Sopenharmony_ci amp_accept_phylink(hdev, mgr, hcon); 5308c2ecf20Sopenharmony_ci rsp.status = A2MP_STATUS_SUCCESS; 5318c2ecf20Sopenharmony_ci } else { 5328c2ecf20Sopenharmony_ci rsp.status = A2MP_STATUS_UNABLE_START_LINK_CREATION; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_cisend_rsp: 5368c2ecf20Sopenharmony_ci if (hdev) 5378c2ecf20Sopenharmony_ci hci_dev_put(hdev); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci /* Reply error now and success after HCI Write Remote AMP Assoc 5408c2ecf20Sopenharmony_ci command complete with success status 5418c2ecf20Sopenharmony_ci */ 5428c2ecf20Sopenharmony_ci if (rsp.status != A2MP_STATUS_SUCCESS) { 5438c2ecf20Sopenharmony_ci a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, hdr->ident, 5448c2ecf20Sopenharmony_ci sizeof(rsp), &rsp); 5458c2ecf20Sopenharmony_ci } else { 5468c2ecf20Sopenharmony_ci set_bit(WRITE_REMOTE_AMP_ASSOC, &mgr->state); 5478c2ecf20Sopenharmony_ci mgr->ident = hdr->ident; 5488c2ecf20Sopenharmony_ci } 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci skb_pull(skb, le16_to_cpu(hdr->len)); 5518c2ecf20Sopenharmony_ci return 0; 5528c2ecf20Sopenharmony_ci} 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_cistatic int a2mp_discphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb, 5558c2ecf20Sopenharmony_ci struct a2mp_cmd *hdr) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci struct a2mp_physlink_req *req = (void *) skb->data; 5588c2ecf20Sopenharmony_ci struct a2mp_physlink_rsp rsp; 5598c2ecf20Sopenharmony_ci struct hci_dev *hdev; 5608c2ecf20Sopenharmony_ci struct hci_conn *hcon; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci if (le16_to_cpu(hdr->len) < sizeof(*req)) 5638c2ecf20Sopenharmony_ci return -EINVAL; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci BT_DBG("local_id %d remote_id %d", req->local_id, req->remote_id); 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci memset(&rsp, 0, sizeof(rsp)); 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci rsp.local_id = req->remote_id; 5708c2ecf20Sopenharmony_ci rsp.remote_id = req->local_id; 5718c2ecf20Sopenharmony_ci rsp.status = A2MP_STATUS_SUCCESS; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci hdev = hci_dev_get(req->remote_id); 5748c2ecf20Sopenharmony_ci if (!hdev) { 5758c2ecf20Sopenharmony_ci rsp.status = A2MP_STATUS_INVALID_CTRL_ID; 5768c2ecf20Sopenharmony_ci goto send_rsp; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK, 5808c2ecf20Sopenharmony_ci &mgr->l2cap_conn->hcon->dst); 5818c2ecf20Sopenharmony_ci if (!hcon) { 5828c2ecf20Sopenharmony_ci bt_dev_err(hdev, "no phys link exist"); 5838c2ecf20Sopenharmony_ci rsp.status = A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS; 5848c2ecf20Sopenharmony_ci goto clean; 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci /* TODO Disconnect Phys Link here */ 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ciclean: 5908c2ecf20Sopenharmony_ci hci_dev_put(hdev); 5918c2ecf20Sopenharmony_ci 5928c2ecf20Sopenharmony_cisend_rsp: 5938c2ecf20Sopenharmony_ci a2mp_send(mgr, A2MP_DISCONNPHYSLINK_RSP, hdr->ident, sizeof(rsp), &rsp); 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(*req)); 5968c2ecf20Sopenharmony_ci return 0; 5978c2ecf20Sopenharmony_ci} 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_cistatic inline int a2mp_cmd_rsp(struct amp_mgr *mgr, struct sk_buff *skb, 6008c2ecf20Sopenharmony_ci struct a2mp_cmd *hdr) 6018c2ecf20Sopenharmony_ci{ 6028c2ecf20Sopenharmony_ci BT_DBG("ident %d code 0x%2.2x", hdr->ident, hdr->code); 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci skb_pull(skb, le16_to_cpu(hdr->len)); 6058c2ecf20Sopenharmony_ci return 0; 6068c2ecf20Sopenharmony_ci} 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci/* Handle A2MP signalling */ 6098c2ecf20Sopenharmony_cistatic int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb) 6108c2ecf20Sopenharmony_ci{ 6118c2ecf20Sopenharmony_ci struct a2mp_cmd *hdr; 6128c2ecf20Sopenharmony_ci struct amp_mgr *mgr = chan->data; 6138c2ecf20Sopenharmony_ci int err = 0; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci amp_mgr_get(mgr); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci while (skb->len >= sizeof(*hdr)) { 6188c2ecf20Sopenharmony_ci u16 len; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci hdr = (void *) skb->data; 6218c2ecf20Sopenharmony_ci len = le16_to_cpu(hdr->len); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci BT_DBG("code 0x%2.2x id %d len %u", hdr->code, hdr->ident, len); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci skb_pull(skb, sizeof(*hdr)); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci if (len > skb->len || !hdr->ident) { 6288c2ecf20Sopenharmony_ci err = -EINVAL; 6298c2ecf20Sopenharmony_ci break; 6308c2ecf20Sopenharmony_ci } 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci mgr->ident = hdr->ident; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci switch (hdr->code) { 6358c2ecf20Sopenharmony_ci case A2MP_COMMAND_REJ: 6368c2ecf20Sopenharmony_ci a2mp_command_rej(mgr, skb, hdr); 6378c2ecf20Sopenharmony_ci break; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci case A2MP_DISCOVER_REQ: 6408c2ecf20Sopenharmony_ci err = a2mp_discover_req(mgr, skb, hdr); 6418c2ecf20Sopenharmony_ci break; 6428c2ecf20Sopenharmony_ci 6438c2ecf20Sopenharmony_ci case A2MP_CHANGE_NOTIFY: 6448c2ecf20Sopenharmony_ci err = a2mp_change_notify(mgr, skb, hdr); 6458c2ecf20Sopenharmony_ci break; 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci case A2MP_GETINFO_REQ: 6488c2ecf20Sopenharmony_ci err = a2mp_getinfo_req(mgr, skb, hdr); 6498c2ecf20Sopenharmony_ci break; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci case A2MP_GETAMPASSOC_REQ: 6528c2ecf20Sopenharmony_ci err = a2mp_getampassoc_req(mgr, skb, hdr); 6538c2ecf20Sopenharmony_ci break; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci case A2MP_CREATEPHYSLINK_REQ: 6568c2ecf20Sopenharmony_ci err = a2mp_createphyslink_req(mgr, skb, hdr); 6578c2ecf20Sopenharmony_ci break; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci case A2MP_DISCONNPHYSLINK_REQ: 6608c2ecf20Sopenharmony_ci err = a2mp_discphyslink_req(mgr, skb, hdr); 6618c2ecf20Sopenharmony_ci break; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci case A2MP_DISCOVER_RSP: 6648c2ecf20Sopenharmony_ci err = a2mp_discover_rsp(mgr, skb, hdr); 6658c2ecf20Sopenharmony_ci break; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci case A2MP_GETINFO_RSP: 6688c2ecf20Sopenharmony_ci err = a2mp_getinfo_rsp(mgr, skb, hdr); 6698c2ecf20Sopenharmony_ci break; 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci case A2MP_GETAMPASSOC_RSP: 6728c2ecf20Sopenharmony_ci err = a2mp_getampassoc_rsp(mgr, skb, hdr); 6738c2ecf20Sopenharmony_ci break; 6748c2ecf20Sopenharmony_ci 6758c2ecf20Sopenharmony_ci case A2MP_CHANGE_RSP: 6768c2ecf20Sopenharmony_ci case A2MP_CREATEPHYSLINK_RSP: 6778c2ecf20Sopenharmony_ci case A2MP_DISCONNPHYSLINK_RSP: 6788c2ecf20Sopenharmony_ci err = a2mp_cmd_rsp(mgr, skb, hdr); 6798c2ecf20Sopenharmony_ci break; 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci default: 6828c2ecf20Sopenharmony_ci BT_ERR("Unknown A2MP sig cmd 0x%2.2x", hdr->code); 6838c2ecf20Sopenharmony_ci err = -EINVAL; 6848c2ecf20Sopenharmony_ci break; 6858c2ecf20Sopenharmony_ci } 6868c2ecf20Sopenharmony_ci } 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci if (err) { 6898c2ecf20Sopenharmony_ci struct a2mp_cmd_rej rej; 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci memset(&rej, 0, sizeof(rej)); 6928c2ecf20Sopenharmony_ci 6938c2ecf20Sopenharmony_ci rej.reason = cpu_to_le16(0); 6948c2ecf20Sopenharmony_ci hdr = (void *) skb->data; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci BT_DBG("Send A2MP Rej: cmd 0x%2.2x err %d", hdr->code, err); 6978c2ecf20Sopenharmony_ci 6988c2ecf20Sopenharmony_ci a2mp_send(mgr, A2MP_COMMAND_REJ, hdr->ident, sizeof(rej), 6998c2ecf20Sopenharmony_ci &rej); 7008c2ecf20Sopenharmony_ci } 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_ci /* Always free skb and return success error code to prevent 7038c2ecf20Sopenharmony_ci from sending L2CAP Disconnect over A2MP channel */ 7048c2ecf20Sopenharmony_ci kfree_skb(skb); 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci amp_mgr_put(mgr); 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci return 0; 7098c2ecf20Sopenharmony_ci} 7108c2ecf20Sopenharmony_ci 7118c2ecf20Sopenharmony_cistatic void a2mp_chan_close_cb(struct l2cap_chan *chan) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci l2cap_chan_put(chan); 7148c2ecf20Sopenharmony_ci} 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_cistatic void a2mp_chan_state_change_cb(struct l2cap_chan *chan, int state, 7178c2ecf20Sopenharmony_ci int err) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci struct amp_mgr *mgr = chan->data; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci if (!mgr) 7228c2ecf20Sopenharmony_ci return; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci BT_DBG("chan %p state %s", chan, state_to_string(state)); 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci chan->state = state; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci switch (state) { 7298c2ecf20Sopenharmony_ci case BT_CLOSED: 7308c2ecf20Sopenharmony_ci if (mgr) 7318c2ecf20Sopenharmony_ci amp_mgr_put(mgr); 7328c2ecf20Sopenharmony_ci break; 7338c2ecf20Sopenharmony_ci } 7348c2ecf20Sopenharmony_ci} 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_cistatic struct sk_buff *a2mp_chan_alloc_skb_cb(struct l2cap_chan *chan, 7378c2ecf20Sopenharmony_ci unsigned long hdr_len, 7388c2ecf20Sopenharmony_ci unsigned long len, int nb) 7398c2ecf20Sopenharmony_ci{ 7408c2ecf20Sopenharmony_ci struct sk_buff *skb; 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_ci skb = bt_skb_alloc(hdr_len + len, GFP_KERNEL); 7438c2ecf20Sopenharmony_ci if (!skb) 7448c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 7458c2ecf20Sopenharmony_ci 7468c2ecf20Sopenharmony_ci return skb; 7478c2ecf20Sopenharmony_ci} 7488c2ecf20Sopenharmony_ci 7498c2ecf20Sopenharmony_cistatic const struct l2cap_ops a2mp_chan_ops = { 7508c2ecf20Sopenharmony_ci .name = "L2CAP A2MP channel", 7518c2ecf20Sopenharmony_ci .recv = a2mp_chan_recv_cb, 7528c2ecf20Sopenharmony_ci .close = a2mp_chan_close_cb, 7538c2ecf20Sopenharmony_ci .state_change = a2mp_chan_state_change_cb, 7548c2ecf20Sopenharmony_ci .alloc_skb = a2mp_chan_alloc_skb_cb, 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci /* Not implemented for A2MP */ 7578c2ecf20Sopenharmony_ci .new_connection = l2cap_chan_no_new_connection, 7588c2ecf20Sopenharmony_ci .teardown = l2cap_chan_no_teardown, 7598c2ecf20Sopenharmony_ci .ready = l2cap_chan_no_ready, 7608c2ecf20Sopenharmony_ci .defer = l2cap_chan_no_defer, 7618c2ecf20Sopenharmony_ci .resume = l2cap_chan_no_resume, 7628c2ecf20Sopenharmony_ci .set_shutdown = l2cap_chan_no_set_shutdown, 7638c2ecf20Sopenharmony_ci .get_sndtimeo = l2cap_chan_no_get_sndtimeo, 7648c2ecf20Sopenharmony_ci}; 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_cistatic struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci struct l2cap_chan *chan; 7698c2ecf20Sopenharmony_ci int err; 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_ci chan = l2cap_chan_create(); 7728c2ecf20Sopenharmony_ci if (!chan) 7738c2ecf20Sopenharmony_ci return NULL; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci BT_DBG("chan %p", chan); 7768c2ecf20Sopenharmony_ci 7778c2ecf20Sopenharmony_ci chan->chan_type = L2CAP_CHAN_FIXED; 7788c2ecf20Sopenharmony_ci chan->scid = L2CAP_CID_A2MP; 7798c2ecf20Sopenharmony_ci chan->dcid = L2CAP_CID_A2MP; 7808c2ecf20Sopenharmony_ci chan->omtu = L2CAP_A2MP_DEFAULT_MTU; 7818c2ecf20Sopenharmony_ci chan->imtu = L2CAP_A2MP_DEFAULT_MTU; 7828c2ecf20Sopenharmony_ci chan->flush_to = L2CAP_DEFAULT_FLUSH_TO; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci chan->ops = &a2mp_chan_ops; 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci l2cap_chan_set_defaults(chan); 7878c2ecf20Sopenharmony_ci chan->remote_max_tx = chan->max_tx; 7888c2ecf20Sopenharmony_ci chan->remote_tx_win = chan->tx_win; 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_ci chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO; 7918c2ecf20Sopenharmony_ci chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci skb_queue_head_init(&chan->tx_q); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci chan->mode = L2CAP_MODE_ERTM; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci err = l2cap_ertm_init(chan); 7988c2ecf20Sopenharmony_ci if (err < 0) { 7998c2ecf20Sopenharmony_ci l2cap_chan_del(chan, 0); 8008c2ecf20Sopenharmony_ci return NULL; 8018c2ecf20Sopenharmony_ci } 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci chan->conf_state = 0; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci if (locked) 8068c2ecf20Sopenharmony_ci __l2cap_chan_add(conn, chan); 8078c2ecf20Sopenharmony_ci else 8088c2ecf20Sopenharmony_ci l2cap_chan_add(conn, chan); 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_ci chan->remote_mps = chan->omtu; 8118c2ecf20Sopenharmony_ci chan->mps = chan->omtu; 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_ci chan->state = BT_CONNECTED; 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci return chan; 8168c2ecf20Sopenharmony_ci} 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci/* AMP Manager functions */ 8198c2ecf20Sopenharmony_cistruct amp_mgr *amp_mgr_get(struct amp_mgr *mgr) 8208c2ecf20Sopenharmony_ci{ 8218c2ecf20Sopenharmony_ci BT_DBG("mgr %p orig refcnt %d", mgr, kref_read(&mgr->kref)); 8228c2ecf20Sopenharmony_ci 8238c2ecf20Sopenharmony_ci kref_get(&mgr->kref); 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_ci return mgr; 8268c2ecf20Sopenharmony_ci} 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_cistatic void amp_mgr_destroy(struct kref *kref) 8298c2ecf20Sopenharmony_ci{ 8308c2ecf20Sopenharmony_ci struct amp_mgr *mgr = container_of(kref, struct amp_mgr, kref); 8318c2ecf20Sopenharmony_ci 8328c2ecf20Sopenharmony_ci BT_DBG("mgr %p", mgr); 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci mutex_lock(&_mgr_list_lock); 8358c2ecf20Sopenharmony_ci list_del(&mgr->list); 8368c2ecf20Sopenharmony_ci mutex_unlock(&_mgr_list_lock); 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_ci amp_ctrl_list_flush(mgr); 8398c2ecf20Sopenharmony_ci kfree(mgr); 8408c2ecf20Sopenharmony_ci} 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ciint amp_mgr_put(struct amp_mgr *mgr) 8438c2ecf20Sopenharmony_ci{ 8448c2ecf20Sopenharmony_ci BT_DBG("mgr %p orig refcnt %d", mgr, kref_read(&mgr->kref)); 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci return kref_put(&mgr->kref, &_mgr_destroy); 8478c2ecf20Sopenharmony_ci} 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_cistatic struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn, bool locked) 8508c2ecf20Sopenharmony_ci{ 8518c2ecf20Sopenharmony_ci struct amp_mgr *mgr; 8528c2ecf20Sopenharmony_ci struct l2cap_chan *chan; 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_ci mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); 8558c2ecf20Sopenharmony_ci if (!mgr) 8568c2ecf20Sopenharmony_ci return NULL; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci BT_DBG("conn %p mgr %p", conn, mgr); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci mgr->l2cap_conn = conn; 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci chan = a2mp_chan_open(conn, locked); 8638c2ecf20Sopenharmony_ci if (!chan) { 8648c2ecf20Sopenharmony_ci kfree(mgr); 8658c2ecf20Sopenharmony_ci return NULL; 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci mgr->a2mp_chan = chan; 8698c2ecf20Sopenharmony_ci chan->data = mgr; 8708c2ecf20Sopenharmony_ci 8718c2ecf20Sopenharmony_ci conn->hcon->amp_mgr = mgr; 8728c2ecf20Sopenharmony_ci 8738c2ecf20Sopenharmony_ci kref_init(&mgr->kref); 8748c2ecf20Sopenharmony_ci 8758c2ecf20Sopenharmony_ci /* Remote AMP ctrl list initialization */ 8768c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&mgr->amp_ctrls); 8778c2ecf20Sopenharmony_ci mutex_init(&mgr->amp_ctrls_lock); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci mutex_lock(&_mgr_list_lock); 8808c2ecf20Sopenharmony_ci list_add(&mgr->list, &_mgr_list); 8818c2ecf20Sopenharmony_ci mutex_unlock(&_mgr_list_lock); 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci return mgr; 8848c2ecf20Sopenharmony_ci} 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_cistruct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn, 8878c2ecf20Sopenharmony_ci struct sk_buff *skb) 8888c2ecf20Sopenharmony_ci{ 8898c2ecf20Sopenharmony_ci struct amp_mgr *mgr; 8908c2ecf20Sopenharmony_ci 8918c2ecf20Sopenharmony_ci if (conn->hcon->type != ACL_LINK) 8928c2ecf20Sopenharmony_ci return NULL; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci mgr = amp_mgr_create(conn, false); 8958c2ecf20Sopenharmony_ci if (!mgr) { 8968c2ecf20Sopenharmony_ci BT_ERR("Could not create AMP manager"); 8978c2ecf20Sopenharmony_ci return NULL; 8988c2ecf20Sopenharmony_ci } 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_ci BT_DBG("mgr: %p chan %p", mgr, mgr->a2mp_chan); 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_ci return mgr->a2mp_chan; 9038c2ecf20Sopenharmony_ci} 9048c2ecf20Sopenharmony_ci 9058c2ecf20Sopenharmony_civoid a2mp_send_getinfo_rsp(struct hci_dev *hdev) 9068c2ecf20Sopenharmony_ci{ 9078c2ecf20Sopenharmony_ci struct amp_mgr *mgr; 9088c2ecf20Sopenharmony_ci struct a2mp_info_rsp rsp; 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci mgr = amp_mgr_lookup_by_state(READ_LOC_AMP_INFO); 9118c2ecf20Sopenharmony_ci if (!mgr) 9128c2ecf20Sopenharmony_ci return; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci BT_DBG("%s mgr %p", hdev->name, mgr); 9158c2ecf20Sopenharmony_ci 9168c2ecf20Sopenharmony_ci memset(&rsp, 0, sizeof(rsp)); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci rsp.id = hdev->id; 9198c2ecf20Sopenharmony_ci rsp.status = A2MP_STATUS_INVALID_CTRL_ID; 9208c2ecf20Sopenharmony_ci 9218c2ecf20Sopenharmony_ci if (hdev->amp_type != AMP_TYPE_BREDR) { 9228c2ecf20Sopenharmony_ci rsp.status = 0; 9238c2ecf20Sopenharmony_ci rsp.total_bw = cpu_to_le32(hdev->amp_total_bw); 9248c2ecf20Sopenharmony_ci rsp.max_bw = cpu_to_le32(hdev->amp_max_bw); 9258c2ecf20Sopenharmony_ci rsp.min_latency = cpu_to_le32(hdev->amp_min_latency); 9268c2ecf20Sopenharmony_ci rsp.pal_cap = cpu_to_le16(hdev->amp_pal_cap); 9278c2ecf20Sopenharmony_ci rsp.assoc_size = cpu_to_le16(hdev->amp_assoc_size); 9288c2ecf20Sopenharmony_ci } 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci a2mp_send(mgr, A2MP_GETINFO_RSP, mgr->ident, sizeof(rsp), &rsp); 9318c2ecf20Sopenharmony_ci amp_mgr_put(mgr); 9328c2ecf20Sopenharmony_ci} 9338c2ecf20Sopenharmony_ci 9348c2ecf20Sopenharmony_civoid a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status) 9358c2ecf20Sopenharmony_ci{ 9368c2ecf20Sopenharmony_ci struct amp_mgr *mgr; 9378c2ecf20Sopenharmony_ci struct amp_assoc *loc_assoc = &hdev->loc_assoc; 9388c2ecf20Sopenharmony_ci struct a2mp_amp_assoc_rsp *rsp; 9398c2ecf20Sopenharmony_ci size_t len; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci mgr = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC); 9428c2ecf20Sopenharmony_ci if (!mgr) 9438c2ecf20Sopenharmony_ci return; 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci BT_DBG("%s mgr %p", hdev->name, mgr); 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci len = sizeof(struct a2mp_amp_assoc_rsp) + loc_assoc->len; 9488c2ecf20Sopenharmony_ci rsp = kzalloc(len, GFP_KERNEL); 9498c2ecf20Sopenharmony_ci if (!rsp) { 9508c2ecf20Sopenharmony_ci amp_mgr_put(mgr); 9518c2ecf20Sopenharmony_ci return; 9528c2ecf20Sopenharmony_ci } 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci rsp->id = hdev->id; 9558c2ecf20Sopenharmony_ci 9568c2ecf20Sopenharmony_ci if (status) { 9578c2ecf20Sopenharmony_ci rsp->status = A2MP_STATUS_INVALID_CTRL_ID; 9588c2ecf20Sopenharmony_ci } else { 9598c2ecf20Sopenharmony_ci rsp->status = A2MP_STATUS_SUCCESS; 9608c2ecf20Sopenharmony_ci memcpy(rsp->amp_assoc, loc_assoc->data, loc_assoc->len); 9618c2ecf20Sopenharmony_ci } 9628c2ecf20Sopenharmony_ci 9638c2ecf20Sopenharmony_ci a2mp_send(mgr, A2MP_GETAMPASSOC_RSP, mgr->ident, len, rsp); 9648c2ecf20Sopenharmony_ci amp_mgr_put(mgr); 9658c2ecf20Sopenharmony_ci kfree(rsp); 9668c2ecf20Sopenharmony_ci} 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_civoid a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status) 9698c2ecf20Sopenharmony_ci{ 9708c2ecf20Sopenharmony_ci struct amp_mgr *mgr; 9718c2ecf20Sopenharmony_ci struct amp_assoc *loc_assoc = &hdev->loc_assoc; 9728c2ecf20Sopenharmony_ci struct a2mp_physlink_req *req; 9738c2ecf20Sopenharmony_ci struct l2cap_chan *bredr_chan; 9748c2ecf20Sopenharmony_ci size_t len; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci mgr = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC_FINAL); 9778c2ecf20Sopenharmony_ci if (!mgr) 9788c2ecf20Sopenharmony_ci return; 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci len = sizeof(*req) + loc_assoc->len; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci BT_DBG("%s mgr %p assoc_len %zu", hdev->name, mgr, len); 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci req = kzalloc(len, GFP_KERNEL); 9858c2ecf20Sopenharmony_ci if (!req) { 9868c2ecf20Sopenharmony_ci amp_mgr_put(mgr); 9878c2ecf20Sopenharmony_ci return; 9888c2ecf20Sopenharmony_ci } 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci bredr_chan = mgr->bredr_chan; 9918c2ecf20Sopenharmony_ci if (!bredr_chan) 9928c2ecf20Sopenharmony_ci goto clean; 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_ci req->local_id = hdev->id; 9958c2ecf20Sopenharmony_ci req->remote_id = bredr_chan->remote_amp_id; 9968c2ecf20Sopenharmony_ci memcpy(req->amp_assoc, loc_assoc->data, loc_assoc->len); 9978c2ecf20Sopenharmony_ci 9988c2ecf20Sopenharmony_ci a2mp_send(mgr, A2MP_CREATEPHYSLINK_REQ, __next_ident(mgr), len, req); 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ciclean: 10018c2ecf20Sopenharmony_ci amp_mgr_put(mgr); 10028c2ecf20Sopenharmony_ci kfree(req); 10038c2ecf20Sopenharmony_ci} 10048c2ecf20Sopenharmony_ci 10058c2ecf20Sopenharmony_civoid a2mp_send_create_phy_link_rsp(struct hci_dev *hdev, u8 status) 10068c2ecf20Sopenharmony_ci{ 10078c2ecf20Sopenharmony_ci struct amp_mgr *mgr; 10088c2ecf20Sopenharmony_ci struct a2mp_physlink_rsp rsp; 10098c2ecf20Sopenharmony_ci struct hci_conn *hs_hcon; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci mgr = amp_mgr_lookup_by_state(WRITE_REMOTE_AMP_ASSOC); 10128c2ecf20Sopenharmony_ci if (!mgr) 10138c2ecf20Sopenharmony_ci return; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci memset(&rsp, 0, sizeof(rsp)); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci hs_hcon = hci_conn_hash_lookup_state(hdev, AMP_LINK, BT_CONNECT); 10188c2ecf20Sopenharmony_ci if (!hs_hcon) { 10198c2ecf20Sopenharmony_ci rsp.status = A2MP_STATUS_UNABLE_START_LINK_CREATION; 10208c2ecf20Sopenharmony_ci } else { 10218c2ecf20Sopenharmony_ci rsp.remote_id = hs_hcon->remote_id; 10228c2ecf20Sopenharmony_ci rsp.status = A2MP_STATUS_SUCCESS; 10238c2ecf20Sopenharmony_ci } 10248c2ecf20Sopenharmony_ci 10258c2ecf20Sopenharmony_ci BT_DBG("%s mgr %p hs_hcon %p status %u", hdev->name, mgr, hs_hcon, 10268c2ecf20Sopenharmony_ci status); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci rsp.local_id = hdev->id; 10298c2ecf20Sopenharmony_ci a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, mgr->ident, sizeof(rsp), &rsp); 10308c2ecf20Sopenharmony_ci amp_mgr_put(mgr); 10318c2ecf20Sopenharmony_ci} 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_civoid a2mp_discover_amp(struct l2cap_chan *chan) 10348c2ecf20Sopenharmony_ci{ 10358c2ecf20Sopenharmony_ci struct l2cap_conn *conn = chan->conn; 10368c2ecf20Sopenharmony_ci struct amp_mgr *mgr = conn->hcon->amp_mgr; 10378c2ecf20Sopenharmony_ci struct a2mp_discov_req req; 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ci BT_DBG("chan %p conn %p mgr %p", chan, conn, mgr); 10408c2ecf20Sopenharmony_ci 10418c2ecf20Sopenharmony_ci if (!mgr) { 10428c2ecf20Sopenharmony_ci mgr = amp_mgr_create(conn, true); 10438c2ecf20Sopenharmony_ci if (!mgr) 10448c2ecf20Sopenharmony_ci return; 10458c2ecf20Sopenharmony_ci } 10468c2ecf20Sopenharmony_ci 10478c2ecf20Sopenharmony_ci mgr->bredr_chan = chan; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci memset(&req, 0, sizeof(req)); 10508c2ecf20Sopenharmony_ci 10518c2ecf20Sopenharmony_ci req.mtu = cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU); 10528c2ecf20Sopenharmony_ci req.ext_feat = 0; 10538c2ecf20Sopenharmony_ci a2mp_send(mgr, A2MP_DISCOVER_REQ, 1, sizeof(req), &req); 10548c2ecf20Sopenharmony_ci} 1055