xref: /freebsd/sys/netgraph/bluetooth/l2cap/ng_l2cap_evnt.c (revision 2ff63af9b88c7413b7d71715b5532625752a248e)
1878ed226SJulian Elischer /*
2878ed226SJulian Elischer  * ng_l2cap_evnt.c
3c398230bSWarner Losh  */
4c398230bSWarner Losh 
5c398230bSWarner Losh /*-
6*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
7fe267a55SPedro F. Giffuni  *
8878ed226SJulian Elischer  * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
9878ed226SJulian Elischer  * All rights reserved.
10878ed226SJulian Elischer  *
11878ed226SJulian Elischer  * Redistribution and use in source and binary forms, with or without
12878ed226SJulian Elischer  * modification, are permitted provided that the following conditions
13878ed226SJulian Elischer  * are met:
14878ed226SJulian Elischer  * 1. Redistributions of source code must retain the above copyright
15878ed226SJulian Elischer  *    notice, this list of conditions and the following disclaimer.
16878ed226SJulian Elischer  * 2. Redistributions in binary form must reproduce the above copyright
17878ed226SJulian Elischer  *    notice, this list of conditions and the following disclaimer in the
18878ed226SJulian Elischer  *    documentation and/or other materials provided with the distribution.
19878ed226SJulian Elischer  *
20878ed226SJulian Elischer  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21878ed226SJulian Elischer  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22878ed226SJulian Elischer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23878ed226SJulian Elischer  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24878ed226SJulian Elischer  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25878ed226SJulian Elischer  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26878ed226SJulian Elischer  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27878ed226SJulian Elischer  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28878ed226SJulian Elischer  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29878ed226SJulian Elischer  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30878ed226SJulian Elischer  * SUCH DAMAGE.
31878ed226SJulian Elischer  *
320986ab12SMaksim Yevmenkin  * $Id: ng_l2cap_evnt.c,v 1.5 2003/09/08 19:11:45 max Exp $
33878ed226SJulian Elischer  */
34878ed226SJulian Elischer 
35878ed226SJulian Elischer #include <sys/param.h>
36878ed226SJulian Elischer #include <sys/systm.h>
37878ed226SJulian Elischer #include <sys/kernel.h>
38878ed226SJulian Elischer #include <sys/endian.h>
39878ed226SJulian Elischer #include <sys/malloc.h>
40878ed226SJulian Elischer #include <sys/mbuf.h>
41878ed226SJulian Elischer #include <sys/queue.h>
42878ed226SJulian Elischer #include <netgraph/ng_message.h>
43878ed226SJulian Elischer #include <netgraph/netgraph.h>
44b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_bluetooth.h>
45b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_hci.h>
46b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_l2cap.h>
47b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_var.h>
48b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_cmds.h>
49b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_evnt.h>
50b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_llpi.h>
51b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h>
52b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_misc.h>
53878ed226SJulian Elischer 
54878ed226SJulian Elischer /******************************************************************************
55878ed226SJulian Elischer  ******************************************************************************
56878ed226SJulian Elischer  **                    L2CAP events processing module
57878ed226SJulian Elischer  ******************************************************************************
58878ed226SJulian Elischer  ******************************************************************************/
59878ed226SJulian Elischer 
60878ed226SJulian Elischer static int ng_l2cap_process_signal_cmd (ng_l2cap_con_p);
61fbc48c2bSTakanori Watanabe static int ng_l2cap_process_lesignal_cmd (ng_l2cap_con_p);
62878ed226SJulian Elischer static int ng_l2cap_process_cmd_rej    (ng_l2cap_con_p, u_int8_t);
63fbc48c2bSTakanori Watanabe static int ng_l2cap_process_cmd_urq    (ng_l2cap_con_p, u_int8_t);
64fbc48c2bSTakanori Watanabe static int ng_l2cap_process_cmd_urs    (ng_l2cap_con_p, u_int8_t);
65878ed226SJulian Elischer static int ng_l2cap_process_con_req    (ng_l2cap_con_p, u_int8_t);
66878ed226SJulian Elischer static int ng_l2cap_process_con_rsp    (ng_l2cap_con_p, u_int8_t);
67878ed226SJulian Elischer static int ng_l2cap_process_cfg_req    (ng_l2cap_con_p, u_int8_t);
68878ed226SJulian Elischer static int ng_l2cap_process_cfg_rsp    (ng_l2cap_con_p, u_int8_t);
69878ed226SJulian Elischer static int ng_l2cap_process_discon_req (ng_l2cap_con_p, u_int8_t);
70878ed226SJulian Elischer static int ng_l2cap_process_discon_rsp (ng_l2cap_con_p, u_int8_t);
71878ed226SJulian Elischer static int ng_l2cap_process_echo_req   (ng_l2cap_con_p, u_int8_t);
72878ed226SJulian Elischer static int ng_l2cap_process_echo_rsp   (ng_l2cap_con_p, u_int8_t);
73878ed226SJulian Elischer static int ng_l2cap_process_info_req   (ng_l2cap_con_p, u_int8_t);
74878ed226SJulian Elischer static int ng_l2cap_process_info_rsp   (ng_l2cap_con_p, u_int8_t);
75878ed226SJulian Elischer static int send_l2cap_reject
76878ed226SJulian Elischer 	(ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, u_int16_t, u_int16_t);
77878ed226SJulian Elischer static int send_l2cap_con_rej
78878ed226SJulian Elischer 	(ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, u_int16_t);
79878ed226SJulian Elischer static int send_l2cap_cfg_rsp
80878ed226SJulian Elischer 	(ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, struct mbuf *);
81fbc48c2bSTakanori Watanabe static int send_l2cap_param_urs
82fbc48c2bSTakanori Watanabe        (ng_l2cap_con_p , u_int8_t , u_int16_t);
83fbc48c2bSTakanori Watanabe 
84878ed226SJulian Elischer static int get_next_l2cap_opt
85878ed226SJulian Elischer 	(struct mbuf *, int *, ng_l2cap_cfg_opt_p, ng_l2cap_cfg_opt_val_p);
86878ed226SJulian Elischer 
87878ed226SJulian Elischer /*
88878ed226SJulian Elischer  * Receive L2CAP packet. First get L2CAP header and verify packet. Than
89878ed226SJulian Elischer  * get destination channel and process packet.
90878ed226SJulian Elischer  */
91878ed226SJulian Elischer 
92878ed226SJulian Elischer int
ng_l2cap_receive(ng_l2cap_con_p con)93878ed226SJulian Elischer ng_l2cap_receive(ng_l2cap_con_p con)
94878ed226SJulian Elischer {
95878ed226SJulian Elischer 	ng_l2cap_p	 l2cap = con->l2cap;
96878ed226SJulian Elischer 	ng_l2cap_hdr_t	*hdr = NULL;
97878ed226SJulian Elischer 	int		 error = 0;
98878ed226SJulian Elischer 
99878ed226SJulian Elischer 	/* Check packet */
100878ed226SJulian Elischer 	if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) {
101878ed226SJulian Elischer 		NG_L2CAP_ERR(
102878ed226SJulian Elischer "%s: %s - invalid L2CAP packet. Packet too small, len=%d\n",
103878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node),
104878ed226SJulian Elischer 			con->rx_pkt->m_pkthdr.len);
105878ed226SJulian Elischer 		error = EMSGSIZE;
106878ed226SJulian Elischer 		goto drop;
107878ed226SJulian Elischer 	}
108878ed226SJulian Elischer 
109878ed226SJulian Elischer 	/* Get L2CAP header */
110878ed226SJulian Elischer 	NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));
111878ed226SJulian Elischer 	if (con->rx_pkt == NULL)
112878ed226SJulian Elischer 		return (ENOBUFS);
113878ed226SJulian Elischer 
114878ed226SJulian Elischer 	hdr = mtod(con->rx_pkt, ng_l2cap_hdr_t *);
115878ed226SJulian Elischer 	hdr->length = le16toh(hdr->length);
116878ed226SJulian Elischer 	hdr->dcid = le16toh(hdr->dcid);
117878ed226SJulian Elischer 
118878ed226SJulian Elischer 	/* Check payload size */
119878ed226SJulian Elischer 	if (hdr->length != con->rx_pkt->m_pkthdr.len - sizeof(*hdr)) {
120878ed226SJulian Elischer 		NG_L2CAP_ERR(
1214ae439a3SMaksim Yevmenkin "%s: %s - invalid L2CAP packet. Payload length mismatch, length=%d, len=%zd\n",
122878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), hdr->length,
123878ed226SJulian Elischer 			con->rx_pkt->m_pkthdr.len - sizeof(*hdr));
124878ed226SJulian Elischer 		error = EMSGSIZE;
125878ed226SJulian Elischer 		goto drop;
126878ed226SJulian Elischer 	}
127878ed226SJulian Elischer 
128878ed226SJulian Elischer 	/* Process packet */
129878ed226SJulian Elischer 	switch (hdr->dcid) {
130878ed226SJulian Elischer 	case NG_L2CAP_SIGNAL_CID: /* L2CAP command */
131878ed226SJulian Elischer 		m_adj(con->rx_pkt, sizeof(*hdr));
132878ed226SJulian Elischer 		error = ng_l2cap_process_signal_cmd(con);
133878ed226SJulian Elischer 		break;
134fbc48c2bSTakanori Watanabe   	case NG_L2CAP_LESIGNAL_CID:
135fbc48c2bSTakanori Watanabe 		m_adj(con->rx_pkt, sizeof(*hdr));
136fbc48c2bSTakanori Watanabe 		error = ng_l2cap_process_lesignal_cmd(con);
137fbc48c2bSTakanori Watanabe 		break;
138878ed226SJulian Elischer 	case NG_L2CAP_CLT_CID: /* Connectionless packet */
139878ed226SJulian Elischer 		error = ng_l2cap_l2ca_clt_receive(con);
140878ed226SJulian Elischer 		break;
141878ed226SJulian Elischer 
142878ed226SJulian Elischer 	default: /* Data packet */
143878ed226SJulian Elischer 		error = ng_l2cap_l2ca_receive(con);
144878ed226SJulian Elischer 		break;
145878ed226SJulian Elischer 	}
146878ed226SJulian Elischer 
147878ed226SJulian Elischer 	return (error);
148878ed226SJulian Elischer drop:
149878ed226SJulian Elischer 	NG_FREE_M(con->rx_pkt);
150878ed226SJulian Elischer 
151878ed226SJulian Elischer 	return (error);
152878ed226SJulian Elischer } /* ng_l2cap_receive */
153878ed226SJulian Elischer 
154878ed226SJulian Elischer /*
155878ed226SJulian Elischer  * Process L2CAP signaling command. We already know that destination channel ID
156878ed226SJulian Elischer  * is 0x1 that means we have received signaling command from peer's L2CAP layer.
157878ed226SJulian Elischer  * So get command header, decode and process it.
158878ed226SJulian Elischer  *
159878ed226SJulian Elischer  * XXX do we need to check signaling MTU here?
160878ed226SJulian Elischer  */
161878ed226SJulian Elischer 
162878ed226SJulian Elischer static int
ng_l2cap_process_signal_cmd(ng_l2cap_con_p con)163878ed226SJulian Elischer ng_l2cap_process_signal_cmd(ng_l2cap_con_p con)
164878ed226SJulian Elischer {
165878ed226SJulian Elischer 	ng_l2cap_p		 l2cap = con->l2cap;
166878ed226SJulian Elischer 	ng_l2cap_cmd_hdr_t	*hdr = NULL;
167878ed226SJulian Elischer 	struct mbuf		*m = NULL;
168878ed226SJulian Elischer 
169878ed226SJulian Elischer 	while (con->rx_pkt != NULL) {
170878ed226SJulian Elischer 		/* Verify packet length */
171878ed226SJulian Elischer 		if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) {
172878ed226SJulian Elischer 			NG_L2CAP_ERR(
173878ed226SJulian Elischer "%s: %s - invalid L2CAP signaling command. Packet too small, len=%d\n",
174878ed226SJulian Elischer 				__func__, NG_NODE_NAME(l2cap->node),
175878ed226SJulian Elischer 				con->rx_pkt->m_pkthdr.len);
176878ed226SJulian Elischer 			NG_FREE_M(con->rx_pkt);
177878ed226SJulian Elischer 
178878ed226SJulian Elischer 			return (EMSGSIZE);
179878ed226SJulian Elischer 		}
180878ed226SJulian Elischer 
181878ed226SJulian Elischer 		/* Get signaling command */
182878ed226SJulian Elischer 		NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));
183878ed226SJulian Elischer 		if (con->rx_pkt == NULL)
184878ed226SJulian Elischer 			return (ENOBUFS);
185878ed226SJulian Elischer 
186878ed226SJulian Elischer 		hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *);
187878ed226SJulian Elischer 		hdr->length = le16toh(hdr->length);
188878ed226SJulian Elischer 		m_adj(con->rx_pkt, sizeof(*hdr));
189878ed226SJulian Elischer 
190878ed226SJulian Elischer 		/* Verify command length */
191878ed226SJulian Elischer 		if (con->rx_pkt->m_pkthdr.len < hdr->length) {
192878ed226SJulian Elischer 			NG_L2CAP_ERR(
193878ed226SJulian Elischer "%s: %s - invalid L2CAP signaling command, code=%#x, ident=%d. " \
194878ed226SJulian Elischer "Invalid command length=%d, m_pkthdr.len=%d\n",
195878ed226SJulian Elischer 				__func__, NG_NODE_NAME(l2cap->node),
196878ed226SJulian Elischer 				hdr->code, hdr->ident, hdr->length,
197878ed226SJulian Elischer 				con->rx_pkt->m_pkthdr.len);
198878ed226SJulian Elischer 			NG_FREE_M(con->rx_pkt);
199878ed226SJulian Elischer 
200878ed226SJulian Elischer 			return (EMSGSIZE);
201878ed226SJulian Elischer 		}
202878ed226SJulian Elischer 
203878ed226SJulian Elischer 		/* Get the command, save the rest (if any) */
204878ed226SJulian Elischer 		if (con->rx_pkt->m_pkthdr.len > hdr->length)
205eb1b1807SGleb Smirnoff 			m = m_split(con->rx_pkt, hdr->length, M_NOWAIT);
206878ed226SJulian Elischer 		else
207878ed226SJulian Elischer 			m = NULL;
208878ed226SJulian Elischer 
209878ed226SJulian Elischer 		/* Process command */
210878ed226SJulian Elischer 		switch (hdr->code) {
211878ed226SJulian Elischer 		case NG_L2CAP_CMD_REJ:
212878ed226SJulian Elischer 			ng_l2cap_process_cmd_rej(con, hdr->ident);
213878ed226SJulian Elischer 			break;
214878ed226SJulian Elischer 
215878ed226SJulian Elischer 		case NG_L2CAP_CON_REQ:
216878ed226SJulian Elischer 			ng_l2cap_process_con_req(con, hdr->ident);
217878ed226SJulian Elischer 			break;
218878ed226SJulian Elischer 
219878ed226SJulian Elischer 		case NG_L2CAP_CON_RSP:
220878ed226SJulian Elischer 			ng_l2cap_process_con_rsp(con, hdr->ident);
221878ed226SJulian Elischer 			break;
222878ed226SJulian Elischer 
223878ed226SJulian Elischer 		case NG_L2CAP_CFG_REQ:
224878ed226SJulian Elischer 			ng_l2cap_process_cfg_req(con, hdr->ident);
225878ed226SJulian Elischer 			break;
226878ed226SJulian Elischer 
227878ed226SJulian Elischer 		case NG_L2CAP_CFG_RSP:
228878ed226SJulian Elischer 			ng_l2cap_process_cfg_rsp(con, hdr->ident);
229878ed226SJulian Elischer 			break;
230878ed226SJulian Elischer 
231878ed226SJulian Elischer 		case NG_L2CAP_DISCON_REQ:
232878ed226SJulian Elischer 			ng_l2cap_process_discon_req(con, hdr->ident);
233878ed226SJulian Elischer 			break;
234878ed226SJulian Elischer 
235878ed226SJulian Elischer 		case NG_L2CAP_DISCON_RSP:
236878ed226SJulian Elischer 			ng_l2cap_process_discon_rsp(con, hdr->ident);
237878ed226SJulian Elischer 			break;
238878ed226SJulian Elischer 
239878ed226SJulian Elischer 		case NG_L2CAP_ECHO_REQ:
240878ed226SJulian Elischer 			ng_l2cap_process_echo_req(con, hdr->ident);
241878ed226SJulian Elischer 			break;
242878ed226SJulian Elischer 
243878ed226SJulian Elischer 		case NG_L2CAP_ECHO_RSP:
244878ed226SJulian Elischer 			ng_l2cap_process_echo_rsp(con, hdr->ident);
245878ed226SJulian Elischer 			break;
246878ed226SJulian Elischer 
247878ed226SJulian Elischer 		case NG_L2CAP_INFO_REQ:
248878ed226SJulian Elischer 			ng_l2cap_process_info_req(con, hdr->ident);
249878ed226SJulian Elischer 			break;
250878ed226SJulian Elischer 
251878ed226SJulian Elischer 		case NG_L2CAP_INFO_RSP:
252878ed226SJulian Elischer 			ng_l2cap_process_info_rsp(con, hdr->ident);
253878ed226SJulian Elischer 			break;
254878ed226SJulian Elischer 
255878ed226SJulian Elischer 		default:
256878ed226SJulian Elischer 			NG_L2CAP_ERR(
257878ed226SJulian Elischer "%s: %s - unknown L2CAP signaling command, code=%#x, ident=%d, length=%d\n",
258878ed226SJulian Elischer 				__func__, NG_NODE_NAME(l2cap->node),
259878ed226SJulian Elischer 				hdr->code, hdr->ident, hdr->length);
260878ed226SJulian Elischer 
261878ed226SJulian Elischer 			/*
262878ed226SJulian Elischer 			 * Send L2CAP_CommandRej. Do not really care
263878ed226SJulian Elischer 			 * about the result
264878ed226SJulian Elischer 			 */
265878ed226SJulian Elischer 
266878ed226SJulian Elischer 			send_l2cap_reject(con, hdr->ident,
267878ed226SJulian Elischer 				NG_L2CAP_REJ_NOT_UNDERSTOOD, 0, 0, 0);
268878ed226SJulian Elischer 			NG_FREE_M(con->rx_pkt);
269878ed226SJulian Elischer 			break;
270878ed226SJulian Elischer 		}
271878ed226SJulian Elischer 
272878ed226SJulian Elischer 		con->rx_pkt = m;
273878ed226SJulian Elischer 	}
274878ed226SJulian Elischer 
275878ed226SJulian Elischer 	return (0);
276878ed226SJulian Elischer } /* ng_l2cap_process_signal_cmd */
277fbc48c2bSTakanori Watanabe static int
ng_l2cap_process_lesignal_cmd(ng_l2cap_con_p con)278fbc48c2bSTakanori Watanabe ng_l2cap_process_lesignal_cmd(ng_l2cap_con_p con)
279fbc48c2bSTakanori Watanabe {
280fbc48c2bSTakanori Watanabe 	ng_l2cap_p		 l2cap = con->l2cap;
281fbc48c2bSTakanori Watanabe 	ng_l2cap_cmd_hdr_t	*hdr = NULL;
282fbc48c2bSTakanori Watanabe 	struct mbuf		*m = NULL;
283fbc48c2bSTakanori Watanabe 
284fbc48c2bSTakanori Watanabe 	while (con->rx_pkt != NULL) {
285fbc48c2bSTakanori Watanabe 		/* Verify packet length */
286fbc48c2bSTakanori Watanabe 		if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) {
287fbc48c2bSTakanori Watanabe 			NG_L2CAP_ERR(
288fbc48c2bSTakanori Watanabe "%s: %s - invalid L2CAP signaling command. Packet too small, len=%d\n",
289fbc48c2bSTakanori Watanabe 				__func__, NG_NODE_NAME(l2cap->node),
290fbc48c2bSTakanori Watanabe 				con->rx_pkt->m_pkthdr.len);
291fbc48c2bSTakanori Watanabe 			NG_FREE_M(con->rx_pkt);
292fbc48c2bSTakanori Watanabe 
293fbc48c2bSTakanori Watanabe 			return (EMSGSIZE);
294fbc48c2bSTakanori Watanabe 		}
295fbc48c2bSTakanori Watanabe 
296fbc48c2bSTakanori Watanabe 		/* Get signaling command */
297fbc48c2bSTakanori Watanabe 		NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));
298fbc48c2bSTakanori Watanabe 		if (con->rx_pkt == NULL)
299fbc48c2bSTakanori Watanabe 			return (ENOBUFS);
300fbc48c2bSTakanori Watanabe 
301fbc48c2bSTakanori Watanabe 		hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *);
302fbc48c2bSTakanori Watanabe 		hdr->length = le16toh(hdr->length);
303fbc48c2bSTakanori Watanabe 		m_adj(con->rx_pkt, sizeof(*hdr));
304fbc48c2bSTakanori Watanabe 
305fbc48c2bSTakanori Watanabe 		/* Verify command length */
306fbc48c2bSTakanori Watanabe 		if (con->rx_pkt->m_pkthdr.len < hdr->length) {
307fbc48c2bSTakanori Watanabe 			NG_L2CAP_ERR(
308fbc48c2bSTakanori Watanabe "%s: %s - invalid L2CAP signaling command, code=%#x, ident=%d. " \
309fbc48c2bSTakanori Watanabe "Invalid command length=%d, m_pkthdr.len=%d\n",
310fbc48c2bSTakanori Watanabe 				__func__, NG_NODE_NAME(l2cap->node),
311fbc48c2bSTakanori Watanabe 				hdr->code, hdr->ident, hdr->length,
312fbc48c2bSTakanori Watanabe 				con->rx_pkt->m_pkthdr.len);
313fbc48c2bSTakanori Watanabe 			NG_FREE_M(con->rx_pkt);
314fbc48c2bSTakanori Watanabe 
315fbc48c2bSTakanori Watanabe 			return (EMSGSIZE);
316fbc48c2bSTakanori Watanabe 		}
317fbc48c2bSTakanori Watanabe 
318fbc48c2bSTakanori Watanabe 		/* Get the command, save the rest (if any) */
319fbc48c2bSTakanori Watanabe 		if (con->rx_pkt->m_pkthdr.len > hdr->length)
320fbc48c2bSTakanori Watanabe 			m = m_split(con->rx_pkt, hdr->length, M_NOWAIT);
321fbc48c2bSTakanori Watanabe 		else
322fbc48c2bSTakanori Watanabe 			m = NULL;
323fbc48c2bSTakanori Watanabe 
324fbc48c2bSTakanori Watanabe 		/* Process command */
325fbc48c2bSTakanori Watanabe 		switch (hdr->code) {
326fbc48c2bSTakanori Watanabe 		case NG_L2CAP_CMD_REJ:
327fbc48c2bSTakanori Watanabe 			ng_l2cap_process_cmd_rej(con, hdr->ident);
328fbc48c2bSTakanori Watanabe 			break;
329fbc48c2bSTakanori Watanabe 		case NG_L2CAP_CMD_PARAM_UPDATE_REQUEST:
330fbc48c2bSTakanori Watanabe 			ng_l2cap_process_cmd_urq(con, hdr->ident);
331fbc48c2bSTakanori Watanabe 			break;
332fbc48c2bSTakanori Watanabe 		case NG_L2CAP_CMD_PARAM_UPDATE_RESPONSE:
333fbc48c2bSTakanori Watanabe 			ng_l2cap_process_cmd_urs(con, hdr->ident);
334fbc48c2bSTakanori Watanabe 			break;
335fbc48c2bSTakanori Watanabe 
336fbc48c2bSTakanori Watanabe 
337fbc48c2bSTakanori Watanabe 		default:
338fbc48c2bSTakanori Watanabe 			NG_L2CAP_ERR(
339fbc48c2bSTakanori Watanabe "%s: %s - unknown L2CAP signaling command, code=%#x, ident=%d, length=%d\n",
340fbc48c2bSTakanori Watanabe 				__func__, NG_NODE_NAME(l2cap->node),
341fbc48c2bSTakanori Watanabe 				hdr->code, hdr->ident, hdr->length);
342fbc48c2bSTakanori Watanabe 
343fbc48c2bSTakanori Watanabe 			/*
344fbc48c2bSTakanori Watanabe 			 * Send L2CAP_CommandRej. Do not really care
345fbc48c2bSTakanori Watanabe 			 * about the result
346fbc48c2bSTakanori Watanabe 			 */
347fbc48c2bSTakanori Watanabe 
348fbc48c2bSTakanori Watanabe 			send_l2cap_reject(con, hdr->ident,
349fbc48c2bSTakanori Watanabe 				NG_L2CAP_REJ_NOT_UNDERSTOOD, 0, 0, 0);
350fbc48c2bSTakanori Watanabe 			NG_FREE_M(con->rx_pkt);
351fbc48c2bSTakanori Watanabe 			break;
352fbc48c2bSTakanori Watanabe 		}
353fbc48c2bSTakanori Watanabe 
354fbc48c2bSTakanori Watanabe 		con->rx_pkt = m;
355fbc48c2bSTakanori Watanabe 	}
356fbc48c2bSTakanori Watanabe 
357fbc48c2bSTakanori Watanabe 	return (0);
358fbc48c2bSTakanori Watanabe } /* ng_l2cap_process_signal_cmd */
359fbc48c2bSTakanori Watanabe /*Update Paramater Request*/
ng_l2cap_process_cmd_urq(ng_l2cap_con_p con,uint8_t ident)360fbc48c2bSTakanori Watanabe static int ng_l2cap_process_cmd_urq(ng_l2cap_con_p con, uint8_t ident)
361fbc48c2bSTakanori Watanabe {
362053359b7SPedro F. Giffuni 	/* We do not implement parameter negotiation for now. */
363fbc48c2bSTakanori Watanabe 	send_l2cap_param_urs(con, ident, NG_L2CAP_UPDATE_PARAM_ACCEPT);
364fbc48c2bSTakanori Watanabe 	NG_FREE_M(con->rx_pkt);
365fbc48c2bSTakanori Watanabe 	return 0;
366fbc48c2bSTakanori Watanabe }
367fbc48c2bSTakanori Watanabe 
ng_l2cap_process_cmd_urs(ng_l2cap_con_p con,uint8_t ident)368fbc48c2bSTakanori Watanabe static int ng_l2cap_process_cmd_urs(ng_l2cap_con_p con, uint8_t ident)
369fbc48c2bSTakanori Watanabe {
370fbc48c2bSTakanori Watanabe 	/* We only support master side yet .*/
371fbc48c2bSTakanori Watanabe 	//send_l2cap_reject(con,ident ... );
372fbc48c2bSTakanori Watanabe 
373fbc48c2bSTakanori Watanabe 	NG_FREE_M(con->rx_pkt);
374fbc48c2bSTakanori Watanabe 	return 0;
375fbc48c2bSTakanori Watanabe }
376878ed226SJulian Elischer 
377878ed226SJulian Elischer /*
378878ed226SJulian Elischer  * Process L2CAP_CommandRej command
379878ed226SJulian Elischer  */
380878ed226SJulian Elischer 
381878ed226SJulian Elischer static int
ng_l2cap_process_cmd_rej(ng_l2cap_con_p con,u_int8_t ident)382878ed226SJulian Elischer ng_l2cap_process_cmd_rej(ng_l2cap_con_p con, u_int8_t ident)
383878ed226SJulian Elischer {
384878ed226SJulian Elischer 	ng_l2cap_p		 l2cap = con->l2cap;
385878ed226SJulian Elischer 	ng_l2cap_cmd_rej_cp	*cp = NULL;
386878ed226SJulian Elischer 	ng_l2cap_cmd_p		 cmd = NULL;
387878ed226SJulian Elischer 
388878ed226SJulian Elischer 	/* Get command parameters */
389878ed226SJulian Elischer 	NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));
390878ed226SJulian Elischer 	if (con->rx_pkt == NULL)
391878ed226SJulian Elischer 		return (ENOBUFS);
392878ed226SJulian Elischer 
393878ed226SJulian Elischer 	cp = mtod(con->rx_pkt, ng_l2cap_cmd_rej_cp *);
394878ed226SJulian Elischer 	cp->reason = le16toh(cp->reason);
395878ed226SJulian Elischer 
396878ed226SJulian Elischer 	/* Check if we have pending command descriptor */
397878ed226SJulian Elischer 	cmd = ng_l2cap_cmd_by_ident(con, ident);
398878ed226SJulian Elischer 	if (cmd != NULL) {
3990986ab12SMaksim Yevmenkin 		/* If command timeout already happened then ignore reject */
4000986ab12SMaksim Yevmenkin 		if (ng_l2cap_command_untimeout(cmd) != 0) {
4010986ab12SMaksim Yevmenkin 			NG_FREE_M(con->rx_pkt);
4020986ab12SMaksim Yevmenkin 			return (ETIMEDOUT);
4030986ab12SMaksim Yevmenkin 		}
404878ed226SJulian Elischer 
405878ed226SJulian Elischer 		ng_l2cap_unlink_cmd(cmd);
406878ed226SJulian Elischer 
407878ed226SJulian Elischer 		switch (cmd->code) {
408878ed226SJulian Elischer 		case NG_L2CAP_CON_REQ:
409878ed226SJulian Elischer 			ng_l2cap_l2ca_con_rsp(cmd->ch,cmd->token,cp->reason,0);
410878ed226SJulian Elischer 			ng_l2cap_free_chan(cmd->ch);
411878ed226SJulian Elischer 			break;
412878ed226SJulian Elischer 
413878ed226SJulian Elischer 		case NG_L2CAP_CFG_REQ:
414878ed226SJulian Elischer 			ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, cp->reason);
415878ed226SJulian Elischer 			break;
416878ed226SJulian Elischer 
417878ed226SJulian Elischer 		case NG_L2CAP_DISCON_REQ:
418878ed226SJulian Elischer 			ng_l2cap_l2ca_discon_rsp(cmd->ch,cmd->token,cp->reason);
419878ed226SJulian Elischer 			ng_l2cap_free_chan(cmd->ch); /* XXX free channel */
420878ed226SJulian Elischer 			break;
421878ed226SJulian Elischer 
422878ed226SJulian Elischer 		case NG_L2CAP_ECHO_REQ:
423878ed226SJulian Elischer 			ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
424878ed226SJulian Elischer 				cp->reason, NULL);
425878ed226SJulian Elischer 			break;
426878ed226SJulian Elischer 
427878ed226SJulian Elischer 		case NG_L2CAP_INFO_REQ:
428878ed226SJulian Elischer 			ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
429878ed226SJulian Elischer 				cp->reason, NULL);
430878ed226SJulian Elischer 			break;
431878ed226SJulian Elischer 
432878ed226SJulian Elischer 		default:
433878ed226SJulian Elischer 			NG_L2CAP_ALERT(
434878ed226SJulian Elischer "%s: %s - unexpected L2CAP_CommandRej. Unexpected L2CAP command opcode=%d\n",
435878ed226SJulian Elischer 				__func__, NG_NODE_NAME(l2cap->node), cmd->code);
436878ed226SJulian Elischer 			break;
437878ed226SJulian Elischer 		}
438878ed226SJulian Elischer 
439878ed226SJulian Elischer 		ng_l2cap_free_cmd(cmd);
440878ed226SJulian Elischer 	} else
441878ed226SJulian Elischer 		NG_L2CAP_ERR(
442878ed226SJulian Elischer "%s: %s - unexpected L2CAP_CommandRej command. " \
443878ed226SJulian Elischer "Requested ident does not exist, ident=%d\n",
444878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), ident);
445878ed226SJulian Elischer 
446878ed226SJulian Elischer 	NG_FREE_M(con->rx_pkt);
447878ed226SJulian Elischer 
448878ed226SJulian Elischer 	return (0);
449878ed226SJulian Elischer } /* ng_l2cap_process_cmd_rej */
450878ed226SJulian Elischer 
451878ed226SJulian Elischer /*
452878ed226SJulian Elischer  * Process L2CAP_ConnectReq command
453878ed226SJulian Elischer  */
454878ed226SJulian Elischer 
455878ed226SJulian Elischer static int
ng_l2cap_process_con_req(ng_l2cap_con_p con,u_int8_t ident)456878ed226SJulian Elischer ng_l2cap_process_con_req(ng_l2cap_con_p con, u_int8_t ident)
457878ed226SJulian Elischer {
458878ed226SJulian Elischer 	ng_l2cap_p		 l2cap = con->l2cap;
459878ed226SJulian Elischer 	struct mbuf		*m = con->rx_pkt;
460878ed226SJulian Elischer 	ng_l2cap_con_req_cp	*cp = NULL;
461878ed226SJulian Elischer 	ng_l2cap_chan_p		 ch = NULL;
462878ed226SJulian Elischer 	int			 error = 0;
463878ed226SJulian Elischer 	u_int16_t		 dcid, psm;
464fbc48c2bSTakanori Watanabe 	int idtype;
465878ed226SJulian Elischer 
466878ed226SJulian Elischer 	/* Get command parameters */
467878ed226SJulian Elischer 	NG_L2CAP_M_PULLUP(m, sizeof(*cp));
468878ed226SJulian Elischer 	if (m == NULL)
469878ed226SJulian Elischer 		return (ENOBUFS);
470878ed226SJulian Elischer 
471878ed226SJulian Elischer 	cp = mtod(m, ng_l2cap_con_req_cp *);
472878ed226SJulian Elischer 	psm = le16toh(cp->psm);
473878ed226SJulian Elischer 	dcid = le16toh(cp->scid);
474878ed226SJulian Elischer 
475878ed226SJulian Elischer 	NG_FREE_M(m);
476878ed226SJulian Elischer 	con->rx_pkt = NULL;
477fbc48c2bSTakanori Watanabe 	if(dcid == NG_L2CAP_ATT_CID)
478fbc48c2bSTakanori Watanabe 		idtype = NG_L2CAP_L2CA_IDTYPE_ATT;
4793a601a23STakanori Watanabe 	else if(dcid == NG_L2CAP_SMP_CID)
4803a601a23STakanori Watanabe 		idtype = NG_L2CAP_L2CA_IDTYPE_SMP;
481fbc48c2bSTakanori Watanabe 	else if( con->linktype != NG_HCI_LINK_ACL)
482fbc48c2bSTakanori Watanabe 		idtype = NG_L2CAP_L2CA_IDTYPE_LE;
483fbc48c2bSTakanori Watanabe 	else
484fbc48c2bSTakanori Watanabe 		idtype = NG_L2CAP_L2CA_IDTYPE_BREDR;
485878ed226SJulian Elischer 
486878ed226SJulian Elischer 	/*
487878ed226SJulian Elischer 	 * Create new channel and send L2CA_ConnectInd notification
488878ed226SJulian Elischer 	 * to the upper layer protocol.
489878ed226SJulian Elischer 	 */
490878ed226SJulian Elischer 
491fbc48c2bSTakanori Watanabe 	ch = ng_l2cap_new_chan(l2cap, con, psm, idtype);
492fbc48c2bSTakanori Watanabe 
493878ed226SJulian Elischer 	if (ch == NULL)
494878ed226SJulian Elischer 		return (send_l2cap_con_rej(con, ident, 0, dcid,
495878ed226SJulian Elischer 				NG_L2CAP_NO_RESOURCES));
496878ed226SJulian Elischer 
497878ed226SJulian Elischer 	/* Update channel IDs */
498878ed226SJulian Elischer 	ch->dcid = dcid;
499878ed226SJulian Elischer 
500878ed226SJulian Elischer 	/* Sent L2CA_ConnectInd notification to the upper layer */
501878ed226SJulian Elischer 	ch->ident = ident;
502878ed226SJulian Elischer 	ch->state = NG_L2CAP_W4_L2CA_CON_RSP;
503878ed226SJulian Elischer 
504878ed226SJulian Elischer 	error = ng_l2cap_l2ca_con_ind(ch);
505878ed226SJulian Elischer 	if (error != 0) {
506878ed226SJulian Elischer 		send_l2cap_con_rej(con, ident, ch->scid, dcid,
507878ed226SJulian Elischer 			(error == ENOMEM)? NG_L2CAP_NO_RESOURCES :
508878ed226SJulian Elischer 				NG_L2CAP_PSM_NOT_SUPPORTED);
509878ed226SJulian Elischer 		ng_l2cap_free_chan(ch);
510878ed226SJulian Elischer 	}
511878ed226SJulian Elischer 
512878ed226SJulian Elischer 	return (error);
513878ed226SJulian Elischer } /* ng_l2cap_process_con_req */
514878ed226SJulian Elischer 
515878ed226SJulian Elischer /*
516878ed226SJulian Elischer  * Process L2CAP_ConnectRsp command
517878ed226SJulian Elischer  */
518878ed226SJulian Elischer 
519878ed226SJulian Elischer static int
ng_l2cap_process_con_rsp(ng_l2cap_con_p con,u_int8_t ident)520878ed226SJulian Elischer ng_l2cap_process_con_rsp(ng_l2cap_con_p con, u_int8_t ident)
521878ed226SJulian Elischer {
522878ed226SJulian Elischer 	ng_l2cap_p		 l2cap = con->l2cap;
523878ed226SJulian Elischer 	struct mbuf		*m = con->rx_pkt;
524878ed226SJulian Elischer 	ng_l2cap_con_rsp_cp	*cp = NULL;
525878ed226SJulian Elischer 	ng_l2cap_cmd_p		 cmd = NULL;
526878ed226SJulian Elischer 	u_int16_t		 scid, dcid, result, status;
527878ed226SJulian Elischer 	int			 error = 0;
528878ed226SJulian Elischer 
529878ed226SJulian Elischer 	/* Get command parameters */
530878ed226SJulian Elischer 	NG_L2CAP_M_PULLUP(m, sizeof(*cp));
531878ed226SJulian Elischer 	if (m == NULL)
532878ed226SJulian Elischer 		return (ENOBUFS);
533878ed226SJulian Elischer 
534878ed226SJulian Elischer 	cp = mtod(m, ng_l2cap_con_rsp_cp *);
535878ed226SJulian Elischer 	dcid = le16toh(cp->dcid);
536878ed226SJulian Elischer 	scid = le16toh(cp->scid);
537878ed226SJulian Elischer 	result = le16toh(cp->result);
538878ed226SJulian Elischer 	status = le16toh(cp->status);
539878ed226SJulian Elischer 
540878ed226SJulian Elischer 	NG_FREE_M(m);
541878ed226SJulian Elischer 	con->rx_pkt = NULL;
542878ed226SJulian Elischer 
543878ed226SJulian Elischer 	/* Check if we have pending command descriptor */
544878ed226SJulian Elischer 	cmd = ng_l2cap_cmd_by_ident(con, ident);
545878ed226SJulian Elischer 	if (cmd == NULL) {
546878ed226SJulian Elischer 		NG_L2CAP_ERR(
547878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConnectRsp command. ident=%d, con_handle=%d\n",
548878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), ident,
549878ed226SJulian Elischer 			con->con_handle);
550878ed226SJulian Elischer 
551878ed226SJulian Elischer 		return (ENOENT);
552878ed226SJulian Elischer 	}
553878ed226SJulian Elischer 
554878ed226SJulian Elischer 	/* Verify channel state, if invalid - do nothing */
555878ed226SJulian Elischer 	if (cmd->ch->state != NG_L2CAP_W4_L2CAP_CON_RSP) {
556878ed226SJulian Elischer 		NG_L2CAP_ERR(
557878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConnectRsp. " \
558878ed226SJulian Elischer "Invalid channel state, cid=%d, state=%d\n",
559878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), scid,
560878ed226SJulian Elischer 			cmd->ch->state);
561878ed226SJulian Elischer 		goto reject;
562878ed226SJulian Elischer 	}
563878ed226SJulian Elischer 
564878ed226SJulian Elischer 	/* Verify CIDs and send reject if does not match */
565878ed226SJulian Elischer 	if (cmd->ch->scid != scid) {
566878ed226SJulian Elischer 		NG_L2CAP_ERR(
567878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConnectRsp. Channel IDs do not match, scid=%d(%d)\n",
568878ed226SJulian Elischer 			 __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,
569878ed226SJulian Elischer 			scid);
570878ed226SJulian Elischer 		goto reject;
571878ed226SJulian Elischer 	}
572878ed226SJulian Elischer 
573878ed226SJulian Elischer 	/*
574878ed226SJulian Elischer 	 * Looks good. We got confirmation from our peer. Now process
575878ed226SJulian Elischer 	 * it. First disable RTX timer. Then check the result and send
5760986ab12SMaksim Yevmenkin 	 * notification to the upper layer. If command timeout already
5770986ab12SMaksim Yevmenkin 	 * happened then ignore response.
578878ed226SJulian Elischer 	 */
579878ed226SJulian Elischer 
5800986ab12SMaksim Yevmenkin 	if ((error = ng_l2cap_command_untimeout(cmd)) != 0)
5810986ab12SMaksim Yevmenkin 		return (error);
582878ed226SJulian Elischer 
583878ed226SJulian Elischer 	if (result == NG_L2CAP_PENDING) {
584878ed226SJulian Elischer 		/*
585878ed226SJulian Elischer 		 * Our peer wants more time to complete connection. We shall
586878ed226SJulian Elischer 		 * start ERTX timer and wait. Keep command in the list.
587878ed226SJulian Elischer 		 */
588878ed226SJulian Elischer 
589878ed226SJulian Elischer 		cmd->ch->dcid = dcid;
590878ed226SJulian Elischer 		ng_l2cap_command_timeout(cmd, bluetooth_l2cap_ertx_timeout());
591878ed226SJulian Elischer 
592878ed226SJulian Elischer 		error = ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token,
593878ed226SJulian Elischer 				result, status);
594878ed226SJulian Elischer 		if (error != 0)
595878ed226SJulian Elischer 			ng_l2cap_free_chan(cmd->ch);
596878ed226SJulian Elischer 	} else {
597878ed226SJulian Elischer 		ng_l2cap_unlink_cmd(cmd);
598878ed226SJulian Elischer 
599878ed226SJulian Elischer 		if (result == NG_L2CAP_SUCCESS) {
600878ed226SJulian Elischer 			/*
601878ed226SJulian Elischer 			 * Channel is open. Complete command and move to CONFIG
602878ed226SJulian Elischer 			 * state. Since we have sent positive confirmation we
603878ed226SJulian Elischer 			 * expect to receive L2CA_Config request from the upper
604878ed226SJulian Elischer 			 * layer protocol.
605878ed226SJulian Elischer 			 */
606878ed226SJulian Elischer 
607878ed226SJulian Elischer 			cmd->ch->dcid = dcid;
6083a601a23STakanori Watanabe 			cmd->ch->state = ((cmd->ch->scid == NG_L2CAP_ATT_CID)||
6093a601a23STakanori Watanabe 					  (cmd->ch->scid == NG_L2CAP_SMP_CID))
6103a601a23STakanori Watanabe 					  ?
611fbc48c2bSTakanori Watanabe 			  NG_L2CAP_OPEN : NG_L2CAP_CONFIG;
612878ed226SJulian Elischer 		} else
613878ed226SJulian Elischer 			/* There was an error, so close the channel */
614878ed226SJulian Elischer 			NG_L2CAP_INFO(
615878ed226SJulian Elischer "%s: %s - failed to open L2CAP channel, result=%d, status=%d\n",
616878ed226SJulian Elischer 				__func__, NG_NODE_NAME(l2cap->node), result,
617878ed226SJulian Elischer 				status);
618878ed226SJulian Elischer 
619878ed226SJulian Elischer 		error = ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token,
620878ed226SJulian Elischer 				result, status);
621878ed226SJulian Elischer 
622878ed226SJulian Elischer 		/* XXX do we have to remove the channel on error? */
623878ed226SJulian Elischer 		if (error != 0 || result != NG_L2CAP_SUCCESS)
624878ed226SJulian Elischer 			ng_l2cap_free_chan(cmd->ch);
625878ed226SJulian Elischer 
626878ed226SJulian Elischer 		ng_l2cap_free_cmd(cmd);
627878ed226SJulian Elischer 	}
628878ed226SJulian Elischer 
629878ed226SJulian Elischer 	return (error);
630878ed226SJulian Elischer 
631878ed226SJulian Elischer reject:
632878ed226SJulian Elischer 	/* Send reject. Do not really care about the result */
633878ed226SJulian Elischer 	send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, dcid);
634878ed226SJulian Elischer 
635878ed226SJulian Elischer 	return (0);
636878ed226SJulian Elischer } /* ng_l2cap_process_con_rsp */
637878ed226SJulian Elischer 
638878ed226SJulian Elischer /*
639878ed226SJulian Elischer  * Process L2CAP_ConfigReq command
640878ed226SJulian Elischer  */
641878ed226SJulian Elischer 
642878ed226SJulian Elischer static int
ng_l2cap_process_cfg_req(ng_l2cap_con_p con,u_int8_t ident)643878ed226SJulian Elischer ng_l2cap_process_cfg_req(ng_l2cap_con_p con, u_int8_t ident)
644878ed226SJulian Elischer {
645878ed226SJulian Elischer 	ng_l2cap_p		 l2cap = con->l2cap;
646878ed226SJulian Elischer 	struct mbuf		*m = con->rx_pkt;
647878ed226SJulian Elischer 	ng_l2cap_cfg_req_cp	*cp = NULL;
648878ed226SJulian Elischer 	ng_l2cap_chan_p		 ch = NULL;
649878ed226SJulian Elischer 	u_int16_t		 dcid, respond, result;
650878ed226SJulian Elischer 	ng_l2cap_cfg_opt_t	 hdr;
651878ed226SJulian Elischer 	ng_l2cap_cfg_opt_val_t	 val;
652878ed226SJulian Elischer 	int			 off, error = 0;
653878ed226SJulian Elischer 
654878ed226SJulian Elischer 	/* Get command parameters */
655878ed226SJulian Elischer 	con->rx_pkt = NULL;
656878ed226SJulian Elischer 	NG_L2CAP_M_PULLUP(m, sizeof(*cp));
657878ed226SJulian Elischer 	if (m == NULL)
658878ed226SJulian Elischer 		return (ENOBUFS);
659878ed226SJulian Elischer 
660878ed226SJulian Elischer 	cp = mtod(m, ng_l2cap_cfg_req_cp *);
661878ed226SJulian Elischer 	dcid = le16toh(cp->dcid);
662878ed226SJulian Elischer 	respond = NG_L2CAP_OPT_CFLAG(le16toh(cp->flags));
663878ed226SJulian Elischer 	m_adj(m, sizeof(*cp));
664878ed226SJulian Elischer 
665878ed226SJulian Elischer 	/* Check if we have this channel and it is in valid state */
666fbc48c2bSTakanori Watanabe 	ch = ng_l2cap_chan_by_scid(l2cap, dcid, NG_L2CAP_L2CA_IDTYPE_BREDR);
667878ed226SJulian Elischer 	if (ch == NULL) {
668878ed226SJulian Elischer 		NG_L2CAP_ERR(
669878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConfigReq command. " \
670878ed226SJulian Elischer "Channel does not exist, cid=%d\n",
671878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), dcid);
672878ed226SJulian Elischer 		goto reject;
673878ed226SJulian Elischer 	}
674878ed226SJulian Elischer 
675878ed226SJulian Elischer 	/* Verify channel state */
676878ed226SJulian Elischer 	if (ch->state != NG_L2CAP_CONFIG && ch->state != NG_L2CAP_OPEN) {
677878ed226SJulian Elischer 		NG_L2CAP_ERR(
678878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConfigReq. " \
679878ed226SJulian Elischer "Invalid channel state, cid=%d, state=%d\n",
680878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), dcid, ch->state);
681878ed226SJulian Elischer 		goto reject;
682878ed226SJulian Elischer 	}
683878ed226SJulian Elischer 
684878ed226SJulian Elischer 	if (ch->state == NG_L2CAP_OPEN) { /* Re-configuration */
685878ed226SJulian Elischer 		ch->cfg_state = 0;
686878ed226SJulian Elischer 		ch->state = NG_L2CAP_CONFIG;
687878ed226SJulian Elischer 	}
688878ed226SJulian Elischer 
689878ed226SJulian Elischer 	for (result = 0, off = 0; ; ) {
690878ed226SJulian Elischer 		error = get_next_l2cap_opt(m, &off, &hdr, &val);
691878ed226SJulian Elischer 		if (error == 0) { /* We done with this packet */
692878ed226SJulian Elischer 			NG_FREE_M(m);
693878ed226SJulian Elischer 			break;
694878ed226SJulian Elischer 		} else if (error > 0) { /* Got option */
695878ed226SJulian Elischer 			switch (hdr.type) {
696878ed226SJulian Elischer 			case NG_L2CAP_OPT_MTU:
697878ed226SJulian Elischer 				ch->omtu = val.mtu;
698878ed226SJulian Elischer 				break;
699878ed226SJulian Elischer 
700878ed226SJulian Elischer 			case NG_L2CAP_OPT_FLUSH_TIMO:
701878ed226SJulian Elischer 				ch->flush_timo = val.flush_timo;
702878ed226SJulian Elischer 				break;
703878ed226SJulian Elischer 
704878ed226SJulian Elischer 			case NG_L2CAP_OPT_QOS:
705878ed226SJulian Elischer 				bcopy(&val.flow, &ch->iflow, sizeof(ch->iflow));
706878ed226SJulian Elischer 				break;
707878ed226SJulian Elischer 
7080986ab12SMaksim Yevmenkin 			default: /* Ignore unknown hint option */
709878ed226SJulian Elischer 				break;
710878ed226SJulian Elischer 			}
711878ed226SJulian Elischer 		} else { /* Oops, something is wrong */
712878ed226SJulian Elischer 			respond = 1;
713878ed226SJulian Elischer 
714878ed226SJulian Elischer 			if (error == -3) {
715878ed226SJulian Elischer 				/*
716878ed226SJulian Elischer 				 * Adjust mbuf so we can get to the start
717878ed226SJulian Elischer 				 * of the first option we did not like.
718878ed226SJulian Elischer 				 */
719878ed226SJulian Elischer 
720878ed226SJulian Elischer 				m_adj(m, off - sizeof(hdr));
721878ed226SJulian Elischer 				m->m_pkthdr.len = sizeof(hdr) + hdr.length;
722878ed226SJulian Elischer 
723878ed226SJulian Elischer 				result = NG_L2CAP_UNKNOWN_OPTION;
724878ed226SJulian Elischer 			} else {
725878ed226SJulian Elischer 				/* XXX FIXME Send other reject codes? */
726878ed226SJulian Elischer 				NG_FREE_M(m);
727878ed226SJulian Elischer 				result = NG_L2CAP_REJECT;
728878ed226SJulian Elischer 			}
729878ed226SJulian Elischer 
730878ed226SJulian Elischer 			break;
731878ed226SJulian Elischer 		}
732878ed226SJulian Elischer 	}
733878ed226SJulian Elischer 
734878ed226SJulian Elischer 	/*
735878ed226SJulian Elischer 	 * Now check and see if we have to respond. If everything was OK then
736878ed226SJulian Elischer 	 * respond contain "C flag" and (if set) we will respond with empty
737878ed226SJulian Elischer 	 * packet and will wait for more options.
738878ed226SJulian Elischer 	 *
739878ed226SJulian Elischer 	 * Other case is that we did not like peer's options and will respond
740878ed226SJulian Elischer 	 * with L2CAP_Config response command with Reject error code.
741878ed226SJulian Elischer 	 *
742878ed226SJulian Elischer 	 * When "respond == 0" than we have received all options and we will
743878ed226SJulian Elischer 	 * sent L2CA_ConfigInd event to the upper layer protocol.
744878ed226SJulian Elischer 	 */
745878ed226SJulian Elischer 
746878ed226SJulian Elischer 	if (respond) {
747878ed226SJulian Elischer 		error = send_l2cap_cfg_rsp(con, ident, ch->dcid, result, m);
748878ed226SJulian Elischer 		if (error != 0) {
749878ed226SJulian Elischer 			ng_l2cap_l2ca_discon_ind(ch);
750878ed226SJulian Elischer 			ng_l2cap_free_chan(ch);
751878ed226SJulian Elischer 		}
752878ed226SJulian Elischer 	} else {
753878ed226SJulian Elischer 		/* Send L2CA_ConfigInd event to the upper layer protocol */
754878ed226SJulian Elischer 		ch->ident = ident;
755878ed226SJulian Elischer 		error = ng_l2cap_l2ca_cfg_ind(ch);
756878ed226SJulian Elischer 		if (error != 0)
757878ed226SJulian Elischer 			ng_l2cap_free_chan(ch);
758878ed226SJulian Elischer 	}
759878ed226SJulian Elischer 
760878ed226SJulian Elischer 	return (error);
761878ed226SJulian Elischer 
762878ed226SJulian Elischer reject:
763878ed226SJulian Elischer 	/* Send reject. Do not really care about the result */
764878ed226SJulian Elischer 	NG_FREE_M(m);
765878ed226SJulian Elischer 
766878ed226SJulian Elischer 	send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, 0, dcid);
767878ed226SJulian Elischer 
768878ed226SJulian Elischer 	return (0);
769878ed226SJulian Elischer } /* ng_l2cap_process_cfg_req */
770878ed226SJulian Elischer 
771878ed226SJulian Elischer /*
772878ed226SJulian Elischer  * Process L2CAP_ConfigRsp command
773878ed226SJulian Elischer  */
774878ed226SJulian Elischer 
775878ed226SJulian Elischer static int
ng_l2cap_process_cfg_rsp(ng_l2cap_con_p con,u_int8_t ident)776878ed226SJulian Elischer ng_l2cap_process_cfg_rsp(ng_l2cap_con_p con, u_int8_t ident)
777878ed226SJulian Elischer {
778878ed226SJulian Elischer 	ng_l2cap_p		 l2cap = con->l2cap;
779878ed226SJulian Elischer 	struct mbuf		*m = con->rx_pkt;
780878ed226SJulian Elischer 	ng_l2cap_cfg_rsp_cp	*cp = NULL;
781878ed226SJulian Elischer 	ng_l2cap_cmd_p		 cmd = NULL;
782878ed226SJulian Elischer 	u_int16_t		 scid, cflag, result;
783878ed226SJulian Elischer 	ng_l2cap_cfg_opt_t	 hdr;
784878ed226SJulian Elischer 	ng_l2cap_cfg_opt_val_t	 val;
785878ed226SJulian Elischer 	int			 off, error = 0;
786878ed226SJulian Elischer 
787878ed226SJulian Elischer 	/* Get command parameters */
788878ed226SJulian Elischer 	con->rx_pkt = NULL;
789878ed226SJulian Elischer 	NG_L2CAP_M_PULLUP(m, sizeof(*cp));
790878ed226SJulian Elischer 	if (m == NULL)
791878ed226SJulian Elischer 		return (ENOBUFS);
792878ed226SJulian Elischer 
793878ed226SJulian Elischer 	cp = mtod(m, ng_l2cap_cfg_rsp_cp *);
794878ed226SJulian Elischer 	scid = le16toh(cp->scid);
795878ed226SJulian Elischer 	cflag = NG_L2CAP_OPT_CFLAG(le16toh(cp->flags));
796878ed226SJulian Elischer 	result = le16toh(cp->result);
797878ed226SJulian Elischer 	m_adj(m, sizeof(*cp));
798878ed226SJulian Elischer 
799878ed226SJulian Elischer 	/* Check if we have this command */
800878ed226SJulian Elischer 	cmd = ng_l2cap_cmd_by_ident(con, ident);
801878ed226SJulian Elischer 	if (cmd == NULL) {
802878ed226SJulian Elischer 		NG_L2CAP_ERR(
803878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConfigRsp command. ident=%d, con_handle=%d\n",
804878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), ident,
805878ed226SJulian Elischer 			con->con_handle);
806878ed226SJulian Elischer 		NG_FREE_M(m);
807878ed226SJulian Elischer 
808878ed226SJulian Elischer 		return (ENOENT);
809878ed226SJulian Elischer 	}
810878ed226SJulian Elischer 
811878ed226SJulian Elischer 	/* Verify CIDs and send reject if does not match */
812878ed226SJulian Elischer 	if (cmd->ch->scid != scid) {
813878ed226SJulian Elischer 		NG_L2CAP_ERR(
814878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConfigRsp. " \
815878ed226SJulian Elischer "Channel ID does not match, scid=%d(%d)\n",
816878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,
817878ed226SJulian Elischer 			scid);
818878ed226SJulian Elischer 		goto reject;
819878ed226SJulian Elischer 	}
820878ed226SJulian Elischer 
821878ed226SJulian Elischer 	/* Verify channel state and reject if invalid */
822878ed226SJulian Elischer 	if (cmd->ch->state != NG_L2CAP_CONFIG) {
823878ed226SJulian Elischer 		NG_L2CAP_ERR(
824878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConfigRsp. " \
825878ed226SJulian Elischer "Invalid channel state, scid=%d, state=%d\n",
826878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,
827878ed226SJulian Elischer 			cmd->ch->state);
828878ed226SJulian Elischer 		goto reject;
829878ed226SJulian Elischer 	}
830878ed226SJulian Elischer 
831878ed226SJulian Elischer 	/*
832878ed226SJulian Elischer 	 * Looks like it is our response, so process it. First parse options,
833878ed226SJulian Elischer 	 * then verify C flag. If it is set then we shall expect more
834878ed226SJulian Elischer 	 * configuration options from the peer and we will wait. Otherwise we
835878ed226SJulian Elischer 	 * have received all options and we will send L2CA_ConfigRsp event to
8360986ab12SMaksim Yevmenkin 	 * the upper layer protocol. If command timeout already happened then
8370986ab12SMaksim Yevmenkin 	 * ignore response.
838878ed226SJulian Elischer 	 */
839878ed226SJulian Elischer 
8400986ab12SMaksim Yevmenkin 	if ((error = ng_l2cap_command_untimeout(cmd)) != 0) {
8410986ab12SMaksim Yevmenkin 		NG_FREE_M(m);
8420986ab12SMaksim Yevmenkin 		return (error);
8430986ab12SMaksim Yevmenkin 	}
844878ed226SJulian Elischer 
845878ed226SJulian Elischer 	for (off = 0; ; ) {
846878ed226SJulian Elischer 		error = get_next_l2cap_opt(m, &off, &hdr, &val);
847878ed226SJulian Elischer 		if (error == 0) /* We done with this packet */
848878ed226SJulian Elischer 			break;
849878ed226SJulian Elischer 		else if (error > 0) { /* Got option */
850878ed226SJulian Elischer 			switch (hdr.type) {
851878ed226SJulian Elischer 			case NG_L2CAP_OPT_MTU:
852878ed226SJulian Elischer 				cmd->ch->imtu = val.mtu;
853878ed226SJulian Elischer 			break;
854878ed226SJulian Elischer 
855878ed226SJulian Elischer 			case NG_L2CAP_OPT_FLUSH_TIMO:
856878ed226SJulian Elischer 				cmd->ch->flush_timo = val.flush_timo;
857878ed226SJulian Elischer 				break;
858878ed226SJulian Elischer 
859878ed226SJulian Elischer 			case NG_L2CAP_OPT_QOS:
860878ed226SJulian Elischer 				bcopy(&val.flow, &cmd->ch->oflow,
861878ed226SJulian Elischer 					sizeof(cmd->ch->oflow));
862878ed226SJulian Elischer 			break;
863878ed226SJulian Elischer 
8640986ab12SMaksim Yevmenkin 			default: /* Ignore unknown hint option */
865878ed226SJulian Elischer 				break;
866878ed226SJulian Elischer 			}
867878ed226SJulian Elischer 		} else {
868878ed226SJulian Elischer 			/*
869878ed226SJulian Elischer 			 * XXX FIXME What to do here?
870878ed226SJulian Elischer 			 *
8710986ab12SMaksim Yevmenkin 			 * This is really BAD :( options packet was broken, or
8720986ab12SMaksim Yevmenkin 			 * peer sent us option that we did not understand. Let
8730986ab12SMaksim Yevmenkin 			 * upper layer know and do not wait for more options.
874878ed226SJulian Elischer 			 */
875878ed226SJulian Elischer 
876878ed226SJulian Elischer 			NG_L2CAP_ALERT(
877878ed226SJulian Elischer "%s: %s - failed to parse configuration options, error=%d\n",
878878ed226SJulian Elischer 				__func__, NG_NODE_NAME(l2cap->node), error);
879878ed226SJulian Elischer 
880878ed226SJulian Elischer 			result = NG_L2CAP_UNKNOWN;
881878ed226SJulian Elischer 			cflag = 0;
882878ed226SJulian Elischer 
883878ed226SJulian Elischer 			break;
884878ed226SJulian Elischer 		}
885878ed226SJulian Elischer 	}
886878ed226SJulian Elischer 
887878ed226SJulian Elischer 	NG_FREE_M(m);
888878ed226SJulian Elischer 
889878ed226SJulian Elischer 	if (cflag) /* Restart timer and wait for more options */
890878ed226SJulian Elischer 		ng_l2cap_command_timeout(cmd, bluetooth_l2cap_rtx_timeout());
891878ed226SJulian Elischer 	else {
892878ed226SJulian Elischer 		ng_l2cap_unlink_cmd(cmd);
893878ed226SJulian Elischer 
894878ed226SJulian Elischer 		/* Send L2CA_Config response to the upper layer protocol */
895878ed226SJulian Elischer 		error = ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, result);
896878ed226SJulian Elischer 		if (error != 0) {
897878ed226SJulian Elischer 			/*
898878ed226SJulian Elischer 			 * XXX FIXME what to do here? we were not able to send
899878ed226SJulian Elischer 			 * response to the upper layer protocol, so for now
900878ed226SJulian Elischer 			 * just close the channel. Send L2CAP_Disconnect to
901878ed226SJulian Elischer 			 * remote peer?
902878ed226SJulian Elischer 			 */
903878ed226SJulian Elischer 
904878ed226SJulian Elischer 			NG_L2CAP_ERR(
905878ed226SJulian Elischer "%s: %s - failed to send L2CA_Config response, error=%d\n",
906878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), error);
907878ed226SJulian Elischer 
908878ed226SJulian Elischer 			ng_l2cap_free_chan(cmd->ch);
909878ed226SJulian Elischer 		}
910878ed226SJulian Elischer 
911878ed226SJulian Elischer 		ng_l2cap_free_cmd(cmd);
912878ed226SJulian Elischer 	}
913878ed226SJulian Elischer 
914878ed226SJulian Elischer 	return (error);
915878ed226SJulian Elischer 
916878ed226SJulian Elischer reject:
917878ed226SJulian Elischer 	/* Send reject. Do not really care about the result */
918878ed226SJulian Elischer 	NG_FREE_M(m);
919878ed226SJulian Elischer 
920878ed226SJulian Elischer 	send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, 0);
921878ed226SJulian Elischer 
922878ed226SJulian Elischer 	return (0);
923878ed226SJulian Elischer } /* ng_l2cap_process_cfg_rsp */
924878ed226SJulian Elischer 
925878ed226SJulian Elischer /*
926878ed226SJulian Elischer  * Process L2CAP_DisconnectReq command
927878ed226SJulian Elischer  */
928878ed226SJulian Elischer 
929878ed226SJulian Elischer static int
ng_l2cap_process_discon_req(ng_l2cap_con_p con,u_int8_t ident)930878ed226SJulian Elischer ng_l2cap_process_discon_req(ng_l2cap_con_p con, u_int8_t ident)
931878ed226SJulian Elischer {
932878ed226SJulian Elischer 	ng_l2cap_p		 l2cap = con->l2cap;
933878ed226SJulian Elischer 	ng_l2cap_discon_req_cp	*cp = NULL;
934878ed226SJulian Elischer 	ng_l2cap_chan_p		 ch = NULL;
935878ed226SJulian Elischer 	ng_l2cap_cmd_p		 cmd = NULL;
936878ed226SJulian Elischer 	u_int16_t		 scid, dcid;
937878ed226SJulian Elischer 
938878ed226SJulian Elischer 	/* Get command parameters */
939878ed226SJulian Elischer 	NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));
940878ed226SJulian Elischer 	if (con->rx_pkt == NULL)
941878ed226SJulian Elischer 		return (ENOBUFS);
942878ed226SJulian Elischer 
943878ed226SJulian Elischer 	cp = mtod(con->rx_pkt, ng_l2cap_discon_req_cp *);
944878ed226SJulian Elischer 	dcid = le16toh(cp->dcid);
945878ed226SJulian Elischer 	scid = le16toh(cp->scid);
946878ed226SJulian Elischer 
947878ed226SJulian Elischer 	NG_FREE_M(con->rx_pkt);
948878ed226SJulian Elischer 
949878ed226SJulian Elischer 	/* Check if we have this channel and it is in valid state */
950fbc48c2bSTakanori Watanabe 	ch = ng_l2cap_chan_by_scid(l2cap, dcid, NG_L2CAP_L2CA_IDTYPE_BREDR);
951878ed226SJulian Elischer 	if (ch == NULL) {
952878ed226SJulian Elischer 		NG_L2CAP_ERR(
953878ed226SJulian Elischer "%s: %s - unexpected L2CAP_DisconnectReq message. " \
954878ed226SJulian Elischer "Channel does not exist, cid=%d\n",
955878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), dcid);
956878ed226SJulian Elischer 		goto reject;
957878ed226SJulian Elischer 	}
958878ed226SJulian Elischer 
959878ed226SJulian Elischer 	/* XXX Verify channel state and reject if invalid -- is that true? */
960f2bb1caeSJulian Elischer 	if (ch->state != NG_L2CAP_OPEN && ch->state != NG_L2CAP_CONFIG &&
961f2bb1caeSJulian Elischer 	    ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) {
962878ed226SJulian Elischer 		NG_L2CAP_ERR(
963878ed226SJulian Elischer "%s: %s - unexpected L2CAP_DisconnectReq. " \
964878ed226SJulian Elischer "Invalid channel state, cid=%d, state=%d\n",
965878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), dcid, ch->state);
966878ed226SJulian Elischer 		goto reject;
967878ed226SJulian Elischer 	}
968878ed226SJulian Elischer 
969878ed226SJulian Elischer 	/* Match destination channel ID */
970878ed226SJulian Elischer 	if (ch->dcid != scid || ch->scid != dcid) {
971878ed226SJulian Elischer 		NG_L2CAP_ERR(
972878ed226SJulian Elischer "%s: %s - unexpected L2CAP_DisconnectReq. " \
973878ed226SJulian Elischer "Channel IDs does not match, channel: scid=%d, dcid=%d, " \
974878ed226SJulian Elischer "request: scid=%d, dcid=%d\n",
975878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), ch->scid, ch->dcid,
976878ed226SJulian Elischer 			scid, dcid);
977878ed226SJulian Elischer 		goto reject;
978878ed226SJulian Elischer 	}
979878ed226SJulian Elischer 
980878ed226SJulian Elischer 	/*
981878ed226SJulian Elischer 	 * Looks good, so notify upper layer protocol that channel is about
982878ed226SJulian Elischer 	 * to be disconnected and send L2CA_DisconnectInd message. Then respond
983878ed226SJulian Elischer 	 * with L2CAP_DisconnectRsp.
984878ed226SJulian Elischer 	 */
985878ed226SJulian Elischer 
986f2bb1caeSJulian Elischer 	if (ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) {
987878ed226SJulian Elischer 		ng_l2cap_l2ca_discon_ind(ch); /* do not care about result */
988878ed226SJulian Elischer 		ng_l2cap_free_chan(ch);
989f2bb1caeSJulian Elischer 	}
990878ed226SJulian Elischer 
991878ed226SJulian Elischer 	/* Send L2CAP_DisconnectRsp */
992878ed226SJulian Elischer 	cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_DISCON_RSP, 0);
993878ed226SJulian Elischer 	if (cmd == NULL)
994878ed226SJulian Elischer 		return (ENOMEM);
995878ed226SJulian Elischer 
996878ed226SJulian Elischer 	_ng_l2cap_discon_rsp(cmd->aux, ident, dcid, scid);
997878ed226SJulian Elischer 	if (cmd->aux == NULL) {
998878ed226SJulian Elischer 		ng_l2cap_free_cmd(cmd);
999878ed226SJulian Elischer 
1000878ed226SJulian Elischer 		return (ENOBUFS);
1001878ed226SJulian Elischer 	}
1002878ed226SJulian Elischer 
1003878ed226SJulian Elischer 	/* Link command to the queue */
1004878ed226SJulian Elischer 	ng_l2cap_link_cmd(con, cmd);
1005878ed226SJulian Elischer 	ng_l2cap_lp_deliver(con);
1006878ed226SJulian Elischer 
1007878ed226SJulian Elischer 	return (0);
1008878ed226SJulian Elischer 
1009878ed226SJulian Elischer reject:
1010878ed226SJulian Elischer 	/* Send reject. Do not really care about the result */
1011878ed226SJulian Elischer 	send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, dcid);
1012878ed226SJulian Elischer 
1013878ed226SJulian Elischer 	return (0);
1014878ed226SJulian Elischer } /* ng_l2cap_process_discon_req */
1015878ed226SJulian Elischer 
1016878ed226SJulian Elischer /*
1017878ed226SJulian Elischer  * Process L2CAP_DisconnectRsp command
1018878ed226SJulian Elischer  */
1019878ed226SJulian Elischer 
1020878ed226SJulian Elischer static int
ng_l2cap_process_discon_rsp(ng_l2cap_con_p con,u_int8_t ident)1021878ed226SJulian Elischer ng_l2cap_process_discon_rsp(ng_l2cap_con_p con, u_int8_t ident)
1022878ed226SJulian Elischer {
1023878ed226SJulian Elischer 	ng_l2cap_p		 l2cap = con->l2cap;
1024878ed226SJulian Elischer 	ng_l2cap_discon_rsp_cp	*cp = NULL;
1025878ed226SJulian Elischer 	ng_l2cap_cmd_p		 cmd = NULL;
1026878ed226SJulian Elischer 	u_int16_t		 scid, dcid;
1027878ed226SJulian Elischer 	int			 error = 0;
1028878ed226SJulian Elischer 
1029878ed226SJulian Elischer 	/* Get command parameters */
1030878ed226SJulian Elischer 	NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));
1031878ed226SJulian Elischer 	if (con->rx_pkt == NULL)
1032878ed226SJulian Elischer 		return (ENOBUFS);
1033878ed226SJulian Elischer 
1034878ed226SJulian Elischer 	cp = mtod(con->rx_pkt, ng_l2cap_discon_rsp_cp *);
1035878ed226SJulian Elischer 	dcid = le16toh(cp->dcid);
1036878ed226SJulian Elischer 	scid = le16toh(cp->scid);
1037878ed226SJulian Elischer 
1038878ed226SJulian Elischer 	NG_FREE_M(con->rx_pkt);
1039878ed226SJulian Elischer 
1040878ed226SJulian Elischer 	/* Check if we have pending command descriptor */
1041878ed226SJulian Elischer 	cmd = ng_l2cap_cmd_by_ident(con, ident);
1042878ed226SJulian Elischer 	if (cmd == NULL) {
1043878ed226SJulian Elischer 		NG_L2CAP_ERR(
1044878ed226SJulian Elischer "%s: %s - unexpected L2CAP_DisconnectRsp command. ident=%d, con_handle=%d\n",
1045878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), ident,
1046878ed226SJulian Elischer 			con->con_handle);
1047878ed226SJulian Elischer 		goto out;
1048878ed226SJulian Elischer 	}
1049878ed226SJulian Elischer 
1050878ed226SJulian Elischer 	/* Verify channel state, do nothing if invalid */
1051878ed226SJulian Elischer 	if (cmd->ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) {
1052878ed226SJulian Elischer 		NG_L2CAP_ERR(
1053878ed226SJulian Elischer "%s: %s - unexpected L2CAP_DisconnectRsp. " \
1054878ed226SJulian Elischer "Invalid channel state, cid=%d, state=%d\n",
1055878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), scid,
1056878ed226SJulian Elischer 			cmd->ch->state);
1057878ed226SJulian Elischer 		goto out;
1058878ed226SJulian Elischer 	}
1059878ed226SJulian Elischer 
1060878ed226SJulian Elischer 	/* Verify CIDs and send reject if does not match */
1061878ed226SJulian Elischer 	if (cmd->ch->scid != scid || cmd->ch->dcid != dcid) {
1062878ed226SJulian Elischer 		NG_L2CAP_ERR(
1063878ed226SJulian Elischer "%s: %s - unexpected L2CAP_DisconnectRsp. " \
1064878ed226SJulian Elischer "Channel IDs do not match, scid=%d(%d), dcid=%d(%d)\n",
1065878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,
1066878ed226SJulian Elischer 			scid, cmd->ch->dcid, dcid);
1067878ed226SJulian Elischer 		goto out;
1068878ed226SJulian Elischer 	}
1069878ed226SJulian Elischer 
1070878ed226SJulian Elischer 	/*
1071053359b7SPedro F. Giffuni 	 * Looks like we have successfully disconnected channel, so notify
10720986ab12SMaksim Yevmenkin 	 * upper layer. If command timeout already happened then ignore
10730986ab12SMaksim Yevmenkin 	 * response.
1074878ed226SJulian Elischer 	 */
1075878ed226SJulian Elischer 
10760986ab12SMaksim Yevmenkin 	if ((error = ng_l2cap_command_untimeout(cmd)) != 0)
10770986ab12SMaksim Yevmenkin 		goto out;
10780986ab12SMaksim Yevmenkin 
1079878ed226SJulian Elischer 	error = ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, NG_L2CAP_SUCCESS);
1080878ed226SJulian Elischer 	ng_l2cap_free_chan(cmd->ch); /* this will free commands too */
1081878ed226SJulian Elischer out:
1082878ed226SJulian Elischer 	return (error);
1083878ed226SJulian Elischer } /* ng_l2cap_process_discon_rsp */
1084878ed226SJulian Elischer 
1085878ed226SJulian Elischer /*
1086878ed226SJulian Elischer  * Process L2CAP_EchoReq command
1087878ed226SJulian Elischer  */
1088878ed226SJulian Elischer 
1089878ed226SJulian Elischer static int
ng_l2cap_process_echo_req(ng_l2cap_con_p con,u_int8_t ident)1090878ed226SJulian Elischer ng_l2cap_process_echo_req(ng_l2cap_con_p con, u_int8_t ident)
1091878ed226SJulian Elischer {
1092878ed226SJulian Elischer 	ng_l2cap_p		 l2cap = con->l2cap;
1093878ed226SJulian Elischer 	ng_l2cap_cmd_hdr_t	*hdr = NULL;
1094878ed226SJulian Elischer 	ng_l2cap_cmd_p		 cmd = NULL;
1095878ed226SJulian Elischer 
1096878ed226SJulian Elischer 	con->rx_pkt = ng_l2cap_prepend(con->rx_pkt, sizeof(*hdr));
1097878ed226SJulian Elischer 	if (con->rx_pkt == NULL) {
1098878ed226SJulian Elischer 		NG_L2CAP_ALERT(
10994ae439a3SMaksim Yevmenkin "%s: %s - ng_l2cap_prepend() failed, size=%zd\n",
1100878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), sizeof(*hdr));
1101878ed226SJulian Elischer 
1102878ed226SJulian Elischer 		return (ENOBUFS);
1103878ed226SJulian Elischer 	}
1104878ed226SJulian Elischer 
1105878ed226SJulian Elischer 	hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *);
1106878ed226SJulian Elischer 	hdr->code = NG_L2CAP_ECHO_RSP;
1107878ed226SJulian Elischer 	hdr->ident = ident;
1108878ed226SJulian Elischer 	hdr->length = htole16(con->rx_pkt->m_pkthdr.len - sizeof(*hdr));
1109878ed226SJulian Elischer 
1110878ed226SJulian Elischer 	cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_ECHO_RSP, 0);
1111878ed226SJulian Elischer 	if (cmd == NULL) {
1112878ed226SJulian Elischer 		NG_FREE_M(con->rx_pkt);
1113878ed226SJulian Elischer 
1114878ed226SJulian Elischer 		return (ENOBUFS);
1115878ed226SJulian Elischer 	}
1116878ed226SJulian Elischer 
1117878ed226SJulian Elischer 	/* Attach data and link command to the queue */
1118878ed226SJulian Elischer 	cmd->aux = con->rx_pkt;
1119878ed226SJulian Elischer 	con->rx_pkt = NULL;
1120878ed226SJulian Elischer 	ng_l2cap_link_cmd(con, cmd);
1121878ed226SJulian Elischer 	ng_l2cap_lp_deliver(con);
1122878ed226SJulian Elischer 
1123878ed226SJulian Elischer 	return (0);
1124878ed226SJulian Elischer } /* ng_l2cap_process_echo_req */
1125878ed226SJulian Elischer 
1126878ed226SJulian Elischer /*
1127878ed226SJulian Elischer  * Process L2CAP_EchoRsp command
1128878ed226SJulian Elischer  */
1129878ed226SJulian Elischer 
1130878ed226SJulian Elischer static int
ng_l2cap_process_echo_rsp(ng_l2cap_con_p con,u_int8_t ident)1131878ed226SJulian Elischer ng_l2cap_process_echo_rsp(ng_l2cap_con_p con, u_int8_t ident)
1132878ed226SJulian Elischer {
1133878ed226SJulian Elischer 	ng_l2cap_p	l2cap = con->l2cap;
1134878ed226SJulian Elischer 	ng_l2cap_cmd_p	cmd = NULL;
1135878ed226SJulian Elischer 	int		error = 0;
1136878ed226SJulian Elischer 
1137878ed226SJulian Elischer 	/* Check if we have this command */
1138878ed226SJulian Elischer 	cmd = ng_l2cap_cmd_by_ident(con, ident);
1139878ed226SJulian Elischer 	if (cmd != NULL) {
11400986ab12SMaksim Yevmenkin 		/* If command timeout already happened then ignore response */
11410986ab12SMaksim Yevmenkin 		if ((error = ng_l2cap_command_untimeout(cmd)) != 0) {
11420986ab12SMaksim Yevmenkin 			NG_FREE_M(con->rx_pkt);
11430986ab12SMaksim Yevmenkin 			return (error);
11440986ab12SMaksim Yevmenkin 		}
1145878ed226SJulian Elischer 
1146878ed226SJulian Elischer 		ng_l2cap_unlink_cmd(cmd);
1147878ed226SJulian Elischer 
1148878ed226SJulian Elischer 		error = ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
1149878ed226SJulian Elischer 				NG_L2CAP_SUCCESS, con->rx_pkt);
1150878ed226SJulian Elischer 
1151878ed226SJulian Elischer 		ng_l2cap_free_cmd(cmd);
1152878ed226SJulian Elischer 		con->rx_pkt = NULL;
1153878ed226SJulian Elischer 	} else {
1154878ed226SJulian Elischer 		NG_L2CAP_ERR(
1155878ed226SJulian Elischer "%s: %s - unexpected L2CAP_EchoRsp command. " \
1156878ed226SJulian Elischer "Requested ident does not exist, ident=%d\n",
1157878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), ident);
1158878ed226SJulian Elischer 		NG_FREE_M(con->rx_pkt);
1159878ed226SJulian Elischer 	}
1160878ed226SJulian Elischer 
1161878ed226SJulian Elischer 	return (error);
1162878ed226SJulian Elischer } /* ng_l2cap_process_echo_rsp */
1163878ed226SJulian Elischer 
1164878ed226SJulian Elischer /*
1165878ed226SJulian Elischer  * Process L2CAP_InfoReq command
1166878ed226SJulian Elischer  */
1167878ed226SJulian Elischer 
1168878ed226SJulian Elischer static int
ng_l2cap_process_info_req(ng_l2cap_con_p con,u_int8_t ident)1169878ed226SJulian Elischer ng_l2cap_process_info_req(ng_l2cap_con_p con, u_int8_t ident)
1170878ed226SJulian Elischer {
1171878ed226SJulian Elischer 	ng_l2cap_p	l2cap = con->l2cap;
1172878ed226SJulian Elischer 	ng_l2cap_cmd_p	cmd = NULL;
1173878ed226SJulian Elischer 	u_int16_t	type;
1174878ed226SJulian Elischer 
1175878ed226SJulian Elischer 	/* Get command parameters */
1176878ed226SJulian Elischer 	NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(ng_l2cap_info_req_cp));
1177878ed226SJulian Elischer 	if (con->rx_pkt == NULL)
1178878ed226SJulian Elischer 		return (ENOBUFS);
1179878ed226SJulian Elischer 
1180878ed226SJulian Elischer 	type = le16toh(mtod(con->rx_pkt, ng_l2cap_info_req_cp *)->type);
1181878ed226SJulian Elischer 	NG_FREE_M(con->rx_pkt);
1182878ed226SJulian Elischer 
1183878ed226SJulian Elischer 	cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_INFO_RSP, 0);
1184878ed226SJulian Elischer 	if (cmd == NULL)
1185878ed226SJulian Elischer 		return (ENOMEM);
1186878ed226SJulian Elischer 
1187878ed226SJulian Elischer 	switch (type) {
1188878ed226SJulian Elischer 	case NG_L2CAP_CONNLESS_MTU:
1189878ed226SJulian Elischer 		_ng_l2cap_info_rsp(cmd->aux, ident, NG_L2CAP_CONNLESS_MTU,
1190878ed226SJulian Elischer 				NG_L2CAP_SUCCESS, NG_L2CAP_MTU_DEFAULT);
1191878ed226SJulian Elischer 		break;
1192878ed226SJulian Elischer 
1193878ed226SJulian Elischer 	default:
1194878ed226SJulian Elischer 		_ng_l2cap_info_rsp(cmd->aux, ident, type,
1195878ed226SJulian Elischer 				NG_L2CAP_NOT_SUPPORTED, 0);
1196878ed226SJulian Elischer 		break;
1197878ed226SJulian Elischer 	}
1198878ed226SJulian Elischer 
1199878ed226SJulian Elischer 	if (cmd->aux == NULL) {
1200878ed226SJulian Elischer 		ng_l2cap_free_cmd(cmd);
1201878ed226SJulian Elischer 
1202878ed226SJulian Elischer 		return (ENOBUFS);
1203878ed226SJulian Elischer 	}
1204878ed226SJulian Elischer 
1205878ed226SJulian Elischer 	/* Link command to the queue */
1206878ed226SJulian Elischer 	ng_l2cap_link_cmd(con, cmd);
1207878ed226SJulian Elischer 	ng_l2cap_lp_deliver(con);
1208878ed226SJulian Elischer 
1209878ed226SJulian Elischer 	return (0);
1210878ed226SJulian Elischer } /* ng_l2cap_process_info_req */
1211878ed226SJulian Elischer 
1212878ed226SJulian Elischer /*
1213878ed226SJulian Elischer  * Process L2CAP_InfoRsp command
1214878ed226SJulian Elischer  */
1215878ed226SJulian Elischer 
1216878ed226SJulian Elischer static int
ng_l2cap_process_info_rsp(ng_l2cap_con_p con,u_int8_t ident)1217878ed226SJulian Elischer ng_l2cap_process_info_rsp(ng_l2cap_con_p con, u_int8_t ident)
1218878ed226SJulian Elischer {
1219878ed226SJulian Elischer 	ng_l2cap_p		 l2cap = con->l2cap;
1220878ed226SJulian Elischer 	ng_l2cap_info_rsp_cp	*cp = NULL;
1221878ed226SJulian Elischer 	ng_l2cap_cmd_p		 cmd = NULL;
1222878ed226SJulian Elischer 	int			 error = 0;
1223878ed226SJulian Elischer 
1224878ed226SJulian Elischer 	/* Get command parameters */
1225878ed226SJulian Elischer 	NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));
1226878ed226SJulian Elischer 	if (con->rx_pkt == NULL)
1227878ed226SJulian Elischer 		return (ENOBUFS);
1228878ed226SJulian Elischer 
1229878ed226SJulian Elischer 	cp = mtod(con->rx_pkt, ng_l2cap_info_rsp_cp *);
1230878ed226SJulian Elischer 	cp->type = le16toh(cp->type);
1231878ed226SJulian Elischer 	cp->result = le16toh(cp->result);
1232878ed226SJulian Elischer 	m_adj(con->rx_pkt, sizeof(*cp));
1233878ed226SJulian Elischer 
1234878ed226SJulian Elischer 	/* Check if we have pending command descriptor */
1235878ed226SJulian Elischer 	cmd = ng_l2cap_cmd_by_ident(con, ident);
1236878ed226SJulian Elischer 	if (cmd == NULL) {
1237878ed226SJulian Elischer 		NG_L2CAP_ERR(
1238878ed226SJulian Elischer "%s: %s - unexpected L2CAP_InfoRsp command. " \
1239878ed226SJulian Elischer "Requested ident does not exist, ident=%d\n",
1240878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), ident);
1241878ed226SJulian Elischer 		NG_FREE_M(con->rx_pkt);
1242878ed226SJulian Elischer 
1243878ed226SJulian Elischer 		return (ENOENT);
1244878ed226SJulian Elischer 	}
1245878ed226SJulian Elischer 
12460986ab12SMaksim Yevmenkin 	/* If command timeout already happened then ignore response */
12470986ab12SMaksim Yevmenkin 	if ((error = ng_l2cap_command_untimeout(cmd)) != 0) {
12480986ab12SMaksim Yevmenkin 		NG_FREE_M(con->rx_pkt);
12490986ab12SMaksim Yevmenkin 		return (error);
12500986ab12SMaksim Yevmenkin 	}
1251878ed226SJulian Elischer 
1252878ed226SJulian Elischer 	ng_l2cap_unlink_cmd(cmd);
1253878ed226SJulian Elischer 
1254878ed226SJulian Elischer 	if (cp->result == NG_L2CAP_SUCCESS) {
1255878ed226SJulian Elischer 		switch (cp->type) {
1256878ed226SJulian Elischer 		case NG_L2CAP_CONNLESS_MTU:
1257878ed226SJulian Elischer 	    		if (con->rx_pkt->m_pkthdr.len == sizeof(u_int16_t))
1258878ed226SJulian Elischer 				*mtod(con->rx_pkt, u_int16_t *) =
1259878ed226SJulian Elischer 					le16toh(*mtod(con->rx_pkt,u_int16_t *));
1260878ed226SJulian Elischer 			else {
1261878ed226SJulian Elischer 				cp->result = NG_L2CAP_UNKNOWN; /* XXX */
1262878ed226SJulian Elischer 
1263878ed226SJulian Elischer 				NG_L2CAP_ERR(
1264878ed226SJulian Elischer "%s: %s - invalid L2CAP_InfoRsp command. " \
1265878ed226SJulian Elischer "Bad connectionless MTU parameter, len=%d\n",
1266878ed226SJulian Elischer 					__func__, NG_NODE_NAME(l2cap->node),
1267878ed226SJulian Elischer 					con->rx_pkt->m_pkthdr.len);
1268878ed226SJulian Elischer 			}
1269878ed226SJulian Elischer 			break;
1270878ed226SJulian Elischer 
1271878ed226SJulian Elischer 		default:
1272878ed226SJulian Elischer 			NG_L2CAP_WARN(
1273878ed226SJulian Elischer "%s: %s - invalid L2CAP_InfoRsp command. Unknown info type=%d\n",
1274878ed226SJulian Elischer 				__func__, NG_NODE_NAME(l2cap->node), cp->type);
1275878ed226SJulian Elischer 			break;
1276878ed226SJulian Elischer 		}
1277878ed226SJulian Elischer 	}
1278878ed226SJulian Elischer 
1279878ed226SJulian Elischer 	error = ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
1280878ed226SJulian Elischer 			cp->result, con->rx_pkt);
1281878ed226SJulian Elischer 
1282878ed226SJulian Elischer 	ng_l2cap_free_cmd(cmd);
1283878ed226SJulian Elischer 	con->rx_pkt = NULL;
1284878ed226SJulian Elischer 
1285878ed226SJulian Elischer 	return (error);
1286878ed226SJulian Elischer } /* ng_l2cap_process_info_rsp */
1287878ed226SJulian Elischer 
1288878ed226SJulian Elischer /*
1289878ed226SJulian Elischer  * Send L2CAP reject
1290878ed226SJulian Elischer  */
1291878ed226SJulian Elischer 
1292878ed226SJulian Elischer static int
send_l2cap_reject(ng_l2cap_con_p con,u_int8_t ident,u_int16_t reason,u_int16_t mtu,u_int16_t scid,u_int16_t dcid)1293878ed226SJulian Elischer send_l2cap_reject(ng_l2cap_con_p con, u_int8_t ident, u_int16_t reason,
1294878ed226SJulian Elischer 		u_int16_t mtu, u_int16_t scid, u_int16_t dcid)
1295878ed226SJulian Elischer {
1296878ed226SJulian Elischer 	ng_l2cap_cmd_p	cmd = NULL;
1297878ed226SJulian Elischer 
1298878ed226SJulian Elischer 	cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CMD_REJ, 0);
1299878ed226SJulian Elischer 	if (cmd == NULL)
1300878ed226SJulian Elischer 		return (ENOMEM);
1301878ed226SJulian Elischer 
1302878ed226SJulian Elischer 	 _ng_l2cap_cmd_rej(cmd->aux, cmd->ident, reason, mtu, scid, dcid);
1303878ed226SJulian Elischer 	if (cmd->aux == NULL) {
1304878ed226SJulian Elischer 		ng_l2cap_free_cmd(cmd);
1305878ed226SJulian Elischer 
1306878ed226SJulian Elischer 		return (ENOBUFS);
1307878ed226SJulian Elischer 	}
1308878ed226SJulian Elischer 
1309878ed226SJulian Elischer 	/* Link command to the queue */
1310878ed226SJulian Elischer 	ng_l2cap_link_cmd(con, cmd);
1311878ed226SJulian Elischer 	ng_l2cap_lp_deliver(con);
1312878ed226SJulian Elischer 
1313878ed226SJulian Elischer 	return (0);
1314878ed226SJulian Elischer } /* send_l2cap_reject */
1315878ed226SJulian Elischer 
1316878ed226SJulian Elischer /*
1317878ed226SJulian Elischer  * Send L2CAP connection reject
1318878ed226SJulian Elischer  */
1319878ed226SJulian Elischer 
1320878ed226SJulian Elischer static int
send_l2cap_con_rej(ng_l2cap_con_p con,u_int8_t ident,u_int16_t scid,u_int16_t dcid,u_int16_t result)1321878ed226SJulian Elischer send_l2cap_con_rej(ng_l2cap_con_p con, u_int8_t ident, u_int16_t scid,
1322878ed226SJulian Elischer 		u_int16_t dcid, u_int16_t result)
1323878ed226SJulian Elischer {
1324878ed226SJulian Elischer 	ng_l2cap_cmd_p	cmd = NULL;
1325878ed226SJulian Elischer 
1326878ed226SJulian Elischer 	cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CON_RSP, 0);
1327878ed226SJulian Elischer 	if (cmd == NULL)
1328878ed226SJulian Elischer 		return (ENOMEM);
1329878ed226SJulian Elischer 
1330878ed226SJulian Elischer 	_ng_l2cap_con_rsp(cmd->aux, cmd->ident, scid, dcid, result, 0);
1331878ed226SJulian Elischer 	if (cmd->aux == NULL) {
1332878ed226SJulian Elischer 		ng_l2cap_free_cmd(cmd);
1333878ed226SJulian Elischer 
1334878ed226SJulian Elischer 		return (ENOBUFS);
1335878ed226SJulian Elischer 	}
1336878ed226SJulian Elischer 
1337878ed226SJulian Elischer 	/* Link command to the queue */
1338878ed226SJulian Elischer 	ng_l2cap_link_cmd(con, cmd);
1339878ed226SJulian Elischer 	ng_l2cap_lp_deliver(con);
1340878ed226SJulian Elischer 
1341878ed226SJulian Elischer 	return (0);
1342878ed226SJulian Elischer } /* send_l2cap_con_rej */
1343878ed226SJulian Elischer 
1344878ed226SJulian Elischer /*
1345878ed226SJulian Elischer  * Send L2CAP config response
1346878ed226SJulian Elischer  */
1347878ed226SJulian Elischer 
1348878ed226SJulian Elischer static int
send_l2cap_cfg_rsp(ng_l2cap_con_p con,u_int8_t ident,u_int16_t scid,u_int16_t result,struct mbuf * opt)1349878ed226SJulian Elischer send_l2cap_cfg_rsp(ng_l2cap_con_p con, u_int8_t ident, u_int16_t scid,
1350878ed226SJulian Elischer 		u_int16_t result, struct mbuf *opt)
1351878ed226SJulian Elischer {
1352878ed226SJulian Elischer 	ng_l2cap_cmd_p	cmd = NULL;
1353878ed226SJulian Elischer 
1354878ed226SJulian Elischer 	cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CFG_RSP, 0);
1355878ed226SJulian Elischer 	if (cmd == NULL) {
1356878ed226SJulian Elischer 		NG_FREE_M(opt);
1357878ed226SJulian Elischer 
1358878ed226SJulian Elischer 		return (ENOMEM);
1359878ed226SJulian Elischer 	}
1360878ed226SJulian Elischer 
1361878ed226SJulian Elischer 	_ng_l2cap_cfg_rsp(cmd->aux, cmd->ident, scid, 0, result, opt);
1362878ed226SJulian Elischer 	if (cmd->aux == NULL) {
1363878ed226SJulian Elischer 		ng_l2cap_free_cmd(cmd);
1364878ed226SJulian Elischer 
1365878ed226SJulian Elischer 		return (ENOBUFS);
1366878ed226SJulian Elischer 	}
1367878ed226SJulian Elischer 
1368878ed226SJulian Elischer 	/* Link command to the queue */
1369878ed226SJulian Elischer 	ng_l2cap_link_cmd(con, cmd);
1370878ed226SJulian Elischer 	ng_l2cap_lp_deliver(con);
1371878ed226SJulian Elischer 
1372878ed226SJulian Elischer 	return (0);
1373878ed226SJulian Elischer } /* send_l2cap_cfg_rsp */
1374878ed226SJulian Elischer 
1375fbc48c2bSTakanori Watanabe static int
send_l2cap_param_urs(ng_l2cap_con_p con,u_int8_t ident,u_int16_t result)1376fbc48c2bSTakanori Watanabe send_l2cap_param_urs(ng_l2cap_con_p con, u_int8_t ident,
1377fbc48c2bSTakanori Watanabe 		     u_int16_t result)
1378fbc48c2bSTakanori Watanabe {
1379fbc48c2bSTakanori Watanabe 	ng_l2cap_cmd_p	cmd = NULL;
1380fbc48c2bSTakanori Watanabe 
1381fbc48c2bSTakanori Watanabe 	cmd = ng_l2cap_new_cmd(con, NULL, ident,
1382fbc48c2bSTakanori Watanabe 			       NG_L2CAP_CMD_PARAM_UPDATE_RESPONSE,
1383fbc48c2bSTakanori Watanabe 			       0);
1384fbc48c2bSTakanori Watanabe 	if (cmd == NULL) {
1385fbc48c2bSTakanori Watanabe 		return (ENOMEM);
1386fbc48c2bSTakanori Watanabe 	}
1387fbc48c2bSTakanori Watanabe 
1388fbc48c2bSTakanori Watanabe 	_ng_l2cap_cmd_urs(cmd->aux, cmd->ident, result);
1389fbc48c2bSTakanori Watanabe 	if (cmd->aux == NULL) {
1390fbc48c2bSTakanori Watanabe 		ng_l2cap_free_cmd(cmd);
1391fbc48c2bSTakanori Watanabe 
1392fbc48c2bSTakanori Watanabe 		return (ENOBUFS);
1393fbc48c2bSTakanori Watanabe 	}
1394fbc48c2bSTakanori Watanabe 
1395fbc48c2bSTakanori Watanabe 	/* Link command to the queue */
1396fbc48c2bSTakanori Watanabe 	ng_l2cap_link_cmd(con, cmd);
1397fbc48c2bSTakanori Watanabe 	ng_l2cap_lp_deliver(con);
1398fbc48c2bSTakanori Watanabe 
1399fbc48c2bSTakanori Watanabe 	return (0);
1400fbc48c2bSTakanori Watanabe } /* send_l2cap_cfg_rsp */
1401fbc48c2bSTakanori Watanabe 
1402878ed226SJulian Elischer /*
1403878ed226SJulian Elischer  * Get next L2CAP configuration option
1404878ed226SJulian Elischer  *
1405878ed226SJulian Elischer  * Return codes:
1406878ed226SJulian Elischer  *  0   no option
1407878ed226SJulian Elischer  *  1   we have got option
1408878ed226SJulian Elischer  * -1   header too short
1409878ed226SJulian Elischer  * -2   bad option value or length
1410878ed226SJulian Elischer  * -3   unknown option
1411878ed226SJulian Elischer  */
1412878ed226SJulian Elischer 
1413878ed226SJulian Elischer static int
get_next_l2cap_opt(struct mbuf * m,int * off,ng_l2cap_cfg_opt_p hdr,ng_l2cap_cfg_opt_val_p val)1414878ed226SJulian Elischer get_next_l2cap_opt(struct mbuf *m, int *off, ng_l2cap_cfg_opt_p hdr,
1415878ed226SJulian Elischer 		ng_l2cap_cfg_opt_val_p val)
1416878ed226SJulian Elischer {
1417878ed226SJulian Elischer 	int	hint, len = m->m_pkthdr.len - (*off);
1418878ed226SJulian Elischer 
1419878ed226SJulian Elischer 	if (len == 0)
1420878ed226SJulian Elischer 		return (0);
1421878ed226SJulian Elischer 	if (len < 0 || len < sizeof(*hdr))
1422878ed226SJulian Elischer 		return (-1);
1423878ed226SJulian Elischer 
1424878ed226SJulian Elischer 	m_copydata(m, *off, sizeof(*hdr), (caddr_t) hdr);
1425878ed226SJulian Elischer 	*off += sizeof(*hdr);
1426878ed226SJulian Elischer 	len  -= sizeof(*hdr);
1427878ed226SJulian Elischer 
1428878ed226SJulian Elischer 	hint = NG_L2CAP_OPT_HINT(hdr->type);
1429878ed226SJulian Elischer 	hdr->type &= NG_L2CAP_OPT_HINT_MASK;
1430878ed226SJulian Elischer 
1431878ed226SJulian Elischer 	switch (hdr->type) {
1432878ed226SJulian Elischer 	case NG_L2CAP_OPT_MTU:
1433878ed226SJulian Elischer 		if (hdr->length != NG_L2CAP_OPT_MTU_SIZE || len < hdr->length)
1434878ed226SJulian Elischer 			return (-2);
1435878ed226SJulian Elischer 
1436878ed226SJulian Elischer 		m_copydata(m, *off, NG_L2CAP_OPT_MTU_SIZE, (caddr_t) val);
1437878ed226SJulian Elischer 		val->mtu = le16toh(val->mtu);
1438878ed226SJulian Elischer 		*off += NG_L2CAP_OPT_MTU_SIZE;
1439878ed226SJulian Elischer 		break;
1440878ed226SJulian Elischer 
1441878ed226SJulian Elischer 	case NG_L2CAP_OPT_FLUSH_TIMO:
1442878ed226SJulian Elischer 		if (hdr->length != NG_L2CAP_OPT_FLUSH_TIMO_SIZE ||
1443878ed226SJulian Elischer 		    len < hdr->length)
1444878ed226SJulian Elischer 			return (-2);
1445878ed226SJulian Elischer 
1446878ed226SJulian Elischer 		m_copydata(m, *off, NG_L2CAP_OPT_FLUSH_TIMO_SIZE, (caddr_t)val);
1447878ed226SJulian Elischer 		val->flush_timo = le16toh(val->flush_timo);
1448878ed226SJulian Elischer 		*off += NG_L2CAP_OPT_FLUSH_TIMO_SIZE;
1449878ed226SJulian Elischer 		break;
1450878ed226SJulian Elischer 
1451878ed226SJulian Elischer 	case NG_L2CAP_OPT_QOS:
1452878ed226SJulian Elischer 		if (hdr->length != NG_L2CAP_OPT_QOS_SIZE || len < hdr->length)
1453878ed226SJulian Elischer 			return (-2);
1454878ed226SJulian Elischer 
1455878ed226SJulian Elischer 		m_copydata(m, *off, NG_L2CAP_OPT_QOS_SIZE, (caddr_t) val);
1456878ed226SJulian Elischer 		val->flow.token_rate = le32toh(val->flow.token_rate);
1457878ed226SJulian Elischer 		val->flow.token_bucket_size =
1458878ed226SJulian Elischer 				le32toh(val->flow.token_bucket_size);
1459878ed226SJulian Elischer 		val->flow.peak_bandwidth = le32toh(val->flow.peak_bandwidth);
1460878ed226SJulian Elischer 		val->flow.latency = le32toh(val->flow.latency);
1461878ed226SJulian Elischer 		val->flow.delay_variation = le32toh(val->flow.delay_variation);
1462878ed226SJulian Elischer 		*off += NG_L2CAP_OPT_QOS_SIZE;
1463878ed226SJulian Elischer 		break;
1464878ed226SJulian Elischer 
1465878ed226SJulian Elischer 	default:
1466878ed226SJulian Elischer 		if (hint)
1467878ed226SJulian Elischer 			*off += hdr->length;
1468878ed226SJulian Elischer 		else
1469878ed226SJulian Elischer 			return (-3);
1470878ed226SJulian Elischer 		break;
1471878ed226SJulian Elischer 	}
1472878ed226SJulian Elischer 
1473878ed226SJulian Elischer 	return (1);
1474878ed226SJulian Elischer } /* get_next_l2cap_opt */
1475