xref: /dflybsd-src/sys/netgraph7/bluetooth/l2cap/ng_l2cap_evnt.c (revision b06ebda0110aaa466b4f7ba6cacac7a26b29f434)
1*b06ebda0SMatthew Dillon /*
2*b06ebda0SMatthew Dillon  * ng_l2cap_evnt.c
3*b06ebda0SMatthew Dillon  */
4*b06ebda0SMatthew Dillon 
5*b06ebda0SMatthew Dillon /*-
6*b06ebda0SMatthew Dillon  * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
7*b06ebda0SMatthew Dillon  * All rights reserved.
8*b06ebda0SMatthew Dillon  *
9*b06ebda0SMatthew Dillon  * Redistribution and use in source and binary forms, with or without
10*b06ebda0SMatthew Dillon  * modification, are permitted provided that the following conditions
11*b06ebda0SMatthew Dillon  * are met:
12*b06ebda0SMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
13*b06ebda0SMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
14*b06ebda0SMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
15*b06ebda0SMatthew Dillon  *    notice, this list of conditions and the following disclaimer in the
16*b06ebda0SMatthew Dillon  *    documentation and/or other materials provided with the distribution.
17*b06ebda0SMatthew Dillon  *
18*b06ebda0SMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19*b06ebda0SMatthew Dillon  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20*b06ebda0SMatthew Dillon  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21*b06ebda0SMatthew Dillon  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22*b06ebda0SMatthew Dillon  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23*b06ebda0SMatthew Dillon  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24*b06ebda0SMatthew Dillon  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25*b06ebda0SMatthew Dillon  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26*b06ebda0SMatthew Dillon  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27*b06ebda0SMatthew Dillon  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28*b06ebda0SMatthew Dillon  * SUCH DAMAGE.
29*b06ebda0SMatthew Dillon  *
30*b06ebda0SMatthew Dillon  * $Id: ng_l2cap_evnt.c,v 1.5 2003/09/08 19:11:45 max Exp $
31*b06ebda0SMatthew Dillon  * $FreeBSD: src/sys/netgraph/bluetooth/l2cap/ng_l2cap_evnt.c,v 1.8 2005/01/07 01:45:43 imp Exp $
32*b06ebda0SMatthew Dillon  */
33*b06ebda0SMatthew Dillon 
34*b06ebda0SMatthew Dillon #include <sys/param.h>
35*b06ebda0SMatthew Dillon #include <sys/systm.h>
36*b06ebda0SMatthew Dillon #include <sys/kernel.h>
37*b06ebda0SMatthew Dillon #include <sys/endian.h>
38*b06ebda0SMatthew Dillon #include <sys/malloc.h>
39*b06ebda0SMatthew Dillon #include <sys/mbuf.h>
40*b06ebda0SMatthew Dillon #include <sys/queue.h>
41*b06ebda0SMatthew Dillon #include <netgraph/ng_message.h>
42*b06ebda0SMatthew Dillon #include <netgraph/netgraph.h>
43*b06ebda0SMatthew Dillon #include <netgraph/bluetooth/include/ng_bluetooth.h>
44*b06ebda0SMatthew Dillon #include <netgraph/bluetooth/include/ng_hci.h>
45*b06ebda0SMatthew Dillon #include <netgraph/bluetooth/include/ng_l2cap.h>
46*b06ebda0SMatthew Dillon #include <netgraph/bluetooth/l2cap/ng_l2cap_var.h>
47*b06ebda0SMatthew Dillon #include <netgraph/bluetooth/l2cap/ng_l2cap_cmds.h>
48*b06ebda0SMatthew Dillon #include <netgraph/bluetooth/l2cap/ng_l2cap_evnt.h>
49*b06ebda0SMatthew Dillon #include <netgraph/bluetooth/l2cap/ng_l2cap_llpi.h>
50*b06ebda0SMatthew Dillon #include <netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h>
51*b06ebda0SMatthew Dillon #include <netgraph/bluetooth/l2cap/ng_l2cap_misc.h>
52*b06ebda0SMatthew Dillon 
53*b06ebda0SMatthew Dillon /******************************************************************************
54*b06ebda0SMatthew Dillon  ******************************************************************************
55*b06ebda0SMatthew Dillon  **                    L2CAP events processing module
56*b06ebda0SMatthew Dillon  ******************************************************************************
57*b06ebda0SMatthew Dillon  ******************************************************************************/
58*b06ebda0SMatthew Dillon 
59*b06ebda0SMatthew Dillon static int ng_l2cap_process_signal_cmd (ng_l2cap_con_p);
60*b06ebda0SMatthew Dillon static int ng_l2cap_process_cmd_rej    (ng_l2cap_con_p, u_int8_t);
61*b06ebda0SMatthew Dillon static int ng_l2cap_process_con_req    (ng_l2cap_con_p, u_int8_t);
62*b06ebda0SMatthew Dillon static int ng_l2cap_process_con_rsp    (ng_l2cap_con_p, u_int8_t);
63*b06ebda0SMatthew Dillon static int ng_l2cap_process_cfg_req    (ng_l2cap_con_p, u_int8_t);
64*b06ebda0SMatthew Dillon static int ng_l2cap_process_cfg_rsp    (ng_l2cap_con_p, u_int8_t);
65*b06ebda0SMatthew Dillon static int ng_l2cap_process_discon_req (ng_l2cap_con_p, u_int8_t);
66*b06ebda0SMatthew Dillon static int ng_l2cap_process_discon_rsp (ng_l2cap_con_p, u_int8_t);
67*b06ebda0SMatthew Dillon static int ng_l2cap_process_echo_req   (ng_l2cap_con_p, u_int8_t);
68*b06ebda0SMatthew Dillon static int ng_l2cap_process_echo_rsp   (ng_l2cap_con_p, u_int8_t);
69*b06ebda0SMatthew Dillon static int ng_l2cap_process_info_req   (ng_l2cap_con_p, u_int8_t);
70*b06ebda0SMatthew Dillon static int ng_l2cap_process_info_rsp   (ng_l2cap_con_p, u_int8_t);
71*b06ebda0SMatthew Dillon static int send_l2cap_reject
72*b06ebda0SMatthew Dillon 	(ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, u_int16_t, u_int16_t);
73*b06ebda0SMatthew Dillon static int send_l2cap_con_rej
74*b06ebda0SMatthew Dillon 	(ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, u_int16_t);
75*b06ebda0SMatthew Dillon static int send_l2cap_cfg_rsp
76*b06ebda0SMatthew Dillon 	(ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, struct mbuf *);
77*b06ebda0SMatthew Dillon static int get_next_l2cap_opt
78*b06ebda0SMatthew Dillon 	(struct mbuf *, int *, ng_l2cap_cfg_opt_p, ng_l2cap_cfg_opt_val_p);
79*b06ebda0SMatthew Dillon 
80*b06ebda0SMatthew Dillon /*
81*b06ebda0SMatthew Dillon  * Receive L2CAP packet. First get L2CAP header and verify packet. Than
82*b06ebda0SMatthew Dillon  * get destination channel and process packet.
83*b06ebda0SMatthew Dillon  */
84*b06ebda0SMatthew Dillon 
85*b06ebda0SMatthew Dillon int
86*b06ebda0SMatthew Dillon ng_l2cap_receive(ng_l2cap_con_p con)
87*b06ebda0SMatthew Dillon {
88*b06ebda0SMatthew Dillon 	ng_l2cap_p	 l2cap = con->l2cap;
89*b06ebda0SMatthew Dillon 	ng_l2cap_hdr_t	*hdr = NULL;
90*b06ebda0SMatthew Dillon 	int		 error = 0;
91*b06ebda0SMatthew Dillon 
92*b06ebda0SMatthew Dillon 	/* Check packet */
93*b06ebda0SMatthew Dillon 	if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) {
94*b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
95*b06ebda0SMatthew Dillon "%s: %s - invalid L2CAP packet. Packet too small, len=%d\n",
96*b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node),
97*b06ebda0SMatthew Dillon 			con->rx_pkt->m_pkthdr.len);
98*b06ebda0SMatthew Dillon 		error = EMSGSIZE;
99*b06ebda0SMatthew Dillon 		goto drop;
100*b06ebda0SMatthew Dillon 	}
101*b06ebda0SMatthew Dillon 
102*b06ebda0SMatthew Dillon 	/* Get L2CAP header */
103*b06ebda0SMatthew Dillon 	NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));
104*b06ebda0SMatthew Dillon 	if (con->rx_pkt == NULL)
105*b06ebda0SMatthew Dillon 		return (ENOBUFS);
106*b06ebda0SMatthew Dillon 
107*b06ebda0SMatthew Dillon 	hdr = mtod(con->rx_pkt, ng_l2cap_hdr_t *);
108*b06ebda0SMatthew Dillon 	hdr->length = le16toh(hdr->length);
109*b06ebda0SMatthew Dillon 	hdr->dcid = le16toh(hdr->dcid);
110*b06ebda0SMatthew Dillon 
111*b06ebda0SMatthew Dillon 	/* Check payload size */
112*b06ebda0SMatthew Dillon 	if (hdr->length != con->rx_pkt->m_pkthdr.len - sizeof(*hdr)) {
113*b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
114*b06ebda0SMatthew Dillon "%s: %s - invalid L2CAP packet. Payload length mismatch, length=%d, len=%zd\n",
115*b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), hdr->length,
116*b06ebda0SMatthew Dillon 			con->rx_pkt->m_pkthdr.len - sizeof(*hdr));
117*b06ebda0SMatthew Dillon 		error = EMSGSIZE;
118*b06ebda0SMatthew Dillon 		goto drop;
119*b06ebda0SMatthew Dillon 	}
120*b06ebda0SMatthew Dillon 
121*b06ebda0SMatthew Dillon 	/* Process packet */
122*b06ebda0SMatthew Dillon 	switch (hdr->dcid) {
123*b06ebda0SMatthew Dillon 	case NG_L2CAP_SIGNAL_CID: /* L2CAP command */
124*b06ebda0SMatthew Dillon 		m_adj(con->rx_pkt, sizeof(*hdr));
125*b06ebda0SMatthew Dillon 		error = ng_l2cap_process_signal_cmd(con);
126*b06ebda0SMatthew Dillon 		break;
127*b06ebda0SMatthew Dillon 
128*b06ebda0SMatthew Dillon 	case NG_L2CAP_CLT_CID: /* Connectionless packet */
129*b06ebda0SMatthew Dillon 		error = ng_l2cap_l2ca_clt_receive(con);
130*b06ebda0SMatthew Dillon 		break;
131*b06ebda0SMatthew Dillon 
132*b06ebda0SMatthew Dillon 	default: /* Data packet */
133*b06ebda0SMatthew Dillon 		error = ng_l2cap_l2ca_receive(con);
134*b06ebda0SMatthew Dillon 		break;
135*b06ebda0SMatthew Dillon 	}
136*b06ebda0SMatthew Dillon 
137*b06ebda0SMatthew Dillon 	return (error);
138*b06ebda0SMatthew Dillon drop:
139*b06ebda0SMatthew Dillon 	NG_FREE_M(con->rx_pkt);
140*b06ebda0SMatthew Dillon 
141*b06ebda0SMatthew Dillon 	return (error);
142*b06ebda0SMatthew Dillon } /* ng_l2cap_receive */
143*b06ebda0SMatthew Dillon 
144*b06ebda0SMatthew Dillon /*
145*b06ebda0SMatthew Dillon  * Process L2CAP signaling command. We already know that destination channel ID
146*b06ebda0SMatthew Dillon  * is 0x1 that means we have received signaling command from peer's L2CAP layer.
147*b06ebda0SMatthew Dillon  * So get command header, decode and process it.
148*b06ebda0SMatthew Dillon  *
149*b06ebda0SMatthew Dillon  * XXX do we need to check signaling MTU here?
150*b06ebda0SMatthew Dillon  */
151*b06ebda0SMatthew Dillon 
152*b06ebda0SMatthew Dillon static int
153*b06ebda0SMatthew Dillon ng_l2cap_process_signal_cmd(ng_l2cap_con_p con)
154*b06ebda0SMatthew Dillon {
155*b06ebda0SMatthew Dillon 	ng_l2cap_p		 l2cap = con->l2cap;
156*b06ebda0SMatthew Dillon 	ng_l2cap_cmd_hdr_t	*hdr = NULL;
157*b06ebda0SMatthew Dillon 	struct mbuf		*m = NULL;
158*b06ebda0SMatthew Dillon 
159*b06ebda0SMatthew Dillon 	while (con->rx_pkt != NULL) {
160*b06ebda0SMatthew Dillon 		/* Verify packet length */
161*b06ebda0SMatthew Dillon 		if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) {
162*b06ebda0SMatthew Dillon 			NG_L2CAP_ERR(
163*b06ebda0SMatthew Dillon "%s: %s - invalid L2CAP signaling command. Packet too small, len=%d\n",
164*b06ebda0SMatthew Dillon 				__func__, NG_NODE_NAME(l2cap->node),
165*b06ebda0SMatthew Dillon 				con->rx_pkt->m_pkthdr.len);
166*b06ebda0SMatthew Dillon 			NG_FREE_M(con->rx_pkt);
167*b06ebda0SMatthew Dillon 
168*b06ebda0SMatthew Dillon 			return (EMSGSIZE);
169*b06ebda0SMatthew Dillon 		}
170*b06ebda0SMatthew Dillon 
171*b06ebda0SMatthew Dillon 		/* Get signaling command */
172*b06ebda0SMatthew Dillon 		NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));
173*b06ebda0SMatthew Dillon 		if (con->rx_pkt == NULL)
174*b06ebda0SMatthew Dillon 			return (ENOBUFS);
175*b06ebda0SMatthew Dillon 
176*b06ebda0SMatthew Dillon 		hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *);
177*b06ebda0SMatthew Dillon 		hdr->length = le16toh(hdr->length);
178*b06ebda0SMatthew Dillon 		m_adj(con->rx_pkt, sizeof(*hdr));
179*b06ebda0SMatthew Dillon 
180*b06ebda0SMatthew Dillon 		/* Verify command length */
181*b06ebda0SMatthew Dillon 		if (con->rx_pkt->m_pkthdr.len < hdr->length) {
182*b06ebda0SMatthew Dillon 			NG_L2CAP_ERR(
183*b06ebda0SMatthew Dillon "%s: %s - invalid L2CAP signaling command, code=%#x, ident=%d. " \
184*b06ebda0SMatthew Dillon "Invalid command length=%d, m_pkthdr.len=%d\n",
185*b06ebda0SMatthew Dillon 				__func__, NG_NODE_NAME(l2cap->node),
186*b06ebda0SMatthew Dillon 				hdr->code, hdr->ident, hdr->length,
187*b06ebda0SMatthew Dillon 				con->rx_pkt->m_pkthdr.len);
188*b06ebda0SMatthew Dillon 			NG_FREE_M(con->rx_pkt);
189*b06ebda0SMatthew Dillon 
190*b06ebda0SMatthew Dillon 			return (EMSGSIZE);
191*b06ebda0SMatthew Dillon 		}
192*b06ebda0SMatthew Dillon 
193*b06ebda0SMatthew Dillon 		/* Get the command, save the rest (if any) */
194*b06ebda0SMatthew Dillon 		if (con->rx_pkt->m_pkthdr.len > hdr->length)
195*b06ebda0SMatthew Dillon 			m = m_split(con->rx_pkt, hdr->length, M_DONTWAIT);
196*b06ebda0SMatthew Dillon 		else
197*b06ebda0SMatthew Dillon 			m = NULL;
198*b06ebda0SMatthew Dillon 
199*b06ebda0SMatthew Dillon 		/* Process command */
200*b06ebda0SMatthew Dillon 		switch (hdr->code) {
201*b06ebda0SMatthew Dillon 		case NG_L2CAP_CMD_REJ:
202*b06ebda0SMatthew Dillon 			ng_l2cap_process_cmd_rej(con, hdr->ident);
203*b06ebda0SMatthew Dillon 			break;
204*b06ebda0SMatthew Dillon 
205*b06ebda0SMatthew Dillon 		case NG_L2CAP_CON_REQ:
206*b06ebda0SMatthew Dillon 			ng_l2cap_process_con_req(con, hdr->ident);
207*b06ebda0SMatthew Dillon 			break;
208*b06ebda0SMatthew Dillon 
209*b06ebda0SMatthew Dillon 		case NG_L2CAP_CON_RSP:
210*b06ebda0SMatthew Dillon 			ng_l2cap_process_con_rsp(con, hdr->ident);
211*b06ebda0SMatthew Dillon 			break;
212*b06ebda0SMatthew Dillon 
213*b06ebda0SMatthew Dillon 		case NG_L2CAP_CFG_REQ:
214*b06ebda0SMatthew Dillon 			ng_l2cap_process_cfg_req(con, hdr->ident);
215*b06ebda0SMatthew Dillon 			break;
216*b06ebda0SMatthew Dillon 
217*b06ebda0SMatthew Dillon 		case NG_L2CAP_CFG_RSP:
218*b06ebda0SMatthew Dillon 			ng_l2cap_process_cfg_rsp(con, hdr->ident);
219*b06ebda0SMatthew Dillon 			break;
220*b06ebda0SMatthew Dillon 
221*b06ebda0SMatthew Dillon 		case NG_L2CAP_DISCON_REQ:
222*b06ebda0SMatthew Dillon 			ng_l2cap_process_discon_req(con, hdr->ident);
223*b06ebda0SMatthew Dillon 			break;
224*b06ebda0SMatthew Dillon 
225*b06ebda0SMatthew Dillon 		case NG_L2CAP_DISCON_RSP:
226*b06ebda0SMatthew Dillon 			ng_l2cap_process_discon_rsp(con, hdr->ident);
227*b06ebda0SMatthew Dillon 			break;
228*b06ebda0SMatthew Dillon 
229*b06ebda0SMatthew Dillon 		case NG_L2CAP_ECHO_REQ:
230*b06ebda0SMatthew Dillon 			ng_l2cap_process_echo_req(con, hdr->ident);
231*b06ebda0SMatthew Dillon 			break;
232*b06ebda0SMatthew Dillon 
233*b06ebda0SMatthew Dillon 		case NG_L2CAP_ECHO_RSP:
234*b06ebda0SMatthew Dillon 			ng_l2cap_process_echo_rsp(con, hdr->ident);
235*b06ebda0SMatthew Dillon 			break;
236*b06ebda0SMatthew Dillon 
237*b06ebda0SMatthew Dillon 		case NG_L2CAP_INFO_REQ:
238*b06ebda0SMatthew Dillon 			ng_l2cap_process_info_req(con, hdr->ident);
239*b06ebda0SMatthew Dillon 			break;
240*b06ebda0SMatthew Dillon 
241*b06ebda0SMatthew Dillon 		case NG_L2CAP_INFO_RSP:
242*b06ebda0SMatthew Dillon 			ng_l2cap_process_info_rsp(con, hdr->ident);
243*b06ebda0SMatthew Dillon 			break;
244*b06ebda0SMatthew Dillon 
245*b06ebda0SMatthew Dillon 		default:
246*b06ebda0SMatthew Dillon 			NG_L2CAP_ERR(
247*b06ebda0SMatthew Dillon "%s: %s - unknown L2CAP signaling command, code=%#x, ident=%d, length=%d\n",
248*b06ebda0SMatthew Dillon 				__func__, NG_NODE_NAME(l2cap->node),
249*b06ebda0SMatthew Dillon 				hdr->code, hdr->ident, hdr->length);
250*b06ebda0SMatthew Dillon 
251*b06ebda0SMatthew Dillon 			/*
252*b06ebda0SMatthew Dillon 			 * Send L2CAP_CommandRej. Do not really care
253*b06ebda0SMatthew Dillon 			 * about the result
254*b06ebda0SMatthew Dillon 			 */
255*b06ebda0SMatthew Dillon 
256*b06ebda0SMatthew Dillon 			send_l2cap_reject(con, hdr->ident,
257*b06ebda0SMatthew Dillon 				NG_L2CAP_REJ_NOT_UNDERSTOOD, 0, 0, 0);
258*b06ebda0SMatthew Dillon 			NG_FREE_M(con->rx_pkt);
259*b06ebda0SMatthew Dillon 			break;
260*b06ebda0SMatthew Dillon 		}
261*b06ebda0SMatthew Dillon 
262*b06ebda0SMatthew Dillon 		con->rx_pkt = m;
263*b06ebda0SMatthew Dillon 	}
264*b06ebda0SMatthew Dillon 
265*b06ebda0SMatthew Dillon 	return (0);
266*b06ebda0SMatthew Dillon } /* ng_l2cap_process_signal_cmd */
267*b06ebda0SMatthew Dillon 
268*b06ebda0SMatthew Dillon /*
269*b06ebda0SMatthew Dillon  * Process L2CAP_CommandRej command
270*b06ebda0SMatthew Dillon  */
271*b06ebda0SMatthew Dillon 
272*b06ebda0SMatthew Dillon static int
273*b06ebda0SMatthew Dillon ng_l2cap_process_cmd_rej(ng_l2cap_con_p con, u_int8_t ident)
274*b06ebda0SMatthew Dillon {
275*b06ebda0SMatthew Dillon 	ng_l2cap_p		 l2cap = con->l2cap;
276*b06ebda0SMatthew Dillon 	ng_l2cap_cmd_rej_cp	*cp = NULL;
277*b06ebda0SMatthew Dillon 	ng_l2cap_cmd_p		 cmd = NULL;
278*b06ebda0SMatthew Dillon 
279*b06ebda0SMatthew Dillon 	/* Get command parameters */
280*b06ebda0SMatthew Dillon 	NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));
281*b06ebda0SMatthew Dillon 	if (con->rx_pkt == NULL)
282*b06ebda0SMatthew Dillon 		return (ENOBUFS);
283*b06ebda0SMatthew Dillon 
284*b06ebda0SMatthew Dillon 	cp = mtod(con->rx_pkt, ng_l2cap_cmd_rej_cp *);
285*b06ebda0SMatthew Dillon 	cp->reason = le16toh(cp->reason);
286*b06ebda0SMatthew Dillon 
287*b06ebda0SMatthew Dillon 	/* Check if we have pending command descriptor */
288*b06ebda0SMatthew Dillon 	cmd = ng_l2cap_cmd_by_ident(con, ident);
289*b06ebda0SMatthew Dillon 	if (cmd != NULL) {
290*b06ebda0SMatthew Dillon 		/* If command timeout already happened then ignore reject */
291*b06ebda0SMatthew Dillon 		if (ng_l2cap_command_untimeout(cmd) != 0) {
292*b06ebda0SMatthew Dillon 			NG_FREE_M(con->rx_pkt);
293*b06ebda0SMatthew Dillon 			return (ETIMEDOUT);
294*b06ebda0SMatthew Dillon 		}
295*b06ebda0SMatthew Dillon 
296*b06ebda0SMatthew Dillon 		ng_l2cap_unlink_cmd(cmd);
297*b06ebda0SMatthew Dillon 
298*b06ebda0SMatthew Dillon 		switch (cmd->code) {
299*b06ebda0SMatthew Dillon 		case NG_L2CAP_CON_REQ:
300*b06ebda0SMatthew Dillon 			ng_l2cap_l2ca_con_rsp(cmd->ch,cmd->token,cp->reason,0);
301*b06ebda0SMatthew Dillon 			ng_l2cap_free_chan(cmd->ch);
302*b06ebda0SMatthew Dillon 			break;
303*b06ebda0SMatthew Dillon 
304*b06ebda0SMatthew Dillon 		case NG_L2CAP_CFG_REQ:
305*b06ebda0SMatthew Dillon 			ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, cp->reason);
306*b06ebda0SMatthew Dillon 			break;
307*b06ebda0SMatthew Dillon 
308*b06ebda0SMatthew Dillon 		case NG_L2CAP_DISCON_REQ:
309*b06ebda0SMatthew Dillon 			ng_l2cap_l2ca_discon_rsp(cmd->ch,cmd->token,cp->reason);
310*b06ebda0SMatthew Dillon 			ng_l2cap_free_chan(cmd->ch); /* XXX free channel */
311*b06ebda0SMatthew Dillon 			break;
312*b06ebda0SMatthew Dillon 
313*b06ebda0SMatthew Dillon 		case NG_L2CAP_ECHO_REQ:
314*b06ebda0SMatthew Dillon 			ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
315*b06ebda0SMatthew Dillon 				cp->reason, NULL);
316*b06ebda0SMatthew Dillon 			break;
317*b06ebda0SMatthew Dillon 
318*b06ebda0SMatthew Dillon 		case NG_L2CAP_INFO_REQ:
319*b06ebda0SMatthew Dillon 			ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
320*b06ebda0SMatthew Dillon 				cp->reason, NULL);
321*b06ebda0SMatthew Dillon 			break;
322*b06ebda0SMatthew Dillon 
323*b06ebda0SMatthew Dillon 		default:
324*b06ebda0SMatthew Dillon 			NG_L2CAP_ALERT(
325*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_CommandRej. Unexpected L2CAP command opcode=%d\n",
326*b06ebda0SMatthew Dillon 				__func__, NG_NODE_NAME(l2cap->node), cmd->code);
327*b06ebda0SMatthew Dillon 			break;
328*b06ebda0SMatthew Dillon 		}
329*b06ebda0SMatthew Dillon 
330*b06ebda0SMatthew Dillon 		ng_l2cap_free_cmd(cmd);
331*b06ebda0SMatthew Dillon 	} else
332*b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
333*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_CommandRej command. " \
334*b06ebda0SMatthew Dillon "Requested ident does not exist, ident=%d\n",
335*b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), ident);
336*b06ebda0SMatthew Dillon 
337*b06ebda0SMatthew Dillon 	NG_FREE_M(con->rx_pkt);
338*b06ebda0SMatthew Dillon 
339*b06ebda0SMatthew Dillon 	return (0);
340*b06ebda0SMatthew Dillon } /* ng_l2cap_process_cmd_rej */
341*b06ebda0SMatthew Dillon 
342*b06ebda0SMatthew Dillon /*
343*b06ebda0SMatthew Dillon  * Process L2CAP_ConnectReq command
344*b06ebda0SMatthew Dillon  */
345*b06ebda0SMatthew Dillon 
346*b06ebda0SMatthew Dillon static int
347*b06ebda0SMatthew Dillon ng_l2cap_process_con_req(ng_l2cap_con_p con, u_int8_t ident)
348*b06ebda0SMatthew Dillon {
349*b06ebda0SMatthew Dillon 	ng_l2cap_p		 l2cap = con->l2cap;
350*b06ebda0SMatthew Dillon 	struct mbuf		*m = con->rx_pkt;
351*b06ebda0SMatthew Dillon 	ng_l2cap_con_req_cp	*cp = NULL;
352*b06ebda0SMatthew Dillon 	ng_l2cap_chan_p		 ch = NULL;
353*b06ebda0SMatthew Dillon 	int			 error = 0;
354*b06ebda0SMatthew Dillon 	u_int16_t		 dcid, psm;
355*b06ebda0SMatthew Dillon 
356*b06ebda0SMatthew Dillon 	/* Get command parameters */
357*b06ebda0SMatthew Dillon 	NG_L2CAP_M_PULLUP(m, sizeof(*cp));
358*b06ebda0SMatthew Dillon 	if (m == NULL)
359*b06ebda0SMatthew Dillon 		return (ENOBUFS);
360*b06ebda0SMatthew Dillon 
361*b06ebda0SMatthew Dillon 	cp = mtod(m, ng_l2cap_con_req_cp *);
362*b06ebda0SMatthew Dillon 	psm = le16toh(cp->psm);
363*b06ebda0SMatthew Dillon 	dcid = le16toh(cp->scid);
364*b06ebda0SMatthew Dillon 
365*b06ebda0SMatthew Dillon 	NG_FREE_M(m);
366*b06ebda0SMatthew Dillon 	con->rx_pkt = NULL;
367*b06ebda0SMatthew Dillon 
368*b06ebda0SMatthew Dillon 	/*
369*b06ebda0SMatthew Dillon 	 * Create new channel and send L2CA_ConnectInd notification
370*b06ebda0SMatthew Dillon 	 * to the upper layer protocol.
371*b06ebda0SMatthew Dillon 	 */
372*b06ebda0SMatthew Dillon 
373*b06ebda0SMatthew Dillon 	ch = ng_l2cap_new_chan(l2cap, con, psm);
374*b06ebda0SMatthew Dillon 	if (ch == NULL)
375*b06ebda0SMatthew Dillon 		return (send_l2cap_con_rej(con, ident, 0, dcid,
376*b06ebda0SMatthew Dillon 				NG_L2CAP_NO_RESOURCES));
377*b06ebda0SMatthew Dillon 
378*b06ebda0SMatthew Dillon 	/* Update channel IDs */
379*b06ebda0SMatthew Dillon 	ch->dcid = dcid;
380*b06ebda0SMatthew Dillon 
381*b06ebda0SMatthew Dillon 	/* Sent L2CA_ConnectInd notification to the upper layer */
382*b06ebda0SMatthew Dillon 	ch->ident = ident;
383*b06ebda0SMatthew Dillon 	ch->state = NG_L2CAP_W4_L2CA_CON_RSP;
384*b06ebda0SMatthew Dillon 
385*b06ebda0SMatthew Dillon 	error = ng_l2cap_l2ca_con_ind(ch);
386*b06ebda0SMatthew Dillon 	if (error != 0) {
387*b06ebda0SMatthew Dillon 		send_l2cap_con_rej(con, ident, ch->scid, dcid,
388*b06ebda0SMatthew Dillon 			(error == ENOMEM)? NG_L2CAP_NO_RESOURCES :
389*b06ebda0SMatthew Dillon 				NG_L2CAP_PSM_NOT_SUPPORTED);
390*b06ebda0SMatthew Dillon 		ng_l2cap_free_chan(ch);
391*b06ebda0SMatthew Dillon 	}
392*b06ebda0SMatthew Dillon 
393*b06ebda0SMatthew Dillon 	return (error);
394*b06ebda0SMatthew Dillon } /* ng_l2cap_process_con_req */
395*b06ebda0SMatthew Dillon 
396*b06ebda0SMatthew Dillon /*
397*b06ebda0SMatthew Dillon  * Process L2CAP_ConnectRsp command
398*b06ebda0SMatthew Dillon  */
399*b06ebda0SMatthew Dillon 
400*b06ebda0SMatthew Dillon static int
401*b06ebda0SMatthew Dillon ng_l2cap_process_con_rsp(ng_l2cap_con_p con, u_int8_t ident)
402*b06ebda0SMatthew Dillon {
403*b06ebda0SMatthew Dillon 	ng_l2cap_p		 l2cap = con->l2cap;
404*b06ebda0SMatthew Dillon 	struct mbuf		*m = con->rx_pkt;
405*b06ebda0SMatthew Dillon 	ng_l2cap_con_rsp_cp	*cp = NULL;
406*b06ebda0SMatthew Dillon 	ng_l2cap_cmd_p		 cmd = NULL;
407*b06ebda0SMatthew Dillon 	u_int16_t		 scid, dcid, result, status;
408*b06ebda0SMatthew Dillon 	int			 error = 0;
409*b06ebda0SMatthew Dillon 
410*b06ebda0SMatthew Dillon 	/* Get command parameters */
411*b06ebda0SMatthew Dillon 	NG_L2CAP_M_PULLUP(m, sizeof(*cp));
412*b06ebda0SMatthew Dillon 	if (m == NULL)
413*b06ebda0SMatthew Dillon 		return (ENOBUFS);
414*b06ebda0SMatthew Dillon 
415*b06ebda0SMatthew Dillon 	cp = mtod(m, ng_l2cap_con_rsp_cp *);
416*b06ebda0SMatthew Dillon 	dcid = le16toh(cp->dcid);
417*b06ebda0SMatthew Dillon 	scid = le16toh(cp->scid);
418*b06ebda0SMatthew Dillon 	result = le16toh(cp->result);
419*b06ebda0SMatthew Dillon 	status = le16toh(cp->status);
420*b06ebda0SMatthew Dillon 
421*b06ebda0SMatthew Dillon 	NG_FREE_M(m);
422*b06ebda0SMatthew Dillon 	con->rx_pkt = NULL;
423*b06ebda0SMatthew Dillon 
424*b06ebda0SMatthew Dillon 	/* Check if we have pending command descriptor */
425*b06ebda0SMatthew Dillon 	cmd = ng_l2cap_cmd_by_ident(con, ident);
426*b06ebda0SMatthew Dillon 	if (cmd == NULL) {
427*b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
428*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_ConnectRsp command. ident=%d, con_handle=%d\n",
429*b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), ident,
430*b06ebda0SMatthew Dillon 			con->con_handle);
431*b06ebda0SMatthew Dillon 
432*b06ebda0SMatthew Dillon 		return (ENOENT);
433*b06ebda0SMatthew Dillon 	}
434*b06ebda0SMatthew Dillon 
435*b06ebda0SMatthew Dillon 	/* Verify channel state, if invalid - do nothing */
436*b06ebda0SMatthew Dillon 	if (cmd->ch->state != NG_L2CAP_W4_L2CAP_CON_RSP) {
437*b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
438*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_ConnectRsp. " \
439*b06ebda0SMatthew Dillon "Invalid channel state, cid=%d, state=%d\n",
440*b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), scid,
441*b06ebda0SMatthew Dillon 			cmd->ch->state);
442*b06ebda0SMatthew Dillon 		goto reject;
443*b06ebda0SMatthew Dillon 	}
444*b06ebda0SMatthew Dillon 
445*b06ebda0SMatthew Dillon 	/* Verify CIDs and send reject if does not match */
446*b06ebda0SMatthew Dillon 	if (cmd->ch->scid != scid) {
447*b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
448*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_ConnectRsp. Channel IDs do not match, scid=%d(%d)\n",
449*b06ebda0SMatthew Dillon 			 __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,
450*b06ebda0SMatthew Dillon 			scid);
451*b06ebda0SMatthew Dillon 		goto reject;
452*b06ebda0SMatthew Dillon 	}
453*b06ebda0SMatthew Dillon 
454*b06ebda0SMatthew Dillon 	/*
455*b06ebda0SMatthew Dillon 	 * Looks good. We got confirmation from our peer. Now process
456*b06ebda0SMatthew Dillon 	 * it. First disable RTX timer. Then check the result and send
457*b06ebda0SMatthew Dillon 	 * notification to the upper layer. If command timeout already
458*b06ebda0SMatthew Dillon 	 * happened then ignore response.
459*b06ebda0SMatthew Dillon 	 */
460*b06ebda0SMatthew Dillon 
461*b06ebda0SMatthew Dillon 	if ((error = ng_l2cap_command_untimeout(cmd)) != 0)
462*b06ebda0SMatthew Dillon 		return (error);
463*b06ebda0SMatthew Dillon 
464*b06ebda0SMatthew Dillon 	if (result == NG_L2CAP_PENDING) {
465*b06ebda0SMatthew Dillon 		/*
466*b06ebda0SMatthew Dillon 		 * Our peer wants more time to complete connection. We shall
467*b06ebda0SMatthew Dillon 		 * start ERTX timer and wait. Keep command in the list.
468*b06ebda0SMatthew Dillon 		 */
469*b06ebda0SMatthew Dillon 
470*b06ebda0SMatthew Dillon 		cmd->ch->dcid = dcid;
471*b06ebda0SMatthew Dillon 		ng_l2cap_command_timeout(cmd, bluetooth_l2cap_ertx_timeout());
472*b06ebda0SMatthew Dillon 
473*b06ebda0SMatthew Dillon 		error = ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token,
474*b06ebda0SMatthew Dillon 				result, status);
475*b06ebda0SMatthew Dillon 		if (error != 0)
476*b06ebda0SMatthew Dillon 			ng_l2cap_free_chan(cmd->ch);
477*b06ebda0SMatthew Dillon 	} else {
478*b06ebda0SMatthew Dillon 		ng_l2cap_unlink_cmd(cmd);
479*b06ebda0SMatthew Dillon 
480*b06ebda0SMatthew Dillon 		if (result == NG_L2CAP_SUCCESS) {
481*b06ebda0SMatthew Dillon 			/*
482*b06ebda0SMatthew Dillon 			 * Channel is open. Complete command and move to CONFIG
483*b06ebda0SMatthew Dillon 			 * state. Since we have sent positive confirmation we
484*b06ebda0SMatthew Dillon 			 * expect to receive L2CA_Config request from the upper
485*b06ebda0SMatthew Dillon 			 * layer protocol.
486*b06ebda0SMatthew Dillon 			 */
487*b06ebda0SMatthew Dillon 
488*b06ebda0SMatthew Dillon 			cmd->ch->dcid = dcid;
489*b06ebda0SMatthew Dillon 			cmd->ch->state = NG_L2CAP_CONFIG;
490*b06ebda0SMatthew Dillon 		} else
491*b06ebda0SMatthew Dillon 			/* There was an error, so close the channel */
492*b06ebda0SMatthew Dillon 			NG_L2CAP_INFO(
493*b06ebda0SMatthew Dillon "%s: %s - failed to open L2CAP channel, result=%d, status=%d\n",
494*b06ebda0SMatthew Dillon 				__func__, NG_NODE_NAME(l2cap->node), result,
495*b06ebda0SMatthew Dillon 				status);
496*b06ebda0SMatthew Dillon 
497*b06ebda0SMatthew Dillon 		error = ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token,
498*b06ebda0SMatthew Dillon 				result, status);
499*b06ebda0SMatthew Dillon 
500*b06ebda0SMatthew Dillon 		/* XXX do we have to remove the channel on error? */
501*b06ebda0SMatthew Dillon 		if (error != 0 || result != NG_L2CAP_SUCCESS)
502*b06ebda0SMatthew Dillon 			ng_l2cap_free_chan(cmd->ch);
503*b06ebda0SMatthew Dillon 
504*b06ebda0SMatthew Dillon 		ng_l2cap_free_cmd(cmd);
505*b06ebda0SMatthew Dillon 	}
506*b06ebda0SMatthew Dillon 
507*b06ebda0SMatthew Dillon 	return (error);
508*b06ebda0SMatthew Dillon 
509*b06ebda0SMatthew Dillon reject:
510*b06ebda0SMatthew Dillon 	/* Send reject. Do not really care about the result */
511*b06ebda0SMatthew Dillon 	send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, dcid);
512*b06ebda0SMatthew Dillon 
513*b06ebda0SMatthew Dillon 	return (0);
514*b06ebda0SMatthew Dillon } /* ng_l2cap_process_con_rsp */
515*b06ebda0SMatthew Dillon 
516*b06ebda0SMatthew Dillon /*
517*b06ebda0SMatthew Dillon  * Process L2CAP_ConfigReq command
518*b06ebda0SMatthew Dillon  */
519*b06ebda0SMatthew Dillon 
520*b06ebda0SMatthew Dillon static int
521*b06ebda0SMatthew Dillon ng_l2cap_process_cfg_req(ng_l2cap_con_p con, u_int8_t ident)
522*b06ebda0SMatthew Dillon {
523*b06ebda0SMatthew Dillon 	ng_l2cap_p		 l2cap = con->l2cap;
524*b06ebda0SMatthew Dillon 	struct mbuf		*m = con->rx_pkt;
525*b06ebda0SMatthew Dillon 	ng_l2cap_cfg_req_cp	*cp = NULL;
526*b06ebda0SMatthew Dillon 	ng_l2cap_chan_p		 ch = NULL;
527*b06ebda0SMatthew Dillon 	u_int16_t		 dcid, respond, result;
528*b06ebda0SMatthew Dillon 	ng_l2cap_cfg_opt_t	 hdr;
529*b06ebda0SMatthew Dillon 	ng_l2cap_cfg_opt_val_t	 val;
530*b06ebda0SMatthew Dillon 	int			 off, error = 0;
531*b06ebda0SMatthew Dillon 
532*b06ebda0SMatthew Dillon 	/* Get command parameters */
533*b06ebda0SMatthew Dillon 	con->rx_pkt = NULL;
534*b06ebda0SMatthew Dillon 	NG_L2CAP_M_PULLUP(m, sizeof(*cp));
535*b06ebda0SMatthew Dillon 	if (m == NULL)
536*b06ebda0SMatthew Dillon 		return (ENOBUFS);
537*b06ebda0SMatthew Dillon 
538*b06ebda0SMatthew Dillon 	cp = mtod(m, ng_l2cap_cfg_req_cp *);
539*b06ebda0SMatthew Dillon 	dcid = le16toh(cp->dcid);
540*b06ebda0SMatthew Dillon 	respond = NG_L2CAP_OPT_CFLAG(le16toh(cp->flags));
541*b06ebda0SMatthew Dillon 	m_adj(m, sizeof(*cp));
542*b06ebda0SMatthew Dillon 
543*b06ebda0SMatthew Dillon 	/* Check if we have this channel and it is in valid state */
544*b06ebda0SMatthew Dillon 	ch = ng_l2cap_chan_by_scid(l2cap, dcid);
545*b06ebda0SMatthew Dillon 	if (ch == NULL) {
546*b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
547*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_ConfigReq command. " \
548*b06ebda0SMatthew Dillon "Channel does not exist, cid=%d\n",
549*b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), dcid);
550*b06ebda0SMatthew Dillon 		goto reject;
551*b06ebda0SMatthew Dillon 	}
552*b06ebda0SMatthew Dillon 
553*b06ebda0SMatthew Dillon 	/* Verify channel state */
554*b06ebda0SMatthew Dillon 	if (ch->state != NG_L2CAP_CONFIG && ch->state != NG_L2CAP_OPEN) {
555*b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
556*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_ConfigReq. " \
557*b06ebda0SMatthew Dillon "Invalid channel state, cid=%d, state=%d\n",
558*b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), dcid, ch->state);
559*b06ebda0SMatthew Dillon 		goto reject;
560*b06ebda0SMatthew Dillon 	}
561*b06ebda0SMatthew Dillon 
562*b06ebda0SMatthew Dillon 	if (ch->state == NG_L2CAP_OPEN) { /* Re-configuration */
563*b06ebda0SMatthew Dillon 		ch->cfg_state = 0;
564*b06ebda0SMatthew Dillon 		ch->state = NG_L2CAP_CONFIG;
565*b06ebda0SMatthew Dillon 	}
566*b06ebda0SMatthew Dillon 
567*b06ebda0SMatthew Dillon 	for (result = 0, off = 0; ; ) {
568*b06ebda0SMatthew Dillon 		error = get_next_l2cap_opt(m, &off, &hdr, &val);
569*b06ebda0SMatthew Dillon 		if (error == 0) { /* We done with this packet */
570*b06ebda0SMatthew Dillon 			NG_FREE_M(m);
571*b06ebda0SMatthew Dillon 			break;
572*b06ebda0SMatthew Dillon 		} else if (error > 0) { /* Got option */
573*b06ebda0SMatthew Dillon 			switch (hdr.type) {
574*b06ebda0SMatthew Dillon 			case NG_L2CAP_OPT_MTU:
575*b06ebda0SMatthew Dillon 				ch->omtu = val.mtu;
576*b06ebda0SMatthew Dillon 				break;
577*b06ebda0SMatthew Dillon 
578*b06ebda0SMatthew Dillon 			case NG_L2CAP_OPT_FLUSH_TIMO:
579*b06ebda0SMatthew Dillon 				ch->flush_timo = val.flush_timo;
580*b06ebda0SMatthew Dillon 				break;
581*b06ebda0SMatthew Dillon 
582*b06ebda0SMatthew Dillon 			case NG_L2CAP_OPT_QOS:
583*b06ebda0SMatthew Dillon 				bcopy(&val.flow, &ch->iflow, sizeof(ch->iflow));
584*b06ebda0SMatthew Dillon 				break;
585*b06ebda0SMatthew Dillon 
586*b06ebda0SMatthew Dillon 			default: /* Ignore unknown hint option */
587*b06ebda0SMatthew Dillon 				break;
588*b06ebda0SMatthew Dillon 			}
589*b06ebda0SMatthew Dillon 		} else { /* Oops, something is wrong */
590*b06ebda0SMatthew Dillon 			respond = 1;
591*b06ebda0SMatthew Dillon 
592*b06ebda0SMatthew Dillon 			if (error == -3) {
593*b06ebda0SMatthew Dillon 
594*b06ebda0SMatthew Dillon 				/*
595*b06ebda0SMatthew Dillon 				 * Adjust mbuf so we can get to the start
596*b06ebda0SMatthew Dillon 				 * of the first option we did not like.
597*b06ebda0SMatthew Dillon 				 */
598*b06ebda0SMatthew Dillon 
599*b06ebda0SMatthew Dillon 				m_adj(m, off - sizeof(hdr));
600*b06ebda0SMatthew Dillon 				m->m_pkthdr.len = sizeof(hdr) + hdr.length;
601*b06ebda0SMatthew Dillon 
602*b06ebda0SMatthew Dillon 				result = NG_L2CAP_UNKNOWN_OPTION;
603*b06ebda0SMatthew Dillon 			} else {
604*b06ebda0SMatthew Dillon 				/* XXX FIXME Send other reject codes? */
605*b06ebda0SMatthew Dillon 				NG_FREE_M(m);
606*b06ebda0SMatthew Dillon 				result = NG_L2CAP_REJECT;
607*b06ebda0SMatthew Dillon 			}
608*b06ebda0SMatthew Dillon 
609*b06ebda0SMatthew Dillon 			break;
610*b06ebda0SMatthew Dillon 		}
611*b06ebda0SMatthew Dillon 	}
612*b06ebda0SMatthew Dillon 
613*b06ebda0SMatthew Dillon 	/*
614*b06ebda0SMatthew Dillon 	 * Now check and see if we have to respond. If everything was OK then
615*b06ebda0SMatthew Dillon 	 * respond contain "C flag" and (if set) we will respond with empty
616*b06ebda0SMatthew Dillon 	 * packet and will wait for more options.
617*b06ebda0SMatthew Dillon 	 *
618*b06ebda0SMatthew Dillon 	 * Other case is that we did not like peer's options and will respond
619*b06ebda0SMatthew Dillon 	 * with L2CAP_Config response command with Reject error code.
620*b06ebda0SMatthew Dillon 	 *
621*b06ebda0SMatthew Dillon 	 * When "respond == 0" than we have received all options and we will
622*b06ebda0SMatthew Dillon 	 * sent L2CA_ConfigInd event to the upper layer protocol.
623*b06ebda0SMatthew Dillon 	 */
624*b06ebda0SMatthew Dillon 
625*b06ebda0SMatthew Dillon 	if (respond) {
626*b06ebda0SMatthew Dillon 		error = send_l2cap_cfg_rsp(con, ident, ch->dcid, result, m);
627*b06ebda0SMatthew Dillon 		if (error != 0) {
628*b06ebda0SMatthew Dillon 			ng_l2cap_l2ca_discon_ind(ch);
629*b06ebda0SMatthew Dillon 			ng_l2cap_free_chan(ch);
630*b06ebda0SMatthew Dillon 		}
631*b06ebda0SMatthew Dillon 	} else {
632*b06ebda0SMatthew Dillon 		/* Send L2CA_ConfigInd event to the upper layer protocol */
633*b06ebda0SMatthew Dillon 		ch->ident = ident;
634*b06ebda0SMatthew Dillon 		error = ng_l2cap_l2ca_cfg_ind(ch);
635*b06ebda0SMatthew Dillon 		if (error != 0)
636*b06ebda0SMatthew Dillon 			ng_l2cap_free_chan(ch);
637*b06ebda0SMatthew Dillon 	}
638*b06ebda0SMatthew Dillon 
639*b06ebda0SMatthew Dillon 	return (error);
640*b06ebda0SMatthew Dillon 
641*b06ebda0SMatthew Dillon reject:
642*b06ebda0SMatthew Dillon 	/* Send reject. Do not really care about the result */
643*b06ebda0SMatthew Dillon 	NG_FREE_M(m);
644*b06ebda0SMatthew Dillon 
645*b06ebda0SMatthew Dillon 	send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, 0, dcid);
646*b06ebda0SMatthew Dillon 
647*b06ebda0SMatthew Dillon 	return (0);
648*b06ebda0SMatthew Dillon } /* ng_l2cap_process_cfg_req */
649*b06ebda0SMatthew Dillon 
650*b06ebda0SMatthew Dillon /*
651*b06ebda0SMatthew Dillon  * Process L2CAP_ConfigRsp command
652*b06ebda0SMatthew Dillon  */
653*b06ebda0SMatthew Dillon 
654*b06ebda0SMatthew Dillon static int
655*b06ebda0SMatthew Dillon ng_l2cap_process_cfg_rsp(ng_l2cap_con_p con, u_int8_t ident)
656*b06ebda0SMatthew Dillon {
657*b06ebda0SMatthew Dillon 	ng_l2cap_p		 l2cap = con->l2cap;
658*b06ebda0SMatthew Dillon 	struct mbuf		*m = con->rx_pkt;
659*b06ebda0SMatthew Dillon 	ng_l2cap_cfg_rsp_cp	*cp = NULL;
660*b06ebda0SMatthew Dillon 	ng_l2cap_cmd_p		 cmd = NULL;
661*b06ebda0SMatthew Dillon 	u_int16_t		 scid, cflag, result;
662*b06ebda0SMatthew Dillon 	ng_l2cap_cfg_opt_t	 hdr;
663*b06ebda0SMatthew Dillon 	ng_l2cap_cfg_opt_val_t	 val;
664*b06ebda0SMatthew Dillon 	int			 off, error = 0;
665*b06ebda0SMatthew Dillon 
666*b06ebda0SMatthew Dillon 	/* Get command parameters */
667*b06ebda0SMatthew Dillon 	con->rx_pkt = NULL;
668*b06ebda0SMatthew Dillon 	NG_L2CAP_M_PULLUP(m, sizeof(*cp));
669*b06ebda0SMatthew Dillon 	if (m == NULL)
670*b06ebda0SMatthew Dillon 		return (ENOBUFS);
671*b06ebda0SMatthew Dillon 
672*b06ebda0SMatthew Dillon 	cp = mtod(m, ng_l2cap_cfg_rsp_cp *);
673*b06ebda0SMatthew Dillon 	scid = le16toh(cp->scid);
674*b06ebda0SMatthew Dillon 	cflag = NG_L2CAP_OPT_CFLAG(le16toh(cp->flags));
675*b06ebda0SMatthew Dillon 	result = le16toh(cp->result);
676*b06ebda0SMatthew Dillon 	m_adj(m, sizeof(*cp));
677*b06ebda0SMatthew Dillon 
678*b06ebda0SMatthew Dillon 	/* Check if we have this command */
679*b06ebda0SMatthew Dillon 	cmd = ng_l2cap_cmd_by_ident(con, ident);
680*b06ebda0SMatthew Dillon 	if (cmd == NULL) {
681*b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
682*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_ConfigRsp command. ident=%d, con_handle=%d\n",
683*b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), ident,
684*b06ebda0SMatthew Dillon 			con->con_handle);
685*b06ebda0SMatthew Dillon 		NG_FREE_M(m);
686*b06ebda0SMatthew Dillon 
687*b06ebda0SMatthew Dillon 		return (ENOENT);
688*b06ebda0SMatthew Dillon 	}
689*b06ebda0SMatthew Dillon 
690*b06ebda0SMatthew Dillon 	/* Verify CIDs and send reject if does not match */
691*b06ebda0SMatthew Dillon 	if (cmd->ch->scid != scid) {
692*b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
693*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_ConfigRsp. " \
694*b06ebda0SMatthew Dillon "Channel ID does not match, scid=%d(%d)\n",
695*b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,
696*b06ebda0SMatthew Dillon 			scid);
697*b06ebda0SMatthew Dillon 		goto reject;
698*b06ebda0SMatthew Dillon 	}
699*b06ebda0SMatthew Dillon 
700*b06ebda0SMatthew Dillon 	/* Verify channel state and reject if invalid */
701*b06ebda0SMatthew Dillon 	if (cmd->ch->state != NG_L2CAP_CONFIG) {
702*b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
703*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_ConfigRsp. " \
704*b06ebda0SMatthew Dillon "Invalid channel state, scid=%d, state=%d\n",
705*b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,
706*b06ebda0SMatthew Dillon 			cmd->ch->state);
707*b06ebda0SMatthew Dillon 		goto reject;
708*b06ebda0SMatthew Dillon 	}
709*b06ebda0SMatthew Dillon 
710*b06ebda0SMatthew Dillon 	/*
711*b06ebda0SMatthew Dillon 	 * Looks like it is our response, so process it. First parse options,
712*b06ebda0SMatthew Dillon 	 * then verify C flag. If it is set then we shall expect more
713*b06ebda0SMatthew Dillon 	 * configuration options from the peer and we will wait. Otherwise we
714*b06ebda0SMatthew Dillon 	 * have received all options and we will send L2CA_ConfigRsp event to
715*b06ebda0SMatthew Dillon 	 * the upper layer protocol. If command timeout already happened then
716*b06ebda0SMatthew Dillon 	 * ignore response.
717*b06ebda0SMatthew Dillon 	 */
718*b06ebda0SMatthew Dillon 
719*b06ebda0SMatthew Dillon 	if ((error = ng_l2cap_command_untimeout(cmd)) != 0) {
720*b06ebda0SMatthew Dillon 		NG_FREE_M(m);
721*b06ebda0SMatthew Dillon 		return (error);
722*b06ebda0SMatthew Dillon 	}
723*b06ebda0SMatthew Dillon 
724*b06ebda0SMatthew Dillon 	for (off = 0; ; ) {
725*b06ebda0SMatthew Dillon 		error = get_next_l2cap_opt(m, &off, &hdr, &val);
726*b06ebda0SMatthew Dillon 		if (error == 0) /* We done with this packet */
727*b06ebda0SMatthew Dillon 			break;
728*b06ebda0SMatthew Dillon 		else if (error > 0) { /* Got option */
729*b06ebda0SMatthew Dillon 			switch (hdr.type) {
730*b06ebda0SMatthew Dillon 			case NG_L2CAP_OPT_MTU:
731*b06ebda0SMatthew Dillon 				cmd->ch->imtu = val.mtu;
732*b06ebda0SMatthew Dillon 			break;
733*b06ebda0SMatthew Dillon 
734*b06ebda0SMatthew Dillon 			case NG_L2CAP_OPT_FLUSH_TIMO:
735*b06ebda0SMatthew Dillon 				cmd->ch->flush_timo = val.flush_timo;
736*b06ebda0SMatthew Dillon 				break;
737*b06ebda0SMatthew Dillon 
738*b06ebda0SMatthew Dillon 			case NG_L2CAP_OPT_QOS:
739*b06ebda0SMatthew Dillon 				bcopy(&val.flow, &cmd->ch->oflow,
740*b06ebda0SMatthew Dillon 					sizeof(cmd->ch->oflow));
741*b06ebda0SMatthew Dillon 			break;
742*b06ebda0SMatthew Dillon 
743*b06ebda0SMatthew Dillon 			default: /* Ignore unknown hint option */
744*b06ebda0SMatthew Dillon 				break;
745*b06ebda0SMatthew Dillon 			}
746*b06ebda0SMatthew Dillon 		} else {
747*b06ebda0SMatthew Dillon 			/*
748*b06ebda0SMatthew Dillon 			 * XXX FIXME What to do here?
749*b06ebda0SMatthew Dillon 			 *
750*b06ebda0SMatthew Dillon 			 * This is really BAD :( options packet was broken, or
751*b06ebda0SMatthew Dillon 			 * peer sent us option that we did not understand. Let
752*b06ebda0SMatthew Dillon 			 * upper layer know and do not wait for more options.
753*b06ebda0SMatthew Dillon 			 */
754*b06ebda0SMatthew Dillon 
755*b06ebda0SMatthew Dillon 			NG_L2CAP_ALERT(
756*b06ebda0SMatthew Dillon "%s: %s - failed to parse configuration options, error=%d\n",
757*b06ebda0SMatthew Dillon 				__func__, NG_NODE_NAME(l2cap->node), error);
758*b06ebda0SMatthew Dillon 
759*b06ebda0SMatthew Dillon 			result = NG_L2CAP_UNKNOWN;
760*b06ebda0SMatthew Dillon 			cflag = 0;
761*b06ebda0SMatthew Dillon 
762*b06ebda0SMatthew Dillon 			break;
763*b06ebda0SMatthew Dillon 		}
764*b06ebda0SMatthew Dillon 	}
765*b06ebda0SMatthew Dillon 
766*b06ebda0SMatthew Dillon 	NG_FREE_M(m);
767*b06ebda0SMatthew Dillon 
768*b06ebda0SMatthew Dillon 	if (cflag) /* Restart timer and wait for more options */
769*b06ebda0SMatthew Dillon 		ng_l2cap_command_timeout(cmd, bluetooth_l2cap_rtx_timeout());
770*b06ebda0SMatthew Dillon 	else {
771*b06ebda0SMatthew Dillon 		ng_l2cap_unlink_cmd(cmd);
772*b06ebda0SMatthew Dillon 
773*b06ebda0SMatthew Dillon 		/* Send L2CA_Config response to the upper layer protocol */
774*b06ebda0SMatthew Dillon 		error = ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, result);
775*b06ebda0SMatthew Dillon 		if (error != 0) {
776*b06ebda0SMatthew Dillon 			/*
777*b06ebda0SMatthew Dillon 			 * XXX FIXME what to do here? we were not able to send
778*b06ebda0SMatthew Dillon 			 * response to the upper layer protocol, so for now
779*b06ebda0SMatthew Dillon 			 * just close the channel. Send L2CAP_Disconnect to
780*b06ebda0SMatthew Dillon 			 * remote peer?
781*b06ebda0SMatthew Dillon 			 */
782*b06ebda0SMatthew Dillon 
783*b06ebda0SMatthew Dillon 			NG_L2CAP_ERR(
784*b06ebda0SMatthew Dillon "%s: %s - failed to send L2CA_Config response, error=%d\n",
785*b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), error);
786*b06ebda0SMatthew Dillon 
787*b06ebda0SMatthew Dillon 			ng_l2cap_free_chan(cmd->ch);
788*b06ebda0SMatthew Dillon 		}
789*b06ebda0SMatthew Dillon 
790*b06ebda0SMatthew Dillon 		ng_l2cap_free_cmd(cmd);
791*b06ebda0SMatthew Dillon 	}
792*b06ebda0SMatthew Dillon 
793*b06ebda0SMatthew Dillon 	return (error);
794*b06ebda0SMatthew Dillon 
795*b06ebda0SMatthew Dillon reject:
796*b06ebda0SMatthew Dillon 	/* Send reject. Do not really care about the result */
797*b06ebda0SMatthew Dillon 	NG_FREE_M(m);
798*b06ebda0SMatthew Dillon 
799*b06ebda0SMatthew Dillon 	send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, 0);
800*b06ebda0SMatthew Dillon 
801*b06ebda0SMatthew Dillon 	return (0);
802*b06ebda0SMatthew Dillon } /* ng_l2cap_process_cfg_rsp */
803*b06ebda0SMatthew Dillon 
804*b06ebda0SMatthew Dillon /*
805*b06ebda0SMatthew Dillon  * Process L2CAP_DisconnectReq command
806*b06ebda0SMatthew Dillon  */
807*b06ebda0SMatthew Dillon 
808*b06ebda0SMatthew Dillon static int
809*b06ebda0SMatthew Dillon ng_l2cap_process_discon_req(ng_l2cap_con_p con, u_int8_t ident)
810*b06ebda0SMatthew Dillon {
811*b06ebda0SMatthew Dillon 	ng_l2cap_p		 l2cap = con->l2cap;
812*b06ebda0SMatthew Dillon 	ng_l2cap_discon_req_cp	*cp = NULL;
813*b06ebda0SMatthew Dillon 	ng_l2cap_chan_p		 ch = NULL;
814*b06ebda0SMatthew Dillon 	ng_l2cap_cmd_p		 cmd = NULL;
815*b06ebda0SMatthew Dillon 	u_int16_t		 scid, dcid;
816*b06ebda0SMatthew Dillon 
817*b06ebda0SMatthew Dillon 	/* Get command parameters */
818*b06ebda0SMatthew Dillon 	NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));
819*b06ebda0SMatthew Dillon 	if (con->rx_pkt == NULL)
820*b06ebda0SMatthew Dillon 		return (ENOBUFS);
821*b06ebda0SMatthew Dillon 
822*b06ebda0SMatthew Dillon 	cp = mtod(con->rx_pkt, ng_l2cap_discon_req_cp *);
823*b06ebda0SMatthew Dillon 	dcid = le16toh(cp->dcid);
824*b06ebda0SMatthew Dillon 	scid = le16toh(cp->scid);
825*b06ebda0SMatthew Dillon 
826*b06ebda0SMatthew Dillon 	NG_FREE_M(con->rx_pkt);
827*b06ebda0SMatthew Dillon 
828*b06ebda0SMatthew Dillon 	/* Check if we have this channel and it is in valid state */
829*b06ebda0SMatthew Dillon 	ch = ng_l2cap_chan_by_scid(l2cap, dcid);
830*b06ebda0SMatthew Dillon 	if (ch == NULL) {
831*b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
832*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_DisconnectReq message. " \
833*b06ebda0SMatthew Dillon "Channel does not exist, cid=%d\n",
834*b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), dcid);
835*b06ebda0SMatthew Dillon 		goto reject;
836*b06ebda0SMatthew Dillon 	}
837*b06ebda0SMatthew Dillon 
838*b06ebda0SMatthew Dillon 	/* XXX Verify channel state and reject if invalid -- is that true? */
839*b06ebda0SMatthew Dillon 	if (ch->state != NG_L2CAP_OPEN && ch->state != NG_L2CAP_CONFIG &&
840*b06ebda0SMatthew Dillon 	    ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) {
841*b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
842*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_DisconnectReq. " \
843*b06ebda0SMatthew Dillon "Invalid channel state, cid=%d, state=%d\n",
844*b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), dcid, ch->state);
845*b06ebda0SMatthew Dillon 		goto reject;
846*b06ebda0SMatthew Dillon 	}
847*b06ebda0SMatthew Dillon 
848*b06ebda0SMatthew Dillon 	/* Match destination channel ID */
849*b06ebda0SMatthew Dillon 	if (ch->dcid != scid || ch->scid != dcid) {
850*b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
851*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_DisconnectReq. " \
852*b06ebda0SMatthew Dillon "Channel IDs does not match, channel: scid=%d, dcid=%d, " \
853*b06ebda0SMatthew Dillon "request: scid=%d, dcid=%d\n",
854*b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), ch->scid, ch->dcid,
855*b06ebda0SMatthew Dillon 			scid, dcid);
856*b06ebda0SMatthew Dillon 		goto reject;
857*b06ebda0SMatthew Dillon 	}
858*b06ebda0SMatthew Dillon 
859*b06ebda0SMatthew Dillon 	/*
860*b06ebda0SMatthew Dillon 	 * Looks good, so notify upper layer protocol that channel is about
861*b06ebda0SMatthew Dillon 	 * to be disconnected and send L2CA_DisconnectInd message. Then respond
862*b06ebda0SMatthew Dillon 	 * with L2CAP_DisconnectRsp.
863*b06ebda0SMatthew Dillon 	 */
864*b06ebda0SMatthew Dillon 
865*b06ebda0SMatthew Dillon 	if (ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) {
866*b06ebda0SMatthew Dillon 		ng_l2cap_l2ca_discon_ind(ch); /* do not care about result */
867*b06ebda0SMatthew Dillon 		ng_l2cap_free_chan(ch);
868*b06ebda0SMatthew Dillon 	}
869*b06ebda0SMatthew Dillon 
870*b06ebda0SMatthew Dillon 	/* Send L2CAP_DisconnectRsp */
871*b06ebda0SMatthew Dillon 	cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_DISCON_RSP, 0);
872*b06ebda0SMatthew Dillon 	if (cmd == NULL)
873*b06ebda0SMatthew Dillon 		return (ENOMEM);
874*b06ebda0SMatthew Dillon 
875*b06ebda0SMatthew Dillon 	_ng_l2cap_discon_rsp(cmd->aux, ident, dcid, scid);
876*b06ebda0SMatthew Dillon 	if (cmd->aux == NULL) {
877*b06ebda0SMatthew Dillon 		ng_l2cap_free_cmd(cmd);
878*b06ebda0SMatthew Dillon 
879*b06ebda0SMatthew Dillon 		return (ENOBUFS);
880*b06ebda0SMatthew Dillon 	}
881*b06ebda0SMatthew Dillon 
882*b06ebda0SMatthew Dillon 	/* Link command to the queue */
883*b06ebda0SMatthew Dillon 	ng_l2cap_link_cmd(con, cmd);
884*b06ebda0SMatthew Dillon 	ng_l2cap_lp_deliver(con);
885*b06ebda0SMatthew Dillon 
886*b06ebda0SMatthew Dillon 	return (0);
887*b06ebda0SMatthew Dillon 
888*b06ebda0SMatthew Dillon reject:
889*b06ebda0SMatthew Dillon 	/* Send reject. Do not really care about the result */
890*b06ebda0SMatthew Dillon 	send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, dcid);
891*b06ebda0SMatthew Dillon 
892*b06ebda0SMatthew Dillon 	return (0);
893*b06ebda0SMatthew Dillon } /* ng_l2cap_process_discon_req */
894*b06ebda0SMatthew Dillon 
895*b06ebda0SMatthew Dillon /*
896*b06ebda0SMatthew Dillon  * Process L2CAP_DisconnectRsp command
897*b06ebda0SMatthew Dillon  */
898*b06ebda0SMatthew Dillon 
899*b06ebda0SMatthew Dillon static int
900*b06ebda0SMatthew Dillon ng_l2cap_process_discon_rsp(ng_l2cap_con_p con, u_int8_t ident)
901*b06ebda0SMatthew Dillon {
902*b06ebda0SMatthew Dillon 	ng_l2cap_p		 l2cap = con->l2cap;
903*b06ebda0SMatthew Dillon 	ng_l2cap_discon_rsp_cp	*cp = NULL;
904*b06ebda0SMatthew Dillon 	ng_l2cap_cmd_p		 cmd = NULL;
905*b06ebda0SMatthew Dillon 	u_int16_t		 scid, dcid;
906*b06ebda0SMatthew Dillon 	int			 error = 0;
907*b06ebda0SMatthew Dillon 
908*b06ebda0SMatthew Dillon 	/* Get command parameters */
909*b06ebda0SMatthew Dillon 	NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));
910*b06ebda0SMatthew Dillon 	if (con->rx_pkt == NULL)
911*b06ebda0SMatthew Dillon 		return (ENOBUFS);
912*b06ebda0SMatthew Dillon 
913*b06ebda0SMatthew Dillon 	cp = mtod(con->rx_pkt, ng_l2cap_discon_rsp_cp *);
914*b06ebda0SMatthew Dillon 	dcid = le16toh(cp->dcid);
915*b06ebda0SMatthew Dillon 	scid = le16toh(cp->scid);
916*b06ebda0SMatthew Dillon 
917*b06ebda0SMatthew Dillon 	NG_FREE_M(con->rx_pkt);
918*b06ebda0SMatthew Dillon 
919*b06ebda0SMatthew Dillon 	/* Check if we have pending command descriptor */
920*b06ebda0SMatthew Dillon 	cmd = ng_l2cap_cmd_by_ident(con, ident);
921*b06ebda0SMatthew Dillon 	if (cmd == NULL) {
922*b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
923*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_DisconnectRsp command. ident=%d, con_handle=%d\n",
924*b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), ident,
925*b06ebda0SMatthew Dillon 			con->con_handle);
926*b06ebda0SMatthew Dillon 		goto out;
927*b06ebda0SMatthew Dillon 	}
928*b06ebda0SMatthew Dillon 
929*b06ebda0SMatthew Dillon 	/* Verify channel state, do nothing if invalid */
930*b06ebda0SMatthew Dillon 	if (cmd->ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) {
931*b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
932*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_DisconnectRsp. " \
933*b06ebda0SMatthew Dillon "Invalid channel state, cid=%d, state=%d\n",
934*b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), scid,
935*b06ebda0SMatthew Dillon 			cmd->ch->state);
936*b06ebda0SMatthew Dillon 		goto out;
937*b06ebda0SMatthew Dillon 	}
938*b06ebda0SMatthew Dillon 
939*b06ebda0SMatthew Dillon 	/* Verify CIDs and send reject if does not match */
940*b06ebda0SMatthew Dillon 	if (cmd->ch->scid != scid || cmd->ch->dcid != dcid) {
941*b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
942*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_DisconnectRsp. " \
943*b06ebda0SMatthew Dillon "Channel IDs do not match, scid=%d(%d), dcid=%d(%d)\n",
944*b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,
945*b06ebda0SMatthew Dillon 			scid, cmd->ch->dcid, dcid);
946*b06ebda0SMatthew Dillon 		goto out;
947*b06ebda0SMatthew Dillon 	}
948*b06ebda0SMatthew Dillon 
949*b06ebda0SMatthew Dillon 	/*
950*b06ebda0SMatthew Dillon 	 * Looks like we have successfuly disconnected channel, so notify
951*b06ebda0SMatthew Dillon 	 * upper layer. If command timeout already happened then ignore
952*b06ebda0SMatthew Dillon 	 * response.
953*b06ebda0SMatthew Dillon 	 */
954*b06ebda0SMatthew Dillon 
955*b06ebda0SMatthew Dillon 	if ((error = ng_l2cap_command_untimeout(cmd)) != 0)
956*b06ebda0SMatthew Dillon 		goto out;
957*b06ebda0SMatthew Dillon 
958*b06ebda0SMatthew Dillon 	error = ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, NG_L2CAP_SUCCESS);
959*b06ebda0SMatthew Dillon 	ng_l2cap_free_chan(cmd->ch); /* this will free commands too */
960*b06ebda0SMatthew Dillon out:
961*b06ebda0SMatthew Dillon 	return (error);
962*b06ebda0SMatthew Dillon } /* ng_l2cap_process_discon_rsp */
963*b06ebda0SMatthew Dillon 
964*b06ebda0SMatthew Dillon /*
965*b06ebda0SMatthew Dillon  * Process L2CAP_EchoReq command
966*b06ebda0SMatthew Dillon  */
967*b06ebda0SMatthew Dillon 
968*b06ebda0SMatthew Dillon static int
969*b06ebda0SMatthew Dillon ng_l2cap_process_echo_req(ng_l2cap_con_p con, u_int8_t ident)
970*b06ebda0SMatthew Dillon {
971*b06ebda0SMatthew Dillon 	ng_l2cap_p		 l2cap = con->l2cap;
972*b06ebda0SMatthew Dillon 	ng_l2cap_cmd_hdr_t	*hdr = NULL;
973*b06ebda0SMatthew Dillon 	ng_l2cap_cmd_p		 cmd = NULL;
974*b06ebda0SMatthew Dillon 
975*b06ebda0SMatthew Dillon 	con->rx_pkt = ng_l2cap_prepend(con->rx_pkt, sizeof(*hdr));
976*b06ebda0SMatthew Dillon 	if (con->rx_pkt == NULL) {
977*b06ebda0SMatthew Dillon 		NG_L2CAP_ALERT(
978*b06ebda0SMatthew Dillon "%s: %s - ng_l2cap_prepend() failed, size=%zd\n",
979*b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), sizeof(*hdr));
980*b06ebda0SMatthew Dillon 
981*b06ebda0SMatthew Dillon 		return (ENOBUFS);
982*b06ebda0SMatthew Dillon 	}
983*b06ebda0SMatthew Dillon 
984*b06ebda0SMatthew Dillon 	hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *);
985*b06ebda0SMatthew Dillon 	hdr->code = NG_L2CAP_ECHO_RSP;
986*b06ebda0SMatthew Dillon 	hdr->ident = ident;
987*b06ebda0SMatthew Dillon 	hdr->length = htole16(con->rx_pkt->m_pkthdr.len - sizeof(*hdr));
988*b06ebda0SMatthew Dillon 
989*b06ebda0SMatthew Dillon 	cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_ECHO_RSP, 0);
990*b06ebda0SMatthew Dillon 	if (cmd == NULL) {
991*b06ebda0SMatthew Dillon 		NG_FREE_M(con->rx_pkt);
992*b06ebda0SMatthew Dillon 
993*b06ebda0SMatthew Dillon 		return (ENOBUFS);
994*b06ebda0SMatthew Dillon 	}
995*b06ebda0SMatthew Dillon 
996*b06ebda0SMatthew Dillon 	/* Attach data and link command to the queue */
997*b06ebda0SMatthew Dillon 	cmd->aux = con->rx_pkt;
998*b06ebda0SMatthew Dillon 	con->rx_pkt = NULL;
999*b06ebda0SMatthew Dillon 	ng_l2cap_link_cmd(con, cmd);
1000*b06ebda0SMatthew Dillon 	ng_l2cap_lp_deliver(con);
1001*b06ebda0SMatthew Dillon 
1002*b06ebda0SMatthew Dillon 	return (0);
1003*b06ebda0SMatthew Dillon } /* ng_l2cap_process_echo_req */
1004*b06ebda0SMatthew Dillon 
1005*b06ebda0SMatthew Dillon /*
1006*b06ebda0SMatthew Dillon  * Process L2CAP_EchoRsp command
1007*b06ebda0SMatthew Dillon  */
1008*b06ebda0SMatthew Dillon 
1009*b06ebda0SMatthew Dillon static int
1010*b06ebda0SMatthew Dillon ng_l2cap_process_echo_rsp(ng_l2cap_con_p con, u_int8_t ident)
1011*b06ebda0SMatthew Dillon {
1012*b06ebda0SMatthew Dillon 	ng_l2cap_p	l2cap = con->l2cap;
1013*b06ebda0SMatthew Dillon 	ng_l2cap_cmd_p	cmd = NULL;
1014*b06ebda0SMatthew Dillon 	int		error = 0;
1015*b06ebda0SMatthew Dillon 
1016*b06ebda0SMatthew Dillon 	/* Check if we have this command */
1017*b06ebda0SMatthew Dillon 	cmd = ng_l2cap_cmd_by_ident(con, ident);
1018*b06ebda0SMatthew Dillon 	if (cmd != NULL) {
1019*b06ebda0SMatthew Dillon 		/* If command timeout already happened then ignore response */
1020*b06ebda0SMatthew Dillon 		if ((error = ng_l2cap_command_untimeout(cmd)) != 0) {
1021*b06ebda0SMatthew Dillon 			NG_FREE_M(con->rx_pkt);
1022*b06ebda0SMatthew Dillon 			return (error);
1023*b06ebda0SMatthew Dillon 		}
1024*b06ebda0SMatthew Dillon 
1025*b06ebda0SMatthew Dillon 		ng_l2cap_unlink_cmd(cmd);
1026*b06ebda0SMatthew Dillon 
1027*b06ebda0SMatthew Dillon 		error = ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
1028*b06ebda0SMatthew Dillon 				NG_L2CAP_SUCCESS, con->rx_pkt);
1029*b06ebda0SMatthew Dillon 
1030*b06ebda0SMatthew Dillon 		ng_l2cap_free_cmd(cmd);
1031*b06ebda0SMatthew Dillon 		con->rx_pkt = NULL;
1032*b06ebda0SMatthew Dillon 	} else {
1033*b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
1034*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_EchoRsp command. " \
1035*b06ebda0SMatthew Dillon "Requested ident does not exist, ident=%d\n",
1036*b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), ident);
1037*b06ebda0SMatthew Dillon 		NG_FREE_M(con->rx_pkt);
1038*b06ebda0SMatthew Dillon 	}
1039*b06ebda0SMatthew Dillon 
1040*b06ebda0SMatthew Dillon 	return (error);
1041*b06ebda0SMatthew Dillon } /* ng_l2cap_process_echo_rsp */
1042*b06ebda0SMatthew Dillon 
1043*b06ebda0SMatthew Dillon /*
1044*b06ebda0SMatthew Dillon  * Process L2CAP_InfoReq command
1045*b06ebda0SMatthew Dillon  */
1046*b06ebda0SMatthew Dillon 
1047*b06ebda0SMatthew Dillon static int
1048*b06ebda0SMatthew Dillon ng_l2cap_process_info_req(ng_l2cap_con_p con, u_int8_t ident)
1049*b06ebda0SMatthew Dillon {
1050*b06ebda0SMatthew Dillon 	ng_l2cap_p	l2cap = con->l2cap;
1051*b06ebda0SMatthew Dillon 	ng_l2cap_cmd_p	cmd = NULL;
1052*b06ebda0SMatthew Dillon 	u_int16_t	type;
1053*b06ebda0SMatthew Dillon 
1054*b06ebda0SMatthew Dillon 	/* Get command parameters */
1055*b06ebda0SMatthew Dillon 	NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(ng_l2cap_info_req_cp));
1056*b06ebda0SMatthew Dillon 	if (con->rx_pkt == NULL)
1057*b06ebda0SMatthew Dillon 		return (ENOBUFS);
1058*b06ebda0SMatthew Dillon 
1059*b06ebda0SMatthew Dillon 	type = le16toh(mtod(con->rx_pkt, ng_l2cap_info_req_cp *)->type);
1060*b06ebda0SMatthew Dillon 	NG_FREE_M(con->rx_pkt);
1061*b06ebda0SMatthew Dillon 
1062*b06ebda0SMatthew Dillon 	cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_INFO_RSP, 0);
1063*b06ebda0SMatthew Dillon 	if (cmd == NULL)
1064*b06ebda0SMatthew Dillon 		return (ENOMEM);
1065*b06ebda0SMatthew Dillon 
1066*b06ebda0SMatthew Dillon 	switch (type) {
1067*b06ebda0SMatthew Dillon 	case NG_L2CAP_CONNLESS_MTU:
1068*b06ebda0SMatthew Dillon 		_ng_l2cap_info_rsp(cmd->aux, ident, NG_L2CAP_CONNLESS_MTU,
1069*b06ebda0SMatthew Dillon 				NG_L2CAP_SUCCESS, NG_L2CAP_MTU_DEFAULT);
1070*b06ebda0SMatthew Dillon 		break;
1071*b06ebda0SMatthew Dillon 
1072*b06ebda0SMatthew Dillon 	default:
1073*b06ebda0SMatthew Dillon 		_ng_l2cap_info_rsp(cmd->aux, ident, type,
1074*b06ebda0SMatthew Dillon 				NG_L2CAP_NOT_SUPPORTED, 0);
1075*b06ebda0SMatthew Dillon 		break;
1076*b06ebda0SMatthew Dillon 	}
1077*b06ebda0SMatthew Dillon 
1078*b06ebda0SMatthew Dillon 	if (cmd->aux == NULL) {
1079*b06ebda0SMatthew Dillon 		ng_l2cap_free_cmd(cmd);
1080*b06ebda0SMatthew Dillon 
1081*b06ebda0SMatthew Dillon 		return (ENOBUFS);
1082*b06ebda0SMatthew Dillon 	}
1083*b06ebda0SMatthew Dillon 
1084*b06ebda0SMatthew Dillon 	/* Link command to the queue */
1085*b06ebda0SMatthew Dillon 	ng_l2cap_link_cmd(con, cmd);
1086*b06ebda0SMatthew Dillon 	ng_l2cap_lp_deliver(con);
1087*b06ebda0SMatthew Dillon 
1088*b06ebda0SMatthew Dillon 	return (0);
1089*b06ebda0SMatthew Dillon } /* ng_l2cap_process_info_req */
1090*b06ebda0SMatthew Dillon 
1091*b06ebda0SMatthew Dillon /*
1092*b06ebda0SMatthew Dillon  * Process L2CAP_InfoRsp command
1093*b06ebda0SMatthew Dillon  */
1094*b06ebda0SMatthew Dillon 
1095*b06ebda0SMatthew Dillon static int
1096*b06ebda0SMatthew Dillon ng_l2cap_process_info_rsp(ng_l2cap_con_p con, u_int8_t ident)
1097*b06ebda0SMatthew Dillon {
1098*b06ebda0SMatthew Dillon 	ng_l2cap_p		 l2cap = con->l2cap;
1099*b06ebda0SMatthew Dillon 	ng_l2cap_info_rsp_cp	*cp = NULL;
1100*b06ebda0SMatthew Dillon 	ng_l2cap_cmd_p		 cmd = NULL;
1101*b06ebda0SMatthew Dillon 	int			 error = 0;
1102*b06ebda0SMatthew Dillon 
1103*b06ebda0SMatthew Dillon 	/* Get command parameters */
1104*b06ebda0SMatthew Dillon 	NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));
1105*b06ebda0SMatthew Dillon 	if (con->rx_pkt == NULL)
1106*b06ebda0SMatthew Dillon 		return (ENOBUFS);
1107*b06ebda0SMatthew Dillon 
1108*b06ebda0SMatthew Dillon 	cp = mtod(con->rx_pkt, ng_l2cap_info_rsp_cp *);
1109*b06ebda0SMatthew Dillon 	cp->type = le16toh(cp->type);
1110*b06ebda0SMatthew Dillon 	cp->result = le16toh(cp->result);
1111*b06ebda0SMatthew Dillon 	m_adj(con->rx_pkt, sizeof(*cp));
1112*b06ebda0SMatthew Dillon 
1113*b06ebda0SMatthew Dillon 	/* Check if we have pending command descriptor */
1114*b06ebda0SMatthew Dillon 	cmd = ng_l2cap_cmd_by_ident(con, ident);
1115*b06ebda0SMatthew Dillon 	if (cmd == NULL) {
1116*b06ebda0SMatthew Dillon 		NG_L2CAP_ERR(
1117*b06ebda0SMatthew Dillon "%s: %s - unexpected L2CAP_InfoRsp command. " \
1118*b06ebda0SMatthew Dillon "Requested ident does not exist, ident=%d\n",
1119*b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(l2cap->node), ident);
1120*b06ebda0SMatthew Dillon 		NG_FREE_M(con->rx_pkt);
1121*b06ebda0SMatthew Dillon 
1122*b06ebda0SMatthew Dillon 		return (ENOENT);
1123*b06ebda0SMatthew Dillon 	}
1124*b06ebda0SMatthew Dillon 
1125*b06ebda0SMatthew Dillon 	/* If command timeout already happened then ignore response */
1126*b06ebda0SMatthew Dillon 	if ((error = ng_l2cap_command_untimeout(cmd)) != 0) {
1127*b06ebda0SMatthew Dillon 		NG_FREE_M(con->rx_pkt);
1128*b06ebda0SMatthew Dillon 		return (error);
1129*b06ebda0SMatthew Dillon 	}
1130*b06ebda0SMatthew Dillon 
1131*b06ebda0SMatthew Dillon 	ng_l2cap_unlink_cmd(cmd);
1132*b06ebda0SMatthew Dillon 
1133*b06ebda0SMatthew Dillon 	if (cp->result == NG_L2CAP_SUCCESS) {
1134*b06ebda0SMatthew Dillon 		switch (cp->type) {
1135*b06ebda0SMatthew Dillon 		case NG_L2CAP_CONNLESS_MTU:
1136*b06ebda0SMatthew Dillon 	    		if (con->rx_pkt->m_pkthdr.len == sizeof(u_int16_t))
1137*b06ebda0SMatthew Dillon 				*mtod(con->rx_pkt, u_int16_t *) =
1138*b06ebda0SMatthew Dillon 					le16toh(*mtod(con->rx_pkt,u_int16_t *));
1139*b06ebda0SMatthew Dillon 			else {
1140*b06ebda0SMatthew Dillon 				cp->result = NG_L2CAP_UNKNOWN; /* XXX */
1141*b06ebda0SMatthew Dillon 
1142*b06ebda0SMatthew Dillon 				NG_L2CAP_ERR(
1143*b06ebda0SMatthew Dillon "%s: %s - invalid L2CAP_InfoRsp command. " \
1144*b06ebda0SMatthew Dillon "Bad connectionless MTU parameter, len=%d\n",
1145*b06ebda0SMatthew Dillon 					__func__, NG_NODE_NAME(l2cap->node),
1146*b06ebda0SMatthew Dillon 					con->rx_pkt->m_pkthdr.len);
1147*b06ebda0SMatthew Dillon 			}
1148*b06ebda0SMatthew Dillon 			break;
1149*b06ebda0SMatthew Dillon 
1150*b06ebda0SMatthew Dillon 		default:
1151*b06ebda0SMatthew Dillon 			NG_L2CAP_WARN(
1152*b06ebda0SMatthew Dillon "%s: %s - invalid L2CAP_InfoRsp command. Unknown info type=%d\n",
1153*b06ebda0SMatthew Dillon 				__func__, NG_NODE_NAME(l2cap->node), cp->type);
1154*b06ebda0SMatthew Dillon 			break;
1155*b06ebda0SMatthew Dillon 		}
1156*b06ebda0SMatthew Dillon 	}
1157*b06ebda0SMatthew Dillon 
1158*b06ebda0SMatthew Dillon 	error = ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
1159*b06ebda0SMatthew Dillon 			cp->result, con->rx_pkt);
1160*b06ebda0SMatthew Dillon 
1161*b06ebda0SMatthew Dillon 	ng_l2cap_free_cmd(cmd);
1162*b06ebda0SMatthew Dillon 	con->rx_pkt = NULL;
1163*b06ebda0SMatthew Dillon 
1164*b06ebda0SMatthew Dillon 	return (error);
1165*b06ebda0SMatthew Dillon } /* ng_l2cap_process_info_rsp */
1166*b06ebda0SMatthew Dillon 
1167*b06ebda0SMatthew Dillon /*
1168*b06ebda0SMatthew Dillon  * Send L2CAP reject
1169*b06ebda0SMatthew Dillon  */
1170*b06ebda0SMatthew Dillon 
1171*b06ebda0SMatthew Dillon static int
1172*b06ebda0SMatthew Dillon send_l2cap_reject(ng_l2cap_con_p con, u_int8_t ident, u_int16_t reason,
1173*b06ebda0SMatthew Dillon 		u_int16_t mtu, u_int16_t scid, u_int16_t dcid)
1174*b06ebda0SMatthew Dillon {
1175*b06ebda0SMatthew Dillon 	ng_l2cap_cmd_p	cmd = NULL;
1176*b06ebda0SMatthew Dillon 
1177*b06ebda0SMatthew Dillon 	cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CMD_REJ, 0);
1178*b06ebda0SMatthew Dillon 	if (cmd == NULL)
1179*b06ebda0SMatthew Dillon 		return (ENOMEM);
1180*b06ebda0SMatthew Dillon 
1181*b06ebda0SMatthew Dillon 	 _ng_l2cap_cmd_rej(cmd->aux, cmd->ident, reason, mtu, scid, dcid);
1182*b06ebda0SMatthew Dillon 	if (cmd->aux == NULL) {
1183*b06ebda0SMatthew Dillon 		ng_l2cap_free_cmd(cmd);
1184*b06ebda0SMatthew Dillon 
1185*b06ebda0SMatthew Dillon 		return (ENOBUFS);
1186*b06ebda0SMatthew Dillon 	}
1187*b06ebda0SMatthew Dillon 
1188*b06ebda0SMatthew Dillon 	/* Link command to the queue */
1189*b06ebda0SMatthew Dillon 	ng_l2cap_link_cmd(con, cmd);
1190*b06ebda0SMatthew Dillon 	ng_l2cap_lp_deliver(con);
1191*b06ebda0SMatthew Dillon 
1192*b06ebda0SMatthew Dillon 	return (0);
1193*b06ebda0SMatthew Dillon } /* send_l2cap_reject */
1194*b06ebda0SMatthew Dillon 
1195*b06ebda0SMatthew Dillon /*
1196*b06ebda0SMatthew Dillon  * Send L2CAP connection reject
1197*b06ebda0SMatthew Dillon  */
1198*b06ebda0SMatthew Dillon 
1199*b06ebda0SMatthew Dillon static int
1200*b06ebda0SMatthew Dillon send_l2cap_con_rej(ng_l2cap_con_p con, u_int8_t ident, u_int16_t scid,
1201*b06ebda0SMatthew Dillon 		u_int16_t dcid, u_int16_t result)
1202*b06ebda0SMatthew Dillon {
1203*b06ebda0SMatthew Dillon 	ng_l2cap_cmd_p	cmd = NULL;
1204*b06ebda0SMatthew Dillon 
1205*b06ebda0SMatthew Dillon 	cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CON_RSP, 0);
1206*b06ebda0SMatthew Dillon 	if (cmd == NULL)
1207*b06ebda0SMatthew Dillon 		return (ENOMEM);
1208*b06ebda0SMatthew Dillon 
1209*b06ebda0SMatthew Dillon 	_ng_l2cap_con_rsp(cmd->aux, cmd->ident, scid, dcid, result, 0);
1210*b06ebda0SMatthew Dillon 	if (cmd->aux == NULL) {
1211*b06ebda0SMatthew Dillon 		ng_l2cap_free_cmd(cmd);
1212*b06ebda0SMatthew Dillon 
1213*b06ebda0SMatthew Dillon 		return (ENOBUFS);
1214*b06ebda0SMatthew Dillon 	}
1215*b06ebda0SMatthew Dillon 
1216*b06ebda0SMatthew Dillon 	/* Link command to the queue */
1217*b06ebda0SMatthew Dillon 	ng_l2cap_link_cmd(con, cmd);
1218*b06ebda0SMatthew Dillon 	ng_l2cap_lp_deliver(con);
1219*b06ebda0SMatthew Dillon 
1220*b06ebda0SMatthew Dillon 	return (0);
1221*b06ebda0SMatthew Dillon } /* send_l2cap_con_rej */
1222*b06ebda0SMatthew Dillon 
1223*b06ebda0SMatthew Dillon /*
1224*b06ebda0SMatthew Dillon  * Send L2CAP config response
1225*b06ebda0SMatthew Dillon  */
1226*b06ebda0SMatthew Dillon 
1227*b06ebda0SMatthew Dillon static int
1228*b06ebda0SMatthew Dillon send_l2cap_cfg_rsp(ng_l2cap_con_p con, u_int8_t ident, u_int16_t scid,
1229*b06ebda0SMatthew Dillon 		u_int16_t result, struct mbuf *opt)
1230*b06ebda0SMatthew Dillon {
1231*b06ebda0SMatthew Dillon 	ng_l2cap_cmd_p	cmd = NULL;
1232*b06ebda0SMatthew Dillon 
1233*b06ebda0SMatthew Dillon 	cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CFG_RSP, 0);
1234*b06ebda0SMatthew Dillon 	if (cmd == NULL) {
1235*b06ebda0SMatthew Dillon 		NG_FREE_M(opt);
1236*b06ebda0SMatthew Dillon 
1237*b06ebda0SMatthew Dillon 		return (ENOMEM);
1238*b06ebda0SMatthew Dillon 	}
1239*b06ebda0SMatthew Dillon 
1240*b06ebda0SMatthew Dillon 	_ng_l2cap_cfg_rsp(cmd->aux, cmd->ident, scid, 0, result, opt);
1241*b06ebda0SMatthew Dillon 	if (cmd->aux == NULL) {
1242*b06ebda0SMatthew Dillon 		ng_l2cap_free_cmd(cmd);
1243*b06ebda0SMatthew Dillon 
1244*b06ebda0SMatthew Dillon 		return (ENOBUFS);
1245*b06ebda0SMatthew Dillon 	}
1246*b06ebda0SMatthew Dillon 
1247*b06ebda0SMatthew Dillon 	/* Link command to the queue */
1248*b06ebda0SMatthew Dillon 	ng_l2cap_link_cmd(con, cmd);
1249*b06ebda0SMatthew Dillon 	ng_l2cap_lp_deliver(con);
1250*b06ebda0SMatthew Dillon 
1251*b06ebda0SMatthew Dillon 	return (0);
1252*b06ebda0SMatthew Dillon } /* send_l2cap_cfg_rsp */
1253*b06ebda0SMatthew Dillon 
1254*b06ebda0SMatthew Dillon /*
1255*b06ebda0SMatthew Dillon  * Get next L2CAP configuration option
1256*b06ebda0SMatthew Dillon  *
1257*b06ebda0SMatthew Dillon  * Return codes:
1258*b06ebda0SMatthew Dillon  *  0   no option
1259*b06ebda0SMatthew Dillon  *  1   we have got option
1260*b06ebda0SMatthew Dillon  * -1   header too short
1261*b06ebda0SMatthew Dillon  * -2   bad option value or length
1262*b06ebda0SMatthew Dillon  * -3   unknown option
1263*b06ebda0SMatthew Dillon  */
1264*b06ebda0SMatthew Dillon 
1265*b06ebda0SMatthew Dillon static int
1266*b06ebda0SMatthew Dillon get_next_l2cap_opt(struct mbuf *m, int *off, ng_l2cap_cfg_opt_p hdr,
1267*b06ebda0SMatthew Dillon 		ng_l2cap_cfg_opt_val_p val)
1268*b06ebda0SMatthew Dillon {
1269*b06ebda0SMatthew Dillon 	int	hint, len = m->m_pkthdr.len - (*off);
1270*b06ebda0SMatthew Dillon 
1271*b06ebda0SMatthew Dillon 	if (len == 0)
1272*b06ebda0SMatthew Dillon 		return (0);
1273*b06ebda0SMatthew Dillon 	if (len < 0 || len < sizeof(*hdr))
1274*b06ebda0SMatthew Dillon 		return (-1);
1275*b06ebda0SMatthew Dillon 
1276*b06ebda0SMatthew Dillon 	m_copydata(m, *off, sizeof(*hdr), (caddr_t) hdr);
1277*b06ebda0SMatthew Dillon 	*off += sizeof(*hdr);
1278*b06ebda0SMatthew Dillon 	len  -= sizeof(*hdr);
1279*b06ebda0SMatthew Dillon 
1280*b06ebda0SMatthew Dillon 	hint = NG_L2CAP_OPT_HINT(hdr->type);
1281*b06ebda0SMatthew Dillon 	hdr->type &= NG_L2CAP_OPT_HINT_MASK;
1282*b06ebda0SMatthew Dillon 
1283*b06ebda0SMatthew Dillon 	switch (hdr->type) {
1284*b06ebda0SMatthew Dillon 	case NG_L2CAP_OPT_MTU:
1285*b06ebda0SMatthew Dillon 		if (hdr->length != NG_L2CAP_OPT_MTU_SIZE || len < hdr->length)
1286*b06ebda0SMatthew Dillon 			return (-2);
1287*b06ebda0SMatthew Dillon 
1288*b06ebda0SMatthew Dillon 		m_copydata(m, *off, NG_L2CAP_OPT_MTU_SIZE, (caddr_t) val);
1289*b06ebda0SMatthew Dillon 		val->mtu = le16toh(val->mtu);
1290*b06ebda0SMatthew Dillon 		*off += NG_L2CAP_OPT_MTU_SIZE;
1291*b06ebda0SMatthew Dillon 		break;
1292*b06ebda0SMatthew Dillon 
1293*b06ebda0SMatthew Dillon 	case NG_L2CAP_OPT_FLUSH_TIMO:
1294*b06ebda0SMatthew Dillon 		if (hdr->length != NG_L2CAP_OPT_FLUSH_TIMO_SIZE ||
1295*b06ebda0SMatthew Dillon 		    len < hdr->length)
1296*b06ebda0SMatthew Dillon 			return (-2);
1297*b06ebda0SMatthew Dillon 
1298*b06ebda0SMatthew Dillon 		m_copydata(m, *off, NG_L2CAP_OPT_FLUSH_TIMO_SIZE, (caddr_t)val);
1299*b06ebda0SMatthew Dillon 		val->flush_timo = le16toh(val->flush_timo);
1300*b06ebda0SMatthew Dillon 		*off += NG_L2CAP_OPT_FLUSH_TIMO_SIZE;
1301*b06ebda0SMatthew Dillon 		break;
1302*b06ebda0SMatthew Dillon 
1303*b06ebda0SMatthew Dillon 	case NG_L2CAP_OPT_QOS:
1304*b06ebda0SMatthew Dillon 		if (hdr->length != NG_L2CAP_OPT_QOS_SIZE || len < hdr->length)
1305*b06ebda0SMatthew Dillon 			return (-2);
1306*b06ebda0SMatthew Dillon 
1307*b06ebda0SMatthew Dillon 		m_copydata(m, *off, NG_L2CAP_OPT_QOS_SIZE, (caddr_t) val);
1308*b06ebda0SMatthew Dillon 		val->flow.token_rate = le32toh(val->flow.token_rate);
1309*b06ebda0SMatthew Dillon 		val->flow.token_bucket_size =
1310*b06ebda0SMatthew Dillon 				le32toh(val->flow.token_bucket_size);
1311*b06ebda0SMatthew Dillon 		val->flow.peak_bandwidth = le32toh(val->flow.peak_bandwidth);
1312*b06ebda0SMatthew Dillon 		val->flow.latency = le32toh(val->flow.latency);
1313*b06ebda0SMatthew Dillon 		val->flow.delay_variation = le32toh(val->flow.delay_variation);
1314*b06ebda0SMatthew Dillon 		*off += NG_L2CAP_OPT_QOS_SIZE;
1315*b06ebda0SMatthew Dillon 		break;
1316*b06ebda0SMatthew Dillon 
1317*b06ebda0SMatthew Dillon 	default:
1318*b06ebda0SMatthew Dillon 		if (hint)
1319*b06ebda0SMatthew Dillon 			*off += hdr->length;
1320*b06ebda0SMatthew Dillon 		else
1321*b06ebda0SMatthew Dillon 			return (-3);
1322*b06ebda0SMatthew Dillon 		break;
1323*b06ebda0SMatthew Dillon 	}
1324*b06ebda0SMatthew Dillon 
1325*b06ebda0SMatthew Dillon 	return (1);
1326*b06ebda0SMatthew Dillon } /* get_next_l2cap_opt */
1327*b06ebda0SMatthew Dillon 
1328