xref: /OK3568_Linux_fs/kernel/net/bluetooth/a2mp.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun    Copyright (c) 2010,2011 Code Aurora Forum.  All rights reserved.
4*4882a593Smuzhiyun    Copyright (c) 2011,2012 Intel Corp.
5*4882a593Smuzhiyun 
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #include <net/bluetooth/bluetooth.h>
9*4882a593Smuzhiyun #include <net/bluetooth/hci_core.h>
10*4882a593Smuzhiyun #include <net/bluetooth/l2cap.h>
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include "hci_request.h"
13*4882a593Smuzhiyun #include "a2mp.h"
14*4882a593Smuzhiyun #include "amp.h"
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun #define A2MP_FEAT_EXT	0x8000
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun /* Global AMP Manager list */
19*4882a593Smuzhiyun static LIST_HEAD(amp_mgr_list);
20*4882a593Smuzhiyun static DEFINE_MUTEX(amp_mgr_list_lock);
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun /* A2MP build & send command helper functions */
__a2mp_build(u8 code,u8 ident,u16 len,void * data)23*4882a593Smuzhiyun static struct a2mp_cmd *__a2mp_build(u8 code, u8 ident, u16 len, void *data)
24*4882a593Smuzhiyun {
25*4882a593Smuzhiyun 	struct a2mp_cmd *cmd;
26*4882a593Smuzhiyun 	int plen;
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun 	plen = sizeof(*cmd) + len;
29*4882a593Smuzhiyun 	cmd = kzalloc(plen, GFP_KERNEL);
30*4882a593Smuzhiyun 	if (!cmd)
31*4882a593Smuzhiyun 		return NULL;
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun 	cmd->code = code;
34*4882a593Smuzhiyun 	cmd->ident = ident;
35*4882a593Smuzhiyun 	cmd->len = cpu_to_le16(len);
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun 	memcpy(cmd->data, data, len);
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun 	return cmd;
40*4882a593Smuzhiyun }
41*4882a593Smuzhiyun 
a2mp_send(struct amp_mgr * mgr,u8 code,u8 ident,u16 len,void * data)42*4882a593Smuzhiyun static void a2mp_send(struct amp_mgr *mgr, u8 code, u8 ident, u16 len, void *data)
43*4882a593Smuzhiyun {
44*4882a593Smuzhiyun 	struct l2cap_chan *chan = mgr->a2mp_chan;
45*4882a593Smuzhiyun 	struct a2mp_cmd *cmd;
46*4882a593Smuzhiyun 	u16 total_len = len + sizeof(*cmd);
47*4882a593Smuzhiyun 	struct kvec iv;
48*4882a593Smuzhiyun 	struct msghdr msg;
49*4882a593Smuzhiyun 
50*4882a593Smuzhiyun 	cmd = __a2mp_build(code, ident, len, data);
51*4882a593Smuzhiyun 	if (!cmd)
52*4882a593Smuzhiyun 		return;
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun 	iv.iov_base = cmd;
55*4882a593Smuzhiyun 	iv.iov_len = total_len;
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 	memset(&msg, 0, sizeof(msg));
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun 	iov_iter_kvec(&msg.msg_iter, WRITE, &iv, 1, total_len);
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun 	l2cap_chan_send(chan, &msg, total_len);
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun 	kfree(cmd);
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun 
__next_ident(struct amp_mgr * mgr)66*4882a593Smuzhiyun static u8 __next_ident(struct amp_mgr *mgr)
67*4882a593Smuzhiyun {
68*4882a593Smuzhiyun 	if (++mgr->ident == 0)
69*4882a593Smuzhiyun 		mgr->ident = 1;
70*4882a593Smuzhiyun 
71*4882a593Smuzhiyun 	return mgr->ident;
72*4882a593Smuzhiyun }
73*4882a593Smuzhiyun 
amp_mgr_lookup_by_state(u8 state)74*4882a593Smuzhiyun static struct amp_mgr *amp_mgr_lookup_by_state(u8 state)
75*4882a593Smuzhiyun {
76*4882a593Smuzhiyun 	struct amp_mgr *mgr;
77*4882a593Smuzhiyun 
78*4882a593Smuzhiyun 	mutex_lock(&amp_mgr_list_lock);
79*4882a593Smuzhiyun 	list_for_each_entry(mgr, &amp_mgr_list, list) {
80*4882a593Smuzhiyun 		if (test_and_clear_bit(state, &mgr->state)) {
81*4882a593Smuzhiyun 			amp_mgr_get(mgr);
82*4882a593Smuzhiyun 			mutex_unlock(&amp_mgr_list_lock);
83*4882a593Smuzhiyun 			return mgr;
84*4882a593Smuzhiyun 		}
85*4882a593Smuzhiyun 	}
86*4882a593Smuzhiyun 	mutex_unlock(&amp_mgr_list_lock);
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	return NULL;
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun /* hci_dev_list shall be locked */
__a2mp_add_cl(struct amp_mgr * mgr,struct a2mp_cl * cl)92*4882a593Smuzhiyun static void __a2mp_add_cl(struct amp_mgr *mgr, struct a2mp_cl *cl)
93*4882a593Smuzhiyun {
94*4882a593Smuzhiyun 	struct hci_dev *hdev;
95*4882a593Smuzhiyun 	int i = 1;
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	cl[0].id = AMP_ID_BREDR;
98*4882a593Smuzhiyun 	cl[0].type = AMP_TYPE_BREDR;
99*4882a593Smuzhiyun 	cl[0].status = AMP_STATUS_BLUETOOTH_ONLY;
100*4882a593Smuzhiyun 
101*4882a593Smuzhiyun 	list_for_each_entry(hdev, &hci_dev_list, list) {
102*4882a593Smuzhiyun 		if (hdev->dev_type == HCI_AMP) {
103*4882a593Smuzhiyun 			cl[i].id = hdev->id;
104*4882a593Smuzhiyun 			cl[i].type = hdev->amp_type;
105*4882a593Smuzhiyun 			if (test_bit(HCI_UP, &hdev->flags))
106*4882a593Smuzhiyun 				cl[i].status = hdev->amp_status;
107*4882a593Smuzhiyun 			else
108*4882a593Smuzhiyun 				cl[i].status = AMP_STATUS_POWERED_DOWN;
109*4882a593Smuzhiyun 			i++;
110*4882a593Smuzhiyun 		}
111*4882a593Smuzhiyun 	}
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun /* Processing A2MP messages */
a2mp_command_rej(struct amp_mgr * mgr,struct sk_buff * skb,struct a2mp_cmd * hdr)115*4882a593Smuzhiyun static int a2mp_command_rej(struct amp_mgr *mgr, struct sk_buff *skb,
116*4882a593Smuzhiyun 			    struct a2mp_cmd *hdr)
117*4882a593Smuzhiyun {
118*4882a593Smuzhiyun 	struct a2mp_cmd_rej *rej = (void *) skb->data;
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun 	if (le16_to_cpu(hdr->len) < sizeof(*rej))
121*4882a593Smuzhiyun 		return -EINVAL;
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	BT_DBG("ident %d reason %d", hdr->ident, le16_to_cpu(rej->reason));
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun 	skb_pull(skb, sizeof(*rej));
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 	return 0;
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun 
a2mp_discover_req(struct amp_mgr * mgr,struct sk_buff * skb,struct a2mp_cmd * hdr)130*4882a593Smuzhiyun static int a2mp_discover_req(struct amp_mgr *mgr, struct sk_buff *skb,
131*4882a593Smuzhiyun 			     struct a2mp_cmd *hdr)
132*4882a593Smuzhiyun {
133*4882a593Smuzhiyun 	struct a2mp_discov_req *req = (void *) skb->data;
134*4882a593Smuzhiyun 	u16 len = le16_to_cpu(hdr->len);
135*4882a593Smuzhiyun 	struct a2mp_discov_rsp *rsp;
136*4882a593Smuzhiyun 	u16 ext_feat;
137*4882a593Smuzhiyun 	u8 num_ctrl;
138*4882a593Smuzhiyun 	struct hci_dev *hdev;
139*4882a593Smuzhiyun 
140*4882a593Smuzhiyun 	if (len < sizeof(*req))
141*4882a593Smuzhiyun 		return -EINVAL;
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 	skb_pull(skb, sizeof(*req));
144*4882a593Smuzhiyun 
145*4882a593Smuzhiyun 	ext_feat = le16_to_cpu(req->ext_feat);
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 	BT_DBG("mtu %d efm 0x%4.4x", le16_to_cpu(req->mtu), ext_feat);
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun 	/* check that packet is not broken for now */
150*4882a593Smuzhiyun 	while (ext_feat & A2MP_FEAT_EXT) {
151*4882a593Smuzhiyun 		if (len < sizeof(ext_feat))
152*4882a593Smuzhiyun 			return -EINVAL;
153*4882a593Smuzhiyun 
154*4882a593Smuzhiyun 		ext_feat = get_unaligned_le16(skb->data);
155*4882a593Smuzhiyun 		BT_DBG("efm 0x%4.4x", ext_feat);
156*4882a593Smuzhiyun 		len -= sizeof(ext_feat);
157*4882a593Smuzhiyun 		skb_pull(skb, sizeof(ext_feat));
158*4882a593Smuzhiyun 	}
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	read_lock(&hci_dev_list_lock);
161*4882a593Smuzhiyun 
162*4882a593Smuzhiyun 	/* at minimum the BR/EDR needs to be listed */
163*4882a593Smuzhiyun 	num_ctrl = 1;
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun 	list_for_each_entry(hdev, &hci_dev_list, list) {
166*4882a593Smuzhiyun 		if (hdev->dev_type == HCI_AMP)
167*4882a593Smuzhiyun 			num_ctrl++;
168*4882a593Smuzhiyun 	}
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun 	len = struct_size(rsp, cl, num_ctrl);
171*4882a593Smuzhiyun 	rsp = kmalloc(len, GFP_ATOMIC);
172*4882a593Smuzhiyun 	if (!rsp) {
173*4882a593Smuzhiyun 		read_unlock(&hci_dev_list_lock);
174*4882a593Smuzhiyun 		return -ENOMEM;
175*4882a593Smuzhiyun 	}
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 	rsp->mtu = cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU);
178*4882a593Smuzhiyun 	rsp->ext_feat = 0;
179*4882a593Smuzhiyun 
180*4882a593Smuzhiyun 	__a2mp_add_cl(mgr, rsp->cl);
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun 	read_unlock(&hci_dev_list_lock);
183*4882a593Smuzhiyun 
184*4882a593Smuzhiyun 	a2mp_send(mgr, A2MP_DISCOVER_RSP, hdr->ident, len, rsp);
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun 	kfree(rsp);
187*4882a593Smuzhiyun 	return 0;
188*4882a593Smuzhiyun }
189*4882a593Smuzhiyun 
a2mp_discover_rsp(struct amp_mgr * mgr,struct sk_buff * skb,struct a2mp_cmd * hdr)190*4882a593Smuzhiyun static int a2mp_discover_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
191*4882a593Smuzhiyun 			     struct a2mp_cmd *hdr)
192*4882a593Smuzhiyun {
193*4882a593Smuzhiyun 	struct a2mp_discov_rsp *rsp = (void *) skb->data;
194*4882a593Smuzhiyun 	u16 len = le16_to_cpu(hdr->len);
195*4882a593Smuzhiyun 	struct a2mp_cl *cl;
196*4882a593Smuzhiyun 	u16 ext_feat;
197*4882a593Smuzhiyun 	bool found = false;
198*4882a593Smuzhiyun 
199*4882a593Smuzhiyun 	if (len < sizeof(*rsp))
200*4882a593Smuzhiyun 		return -EINVAL;
201*4882a593Smuzhiyun 
202*4882a593Smuzhiyun 	len -= sizeof(*rsp);
203*4882a593Smuzhiyun 	skb_pull(skb, sizeof(*rsp));
204*4882a593Smuzhiyun 
205*4882a593Smuzhiyun 	ext_feat = le16_to_cpu(rsp->ext_feat);
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun 	BT_DBG("mtu %d efm 0x%4.4x", le16_to_cpu(rsp->mtu), ext_feat);
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	/* check that packet is not broken for now */
210*4882a593Smuzhiyun 	while (ext_feat & A2MP_FEAT_EXT) {
211*4882a593Smuzhiyun 		if (len < sizeof(ext_feat))
212*4882a593Smuzhiyun 			return -EINVAL;
213*4882a593Smuzhiyun 
214*4882a593Smuzhiyun 		ext_feat = get_unaligned_le16(skb->data);
215*4882a593Smuzhiyun 		BT_DBG("efm 0x%4.4x", ext_feat);
216*4882a593Smuzhiyun 		len -= sizeof(ext_feat);
217*4882a593Smuzhiyun 		skb_pull(skb, sizeof(ext_feat));
218*4882a593Smuzhiyun 	}
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun 	cl = (void *) skb->data;
221*4882a593Smuzhiyun 	while (len >= sizeof(*cl)) {
222*4882a593Smuzhiyun 		BT_DBG("Remote AMP id %d type %d status %d", cl->id, cl->type,
223*4882a593Smuzhiyun 		       cl->status);
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun 		if (cl->id != AMP_ID_BREDR && cl->type != AMP_TYPE_BREDR) {
226*4882a593Smuzhiyun 			struct a2mp_info_req req;
227*4882a593Smuzhiyun 
228*4882a593Smuzhiyun 			found = true;
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 			memset(&req, 0, sizeof(req));
231*4882a593Smuzhiyun 
232*4882a593Smuzhiyun 			req.id = cl->id;
233*4882a593Smuzhiyun 			a2mp_send(mgr, A2MP_GETINFO_REQ, __next_ident(mgr),
234*4882a593Smuzhiyun 				  sizeof(req), &req);
235*4882a593Smuzhiyun 		}
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun 		len -= sizeof(*cl);
238*4882a593Smuzhiyun 		cl = skb_pull(skb, sizeof(*cl));
239*4882a593Smuzhiyun 	}
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun 	/* Fall back to L2CAP init sequence */
242*4882a593Smuzhiyun 	if (!found) {
243*4882a593Smuzhiyun 		struct l2cap_conn *conn = mgr->l2cap_conn;
244*4882a593Smuzhiyun 		struct l2cap_chan *chan;
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun 		mutex_lock(&conn->chan_lock);
247*4882a593Smuzhiyun 
248*4882a593Smuzhiyun 		list_for_each_entry(chan, &conn->chan_l, list) {
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun 			BT_DBG("chan %p state %s", chan,
251*4882a593Smuzhiyun 			       state_to_string(chan->state));
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun 			if (chan->scid == L2CAP_CID_A2MP)
254*4882a593Smuzhiyun 				continue;
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun 			l2cap_chan_lock(chan);
257*4882a593Smuzhiyun 
258*4882a593Smuzhiyun 			if (chan->state == BT_CONNECT)
259*4882a593Smuzhiyun 				l2cap_send_conn_req(chan);
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun 			l2cap_chan_unlock(chan);
262*4882a593Smuzhiyun 		}
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun 		mutex_unlock(&conn->chan_lock);
265*4882a593Smuzhiyun 	}
266*4882a593Smuzhiyun 
267*4882a593Smuzhiyun 	return 0;
268*4882a593Smuzhiyun }
269*4882a593Smuzhiyun 
a2mp_change_notify(struct amp_mgr * mgr,struct sk_buff * skb,struct a2mp_cmd * hdr)270*4882a593Smuzhiyun static int a2mp_change_notify(struct amp_mgr *mgr, struct sk_buff *skb,
271*4882a593Smuzhiyun 			      struct a2mp_cmd *hdr)
272*4882a593Smuzhiyun {
273*4882a593Smuzhiyun 	struct a2mp_cl *cl = (void *) skb->data;
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun 	while (skb->len >= sizeof(*cl)) {
276*4882a593Smuzhiyun 		BT_DBG("Controller id %d type %d status %d", cl->id, cl->type,
277*4882a593Smuzhiyun 		       cl->status);
278*4882a593Smuzhiyun 		cl = skb_pull(skb, sizeof(*cl));
279*4882a593Smuzhiyun 	}
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	/* TODO send A2MP_CHANGE_RSP */
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 	return 0;
284*4882a593Smuzhiyun }
285*4882a593Smuzhiyun 
read_local_amp_info_complete(struct hci_dev * hdev,u8 status,u16 opcode)286*4882a593Smuzhiyun static void read_local_amp_info_complete(struct hci_dev *hdev, u8 status,
287*4882a593Smuzhiyun 					 u16 opcode)
288*4882a593Smuzhiyun {
289*4882a593Smuzhiyun 	BT_DBG("%s status 0x%2.2x", hdev->name, status);
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 	a2mp_send_getinfo_rsp(hdev);
292*4882a593Smuzhiyun }
293*4882a593Smuzhiyun 
a2mp_getinfo_req(struct amp_mgr * mgr,struct sk_buff * skb,struct a2mp_cmd * hdr)294*4882a593Smuzhiyun static int a2mp_getinfo_req(struct amp_mgr *mgr, struct sk_buff *skb,
295*4882a593Smuzhiyun 			    struct a2mp_cmd *hdr)
296*4882a593Smuzhiyun {
297*4882a593Smuzhiyun 	struct a2mp_info_req *req  = (void *) skb->data;
298*4882a593Smuzhiyun 	struct hci_dev *hdev;
299*4882a593Smuzhiyun 	struct hci_request hreq;
300*4882a593Smuzhiyun 	int err = 0;
301*4882a593Smuzhiyun 
302*4882a593Smuzhiyun 	if (le16_to_cpu(hdr->len) < sizeof(*req))
303*4882a593Smuzhiyun 		return -EINVAL;
304*4882a593Smuzhiyun 
305*4882a593Smuzhiyun 	BT_DBG("id %d", req->id);
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun 	hdev = hci_dev_get(req->id);
308*4882a593Smuzhiyun 	if (!hdev || hdev->dev_type != HCI_AMP) {
309*4882a593Smuzhiyun 		struct a2mp_info_rsp rsp;
310*4882a593Smuzhiyun 
311*4882a593Smuzhiyun 		memset(&rsp, 0, sizeof(rsp));
312*4882a593Smuzhiyun 
313*4882a593Smuzhiyun 		rsp.id = req->id;
314*4882a593Smuzhiyun 		rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun 		a2mp_send(mgr, A2MP_GETINFO_RSP, hdr->ident, sizeof(rsp),
317*4882a593Smuzhiyun 			  &rsp);
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun 		goto done;
320*4882a593Smuzhiyun 	}
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	set_bit(READ_LOC_AMP_INFO, &mgr->state);
323*4882a593Smuzhiyun 	hci_req_init(&hreq, hdev);
324*4882a593Smuzhiyun 	hci_req_add(&hreq, HCI_OP_READ_LOCAL_AMP_INFO, 0, NULL);
325*4882a593Smuzhiyun 	err = hci_req_run(&hreq, read_local_amp_info_complete);
326*4882a593Smuzhiyun 	if (err < 0)
327*4882a593Smuzhiyun 		a2mp_send_getinfo_rsp(hdev);
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun done:
330*4882a593Smuzhiyun 	if (hdev)
331*4882a593Smuzhiyun 		hci_dev_put(hdev);
332*4882a593Smuzhiyun 
333*4882a593Smuzhiyun 	skb_pull(skb, sizeof(*req));
334*4882a593Smuzhiyun 	return 0;
335*4882a593Smuzhiyun }
336*4882a593Smuzhiyun 
a2mp_getinfo_rsp(struct amp_mgr * mgr,struct sk_buff * skb,struct a2mp_cmd * hdr)337*4882a593Smuzhiyun static int a2mp_getinfo_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
338*4882a593Smuzhiyun 			    struct a2mp_cmd *hdr)
339*4882a593Smuzhiyun {
340*4882a593Smuzhiyun 	struct a2mp_info_rsp *rsp = (struct a2mp_info_rsp *) skb->data;
341*4882a593Smuzhiyun 	struct a2mp_amp_assoc_req req;
342*4882a593Smuzhiyun 	struct amp_ctrl *ctrl;
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun 	if (le16_to_cpu(hdr->len) < sizeof(*rsp))
345*4882a593Smuzhiyun 		return -EINVAL;
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun 	BT_DBG("id %d status 0x%2.2x", rsp->id, rsp->status);
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun 	if (rsp->status)
350*4882a593Smuzhiyun 		return -EINVAL;
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun 	ctrl = amp_ctrl_add(mgr, rsp->id);
353*4882a593Smuzhiyun 	if (!ctrl)
354*4882a593Smuzhiyun 		return -ENOMEM;
355*4882a593Smuzhiyun 
356*4882a593Smuzhiyun 	memset(&req, 0, sizeof(req));
357*4882a593Smuzhiyun 
358*4882a593Smuzhiyun 	req.id = rsp->id;
359*4882a593Smuzhiyun 	a2mp_send(mgr, A2MP_GETAMPASSOC_REQ, __next_ident(mgr), sizeof(req),
360*4882a593Smuzhiyun 		  &req);
361*4882a593Smuzhiyun 
362*4882a593Smuzhiyun 	skb_pull(skb, sizeof(*rsp));
363*4882a593Smuzhiyun 	return 0;
364*4882a593Smuzhiyun }
365*4882a593Smuzhiyun 
a2mp_getampassoc_req(struct amp_mgr * mgr,struct sk_buff * skb,struct a2mp_cmd * hdr)366*4882a593Smuzhiyun static int a2mp_getampassoc_req(struct amp_mgr *mgr, struct sk_buff *skb,
367*4882a593Smuzhiyun 				struct a2mp_cmd *hdr)
368*4882a593Smuzhiyun {
369*4882a593Smuzhiyun 	struct a2mp_amp_assoc_req *req = (void *) skb->data;
370*4882a593Smuzhiyun 	struct hci_dev *hdev;
371*4882a593Smuzhiyun 	struct amp_mgr *tmp;
372*4882a593Smuzhiyun 
373*4882a593Smuzhiyun 	if (le16_to_cpu(hdr->len) < sizeof(*req))
374*4882a593Smuzhiyun 		return -EINVAL;
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun 	BT_DBG("id %d", req->id);
377*4882a593Smuzhiyun 
378*4882a593Smuzhiyun 	/* Make sure that other request is not processed */
379*4882a593Smuzhiyun 	tmp = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC);
380*4882a593Smuzhiyun 
381*4882a593Smuzhiyun 	hdev = hci_dev_get(req->id);
382*4882a593Smuzhiyun 	if (!hdev || hdev->amp_type == AMP_TYPE_BREDR || tmp) {
383*4882a593Smuzhiyun 		struct a2mp_amp_assoc_rsp rsp;
384*4882a593Smuzhiyun 
385*4882a593Smuzhiyun 		memset(&rsp, 0, sizeof(rsp));
386*4882a593Smuzhiyun 		rsp.id = req->id;
387*4882a593Smuzhiyun 
388*4882a593Smuzhiyun 		if (tmp) {
389*4882a593Smuzhiyun 			rsp.status = A2MP_STATUS_COLLISION_OCCURED;
390*4882a593Smuzhiyun 			amp_mgr_put(tmp);
391*4882a593Smuzhiyun 		} else {
392*4882a593Smuzhiyun 			rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
393*4882a593Smuzhiyun 		}
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun 		a2mp_send(mgr, A2MP_GETAMPASSOC_RSP, hdr->ident, sizeof(rsp),
396*4882a593Smuzhiyun 			  &rsp);
397*4882a593Smuzhiyun 
398*4882a593Smuzhiyun 		goto done;
399*4882a593Smuzhiyun 	}
400*4882a593Smuzhiyun 
401*4882a593Smuzhiyun 	amp_read_loc_assoc(hdev, mgr);
402*4882a593Smuzhiyun 
403*4882a593Smuzhiyun done:
404*4882a593Smuzhiyun 	if (hdev)
405*4882a593Smuzhiyun 		hci_dev_put(hdev);
406*4882a593Smuzhiyun 
407*4882a593Smuzhiyun 	skb_pull(skb, sizeof(*req));
408*4882a593Smuzhiyun 	return 0;
409*4882a593Smuzhiyun }
410*4882a593Smuzhiyun 
a2mp_getampassoc_rsp(struct amp_mgr * mgr,struct sk_buff * skb,struct a2mp_cmd * hdr)411*4882a593Smuzhiyun static int a2mp_getampassoc_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
412*4882a593Smuzhiyun 				struct a2mp_cmd *hdr)
413*4882a593Smuzhiyun {
414*4882a593Smuzhiyun 	struct a2mp_amp_assoc_rsp *rsp = (void *) skb->data;
415*4882a593Smuzhiyun 	u16 len = le16_to_cpu(hdr->len);
416*4882a593Smuzhiyun 	struct hci_dev *hdev;
417*4882a593Smuzhiyun 	struct amp_ctrl *ctrl;
418*4882a593Smuzhiyun 	struct hci_conn *hcon;
419*4882a593Smuzhiyun 	size_t assoc_len;
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun 	if (len < sizeof(*rsp))
422*4882a593Smuzhiyun 		return -EINVAL;
423*4882a593Smuzhiyun 
424*4882a593Smuzhiyun 	assoc_len = len - sizeof(*rsp);
425*4882a593Smuzhiyun 
426*4882a593Smuzhiyun 	BT_DBG("id %d status 0x%2.2x assoc len %zu", rsp->id, rsp->status,
427*4882a593Smuzhiyun 	       assoc_len);
428*4882a593Smuzhiyun 
429*4882a593Smuzhiyun 	if (rsp->status)
430*4882a593Smuzhiyun 		return -EINVAL;
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun 	/* Save remote ASSOC data */
433*4882a593Smuzhiyun 	ctrl = amp_ctrl_lookup(mgr, rsp->id);
434*4882a593Smuzhiyun 	if (ctrl) {
435*4882a593Smuzhiyun 		u8 *assoc;
436*4882a593Smuzhiyun 
437*4882a593Smuzhiyun 		assoc = kmemdup(rsp->amp_assoc, assoc_len, GFP_KERNEL);
438*4882a593Smuzhiyun 		if (!assoc) {
439*4882a593Smuzhiyun 			amp_ctrl_put(ctrl);
440*4882a593Smuzhiyun 			return -ENOMEM;
441*4882a593Smuzhiyun 		}
442*4882a593Smuzhiyun 
443*4882a593Smuzhiyun 		ctrl->assoc = assoc;
444*4882a593Smuzhiyun 		ctrl->assoc_len = assoc_len;
445*4882a593Smuzhiyun 		ctrl->assoc_rem_len = assoc_len;
446*4882a593Smuzhiyun 		ctrl->assoc_len_so_far = 0;
447*4882a593Smuzhiyun 
448*4882a593Smuzhiyun 		amp_ctrl_put(ctrl);
449*4882a593Smuzhiyun 	}
450*4882a593Smuzhiyun 
451*4882a593Smuzhiyun 	/* Create Phys Link */
452*4882a593Smuzhiyun 	hdev = hci_dev_get(rsp->id);
453*4882a593Smuzhiyun 	if (!hdev)
454*4882a593Smuzhiyun 		return -EINVAL;
455*4882a593Smuzhiyun 
456*4882a593Smuzhiyun 	hcon = phylink_add(hdev, mgr, rsp->id, true);
457*4882a593Smuzhiyun 	if (!hcon)
458*4882a593Smuzhiyun 		goto done;
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	BT_DBG("Created hcon %p: loc:%d -> rem:%d", hcon, hdev->id, rsp->id);
461*4882a593Smuzhiyun 
462*4882a593Smuzhiyun 	mgr->bredr_chan->remote_amp_id = rsp->id;
463*4882a593Smuzhiyun 
464*4882a593Smuzhiyun 	amp_create_phylink(hdev, mgr, hcon);
465*4882a593Smuzhiyun 
466*4882a593Smuzhiyun done:
467*4882a593Smuzhiyun 	hci_dev_put(hdev);
468*4882a593Smuzhiyun 	skb_pull(skb, len);
469*4882a593Smuzhiyun 	return 0;
470*4882a593Smuzhiyun }
471*4882a593Smuzhiyun 
a2mp_createphyslink_req(struct amp_mgr * mgr,struct sk_buff * skb,struct a2mp_cmd * hdr)472*4882a593Smuzhiyun static int a2mp_createphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
473*4882a593Smuzhiyun 				   struct a2mp_cmd *hdr)
474*4882a593Smuzhiyun {
475*4882a593Smuzhiyun 	struct a2mp_physlink_req *req = (void *) skb->data;
476*4882a593Smuzhiyun 	struct a2mp_physlink_rsp rsp;
477*4882a593Smuzhiyun 	struct hci_dev *hdev;
478*4882a593Smuzhiyun 	struct hci_conn *hcon;
479*4882a593Smuzhiyun 	struct amp_ctrl *ctrl;
480*4882a593Smuzhiyun 
481*4882a593Smuzhiyun 	if (le16_to_cpu(hdr->len) < sizeof(*req))
482*4882a593Smuzhiyun 		return -EINVAL;
483*4882a593Smuzhiyun 
484*4882a593Smuzhiyun 	BT_DBG("local_id %d, remote_id %d", req->local_id, req->remote_id);
485*4882a593Smuzhiyun 
486*4882a593Smuzhiyun 	memset(&rsp, 0, sizeof(rsp));
487*4882a593Smuzhiyun 
488*4882a593Smuzhiyun 	rsp.local_id = req->remote_id;
489*4882a593Smuzhiyun 	rsp.remote_id = req->local_id;
490*4882a593Smuzhiyun 
491*4882a593Smuzhiyun 	hdev = hci_dev_get(req->remote_id);
492*4882a593Smuzhiyun 	if (!hdev || hdev->amp_type == AMP_TYPE_BREDR) {
493*4882a593Smuzhiyun 		rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
494*4882a593Smuzhiyun 		goto send_rsp;
495*4882a593Smuzhiyun 	}
496*4882a593Smuzhiyun 
497*4882a593Smuzhiyun 	ctrl = amp_ctrl_lookup(mgr, rsp.remote_id);
498*4882a593Smuzhiyun 	if (!ctrl) {
499*4882a593Smuzhiyun 		ctrl = amp_ctrl_add(mgr, rsp.remote_id);
500*4882a593Smuzhiyun 		if (ctrl) {
501*4882a593Smuzhiyun 			amp_ctrl_get(ctrl);
502*4882a593Smuzhiyun 		} else {
503*4882a593Smuzhiyun 			rsp.status = A2MP_STATUS_UNABLE_START_LINK_CREATION;
504*4882a593Smuzhiyun 			goto send_rsp;
505*4882a593Smuzhiyun 		}
506*4882a593Smuzhiyun 	}
507*4882a593Smuzhiyun 
508*4882a593Smuzhiyun 	if (ctrl) {
509*4882a593Smuzhiyun 		size_t assoc_len = le16_to_cpu(hdr->len) - sizeof(*req);
510*4882a593Smuzhiyun 		u8 *assoc;
511*4882a593Smuzhiyun 
512*4882a593Smuzhiyun 		assoc = kmemdup(req->amp_assoc, assoc_len, GFP_KERNEL);
513*4882a593Smuzhiyun 		if (!assoc) {
514*4882a593Smuzhiyun 			amp_ctrl_put(ctrl);
515*4882a593Smuzhiyun 			hci_dev_put(hdev);
516*4882a593Smuzhiyun 			return -ENOMEM;
517*4882a593Smuzhiyun 		}
518*4882a593Smuzhiyun 
519*4882a593Smuzhiyun 		ctrl->assoc = assoc;
520*4882a593Smuzhiyun 		ctrl->assoc_len = assoc_len;
521*4882a593Smuzhiyun 		ctrl->assoc_rem_len = assoc_len;
522*4882a593Smuzhiyun 		ctrl->assoc_len_so_far = 0;
523*4882a593Smuzhiyun 
524*4882a593Smuzhiyun 		amp_ctrl_put(ctrl);
525*4882a593Smuzhiyun 	}
526*4882a593Smuzhiyun 
527*4882a593Smuzhiyun 	hcon = phylink_add(hdev, mgr, req->local_id, false);
528*4882a593Smuzhiyun 	if (hcon) {
529*4882a593Smuzhiyun 		amp_accept_phylink(hdev, mgr, hcon);
530*4882a593Smuzhiyun 		rsp.status = A2MP_STATUS_SUCCESS;
531*4882a593Smuzhiyun 	} else {
532*4882a593Smuzhiyun 		rsp.status = A2MP_STATUS_UNABLE_START_LINK_CREATION;
533*4882a593Smuzhiyun 	}
534*4882a593Smuzhiyun 
535*4882a593Smuzhiyun send_rsp:
536*4882a593Smuzhiyun 	if (hdev)
537*4882a593Smuzhiyun 		hci_dev_put(hdev);
538*4882a593Smuzhiyun 
539*4882a593Smuzhiyun 	/* Reply error now and success after HCI Write Remote AMP Assoc
540*4882a593Smuzhiyun 	   command complete with success status
541*4882a593Smuzhiyun 	 */
542*4882a593Smuzhiyun 	if (rsp.status != A2MP_STATUS_SUCCESS) {
543*4882a593Smuzhiyun 		a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, hdr->ident,
544*4882a593Smuzhiyun 			  sizeof(rsp), &rsp);
545*4882a593Smuzhiyun 	} else {
546*4882a593Smuzhiyun 		set_bit(WRITE_REMOTE_AMP_ASSOC, &mgr->state);
547*4882a593Smuzhiyun 		mgr->ident = hdr->ident;
548*4882a593Smuzhiyun 	}
549*4882a593Smuzhiyun 
550*4882a593Smuzhiyun 	skb_pull(skb, le16_to_cpu(hdr->len));
551*4882a593Smuzhiyun 	return 0;
552*4882a593Smuzhiyun }
553*4882a593Smuzhiyun 
a2mp_discphyslink_req(struct amp_mgr * mgr,struct sk_buff * skb,struct a2mp_cmd * hdr)554*4882a593Smuzhiyun static int a2mp_discphyslink_req(struct amp_mgr *mgr, struct sk_buff *skb,
555*4882a593Smuzhiyun 				 struct a2mp_cmd *hdr)
556*4882a593Smuzhiyun {
557*4882a593Smuzhiyun 	struct a2mp_physlink_req *req = (void *) skb->data;
558*4882a593Smuzhiyun 	struct a2mp_physlink_rsp rsp;
559*4882a593Smuzhiyun 	struct hci_dev *hdev;
560*4882a593Smuzhiyun 	struct hci_conn *hcon;
561*4882a593Smuzhiyun 
562*4882a593Smuzhiyun 	if (le16_to_cpu(hdr->len) < sizeof(*req))
563*4882a593Smuzhiyun 		return -EINVAL;
564*4882a593Smuzhiyun 
565*4882a593Smuzhiyun 	BT_DBG("local_id %d remote_id %d", req->local_id, req->remote_id);
566*4882a593Smuzhiyun 
567*4882a593Smuzhiyun 	memset(&rsp, 0, sizeof(rsp));
568*4882a593Smuzhiyun 
569*4882a593Smuzhiyun 	rsp.local_id = req->remote_id;
570*4882a593Smuzhiyun 	rsp.remote_id = req->local_id;
571*4882a593Smuzhiyun 	rsp.status = A2MP_STATUS_SUCCESS;
572*4882a593Smuzhiyun 
573*4882a593Smuzhiyun 	hdev = hci_dev_get(req->remote_id);
574*4882a593Smuzhiyun 	if (!hdev) {
575*4882a593Smuzhiyun 		rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
576*4882a593Smuzhiyun 		goto send_rsp;
577*4882a593Smuzhiyun 	}
578*4882a593Smuzhiyun 
579*4882a593Smuzhiyun 	hcon = hci_conn_hash_lookup_ba(hdev, AMP_LINK,
580*4882a593Smuzhiyun 				       &mgr->l2cap_conn->hcon->dst);
581*4882a593Smuzhiyun 	if (!hcon) {
582*4882a593Smuzhiyun 		bt_dev_err(hdev, "no phys link exist");
583*4882a593Smuzhiyun 		rsp.status = A2MP_STATUS_NO_PHYSICAL_LINK_EXISTS;
584*4882a593Smuzhiyun 		goto clean;
585*4882a593Smuzhiyun 	}
586*4882a593Smuzhiyun 
587*4882a593Smuzhiyun 	/* TODO Disconnect Phys Link here */
588*4882a593Smuzhiyun 
589*4882a593Smuzhiyun clean:
590*4882a593Smuzhiyun 	hci_dev_put(hdev);
591*4882a593Smuzhiyun 
592*4882a593Smuzhiyun send_rsp:
593*4882a593Smuzhiyun 	a2mp_send(mgr, A2MP_DISCONNPHYSLINK_RSP, hdr->ident, sizeof(rsp), &rsp);
594*4882a593Smuzhiyun 
595*4882a593Smuzhiyun 	skb_pull(skb, sizeof(*req));
596*4882a593Smuzhiyun 	return 0;
597*4882a593Smuzhiyun }
598*4882a593Smuzhiyun 
a2mp_cmd_rsp(struct amp_mgr * mgr,struct sk_buff * skb,struct a2mp_cmd * hdr)599*4882a593Smuzhiyun static inline int a2mp_cmd_rsp(struct amp_mgr *mgr, struct sk_buff *skb,
600*4882a593Smuzhiyun 			       struct a2mp_cmd *hdr)
601*4882a593Smuzhiyun {
602*4882a593Smuzhiyun 	BT_DBG("ident %d code 0x%2.2x", hdr->ident, hdr->code);
603*4882a593Smuzhiyun 
604*4882a593Smuzhiyun 	skb_pull(skb, le16_to_cpu(hdr->len));
605*4882a593Smuzhiyun 	return 0;
606*4882a593Smuzhiyun }
607*4882a593Smuzhiyun 
608*4882a593Smuzhiyun /* Handle A2MP signalling */
a2mp_chan_recv_cb(struct l2cap_chan * chan,struct sk_buff * skb)609*4882a593Smuzhiyun static int a2mp_chan_recv_cb(struct l2cap_chan *chan, struct sk_buff *skb)
610*4882a593Smuzhiyun {
611*4882a593Smuzhiyun 	struct a2mp_cmd *hdr;
612*4882a593Smuzhiyun 	struct amp_mgr *mgr = chan->data;
613*4882a593Smuzhiyun 	int err = 0;
614*4882a593Smuzhiyun 
615*4882a593Smuzhiyun 	amp_mgr_get(mgr);
616*4882a593Smuzhiyun 
617*4882a593Smuzhiyun 	while (skb->len >= sizeof(*hdr)) {
618*4882a593Smuzhiyun 		u16 len;
619*4882a593Smuzhiyun 
620*4882a593Smuzhiyun 		hdr = (void *) skb->data;
621*4882a593Smuzhiyun 		len = le16_to_cpu(hdr->len);
622*4882a593Smuzhiyun 
623*4882a593Smuzhiyun 		BT_DBG("code 0x%2.2x id %d len %u", hdr->code, hdr->ident, len);
624*4882a593Smuzhiyun 
625*4882a593Smuzhiyun 		skb_pull(skb, sizeof(*hdr));
626*4882a593Smuzhiyun 
627*4882a593Smuzhiyun 		if (len > skb->len || !hdr->ident) {
628*4882a593Smuzhiyun 			err = -EINVAL;
629*4882a593Smuzhiyun 			break;
630*4882a593Smuzhiyun 		}
631*4882a593Smuzhiyun 
632*4882a593Smuzhiyun 		mgr->ident = hdr->ident;
633*4882a593Smuzhiyun 
634*4882a593Smuzhiyun 		switch (hdr->code) {
635*4882a593Smuzhiyun 		case A2MP_COMMAND_REJ:
636*4882a593Smuzhiyun 			a2mp_command_rej(mgr, skb, hdr);
637*4882a593Smuzhiyun 			break;
638*4882a593Smuzhiyun 
639*4882a593Smuzhiyun 		case A2MP_DISCOVER_REQ:
640*4882a593Smuzhiyun 			err = a2mp_discover_req(mgr, skb, hdr);
641*4882a593Smuzhiyun 			break;
642*4882a593Smuzhiyun 
643*4882a593Smuzhiyun 		case A2MP_CHANGE_NOTIFY:
644*4882a593Smuzhiyun 			err = a2mp_change_notify(mgr, skb, hdr);
645*4882a593Smuzhiyun 			break;
646*4882a593Smuzhiyun 
647*4882a593Smuzhiyun 		case A2MP_GETINFO_REQ:
648*4882a593Smuzhiyun 			err = a2mp_getinfo_req(mgr, skb, hdr);
649*4882a593Smuzhiyun 			break;
650*4882a593Smuzhiyun 
651*4882a593Smuzhiyun 		case A2MP_GETAMPASSOC_REQ:
652*4882a593Smuzhiyun 			err = a2mp_getampassoc_req(mgr, skb, hdr);
653*4882a593Smuzhiyun 			break;
654*4882a593Smuzhiyun 
655*4882a593Smuzhiyun 		case A2MP_CREATEPHYSLINK_REQ:
656*4882a593Smuzhiyun 			err = a2mp_createphyslink_req(mgr, skb, hdr);
657*4882a593Smuzhiyun 			break;
658*4882a593Smuzhiyun 
659*4882a593Smuzhiyun 		case A2MP_DISCONNPHYSLINK_REQ:
660*4882a593Smuzhiyun 			err = a2mp_discphyslink_req(mgr, skb, hdr);
661*4882a593Smuzhiyun 			break;
662*4882a593Smuzhiyun 
663*4882a593Smuzhiyun 		case A2MP_DISCOVER_RSP:
664*4882a593Smuzhiyun 			err = a2mp_discover_rsp(mgr, skb, hdr);
665*4882a593Smuzhiyun 			break;
666*4882a593Smuzhiyun 
667*4882a593Smuzhiyun 		case A2MP_GETINFO_RSP:
668*4882a593Smuzhiyun 			err = a2mp_getinfo_rsp(mgr, skb, hdr);
669*4882a593Smuzhiyun 			break;
670*4882a593Smuzhiyun 
671*4882a593Smuzhiyun 		case A2MP_GETAMPASSOC_RSP:
672*4882a593Smuzhiyun 			err = a2mp_getampassoc_rsp(mgr, skb, hdr);
673*4882a593Smuzhiyun 			break;
674*4882a593Smuzhiyun 
675*4882a593Smuzhiyun 		case A2MP_CHANGE_RSP:
676*4882a593Smuzhiyun 		case A2MP_CREATEPHYSLINK_RSP:
677*4882a593Smuzhiyun 		case A2MP_DISCONNPHYSLINK_RSP:
678*4882a593Smuzhiyun 			err = a2mp_cmd_rsp(mgr, skb, hdr);
679*4882a593Smuzhiyun 			break;
680*4882a593Smuzhiyun 
681*4882a593Smuzhiyun 		default:
682*4882a593Smuzhiyun 			BT_ERR("Unknown A2MP sig cmd 0x%2.2x", hdr->code);
683*4882a593Smuzhiyun 			err = -EINVAL;
684*4882a593Smuzhiyun 			break;
685*4882a593Smuzhiyun 		}
686*4882a593Smuzhiyun 	}
687*4882a593Smuzhiyun 
688*4882a593Smuzhiyun 	if (err) {
689*4882a593Smuzhiyun 		struct a2mp_cmd_rej rej;
690*4882a593Smuzhiyun 
691*4882a593Smuzhiyun 		memset(&rej, 0, sizeof(rej));
692*4882a593Smuzhiyun 
693*4882a593Smuzhiyun 		rej.reason = cpu_to_le16(0);
694*4882a593Smuzhiyun 		hdr = (void *) skb->data;
695*4882a593Smuzhiyun 
696*4882a593Smuzhiyun 		BT_DBG("Send A2MP Rej: cmd 0x%2.2x err %d", hdr->code, err);
697*4882a593Smuzhiyun 
698*4882a593Smuzhiyun 		a2mp_send(mgr, A2MP_COMMAND_REJ, hdr->ident, sizeof(rej),
699*4882a593Smuzhiyun 			  &rej);
700*4882a593Smuzhiyun 	}
701*4882a593Smuzhiyun 
702*4882a593Smuzhiyun 	/* Always free skb and return success error code to prevent
703*4882a593Smuzhiyun 	   from sending L2CAP Disconnect over A2MP channel */
704*4882a593Smuzhiyun 	kfree_skb(skb);
705*4882a593Smuzhiyun 
706*4882a593Smuzhiyun 	amp_mgr_put(mgr);
707*4882a593Smuzhiyun 
708*4882a593Smuzhiyun 	return 0;
709*4882a593Smuzhiyun }
710*4882a593Smuzhiyun 
a2mp_chan_close_cb(struct l2cap_chan * chan)711*4882a593Smuzhiyun static void a2mp_chan_close_cb(struct l2cap_chan *chan)
712*4882a593Smuzhiyun {
713*4882a593Smuzhiyun 	l2cap_chan_put(chan);
714*4882a593Smuzhiyun }
715*4882a593Smuzhiyun 
a2mp_chan_state_change_cb(struct l2cap_chan * chan,int state,int err)716*4882a593Smuzhiyun static void a2mp_chan_state_change_cb(struct l2cap_chan *chan, int state,
717*4882a593Smuzhiyun 				      int err)
718*4882a593Smuzhiyun {
719*4882a593Smuzhiyun 	struct amp_mgr *mgr = chan->data;
720*4882a593Smuzhiyun 
721*4882a593Smuzhiyun 	if (!mgr)
722*4882a593Smuzhiyun 		return;
723*4882a593Smuzhiyun 
724*4882a593Smuzhiyun 	BT_DBG("chan %p state %s", chan, state_to_string(state));
725*4882a593Smuzhiyun 
726*4882a593Smuzhiyun 	chan->state = state;
727*4882a593Smuzhiyun 
728*4882a593Smuzhiyun 	switch (state) {
729*4882a593Smuzhiyun 	case BT_CLOSED:
730*4882a593Smuzhiyun 		if (mgr)
731*4882a593Smuzhiyun 			amp_mgr_put(mgr);
732*4882a593Smuzhiyun 		break;
733*4882a593Smuzhiyun 	}
734*4882a593Smuzhiyun }
735*4882a593Smuzhiyun 
a2mp_chan_alloc_skb_cb(struct l2cap_chan * chan,unsigned long hdr_len,unsigned long len,int nb)736*4882a593Smuzhiyun static struct sk_buff *a2mp_chan_alloc_skb_cb(struct l2cap_chan *chan,
737*4882a593Smuzhiyun 					      unsigned long hdr_len,
738*4882a593Smuzhiyun 					      unsigned long len, int nb)
739*4882a593Smuzhiyun {
740*4882a593Smuzhiyun 	struct sk_buff *skb;
741*4882a593Smuzhiyun 
742*4882a593Smuzhiyun 	skb = bt_skb_alloc(hdr_len + len, GFP_KERNEL);
743*4882a593Smuzhiyun 	if (!skb)
744*4882a593Smuzhiyun 		return ERR_PTR(-ENOMEM);
745*4882a593Smuzhiyun 
746*4882a593Smuzhiyun 	return skb;
747*4882a593Smuzhiyun }
748*4882a593Smuzhiyun 
749*4882a593Smuzhiyun static const struct l2cap_ops a2mp_chan_ops = {
750*4882a593Smuzhiyun 	.name = "L2CAP A2MP channel",
751*4882a593Smuzhiyun 	.recv = a2mp_chan_recv_cb,
752*4882a593Smuzhiyun 	.close = a2mp_chan_close_cb,
753*4882a593Smuzhiyun 	.state_change = a2mp_chan_state_change_cb,
754*4882a593Smuzhiyun 	.alloc_skb = a2mp_chan_alloc_skb_cb,
755*4882a593Smuzhiyun 
756*4882a593Smuzhiyun 	/* Not implemented for A2MP */
757*4882a593Smuzhiyun 	.new_connection = l2cap_chan_no_new_connection,
758*4882a593Smuzhiyun 	.teardown = l2cap_chan_no_teardown,
759*4882a593Smuzhiyun 	.ready = l2cap_chan_no_ready,
760*4882a593Smuzhiyun 	.defer = l2cap_chan_no_defer,
761*4882a593Smuzhiyun 	.resume = l2cap_chan_no_resume,
762*4882a593Smuzhiyun 	.set_shutdown = l2cap_chan_no_set_shutdown,
763*4882a593Smuzhiyun 	.get_sndtimeo = l2cap_chan_no_get_sndtimeo,
764*4882a593Smuzhiyun };
765*4882a593Smuzhiyun 
a2mp_chan_open(struct l2cap_conn * conn,bool locked)766*4882a593Smuzhiyun static struct l2cap_chan *a2mp_chan_open(struct l2cap_conn *conn, bool locked)
767*4882a593Smuzhiyun {
768*4882a593Smuzhiyun 	struct l2cap_chan *chan;
769*4882a593Smuzhiyun 	int err;
770*4882a593Smuzhiyun 
771*4882a593Smuzhiyun 	chan = l2cap_chan_create();
772*4882a593Smuzhiyun 	if (!chan)
773*4882a593Smuzhiyun 		return NULL;
774*4882a593Smuzhiyun 
775*4882a593Smuzhiyun 	BT_DBG("chan %p", chan);
776*4882a593Smuzhiyun 
777*4882a593Smuzhiyun 	chan->chan_type = L2CAP_CHAN_FIXED;
778*4882a593Smuzhiyun 	chan->scid = L2CAP_CID_A2MP;
779*4882a593Smuzhiyun 	chan->dcid = L2CAP_CID_A2MP;
780*4882a593Smuzhiyun 	chan->omtu = L2CAP_A2MP_DEFAULT_MTU;
781*4882a593Smuzhiyun 	chan->imtu = L2CAP_A2MP_DEFAULT_MTU;
782*4882a593Smuzhiyun 	chan->flush_to = L2CAP_DEFAULT_FLUSH_TO;
783*4882a593Smuzhiyun 
784*4882a593Smuzhiyun 	chan->ops = &a2mp_chan_ops;
785*4882a593Smuzhiyun 
786*4882a593Smuzhiyun 	l2cap_chan_set_defaults(chan);
787*4882a593Smuzhiyun 	chan->remote_max_tx = chan->max_tx;
788*4882a593Smuzhiyun 	chan->remote_tx_win = chan->tx_win;
789*4882a593Smuzhiyun 
790*4882a593Smuzhiyun 	chan->retrans_timeout = L2CAP_DEFAULT_RETRANS_TO;
791*4882a593Smuzhiyun 	chan->monitor_timeout = L2CAP_DEFAULT_MONITOR_TO;
792*4882a593Smuzhiyun 
793*4882a593Smuzhiyun 	skb_queue_head_init(&chan->tx_q);
794*4882a593Smuzhiyun 
795*4882a593Smuzhiyun 	chan->mode = L2CAP_MODE_ERTM;
796*4882a593Smuzhiyun 
797*4882a593Smuzhiyun 	err = l2cap_ertm_init(chan);
798*4882a593Smuzhiyun 	if (err < 0) {
799*4882a593Smuzhiyun 		l2cap_chan_del(chan, 0);
800*4882a593Smuzhiyun 		return NULL;
801*4882a593Smuzhiyun 	}
802*4882a593Smuzhiyun 
803*4882a593Smuzhiyun 	chan->conf_state = 0;
804*4882a593Smuzhiyun 
805*4882a593Smuzhiyun 	if (locked)
806*4882a593Smuzhiyun 		__l2cap_chan_add(conn, chan);
807*4882a593Smuzhiyun 	else
808*4882a593Smuzhiyun 		l2cap_chan_add(conn, chan);
809*4882a593Smuzhiyun 
810*4882a593Smuzhiyun 	chan->remote_mps = chan->omtu;
811*4882a593Smuzhiyun 	chan->mps = chan->omtu;
812*4882a593Smuzhiyun 
813*4882a593Smuzhiyun 	chan->state = BT_CONNECTED;
814*4882a593Smuzhiyun 
815*4882a593Smuzhiyun 	return chan;
816*4882a593Smuzhiyun }
817*4882a593Smuzhiyun 
818*4882a593Smuzhiyun /* AMP Manager functions */
amp_mgr_get(struct amp_mgr * mgr)819*4882a593Smuzhiyun struct amp_mgr *amp_mgr_get(struct amp_mgr *mgr)
820*4882a593Smuzhiyun {
821*4882a593Smuzhiyun 	BT_DBG("mgr %p orig refcnt %d", mgr, kref_read(&mgr->kref));
822*4882a593Smuzhiyun 
823*4882a593Smuzhiyun 	kref_get(&mgr->kref);
824*4882a593Smuzhiyun 
825*4882a593Smuzhiyun 	return mgr;
826*4882a593Smuzhiyun }
827*4882a593Smuzhiyun 
amp_mgr_destroy(struct kref * kref)828*4882a593Smuzhiyun static void amp_mgr_destroy(struct kref *kref)
829*4882a593Smuzhiyun {
830*4882a593Smuzhiyun 	struct amp_mgr *mgr = container_of(kref, struct amp_mgr, kref);
831*4882a593Smuzhiyun 
832*4882a593Smuzhiyun 	BT_DBG("mgr %p", mgr);
833*4882a593Smuzhiyun 
834*4882a593Smuzhiyun 	mutex_lock(&amp_mgr_list_lock);
835*4882a593Smuzhiyun 	list_del(&mgr->list);
836*4882a593Smuzhiyun 	mutex_unlock(&amp_mgr_list_lock);
837*4882a593Smuzhiyun 
838*4882a593Smuzhiyun 	amp_ctrl_list_flush(mgr);
839*4882a593Smuzhiyun 	kfree(mgr);
840*4882a593Smuzhiyun }
841*4882a593Smuzhiyun 
amp_mgr_put(struct amp_mgr * mgr)842*4882a593Smuzhiyun int amp_mgr_put(struct amp_mgr *mgr)
843*4882a593Smuzhiyun {
844*4882a593Smuzhiyun 	BT_DBG("mgr %p orig refcnt %d", mgr, kref_read(&mgr->kref));
845*4882a593Smuzhiyun 
846*4882a593Smuzhiyun 	return kref_put(&mgr->kref, &amp_mgr_destroy);
847*4882a593Smuzhiyun }
848*4882a593Smuzhiyun 
amp_mgr_create(struct l2cap_conn * conn,bool locked)849*4882a593Smuzhiyun static struct amp_mgr *amp_mgr_create(struct l2cap_conn *conn, bool locked)
850*4882a593Smuzhiyun {
851*4882a593Smuzhiyun 	struct amp_mgr *mgr;
852*4882a593Smuzhiyun 	struct l2cap_chan *chan;
853*4882a593Smuzhiyun 
854*4882a593Smuzhiyun 	mgr = kzalloc(sizeof(*mgr), GFP_KERNEL);
855*4882a593Smuzhiyun 	if (!mgr)
856*4882a593Smuzhiyun 		return NULL;
857*4882a593Smuzhiyun 
858*4882a593Smuzhiyun 	BT_DBG("conn %p mgr %p", conn, mgr);
859*4882a593Smuzhiyun 
860*4882a593Smuzhiyun 	mgr->l2cap_conn = conn;
861*4882a593Smuzhiyun 
862*4882a593Smuzhiyun 	chan = a2mp_chan_open(conn, locked);
863*4882a593Smuzhiyun 	if (!chan) {
864*4882a593Smuzhiyun 		kfree(mgr);
865*4882a593Smuzhiyun 		return NULL;
866*4882a593Smuzhiyun 	}
867*4882a593Smuzhiyun 
868*4882a593Smuzhiyun 	mgr->a2mp_chan = chan;
869*4882a593Smuzhiyun 	chan->data = mgr;
870*4882a593Smuzhiyun 
871*4882a593Smuzhiyun 	conn->hcon->amp_mgr = mgr;
872*4882a593Smuzhiyun 
873*4882a593Smuzhiyun 	kref_init(&mgr->kref);
874*4882a593Smuzhiyun 
875*4882a593Smuzhiyun 	/* Remote AMP ctrl list initialization */
876*4882a593Smuzhiyun 	INIT_LIST_HEAD(&mgr->amp_ctrls);
877*4882a593Smuzhiyun 	mutex_init(&mgr->amp_ctrls_lock);
878*4882a593Smuzhiyun 
879*4882a593Smuzhiyun 	mutex_lock(&amp_mgr_list_lock);
880*4882a593Smuzhiyun 	list_add(&mgr->list, &amp_mgr_list);
881*4882a593Smuzhiyun 	mutex_unlock(&amp_mgr_list_lock);
882*4882a593Smuzhiyun 
883*4882a593Smuzhiyun 	return mgr;
884*4882a593Smuzhiyun }
885*4882a593Smuzhiyun 
a2mp_channel_create(struct l2cap_conn * conn,struct sk_buff * skb)886*4882a593Smuzhiyun struct l2cap_chan *a2mp_channel_create(struct l2cap_conn *conn,
887*4882a593Smuzhiyun 				       struct sk_buff *skb)
888*4882a593Smuzhiyun {
889*4882a593Smuzhiyun 	struct amp_mgr *mgr;
890*4882a593Smuzhiyun 
891*4882a593Smuzhiyun 	if (conn->hcon->type != ACL_LINK)
892*4882a593Smuzhiyun 		return NULL;
893*4882a593Smuzhiyun 
894*4882a593Smuzhiyun 	mgr = amp_mgr_create(conn, false);
895*4882a593Smuzhiyun 	if (!mgr) {
896*4882a593Smuzhiyun 		BT_ERR("Could not create AMP manager");
897*4882a593Smuzhiyun 		return NULL;
898*4882a593Smuzhiyun 	}
899*4882a593Smuzhiyun 
900*4882a593Smuzhiyun 	BT_DBG("mgr: %p chan %p", mgr, mgr->a2mp_chan);
901*4882a593Smuzhiyun 
902*4882a593Smuzhiyun 	return mgr->a2mp_chan;
903*4882a593Smuzhiyun }
904*4882a593Smuzhiyun 
a2mp_send_getinfo_rsp(struct hci_dev * hdev)905*4882a593Smuzhiyun void a2mp_send_getinfo_rsp(struct hci_dev *hdev)
906*4882a593Smuzhiyun {
907*4882a593Smuzhiyun 	struct amp_mgr *mgr;
908*4882a593Smuzhiyun 	struct a2mp_info_rsp rsp;
909*4882a593Smuzhiyun 
910*4882a593Smuzhiyun 	mgr = amp_mgr_lookup_by_state(READ_LOC_AMP_INFO);
911*4882a593Smuzhiyun 	if (!mgr)
912*4882a593Smuzhiyun 		return;
913*4882a593Smuzhiyun 
914*4882a593Smuzhiyun 	BT_DBG("%s mgr %p", hdev->name, mgr);
915*4882a593Smuzhiyun 
916*4882a593Smuzhiyun 	memset(&rsp, 0, sizeof(rsp));
917*4882a593Smuzhiyun 
918*4882a593Smuzhiyun 	rsp.id = hdev->id;
919*4882a593Smuzhiyun 	rsp.status = A2MP_STATUS_INVALID_CTRL_ID;
920*4882a593Smuzhiyun 
921*4882a593Smuzhiyun 	if (hdev->amp_type != AMP_TYPE_BREDR) {
922*4882a593Smuzhiyun 		rsp.status = 0;
923*4882a593Smuzhiyun 		rsp.total_bw = cpu_to_le32(hdev->amp_total_bw);
924*4882a593Smuzhiyun 		rsp.max_bw = cpu_to_le32(hdev->amp_max_bw);
925*4882a593Smuzhiyun 		rsp.min_latency = cpu_to_le32(hdev->amp_min_latency);
926*4882a593Smuzhiyun 		rsp.pal_cap = cpu_to_le16(hdev->amp_pal_cap);
927*4882a593Smuzhiyun 		rsp.assoc_size = cpu_to_le16(hdev->amp_assoc_size);
928*4882a593Smuzhiyun 	}
929*4882a593Smuzhiyun 
930*4882a593Smuzhiyun 	a2mp_send(mgr, A2MP_GETINFO_RSP, mgr->ident, sizeof(rsp), &rsp);
931*4882a593Smuzhiyun 	amp_mgr_put(mgr);
932*4882a593Smuzhiyun }
933*4882a593Smuzhiyun 
a2mp_send_getampassoc_rsp(struct hci_dev * hdev,u8 status)934*4882a593Smuzhiyun void a2mp_send_getampassoc_rsp(struct hci_dev *hdev, u8 status)
935*4882a593Smuzhiyun {
936*4882a593Smuzhiyun 	struct amp_mgr *mgr;
937*4882a593Smuzhiyun 	struct amp_assoc *loc_assoc = &hdev->loc_assoc;
938*4882a593Smuzhiyun 	struct a2mp_amp_assoc_rsp *rsp;
939*4882a593Smuzhiyun 	size_t len;
940*4882a593Smuzhiyun 
941*4882a593Smuzhiyun 	mgr = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC);
942*4882a593Smuzhiyun 	if (!mgr)
943*4882a593Smuzhiyun 		return;
944*4882a593Smuzhiyun 
945*4882a593Smuzhiyun 	BT_DBG("%s mgr %p", hdev->name, mgr);
946*4882a593Smuzhiyun 
947*4882a593Smuzhiyun 	len = sizeof(struct a2mp_amp_assoc_rsp) + loc_assoc->len;
948*4882a593Smuzhiyun 	rsp = kzalloc(len, GFP_KERNEL);
949*4882a593Smuzhiyun 	if (!rsp) {
950*4882a593Smuzhiyun 		amp_mgr_put(mgr);
951*4882a593Smuzhiyun 		return;
952*4882a593Smuzhiyun 	}
953*4882a593Smuzhiyun 
954*4882a593Smuzhiyun 	rsp->id = hdev->id;
955*4882a593Smuzhiyun 
956*4882a593Smuzhiyun 	if (status) {
957*4882a593Smuzhiyun 		rsp->status = A2MP_STATUS_INVALID_CTRL_ID;
958*4882a593Smuzhiyun 	} else {
959*4882a593Smuzhiyun 		rsp->status = A2MP_STATUS_SUCCESS;
960*4882a593Smuzhiyun 		memcpy(rsp->amp_assoc, loc_assoc->data, loc_assoc->len);
961*4882a593Smuzhiyun 	}
962*4882a593Smuzhiyun 
963*4882a593Smuzhiyun 	a2mp_send(mgr, A2MP_GETAMPASSOC_RSP, mgr->ident, len, rsp);
964*4882a593Smuzhiyun 	amp_mgr_put(mgr);
965*4882a593Smuzhiyun 	kfree(rsp);
966*4882a593Smuzhiyun }
967*4882a593Smuzhiyun 
a2mp_send_create_phy_link_req(struct hci_dev * hdev,u8 status)968*4882a593Smuzhiyun void a2mp_send_create_phy_link_req(struct hci_dev *hdev, u8 status)
969*4882a593Smuzhiyun {
970*4882a593Smuzhiyun 	struct amp_mgr *mgr;
971*4882a593Smuzhiyun 	struct amp_assoc *loc_assoc = &hdev->loc_assoc;
972*4882a593Smuzhiyun 	struct a2mp_physlink_req *req;
973*4882a593Smuzhiyun 	struct l2cap_chan *bredr_chan;
974*4882a593Smuzhiyun 	size_t len;
975*4882a593Smuzhiyun 
976*4882a593Smuzhiyun 	mgr = amp_mgr_lookup_by_state(READ_LOC_AMP_ASSOC_FINAL);
977*4882a593Smuzhiyun 	if (!mgr)
978*4882a593Smuzhiyun 		return;
979*4882a593Smuzhiyun 
980*4882a593Smuzhiyun 	len = sizeof(*req) + loc_assoc->len;
981*4882a593Smuzhiyun 
982*4882a593Smuzhiyun 	BT_DBG("%s mgr %p assoc_len %zu", hdev->name, mgr, len);
983*4882a593Smuzhiyun 
984*4882a593Smuzhiyun 	req = kzalloc(len, GFP_KERNEL);
985*4882a593Smuzhiyun 	if (!req) {
986*4882a593Smuzhiyun 		amp_mgr_put(mgr);
987*4882a593Smuzhiyun 		return;
988*4882a593Smuzhiyun 	}
989*4882a593Smuzhiyun 
990*4882a593Smuzhiyun 	bredr_chan = mgr->bredr_chan;
991*4882a593Smuzhiyun 	if (!bredr_chan)
992*4882a593Smuzhiyun 		goto clean;
993*4882a593Smuzhiyun 
994*4882a593Smuzhiyun 	req->local_id = hdev->id;
995*4882a593Smuzhiyun 	req->remote_id = bredr_chan->remote_amp_id;
996*4882a593Smuzhiyun 	memcpy(req->amp_assoc, loc_assoc->data, loc_assoc->len);
997*4882a593Smuzhiyun 
998*4882a593Smuzhiyun 	a2mp_send(mgr, A2MP_CREATEPHYSLINK_REQ, __next_ident(mgr), len, req);
999*4882a593Smuzhiyun 
1000*4882a593Smuzhiyun clean:
1001*4882a593Smuzhiyun 	amp_mgr_put(mgr);
1002*4882a593Smuzhiyun 	kfree(req);
1003*4882a593Smuzhiyun }
1004*4882a593Smuzhiyun 
a2mp_send_create_phy_link_rsp(struct hci_dev * hdev,u8 status)1005*4882a593Smuzhiyun void a2mp_send_create_phy_link_rsp(struct hci_dev *hdev, u8 status)
1006*4882a593Smuzhiyun {
1007*4882a593Smuzhiyun 	struct amp_mgr *mgr;
1008*4882a593Smuzhiyun 	struct a2mp_physlink_rsp rsp;
1009*4882a593Smuzhiyun 	struct hci_conn *hs_hcon;
1010*4882a593Smuzhiyun 
1011*4882a593Smuzhiyun 	mgr = amp_mgr_lookup_by_state(WRITE_REMOTE_AMP_ASSOC);
1012*4882a593Smuzhiyun 	if (!mgr)
1013*4882a593Smuzhiyun 		return;
1014*4882a593Smuzhiyun 
1015*4882a593Smuzhiyun 	memset(&rsp, 0, sizeof(rsp));
1016*4882a593Smuzhiyun 
1017*4882a593Smuzhiyun 	hs_hcon = hci_conn_hash_lookup_state(hdev, AMP_LINK, BT_CONNECT);
1018*4882a593Smuzhiyun 	if (!hs_hcon) {
1019*4882a593Smuzhiyun 		rsp.status = A2MP_STATUS_UNABLE_START_LINK_CREATION;
1020*4882a593Smuzhiyun 	} else {
1021*4882a593Smuzhiyun 		rsp.remote_id = hs_hcon->remote_id;
1022*4882a593Smuzhiyun 		rsp.status = A2MP_STATUS_SUCCESS;
1023*4882a593Smuzhiyun 	}
1024*4882a593Smuzhiyun 
1025*4882a593Smuzhiyun 	BT_DBG("%s mgr %p hs_hcon %p status %u", hdev->name, mgr, hs_hcon,
1026*4882a593Smuzhiyun 	       status);
1027*4882a593Smuzhiyun 
1028*4882a593Smuzhiyun 	rsp.local_id = hdev->id;
1029*4882a593Smuzhiyun 	a2mp_send(mgr, A2MP_CREATEPHYSLINK_RSP, mgr->ident, sizeof(rsp), &rsp);
1030*4882a593Smuzhiyun 	amp_mgr_put(mgr);
1031*4882a593Smuzhiyun }
1032*4882a593Smuzhiyun 
a2mp_discover_amp(struct l2cap_chan * chan)1033*4882a593Smuzhiyun void a2mp_discover_amp(struct l2cap_chan *chan)
1034*4882a593Smuzhiyun {
1035*4882a593Smuzhiyun 	struct l2cap_conn *conn = chan->conn;
1036*4882a593Smuzhiyun 	struct amp_mgr *mgr = conn->hcon->amp_mgr;
1037*4882a593Smuzhiyun 	struct a2mp_discov_req req;
1038*4882a593Smuzhiyun 
1039*4882a593Smuzhiyun 	BT_DBG("chan %p conn %p mgr %p", chan, conn, mgr);
1040*4882a593Smuzhiyun 
1041*4882a593Smuzhiyun 	if (!mgr) {
1042*4882a593Smuzhiyun 		mgr = amp_mgr_create(conn, true);
1043*4882a593Smuzhiyun 		if (!mgr)
1044*4882a593Smuzhiyun 			return;
1045*4882a593Smuzhiyun 	}
1046*4882a593Smuzhiyun 
1047*4882a593Smuzhiyun 	mgr->bredr_chan = chan;
1048*4882a593Smuzhiyun 
1049*4882a593Smuzhiyun 	memset(&req, 0, sizeof(req));
1050*4882a593Smuzhiyun 
1051*4882a593Smuzhiyun 	req.mtu = cpu_to_le16(L2CAP_A2MP_DEFAULT_MTU);
1052*4882a593Smuzhiyun 	req.ext_feat = 0;
1053*4882a593Smuzhiyun 	a2mp_send(mgr, A2MP_DISCOVER_REQ, 1, sizeof(req), &req);
1054*4882a593Smuzhiyun }
1055