xref: /freebsd/sys/netgraph/bluetooth/l2cap/ng_l2cap_evnt.c (revision c398230b64aea809cb7c5cea8db580af7097920c)
1878ed226SJulian Elischer /*
2878ed226SJulian Elischer  * ng_l2cap_evnt.c
3c398230bSWarner Losh  */
4c398230bSWarner Losh 
5c398230bSWarner Losh /*-
6878ed226SJulian Elischer  * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
7878ed226SJulian Elischer  * All rights reserved.
8878ed226SJulian Elischer  *
9878ed226SJulian Elischer  * Redistribution and use in source and binary forms, with or without
10878ed226SJulian Elischer  * modification, are permitted provided that the following conditions
11878ed226SJulian Elischer  * are met:
12878ed226SJulian Elischer  * 1. Redistributions of source code must retain the above copyright
13878ed226SJulian Elischer  *    notice, this list of conditions and the following disclaimer.
14878ed226SJulian Elischer  * 2. Redistributions in binary form must reproduce the above copyright
15878ed226SJulian Elischer  *    notice, this list of conditions and the following disclaimer in the
16878ed226SJulian Elischer  *    documentation and/or other materials provided with the distribution.
17878ed226SJulian Elischer  *
18878ed226SJulian Elischer  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19878ed226SJulian Elischer  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20878ed226SJulian Elischer  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21878ed226SJulian Elischer  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22878ed226SJulian Elischer  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23878ed226SJulian Elischer  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24878ed226SJulian Elischer  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25878ed226SJulian Elischer  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26878ed226SJulian Elischer  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27878ed226SJulian Elischer  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28878ed226SJulian Elischer  * SUCH DAMAGE.
29878ed226SJulian Elischer  *
300986ab12SMaksim Yevmenkin  * $Id: ng_l2cap_evnt.c,v 1.5 2003/09/08 19:11:45 max Exp $
31878ed226SJulian Elischer  * $FreeBSD$
32878ed226SJulian Elischer  */
33878ed226SJulian Elischer 
34878ed226SJulian Elischer #include <sys/param.h>
35878ed226SJulian Elischer #include <sys/systm.h>
36878ed226SJulian Elischer #include <sys/kernel.h>
37878ed226SJulian Elischer #include <sys/endian.h>
38878ed226SJulian Elischer #include <sys/malloc.h>
39878ed226SJulian Elischer #include <sys/mbuf.h>
40878ed226SJulian Elischer #include <sys/queue.h>
41878ed226SJulian Elischer #include <netgraph/ng_message.h>
42878ed226SJulian Elischer #include <netgraph/netgraph.h>
43b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_bluetooth.h>
44b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_hci.h>
45b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_l2cap.h>
46b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_var.h>
47b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_cmds.h>
48b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_evnt.h>
49b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_llpi.h>
50b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h>
51b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_misc.h>
52878ed226SJulian Elischer 
53878ed226SJulian Elischer /******************************************************************************
54878ed226SJulian Elischer  ******************************************************************************
55878ed226SJulian Elischer  **                    L2CAP events processing module
56878ed226SJulian Elischer  ******************************************************************************
57878ed226SJulian Elischer  ******************************************************************************/
58878ed226SJulian Elischer 
59878ed226SJulian Elischer static int ng_l2cap_process_signal_cmd (ng_l2cap_con_p);
60878ed226SJulian Elischer static int ng_l2cap_process_cmd_rej    (ng_l2cap_con_p, u_int8_t);
61878ed226SJulian Elischer static int ng_l2cap_process_con_req    (ng_l2cap_con_p, u_int8_t);
62878ed226SJulian Elischer static int ng_l2cap_process_con_rsp    (ng_l2cap_con_p, u_int8_t);
63878ed226SJulian Elischer static int ng_l2cap_process_cfg_req    (ng_l2cap_con_p, u_int8_t);
64878ed226SJulian Elischer static int ng_l2cap_process_cfg_rsp    (ng_l2cap_con_p, u_int8_t);
65878ed226SJulian Elischer static int ng_l2cap_process_discon_req (ng_l2cap_con_p, u_int8_t);
66878ed226SJulian Elischer static int ng_l2cap_process_discon_rsp (ng_l2cap_con_p, u_int8_t);
67878ed226SJulian Elischer static int ng_l2cap_process_echo_req   (ng_l2cap_con_p, u_int8_t);
68878ed226SJulian Elischer static int ng_l2cap_process_echo_rsp   (ng_l2cap_con_p, u_int8_t);
69878ed226SJulian Elischer static int ng_l2cap_process_info_req   (ng_l2cap_con_p, u_int8_t);
70878ed226SJulian Elischer static int ng_l2cap_process_info_rsp   (ng_l2cap_con_p, u_int8_t);
71878ed226SJulian Elischer static int send_l2cap_reject
72878ed226SJulian Elischer 	(ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, u_int16_t, u_int16_t);
73878ed226SJulian Elischer static int send_l2cap_con_rej
74878ed226SJulian Elischer 	(ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, u_int16_t);
75878ed226SJulian Elischer static int send_l2cap_cfg_rsp
76878ed226SJulian Elischer 	(ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, struct mbuf *);
77878ed226SJulian Elischer static int get_next_l2cap_opt
78878ed226SJulian Elischer 	(struct mbuf *, int *, ng_l2cap_cfg_opt_p, ng_l2cap_cfg_opt_val_p);
79878ed226SJulian Elischer 
80878ed226SJulian Elischer /*
81878ed226SJulian Elischer  * Receive L2CAP packet. First get L2CAP header and verify packet. Than
82878ed226SJulian Elischer  * get destination channel and process packet.
83878ed226SJulian Elischer  */
84878ed226SJulian Elischer 
85878ed226SJulian Elischer int
86878ed226SJulian Elischer ng_l2cap_receive(ng_l2cap_con_p con)
87878ed226SJulian Elischer {
88878ed226SJulian Elischer 	ng_l2cap_p	 l2cap = con->l2cap;
89878ed226SJulian Elischer 	ng_l2cap_hdr_t	*hdr = NULL;
90878ed226SJulian Elischer 	int		 error = 0;
91878ed226SJulian Elischer 
92878ed226SJulian Elischer 	/* Check packet */
93878ed226SJulian Elischer 	if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) {
94878ed226SJulian Elischer 		NG_L2CAP_ERR(
95878ed226SJulian Elischer "%s: %s - invalid L2CAP packet. Packet too small, len=%d\n",
96878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node),
97878ed226SJulian Elischer 			con->rx_pkt->m_pkthdr.len);
98878ed226SJulian Elischer 		error = EMSGSIZE;
99878ed226SJulian Elischer 		goto drop;
100878ed226SJulian Elischer 	}
101878ed226SJulian Elischer 
102878ed226SJulian Elischer 	/* Get L2CAP header */
103878ed226SJulian Elischer 	NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));
104878ed226SJulian Elischer 	if (con->rx_pkt == NULL)
105878ed226SJulian Elischer 		return (ENOBUFS);
106878ed226SJulian Elischer 
107878ed226SJulian Elischer 	hdr = mtod(con->rx_pkt, ng_l2cap_hdr_t *);
108878ed226SJulian Elischer 	hdr->length = le16toh(hdr->length);
109878ed226SJulian Elischer 	hdr->dcid = le16toh(hdr->dcid);
110878ed226SJulian Elischer 
111878ed226SJulian Elischer 	/* Check payload size */
112878ed226SJulian Elischer 	if (hdr->length != con->rx_pkt->m_pkthdr.len - sizeof(*hdr)) {
113878ed226SJulian Elischer 		NG_L2CAP_ERR(
1144ae439a3SMaksim Yevmenkin "%s: %s - invalid L2CAP packet. Payload length mismatch, length=%d, len=%zd\n",
115878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), hdr->length,
116878ed226SJulian Elischer 			con->rx_pkt->m_pkthdr.len - sizeof(*hdr));
117878ed226SJulian Elischer 		error = EMSGSIZE;
118878ed226SJulian Elischer 		goto drop;
119878ed226SJulian Elischer 	}
120878ed226SJulian Elischer 
121878ed226SJulian Elischer 	/* Process packet */
122878ed226SJulian Elischer 	switch (hdr->dcid) {
123878ed226SJulian Elischer 	case NG_L2CAP_SIGNAL_CID: /* L2CAP command */
124878ed226SJulian Elischer 		m_adj(con->rx_pkt, sizeof(*hdr));
125878ed226SJulian Elischer 		error = ng_l2cap_process_signal_cmd(con);
126878ed226SJulian Elischer 		break;
127878ed226SJulian Elischer 
128878ed226SJulian Elischer 	case NG_L2CAP_CLT_CID: /* Connectionless packet */
129878ed226SJulian Elischer 		error = ng_l2cap_l2ca_clt_receive(con);
130878ed226SJulian Elischer 		break;
131878ed226SJulian Elischer 
132878ed226SJulian Elischer 	default: /* Data packet */
133878ed226SJulian Elischer 		error = ng_l2cap_l2ca_receive(con);
134878ed226SJulian Elischer 		break;
135878ed226SJulian Elischer 	}
136878ed226SJulian Elischer 
137878ed226SJulian Elischer 	return (error);
138878ed226SJulian Elischer drop:
139878ed226SJulian Elischer 	NG_FREE_M(con->rx_pkt);
140878ed226SJulian Elischer 
141878ed226SJulian Elischer 	return (error);
142878ed226SJulian Elischer } /* ng_l2cap_receive */
143878ed226SJulian Elischer 
144878ed226SJulian Elischer /*
145878ed226SJulian Elischer  * Process L2CAP signaling command. We already know that destination channel ID
146878ed226SJulian Elischer  * is 0x1 that means we have received signaling command from peer's L2CAP layer.
147878ed226SJulian Elischer  * So get command header, decode and process it.
148878ed226SJulian Elischer  *
149878ed226SJulian Elischer  * XXX do we need to check signaling MTU here?
150878ed226SJulian Elischer  */
151878ed226SJulian Elischer 
152878ed226SJulian Elischer static int
153878ed226SJulian Elischer ng_l2cap_process_signal_cmd(ng_l2cap_con_p con)
154878ed226SJulian Elischer {
155878ed226SJulian Elischer 	ng_l2cap_p		 l2cap = con->l2cap;
156878ed226SJulian Elischer 	ng_l2cap_cmd_hdr_t	*hdr = NULL;
157878ed226SJulian Elischer 	struct mbuf		*m = NULL;
158878ed226SJulian Elischer 
159878ed226SJulian Elischer 	while (con->rx_pkt != NULL) {
160878ed226SJulian Elischer 		/* Verify packet length */
161878ed226SJulian Elischer 		if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) {
162878ed226SJulian Elischer 			NG_L2CAP_ERR(
163878ed226SJulian Elischer "%s: %s - invalid L2CAP signaling command. Packet too small, len=%d\n",
164878ed226SJulian Elischer 				__func__, NG_NODE_NAME(l2cap->node),
165878ed226SJulian Elischer 				con->rx_pkt->m_pkthdr.len);
166878ed226SJulian Elischer 			NG_FREE_M(con->rx_pkt);
167878ed226SJulian Elischer 
168878ed226SJulian Elischer 			return (EMSGSIZE);
169878ed226SJulian Elischer 		}
170878ed226SJulian Elischer 
171878ed226SJulian Elischer 		/* Get signaling command */
172878ed226SJulian Elischer 		NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));
173878ed226SJulian Elischer 		if (con->rx_pkt == NULL)
174878ed226SJulian Elischer 			return (ENOBUFS);
175878ed226SJulian Elischer 
176878ed226SJulian Elischer 		hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *);
177878ed226SJulian Elischer 		hdr->length = le16toh(hdr->length);
178878ed226SJulian Elischer 		m_adj(con->rx_pkt, sizeof(*hdr));
179878ed226SJulian Elischer 
180878ed226SJulian Elischer 		/* Verify command length */
181878ed226SJulian Elischer 		if (con->rx_pkt->m_pkthdr.len < hdr->length) {
182878ed226SJulian Elischer 			NG_L2CAP_ERR(
183878ed226SJulian Elischer "%s: %s - invalid L2CAP signaling command, code=%#x, ident=%d. " \
184878ed226SJulian Elischer "Invalid command length=%d, m_pkthdr.len=%d\n",
185878ed226SJulian Elischer 				__func__, NG_NODE_NAME(l2cap->node),
186878ed226SJulian Elischer 				hdr->code, hdr->ident, hdr->length,
187878ed226SJulian Elischer 				con->rx_pkt->m_pkthdr.len);
188878ed226SJulian Elischer 			NG_FREE_M(con->rx_pkt);
189878ed226SJulian Elischer 
190878ed226SJulian Elischer 			return (EMSGSIZE);
191878ed226SJulian Elischer 		}
192878ed226SJulian Elischer 
193878ed226SJulian Elischer 		/* Get the command, save the rest (if any) */
194878ed226SJulian Elischer 		if (con->rx_pkt->m_pkthdr.len > hdr->length)
195a163d034SWarner Losh 			m = m_split(con->rx_pkt, hdr->length, M_DONTWAIT);
196878ed226SJulian Elischer 		else
197878ed226SJulian Elischer 			m = NULL;
198878ed226SJulian Elischer 
199878ed226SJulian Elischer 		/* Process command */
200878ed226SJulian Elischer 		switch (hdr->code) {
201878ed226SJulian Elischer 		case NG_L2CAP_CMD_REJ:
202878ed226SJulian Elischer 			ng_l2cap_process_cmd_rej(con, hdr->ident);
203878ed226SJulian Elischer 			break;
204878ed226SJulian Elischer 
205878ed226SJulian Elischer 		case NG_L2CAP_CON_REQ:
206878ed226SJulian Elischer 			ng_l2cap_process_con_req(con, hdr->ident);
207878ed226SJulian Elischer 			break;
208878ed226SJulian Elischer 
209878ed226SJulian Elischer 		case NG_L2CAP_CON_RSP:
210878ed226SJulian Elischer 			ng_l2cap_process_con_rsp(con, hdr->ident);
211878ed226SJulian Elischer 			break;
212878ed226SJulian Elischer 
213878ed226SJulian Elischer 		case NG_L2CAP_CFG_REQ:
214878ed226SJulian Elischer 			ng_l2cap_process_cfg_req(con, hdr->ident);
215878ed226SJulian Elischer 			break;
216878ed226SJulian Elischer 
217878ed226SJulian Elischer 		case NG_L2CAP_CFG_RSP:
218878ed226SJulian Elischer 			ng_l2cap_process_cfg_rsp(con, hdr->ident);
219878ed226SJulian Elischer 			break;
220878ed226SJulian Elischer 
221878ed226SJulian Elischer 		case NG_L2CAP_DISCON_REQ:
222878ed226SJulian Elischer 			ng_l2cap_process_discon_req(con, hdr->ident);
223878ed226SJulian Elischer 			break;
224878ed226SJulian Elischer 
225878ed226SJulian Elischer 		case NG_L2CAP_DISCON_RSP:
226878ed226SJulian Elischer 			ng_l2cap_process_discon_rsp(con, hdr->ident);
227878ed226SJulian Elischer 			break;
228878ed226SJulian Elischer 
229878ed226SJulian Elischer 		case NG_L2CAP_ECHO_REQ:
230878ed226SJulian Elischer 			ng_l2cap_process_echo_req(con, hdr->ident);
231878ed226SJulian Elischer 			break;
232878ed226SJulian Elischer 
233878ed226SJulian Elischer 		case NG_L2CAP_ECHO_RSP:
234878ed226SJulian Elischer 			ng_l2cap_process_echo_rsp(con, hdr->ident);
235878ed226SJulian Elischer 			break;
236878ed226SJulian Elischer 
237878ed226SJulian Elischer 		case NG_L2CAP_INFO_REQ:
238878ed226SJulian Elischer 			ng_l2cap_process_info_req(con, hdr->ident);
239878ed226SJulian Elischer 			break;
240878ed226SJulian Elischer 
241878ed226SJulian Elischer 		case NG_L2CAP_INFO_RSP:
242878ed226SJulian Elischer 			ng_l2cap_process_info_rsp(con, hdr->ident);
243878ed226SJulian Elischer 			break;
244878ed226SJulian Elischer 
245878ed226SJulian Elischer 		default:
246878ed226SJulian Elischer 			NG_L2CAP_ERR(
247878ed226SJulian Elischer "%s: %s - unknown L2CAP signaling command, code=%#x, ident=%d, length=%d\n",
248878ed226SJulian Elischer 				__func__, NG_NODE_NAME(l2cap->node),
249878ed226SJulian Elischer 				hdr->code, hdr->ident, hdr->length);
250878ed226SJulian Elischer 
251878ed226SJulian Elischer 			/*
252878ed226SJulian Elischer 			 * Send L2CAP_CommandRej. Do not really care
253878ed226SJulian Elischer 			 * about the result
254878ed226SJulian Elischer 			 */
255878ed226SJulian Elischer 
256878ed226SJulian Elischer 			send_l2cap_reject(con, hdr->ident,
257878ed226SJulian Elischer 				NG_L2CAP_REJ_NOT_UNDERSTOOD, 0, 0, 0);
258878ed226SJulian Elischer 			NG_FREE_M(con->rx_pkt);
259878ed226SJulian Elischer 			break;
260878ed226SJulian Elischer 		}
261878ed226SJulian Elischer 
262878ed226SJulian Elischer 		con->rx_pkt = m;
263878ed226SJulian Elischer 	}
264878ed226SJulian Elischer 
265878ed226SJulian Elischer 	return (0);
266878ed226SJulian Elischer } /* ng_l2cap_process_signal_cmd */
267878ed226SJulian Elischer 
268878ed226SJulian Elischer /*
269878ed226SJulian Elischer  * Process L2CAP_CommandRej command
270878ed226SJulian Elischer  */
271878ed226SJulian Elischer 
272878ed226SJulian Elischer static int
273878ed226SJulian Elischer ng_l2cap_process_cmd_rej(ng_l2cap_con_p con, u_int8_t ident)
274878ed226SJulian Elischer {
275878ed226SJulian Elischer 	ng_l2cap_p		 l2cap = con->l2cap;
276878ed226SJulian Elischer 	ng_l2cap_cmd_rej_cp	*cp = NULL;
277878ed226SJulian Elischer 	ng_l2cap_cmd_p		 cmd = NULL;
278878ed226SJulian Elischer 
279878ed226SJulian Elischer 	/* Get command parameters */
280878ed226SJulian Elischer 	NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));
281878ed226SJulian Elischer 	if (con->rx_pkt == NULL)
282878ed226SJulian Elischer 		return (ENOBUFS);
283878ed226SJulian Elischer 
284878ed226SJulian Elischer 	cp = mtod(con->rx_pkt, ng_l2cap_cmd_rej_cp *);
285878ed226SJulian Elischer 	cp->reason = le16toh(cp->reason);
286878ed226SJulian Elischer 
287878ed226SJulian Elischer 	/* Check if we have pending command descriptor */
288878ed226SJulian Elischer 	cmd = ng_l2cap_cmd_by_ident(con, ident);
289878ed226SJulian Elischer 	if (cmd != NULL) {
2900986ab12SMaksim Yevmenkin 		/* If command timeout already happened then ignore reject */
2910986ab12SMaksim Yevmenkin 		if (ng_l2cap_command_untimeout(cmd) != 0) {
2920986ab12SMaksim Yevmenkin 			NG_FREE_M(con->rx_pkt);
2930986ab12SMaksim Yevmenkin 			return (ETIMEDOUT);
2940986ab12SMaksim Yevmenkin 		}
295878ed226SJulian Elischer 
296878ed226SJulian Elischer 		ng_l2cap_unlink_cmd(cmd);
297878ed226SJulian Elischer 
298878ed226SJulian Elischer 		switch (cmd->code) {
299878ed226SJulian Elischer 		case NG_L2CAP_CON_REQ:
300878ed226SJulian Elischer 			ng_l2cap_l2ca_con_rsp(cmd->ch,cmd->token,cp->reason,0);
301878ed226SJulian Elischer 			ng_l2cap_free_chan(cmd->ch);
302878ed226SJulian Elischer 			break;
303878ed226SJulian Elischer 
304878ed226SJulian Elischer 		case NG_L2CAP_CFG_REQ:
305878ed226SJulian Elischer 			ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, cp->reason);
306878ed226SJulian Elischer 			break;
307878ed226SJulian Elischer 
308878ed226SJulian Elischer 		case NG_L2CAP_DISCON_REQ:
309878ed226SJulian Elischer 			ng_l2cap_l2ca_discon_rsp(cmd->ch,cmd->token,cp->reason);
310878ed226SJulian Elischer 			ng_l2cap_free_chan(cmd->ch); /* XXX free channel */
311878ed226SJulian Elischer 			break;
312878ed226SJulian Elischer 
313878ed226SJulian Elischer 		case NG_L2CAP_ECHO_REQ:
314878ed226SJulian Elischer 			ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
315878ed226SJulian Elischer 				cp->reason, NULL);
316878ed226SJulian Elischer 			break;
317878ed226SJulian Elischer 
318878ed226SJulian Elischer 		case NG_L2CAP_INFO_REQ:
319878ed226SJulian Elischer 			ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
320878ed226SJulian Elischer 				cp->reason, NULL);
321878ed226SJulian Elischer 			break;
322878ed226SJulian Elischer 
323878ed226SJulian Elischer 		default:
324878ed226SJulian Elischer 			NG_L2CAP_ALERT(
325878ed226SJulian Elischer "%s: %s - unexpected L2CAP_CommandRej. Unexpected L2CAP command opcode=%d\n",
326878ed226SJulian Elischer 				__func__, NG_NODE_NAME(l2cap->node), cmd->code);
327878ed226SJulian Elischer 			break;
328878ed226SJulian Elischer 		}
329878ed226SJulian Elischer 
330878ed226SJulian Elischer 		ng_l2cap_free_cmd(cmd);
331878ed226SJulian Elischer 	} else
332878ed226SJulian Elischer 		NG_L2CAP_ERR(
333878ed226SJulian Elischer "%s: %s - unexpected L2CAP_CommandRej command. " \
334878ed226SJulian Elischer "Requested ident does not exist, ident=%d\n",
335878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), ident);
336878ed226SJulian Elischer 
337878ed226SJulian Elischer 	NG_FREE_M(con->rx_pkt);
338878ed226SJulian Elischer 
339878ed226SJulian Elischer 	return (0);
340878ed226SJulian Elischer } /* ng_l2cap_process_cmd_rej */
341878ed226SJulian Elischer 
342878ed226SJulian Elischer /*
343878ed226SJulian Elischer  * Process L2CAP_ConnectReq command
344878ed226SJulian Elischer  */
345878ed226SJulian Elischer 
346878ed226SJulian Elischer static int
347878ed226SJulian Elischer ng_l2cap_process_con_req(ng_l2cap_con_p con, u_int8_t ident)
348878ed226SJulian Elischer {
349878ed226SJulian Elischer 	ng_l2cap_p		 l2cap = con->l2cap;
350878ed226SJulian Elischer 	struct mbuf		*m = con->rx_pkt;
351878ed226SJulian Elischer 	ng_l2cap_con_req_cp	*cp = NULL;
352878ed226SJulian Elischer 	ng_l2cap_chan_p		 ch = NULL;
353878ed226SJulian Elischer 	int			 error = 0;
354878ed226SJulian Elischer 	u_int16_t		 dcid, psm;
355878ed226SJulian Elischer 
356878ed226SJulian Elischer 	/* Get command parameters */
357878ed226SJulian Elischer 	NG_L2CAP_M_PULLUP(m, sizeof(*cp));
358878ed226SJulian Elischer 	if (m == NULL)
359878ed226SJulian Elischer 		return (ENOBUFS);
360878ed226SJulian Elischer 
361878ed226SJulian Elischer 	cp = mtod(m, ng_l2cap_con_req_cp *);
362878ed226SJulian Elischer 	psm = le16toh(cp->psm);
363878ed226SJulian Elischer 	dcid = le16toh(cp->scid);
364878ed226SJulian Elischer 
365878ed226SJulian Elischer 	NG_FREE_M(m);
366878ed226SJulian Elischer 	con->rx_pkt = NULL;
367878ed226SJulian Elischer 
368878ed226SJulian Elischer 	/*
369878ed226SJulian Elischer 	 * Create new channel and send L2CA_ConnectInd notification
370878ed226SJulian Elischer 	 * to the upper layer protocol.
371878ed226SJulian Elischer 	 */
372878ed226SJulian Elischer 
373878ed226SJulian Elischer 	ch = ng_l2cap_new_chan(l2cap, con, psm);
374878ed226SJulian Elischer 	if (ch == NULL)
375878ed226SJulian Elischer 		return (send_l2cap_con_rej(con, ident, 0, dcid,
376878ed226SJulian Elischer 				NG_L2CAP_NO_RESOURCES));
377878ed226SJulian Elischer 
378878ed226SJulian Elischer 	/* Update channel IDs */
379878ed226SJulian Elischer 	ch->dcid = dcid;
380878ed226SJulian Elischer 
381878ed226SJulian Elischer 	/* Sent L2CA_ConnectInd notification to the upper layer */
382878ed226SJulian Elischer 	ch->ident = ident;
383878ed226SJulian Elischer 	ch->state = NG_L2CAP_W4_L2CA_CON_RSP;
384878ed226SJulian Elischer 
385878ed226SJulian Elischer 	error = ng_l2cap_l2ca_con_ind(ch);
386878ed226SJulian Elischer 	if (error != 0) {
387878ed226SJulian Elischer 		send_l2cap_con_rej(con, ident, ch->scid, dcid,
388878ed226SJulian Elischer 			(error == ENOMEM)? NG_L2CAP_NO_RESOURCES :
389878ed226SJulian Elischer 				NG_L2CAP_PSM_NOT_SUPPORTED);
390878ed226SJulian Elischer 		ng_l2cap_free_chan(ch);
391878ed226SJulian Elischer 	}
392878ed226SJulian Elischer 
393878ed226SJulian Elischer 	return (error);
394878ed226SJulian Elischer } /* ng_l2cap_process_con_req */
395878ed226SJulian Elischer 
396878ed226SJulian Elischer /*
397878ed226SJulian Elischer  * Process L2CAP_ConnectRsp command
398878ed226SJulian Elischer  */
399878ed226SJulian Elischer 
400878ed226SJulian Elischer static int
401878ed226SJulian Elischer ng_l2cap_process_con_rsp(ng_l2cap_con_p con, u_int8_t ident)
402878ed226SJulian Elischer {
403878ed226SJulian Elischer 	ng_l2cap_p		 l2cap = con->l2cap;
404878ed226SJulian Elischer 	struct mbuf		*m = con->rx_pkt;
405878ed226SJulian Elischer 	ng_l2cap_con_rsp_cp	*cp = NULL;
406878ed226SJulian Elischer 	ng_l2cap_cmd_p		 cmd = NULL;
407878ed226SJulian Elischer 	u_int16_t		 scid, dcid, result, status;
408878ed226SJulian Elischer 	int			 error = 0;
409878ed226SJulian Elischer 
410878ed226SJulian Elischer 	/* Get command parameters */
411878ed226SJulian Elischer 	NG_L2CAP_M_PULLUP(m, sizeof(*cp));
412878ed226SJulian Elischer 	if (m == NULL)
413878ed226SJulian Elischer 		return (ENOBUFS);
414878ed226SJulian Elischer 
415878ed226SJulian Elischer 	cp = mtod(m, ng_l2cap_con_rsp_cp *);
416878ed226SJulian Elischer 	dcid = le16toh(cp->dcid);
417878ed226SJulian Elischer 	scid = le16toh(cp->scid);
418878ed226SJulian Elischer 	result = le16toh(cp->result);
419878ed226SJulian Elischer 	status = le16toh(cp->status);
420878ed226SJulian Elischer 
421878ed226SJulian Elischer 	NG_FREE_M(m);
422878ed226SJulian Elischer 	con->rx_pkt = NULL;
423878ed226SJulian Elischer 
424878ed226SJulian Elischer 	/* Check if we have pending command descriptor */
425878ed226SJulian Elischer 	cmd = ng_l2cap_cmd_by_ident(con, ident);
426878ed226SJulian Elischer 	if (cmd == NULL) {
427878ed226SJulian Elischer 		NG_L2CAP_ERR(
428878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConnectRsp command. ident=%d, con_handle=%d\n",
429878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), ident,
430878ed226SJulian Elischer 			con->con_handle);
431878ed226SJulian Elischer 
432878ed226SJulian Elischer 		return (ENOENT);
433878ed226SJulian Elischer 	}
434878ed226SJulian Elischer 
435878ed226SJulian Elischer 	/* Verify channel state, if invalid - do nothing */
436878ed226SJulian Elischer 	if (cmd->ch->state != NG_L2CAP_W4_L2CAP_CON_RSP) {
437878ed226SJulian Elischer 		NG_L2CAP_ERR(
438878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConnectRsp. " \
439878ed226SJulian Elischer "Invalid channel state, cid=%d, state=%d\n",
440878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), scid,
441878ed226SJulian Elischer 			cmd->ch->state);
442878ed226SJulian Elischer 		goto reject;
443878ed226SJulian Elischer 	}
444878ed226SJulian Elischer 
445878ed226SJulian Elischer 	/* Verify CIDs and send reject if does not match */
446878ed226SJulian Elischer 	if (cmd->ch->scid != scid) {
447878ed226SJulian Elischer 		NG_L2CAP_ERR(
448878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConnectRsp. Channel IDs do not match, scid=%d(%d)\n",
449878ed226SJulian Elischer 			 __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,
450878ed226SJulian Elischer 			scid);
451878ed226SJulian Elischer 		goto reject;
452878ed226SJulian Elischer 	}
453878ed226SJulian Elischer 
454878ed226SJulian Elischer 	/*
455878ed226SJulian Elischer 	 * Looks good. We got confirmation from our peer. Now process
456878ed226SJulian Elischer 	 * it. First disable RTX timer. Then check the result and send
4570986ab12SMaksim Yevmenkin 	 * notification to the upper layer. If command timeout already
4580986ab12SMaksim Yevmenkin 	 * happened then ignore response.
459878ed226SJulian Elischer 	 */
460878ed226SJulian Elischer 
4610986ab12SMaksim Yevmenkin 	if ((error = ng_l2cap_command_untimeout(cmd)) != 0)
4620986ab12SMaksim Yevmenkin 		return (error);
463878ed226SJulian Elischer 
464878ed226SJulian Elischer 	if (result == NG_L2CAP_PENDING) {
465878ed226SJulian Elischer 		/*
466878ed226SJulian Elischer 		 * Our peer wants more time to complete connection. We shall
467878ed226SJulian Elischer 		 * start ERTX timer and wait. Keep command in the list.
468878ed226SJulian Elischer 		 */
469878ed226SJulian Elischer 
470878ed226SJulian Elischer 		cmd->ch->dcid = dcid;
471878ed226SJulian Elischer 		ng_l2cap_command_timeout(cmd, bluetooth_l2cap_ertx_timeout());
472878ed226SJulian Elischer 
473878ed226SJulian Elischer 		error = ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token,
474878ed226SJulian Elischer 				result, status);
475878ed226SJulian Elischer 		if (error != 0)
476878ed226SJulian Elischer 			ng_l2cap_free_chan(cmd->ch);
477878ed226SJulian Elischer 	} else {
478878ed226SJulian Elischer 		ng_l2cap_unlink_cmd(cmd);
479878ed226SJulian Elischer 
480878ed226SJulian Elischer 		if (result == NG_L2CAP_SUCCESS) {
481878ed226SJulian Elischer 			/*
482878ed226SJulian Elischer 			 * Channel is open. Complete command and move to CONFIG
483878ed226SJulian Elischer 			 * state. Since we have sent positive confirmation we
484878ed226SJulian Elischer 			 * expect to receive L2CA_Config request from the upper
485878ed226SJulian Elischer 			 * layer protocol.
486878ed226SJulian Elischer 			 */
487878ed226SJulian Elischer 
488878ed226SJulian Elischer 			cmd->ch->dcid = dcid;
489878ed226SJulian Elischer 			cmd->ch->state = NG_L2CAP_CONFIG;
490878ed226SJulian Elischer 		} else
491878ed226SJulian Elischer 			/* There was an error, so close the channel */
492878ed226SJulian Elischer 			NG_L2CAP_INFO(
493878ed226SJulian Elischer "%s: %s - failed to open L2CAP channel, result=%d, status=%d\n",
494878ed226SJulian Elischer 				__func__, NG_NODE_NAME(l2cap->node), result,
495878ed226SJulian Elischer 				status);
496878ed226SJulian Elischer 
497878ed226SJulian Elischer 		error = ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token,
498878ed226SJulian Elischer 				result, status);
499878ed226SJulian Elischer 
500878ed226SJulian Elischer 		/* XXX do we have to remove the channel on error? */
501878ed226SJulian Elischer 		if (error != 0 || result != NG_L2CAP_SUCCESS)
502878ed226SJulian Elischer 			ng_l2cap_free_chan(cmd->ch);
503878ed226SJulian Elischer 
504878ed226SJulian Elischer 		ng_l2cap_free_cmd(cmd);
505878ed226SJulian Elischer 	}
506878ed226SJulian Elischer 
507878ed226SJulian Elischer 	return (error);
508878ed226SJulian Elischer 
509878ed226SJulian Elischer reject:
510878ed226SJulian Elischer 	/* Send reject. Do not really care about the result */
511878ed226SJulian Elischer 	send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, dcid);
512878ed226SJulian Elischer 
513878ed226SJulian Elischer 	return (0);
514878ed226SJulian Elischer } /* ng_l2cap_process_con_rsp */
515878ed226SJulian Elischer 
516878ed226SJulian Elischer /*
517878ed226SJulian Elischer  * Process L2CAP_ConfigReq command
518878ed226SJulian Elischer  */
519878ed226SJulian Elischer 
520878ed226SJulian Elischer static int
521878ed226SJulian Elischer ng_l2cap_process_cfg_req(ng_l2cap_con_p con, u_int8_t ident)
522878ed226SJulian Elischer {
523878ed226SJulian Elischer 	ng_l2cap_p		 l2cap = con->l2cap;
524878ed226SJulian Elischer 	struct mbuf		*m = con->rx_pkt;
525878ed226SJulian Elischer 	ng_l2cap_cfg_req_cp	*cp = NULL;
526878ed226SJulian Elischer 	ng_l2cap_chan_p		 ch = NULL;
527878ed226SJulian Elischer 	u_int16_t		 dcid, respond, result;
528878ed226SJulian Elischer 	ng_l2cap_cfg_opt_t	 hdr;
529878ed226SJulian Elischer 	ng_l2cap_cfg_opt_val_t	 val;
530878ed226SJulian Elischer 	int			 off, error = 0;
531878ed226SJulian Elischer 
532878ed226SJulian Elischer 	/* Get command parameters */
533878ed226SJulian Elischer 	con->rx_pkt = NULL;
534878ed226SJulian Elischer 	NG_L2CAP_M_PULLUP(m, sizeof(*cp));
535878ed226SJulian Elischer 	if (m == NULL)
536878ed226SJulian Elischer 		return (ENOBUFS);
537878ed226SJulian Elischer 
538878ed226SJulian Elischer 	cp = mtod(m, ng_l2cap_cfg_req_cp *);
539878ed226SJulian Elischer 	dcid = le16toh(cp->dcid);
540878ed226SJulian Elischer 	respond = NG_L2CAP_OPT_CFLAG(le16toh(cp->flags));
541878ed226SJulian Elischer 	m_adj(m, sizeof(*cp));
542878ed226SJulian Elischer 
543878ed226SJulian Elischer 	/* Check if we have this channel and it is in valid state */
544878ed226SJulian Elischer 	ch = ng_l2cap_chan_by_scid(l2cap, dcid);
545878ed226SJulian Elischer 	if (ch == NULL) {
546878ed226SJulian Elischer 		NG_L2CAP_ERR(
547878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConfigReq command. " \
548878ed226SJulian Elischer "Channel does not exist, cid=%d\n",
549878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), dcid);
550878ed226SJulian Elischer 		goto reject;
551878ed226SJulian Elischer 	}
552878ed226SJulian Elischer 
553878ed226SJulian Elischer 	/* Verify channel state */
554878ed226SJulian Elischer 	if (ch->state != NG_L2CAP_CONFIG && ch->state != NG_L2CAP_OPEN) {
555878ed226SJulian Elischer 		NG_L2CAP_ERR(
556878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConfigReq. " \
557878ed226SJulian Elischer "Invalid channel state, cid=%d, state=%d\n",
558878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), dcid, ch->state);
559878ed226SJulian Elischer 		goto reject;
560878ed226SJulian Elischer 	}
561878ed226SJulian Elischer 
562878ed226SJulian Elischer 	if (ch->state == NG_L2CAP_OPEN) { /* Re-configuration */
563878ed226SJulian Elischer 		ch->cfg_state = 0;
564878ed226SJulian Elischer 		ch->state = NG_L2CAP_CONFIG;
565878ed226SJulian Elischer 	}
566878ed226SJulian Elischer 
567878ed226SJulian Elischer 	for (result = 0, off = 0; ; ) {
568878ed226SJulian Elischer 		error = get_next_l2cap_opt(m, &off, &hdr, &val);
569878ed226SJulian Elischer 		if (error == 0) { /* We done with this packet */
570878ed226SJulian Elischer 			NG_FREE_M(m);
571878ed226SJulian Elischer 			break;
572878ed226SJulian Elischer 		} else if (error > 0) { /* Got option */
573878ed226SJulian Elischer 			switch (hdr.type) {
574878ed226SJulian Elischer 			case NG_L2CAP_OPT_MTU:
575878ed226SJulian Elischer 				ch->omtu = val.mtu;
576878ed226SJulian Elischer 				break;
577878ed226SJulian Elischer 
578878ed226SJulian Elischer 			case NG_L2CAP_OPT_FLUSH_TIMO:
579878ed226SJulian Elischer 				ch->flush_timo = val.flush_timo;
580878ed226SJulian Elischer 				break;
581878ed226SJulian Elischer 
582878ed226SJulian Elischer 			case NG_L2CAP_OPT_QOS:
583878ed226SJulian Elischer 				bcopy(&val.flow, &ch->iflow, sizeof(ch->iflow));
584878ed226SJulian Elischer 				break;
585878ed226SJulian Elischer 
5860986ab12SMaksim Yevmenkin 			default: /* Ignore unknown hint option */
587878ed226SJulian Elischer 				break;
588878ed226SJulian Elischer 			}
589878ed226SJulian Elischer 		} else { /* Oops, something is wrong */
590878ed226SJulian Elischer 			respond = 1;
591878ed226SJulian Elischer 
592878ed226SJulian Elischer 			if (error == -3) {
593878ed226SJulian Elischer 
594878ed226SJulian Elischer 				/*
595878ed226SJulian Elischer 				 * Adjust mbuf so we can get to the start
596878ed226SJulian Elischer 				 * of the first option we did not like.
597878ed226SJulian Elischer 				 */
598878ed226SJulian Elischer 
599878ed226SJulian Elischer 				m_adj(m, off - sizeof(hdr));
600878ed226SJulian Elischer 				m->m_pkthdr.len = sizeof(hdr) + hdr.length;
601878ed226SJulian Elischer 
602878ed226SJulian Elischer 				result = NG_L2CAP_UNKNOWN_OPTION;
603878ed226SJulian Elischer 			} else {
604878ed226SJulian Elischer 				/* XXX FIXME Send other reject codes? */
605878ed226SJulian Elischer 				NG_FREE_M(m);
606878ed226SJulian Elischer 				result = NG_L2CAP_REJECT;
607878ed226SJulian Elischer 			}
608878ed226SJulian Elischer 
609878ed226SJulian Elischer 			break;
610878ed226SJulian Elischer 		}
611878ed226SJulian Elischer 	}
612878ed226SJulian Elischer 
613878ed226SJulian Elischer 	/*
614878ed226SJulian Elischer 	 * Now check and see if we have to respond. If everything was OK then
615878ed226SJulian Elischer 	 * respond contain "C flag" and (if set) we will respond with empty
616878ed226SJulian Elischer 	 * packet and will wait for more options.
617878ed226SJulian Elischer 	 *
618878ed226SJulian Elischer 	 * Other case is that we did not like peer's options and will respond
619878ed226SJulian Elischer 	 * with L2CAP_Config response command with Reject error code.
620878ed226SJulian Elischer 	 *
621878ed226SJulian Elischer 	 * When "respond == 0" than we have received all options and we will
622878ed226SJulian Elischer 	 * sent L2CA_ConfigInd event to the upper layer protocol.
623878ed226SJulian Elischer 	 */
624878ed226SJulian Elischer 
625878ed226SJulian Elischer 	if (respond) {
626878ed226SJulian Elischer 		error = send_l2cap_cfg_rsp(con, ident, ch->dcid, result, m);
627878ed226SJulian Elischer 		if (error != 0) {
628878ed226SJulian Elischer 			ng_l2cap_l2ca_discon_ind(ch);
629878ed226SJulian Elischer 			ng_l2cap_free_chan(ch);
630878ed226SJulian Elischer 		}
631878ed226SJulian Elischer 	} else {
632878ed226SJulian Elischer 		/* Send L2CA_ConfigInd event to the upper layer protocol */
633878ed226SJulian Elischer 		ch->ident = ident;
634878ed226SJulian Elischer 		error = ng_l2cap_l2ca_cfg_ind(ch);
635878ed226SJulian Elischer 		if (error != 0)
636878ed226SJulian Elischer 			ng_l2cap_free_chan(ch);
637878ed226SJulian Elischer 	}
638878ed226SJulian Elischer 
639878ed226SJulian Elischer 	return (error);
640878ed226SJulian Elischer 
641878ed226SJulian Elischer reject:
642878ed226SJulian Elischer 	/* Send reject. Do not really care about the result */
643878ed226SJulian Elischer 	NG_FREE_M(m);
644878ed226SJulian Elischer 
645878ed226SJulian Elischer 	send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, 0, dcid);
646878ed226SJulian Elischer 
647878ed226SJulian Elischer 	return (0);
648878ed226SJulian Elischer } /* ng_l2cap_process_cfg_req */
649878ed226SJulian Elischer 
650878ed226SJulian Elischer /*
651878ed226SJulian Elischer  * Process L2CAP_ConfigRsp command
652878ed226SJulian Elischer  */
653878ed226SJulian Elischer 
654878ed226SJulian Elischer static int
655878ed226SJulian Elischer ng_l2cap_process_cfg_rsp(ng_l2cap_con_p con, u_int8_t ident)
656878ed226SJulian Elischer {
657878ed226SJulian Elischer 	ng_l2cap_p		 l2cap = con->l2cap;
658878ed226SJulian Elischer 	struct mbuf		*m = con->rx_pkt;
659878ed226SJulian Elischer 	ng_l2cap_cfg_rsp_cp	*cp = NULL;
660878ed226SJulian Elischer 	ng_l2cap_cmd_p		 cmd = NULL;
661878ed226SJulian Elischer 	u_int16_t		 scid, cflag, result;
662878ed226SJulian Elischer 	ng_l2cap_cfg_opt_t	 hdr;
663878ed226SJulian Elischer 	ng_l2cap_cfg_opt_val_t	 val;
664878ed226SJulian Elischer 	int			 off, error = 0;
665878ed226SJulian Elischer 
666878ed226SJulian Elischer 	/* Get command parameters */
667878ed226SJulian Elischer 	con->rx_pkt = NULL;
668878ed226SJulian Elischer 	NG_L2CAP_M_PULLUP(m, sizeof(*cp));
669878ed226SJulian Elischer 	if (m == NULL)
670878ed226SJulian Elischer 		return (ENOBUFS);
671878ed226SJulian Elischer 
672878ed226SJulian Elischer 	cp = mtod(m, ng_l2cap_cfg_rsp_cp *);
673878ed226SJulian Elischer 	scid = le16toh(cp->scid);
674878ed226SJulian Elischer 	cflag = NG_L2CAP_OPT_CFLAG(le16toh(cp->flags));
675878ed226SJulian Elischer 	result = le16toh(cp->result);
676878ed226SJulian Elischer 	m_adj(m, sizeof(*cp));
677878ed226SJulian Elischer 
678878ed226SJulian Elischer 	/* Check if we have this command */
679878ed226SJulian Elischer 	cmd = ng_l2cap_cmd_by_ident(con, ident);
680878ed226SJulian Elischer 	if (cmd == NULL) {
681878ed226SJulian Elischer 		NG_L2CAP_ERR(
682878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConfigRsp command. ident=%d, con_handle=%d\n",
683878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), ident,
684878ed226SJulian Elischer 			con->con_handle);
685878ed226SJulian Elischer 		NG_FREE_M(m);
686878ed226SJulian Elischer 
687878ed226SJulian Elischer 		return (ENOENT);
688878ed226SJulian Elischer 	}
689878ed226SJulian Elischer 
690878ed226SJulian Elischer 	/* Verify CIDs and send reject if does not match */
691878ed226SJulian Elischer 	if (cmd->ch->scid != scid) {
692878ed226SJulian Elischer 		NG_L2CAP_ERR(
693878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConfigRsp. " \
694878ed226SJulian Elischer "Channel ID does not match, scid=%d(%d)\n",
695878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,
696878ed226SJulian Elischer 			scid);
697878ed226SJulian Elischer 		goto reject;
698878ed226SJulian Elischer 	}
699878ed226SJulian Elischer 
700878ed226SJulian Elischer 	/* Verify channel state and reject if invalid */
701878ed226SJulian Elischer 	if (cmd->ch->state != NG_L2CAP_CONFIG) {
702878ed226SJulian Elischer 		NG_L2CAP_ERR(
703878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConfigRsp. " \
704878ed226SJulian Elischer "Invalid channel state, scid=%d, state=%d\n",
705878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,
706878ed226SJulian Elischer 			cmd->ch->state);
707878ed226SJulian Elischer 		goto reject;
708878ed226SJulian Elischer 	}
709878ed226SJulian Elischer 
710878ed226SJulian Elischer 	/*
711878ed226SJulian Elischer 	 * Looks like it is our response, so process it. First parse options,
712878ed226SJulian Elischer 	 * then verify C flag. If it is set then we shall expect more
713878ed226SJulian Elischer 	 * configuration options from the peer and we will wait. Otherwise we
714878ed226SJulian Elischer 	 * have received all options and we will send L2CA_ConfigRsp event to
7150986ab12SMaksim Yevmenkin 	 * the upper layer protocol. If command timeout already happened then
7160986ab12SMaksim Yevmenkin 	 * ignore response.
717878ed226SJulian Elischer 	 */
718878ed226SJulian Elischer 
7190986ab12SMaksim Yevmenkin 	if ((error = ng_l2cap_command_untimeout(cmd)) != 0) {
7200986ab12SMaksim Yevmenkin 		NG_FREE_M(m);
7210986ab12SMaksim Yevmenkin 		return (error);
7220986ab12SMaksim Yevmenkin 	}
723878ed226SJulian Elischer 
724878ed226SJulian Elischer 	for (off = 0; ; ) {
725878ed226SJulian Elischer 		error = get_next_l2cap_opt(m, &off, &hdr, &val);
726878ed226SJulian Elischer 		if (error == 0) /* We done with this packet */
727878ed226SJulian Elischer 			break;
728878ed226SJulian Elischer 		else if (error > 0) { /* Got option */
729878ed226SJulian Elischer 			switch (hdr.type) {
730878ed226SJulian Elischer 			case NG_L2CAP_OPT_MTU:
731878ed226SJulian Elischer 				cmd->ch->imtu = val.mtu;
732878ed226SJulian Elischer 			break;
733878ed226SJulian Elischer 
734878ed226SJulian Elischer 			case NG_L2CAP_OPT_FLUSH_TIMO:
735878ed226SJulian Elischer 				cmd->ch->flush_timo = val.flush_timo;
736878ed226SJulian Elischer 				break;
737878ed226SJulian Elischer 
738878ed226SJulian Elischer 			case NG_L2CAP_OPT_QOS:
739878ed226SJulian Elischer 				bcopy(&val.flow, &cmd->ch->oflow,
740878ed226SJulian Elischer 					sizeof(cmd->ch->oflow));
741878ed226SJulian Elischer 			break;
742878ed226SJulian Elischer 
7430986ab12SMaksim Yevmenkin 			default: /* Ignore unknown hint option */
744878ed226SJulian Elischer 				break;
745878ed226SJulian Elischer 			}
746878ed226SJulian Elischer 		} else {
747878ed226SJulian Elischer 			/*
748878ed226SJulian Elischer 			 * XXX FIXME What to do here?
749878ed226SJulian Elischer 			 *
7500986ab12SMaksim Yevmenkin 			 * This is really BAD :( options packet was broken, or
7510986ab12SMaksim Yevmenkin 			 * peer sent us option that we did not understand. Let
7520986ab12SMaksim Yevmenkin 			 * upper layer know and do not wait for more options.
753878ed226SJulian Elischer 			 */
754878ed226SJulian Elischer 
755878ed226SJulian Elischer 			NG_L2CAP_ALERT(
756878ed226SJulian Elischer "%s: %s - failed to parse configuration options, error=%d\n",
757878ed226SJulian Elischer 				__func__, NG_NODE_NAME(l2cap->node), error);
758878ed226SJulian Elischer 
759878ed226SJulian Elischer 			result = NG_L2CAP_UNKNOWN;
760878ed226SJulian Elischer 			cflag = 0;
761878ed226SJulian Elischer 
762878ed226SJulian Elischer 			break;
763878ed226SJulian Elischer 		}
764878ed226SJulian Elischer 	}
765878ed226SJulian Elischer 
766878ed226SJulian Elischer 	NG_FREE_M(m);
767878ed226SJulian Elischer 
768878ed226SJulian Elischer 	if (cflag) /* Restart timer and wait for more options */
769878ed226SJulian Elischer 		ng_l2cap_command_timeout(cmd, bluetooth_l2cap_rtx_timeout());
770878ed226SJulian Elischer 	else {
771878ed226SJulian Elischer 		ng_l2cap_unlink_cmd(cmd);
772878ed226SJulian Elischer 
773878ed226SJulian Elischer 		/* Send L2CA_Config response to the upper layer protocol */
774878ed226SJulian Elischer 		error = ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, result);
775878ed226SJulian Elischer 		if (error != 0) {
776878ed226SJulian Elischer 			/*
777878ed226SJulian Elischer 			 * XXX FIXME what to do here? we were not able to send
778878ed226SJulian Elischer 			 * response to the upper layer protocol, so for now
779878ed226SJulian Elischer 			 * just close the channel. Send L2CAP_Disconnect to
780878ed226SJulian Elischer 			 * remote peer?
781878ed226SJulian Elischer 			 */
782878ed226SJulian Elischer 
783878ed226SJulian Elischer 			NG_L2CAP_ERR(
784878ed226SJulian Elischer "%s: %s - failed to send L2CA_Config response, error=%d\n",
785878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), error);
786878ed226SJulian Elischer 
787878ed226SJulian Elischer 			ng_l2cap_free_chan(cmd->ch);
788878ed226SJulian Elischer 		}
789878ed226SJulian Elischer 
790878ed226SJulian Elischer 		ng_l2cap_free_cmd(cmd);
791878ed226SJulian Elischer 	}
792878ed226SJulian Elischer 
793878ed226SJulian Elischer 	return (error);
794878ed226SJulian Elischer 
795878ed226SJulian Elischer reject:
796878ed226SJulian Elischer 	/* Send reject. Do not really care about the result */
797878ed226SJulian Elischer 	NG_FREE_M(m);
798878ed226SJulian Elischer 
799878ed226SJulian Elischer 	send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, 0);
800878ed226SJulian Elischer 
801878ed226SJulian Elischer 	return (0);
802878ed226SJulian Elischer } /* ng_l2cap_process_cfg_rsp */
803878ed226SJulian Elischer 
804878ed226SJulian Elischer /*
805878ed226SJulian Elischer  * Process L2CAP_DisconnectReq command
806878ed226SJulian Elischer  */
807878ed226SJulian Elischer 
808878ed226SJulian Elischer static int
809878ed226SJulian Elischer ng_l2cap_process_discon_req(ng_l2cap_con_p con, u_int8_t ident)
810878ed226SJulian Elischer {
811878ed226SJulian Elischer 	ng_l2cap_p		 l2cap = con->l2cap;
812878ed226SJulian Elischer 	ng_l2cap_discon_req_cp	*cp = NULL;
813878ed226SJulian Elischer 	ng_l2cap_chan_p		 ch = NULL;
814878ed226SJulian Elischer 	ng_l2cap_cmd_p		 cmd = NULL;
815878ed226SJulian Elischer 	u_int16_t		 scid, dcid;
816878ed226SJulian Elischer 
817878ed226SJulian Elischer 	/* Get command parameters */
818878ed226SJulian Elischer 	NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));
819878ed226SJulian Elischer 	if (con->rx_pkt == NULL)
820878ed226SJulian Elischer 		return (ENOBUFS);
821878ed226SJulian Elischer 
822878ed226SJulian Elischer 	cp = mtod(con->rx_pkt, ng_l2cap_discon_req_cp *);
823878ed226SJulian Elischer 	dcid = le16toh(cp->dcid);
824878ed226SJulian Elischer 	scid = le16toh(cp->scid);
825878ed226SJulian Elischer 
826878ed226SJulian Elischer 	NG_FREE_M(con->rx_pkt);
827878ed226SJulian Elischer 
828878ed226SJulian Elischer 	/* Check if we have this channel and it is in valid state */
829878ed226SJulian Elischer 	ch = ng_l2cap_chan_by_scid(l2cap, dcid);
830878ed226SJulian Elischer 	if (ch == NULL) {
831878ed226SJulian Elischer 		NG_L2CAP_ERR(
832878ed226SJulian Elischer "%s: %s - unexpected L2CAP_DisconnectReq message. " \
833878ed226SJulian Elischer "Channel does not exist, cid=%d\n",
834878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), dcid);
835878ed226SJulian Elischer 		goto reject;
836878ed226SJulian Elischer 	}
837878ed226SJulian Elischer 
838878ed226SJulian Elischer 	/* XXX Verify channel state and reject if invalid -- is that true? */
839f2bb1caeSJulian Elischer 	if (ch->state != NG_L2CAP_OPEN && ch->state != NG_L2CAP_CONFIG &&
840f2bb1caeSJulian Elischer 	    ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) {
841878ed226SJulian Elischer 		NG_L2CAP_ERR(
842878ed226SJulian Elischer "%s: %s - unexpected L2CAP_DisconnectReq. " \
843878ed226SJulian Elischer "Invalid channel state, cid=%d, state=%d\n",
844878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), dcid, ch->state);
845878ed226SJulian Elischer 		goto reject;
846878ed226SJulian Elischer 	}
847878ed226SJulian Elischer 
848878ed226SJulian Elischer 	/* Match destination channel ID */
849878ed226SJulian Elischer 	if (ch->dcid != scid || ch->scid != dcid) {
850878ed226SJulian Elischer 		NG_L2CAP_ERR(
851878ed226SJulian Elischer "%s: %s - unexpected L2CAP_DisconnectReq. " \
852878ed226SJulian Elischer "Channel IDs does not match, channel: scid=%d, dcid=%d, " \
853878ed226SJulian Elischer "request: scid=%d, dcid=%d\n",
854878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), ch->scid, ch->dcid,
855878ed226SJulian Elischer 			scid, dcid);
856878ed226SJulian Elischer 		goto reject;
857878ed226SJulian Elischer 	}
858878ed226SJulian Elischer 
859878ed226SJulian Elischer 	/*
860878ed226SJulian Elischer 	 * Looks good, so notify upper layer protocol that channel is about
861878ed226SJulian Elischer 	 * to be disconnected and send L2CA_DisconnectInd message. Then respond
862878ed226SJulian Elischer 	 * with L2CAP_DisconnectRsp.
863878ed226SJulian Elischer 	 */
864878ed226SJulian Elischer 
865f2bb1caeSJulian Elischer 	if (ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) {
866878ed226SJulian Elischer 		ng_l2cap_l2ca_discon_ind(ch); /* do not care about result */
867878ed226SJulian Elischer 		ng_l2cap_free_chan(ch);
868f2bb1caeSJulian Elischer 	}
869878ed226SJulian Elischer 
870878ed226SJulian Elischer 	/* Send L2CAP_DisconnectRsp */
871878ed226SJulian Elischer 	cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_DISCON_RSP, 0);
872878ed226SJulian Elischer 	if (cmd == NULL)
873878ed226SJulian Elischer 		return (ENOMEM);
874878ed226SJulian Elischer 
875878ed226SJulian Elischer 	_ng_l2cap_discon_rsp(cmd->aux, ident, dcid, scid);
876878ed226SJulian Elischer 	if (cmd->aux == NULL) {
877878ed226SJulian Elischer 		ng_l2cap_free_cmd(cmd);
878878ed226SJulian Elischer 
879878ed226SJulian Elischer 		return (ENOBUFS);
880878ed226SJulian Elischer 	}
881878ed226SJulian Elischer 
882878ed226SJulian Elischer 	/* Link command to the queue */
883878ed226SJulian Elischer 	ng_l2cap_link_cmd(con, cmd);
884878ed226SJulian Elischer 	ng_l2cap_lp_deliver(con);
885878ed226SJulian Elischer 
886878ed226SJulian Elischer 	return (0);
887878ed226SJulian Elischer 
888878ed226SJulian Elischer reject:
889878ed226SJulian Elischer 	/* Send reject. Do not really care about the result */
890878ed226SJulian Elischer 	send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, dcid);
891878ed226SJulian Elischer 
892878ed226SJulian Elischer 	return (0);
893878ed226SJulian Elischer } /* ng_l2cap_process_discon_req */
894878ed226SJulian Elischer 
895878ed226SJulian Elischer /*
896878ed226SJulian Elischer  * Process L2CAP_DisconnectRsp command
897878ed226SJulian Elischer  */
898878ed226SJulian Elischer 
899878ed226SJulian Elischer static int
900878ed226SJulian Elischer ng_l2cap_process_discon_rsp(ng_l2cap_con_p con, u_int8_t ident)
901878ed226SJulian Elischer {
902878ed226SJulian Elischer 	ng_l2cap_p		 l2cap = con->l2cap;
903878ed226SJulian Elischer 	ng_l2cap_discon_rsp_cp	*cp = NULL;
904878ed226SJulian Elischer 	ng_l2cap_cmd_p		 cmd = NULL;
905878ed226SJulian Elischer 	u_int16_t		 scid, dcid;
906878ed226SJulian Elischer 	int			 error = 0;
907878ed226SJulian Elischer 
908878ed226SJulian Elischer 	/* Get command parameters */
909878ed226SJulian Elischer 	NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));
910878ed226SJulian Elischer 	if (con->rx_pkt == NULL)
911878ed226SJulian Elischer 		return (ENOBUFS);
912878ed226SJulian Elischer 
913878ed226SJulian Elischer 	cp = mtod(con->rx_pkt, ng_l2cap_discon_rsp_cp *);
914878ed226SJulian Elischer 	dcid = le16toh(cp->dcid);
915878ed226SJulian Elischer 	scid = le16toh(cp->scid);
916878ed226SJulian Elischer 
917878ed226SJulian Elischer 	NG_FREE_M(con->rx_pkt);
918878ed226SJulian Elischer 
919878ed226SJulian Elischer 	/* Check if we have pending command descriptor */
920878ed226SJulian Elischer 	cmd = ng_l2cap_cmd_by_ident(con, ident);
921878ed226SJulian Elischer 	if (cmd == NULL) {
922878ed226SJulian Elischer 		NG_L2CAP_ERR(
923878ed226SJulian Elischer "%s: %s - unexpected L2CAP_DisconnectRsp command. ident=%d, con_handle=%d\n",
924878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), ident,
925878ed226SJulian Elischer 			con->con_handle);
926878ed226SJulian Elischer 		goto out;
927878ed226SJulian Elischer 	}
928878ed226SJulian Elischer 
929878ed226SJulian Elischer 	/* Verify channel state, do nothing if invalid */
930878ed226SJulian Elischer 	if (cmd->ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) {
931878ed226SJulian Elischer 		NG_L2CAP_ERR(
932878ed226SJulian Elischer "%s: %s - unexpected L2CAP_DisconnectRsp. " \
933878ed226SJulian Elischer "Invalid channel state, cid=%d, state=%d\n",
934878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), scid,
935878ed226SJulian Elischer 			cmd->ch->state);
936878ed226SJulian Elischer 		goto out;
937878ed226SJulian Elischer 	}
938878ed226SJulian Elischer 
939878ed226SJulian Elischer 	/* Verify CIDs and send reject if does not match */
940878ed226SJulian Elischer 	if (cmd->ch->scid != scid || cmd->ch->dcid != dcid) {
941878ed226SJulian Elischer 		NG_L2CAP_ERR(
942878ed226SJulian Elischer "%s: %s - unexpected L2CAP_DisconnectRsp. " \
943878ed226SJulian Elischer "Channel IDs do not match, scid=%d(%d), dcid=%d(%d)\n",
944878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,
945878ed226SJulian Elischer 			scid, cmd->ch->dcid, dcid);
946878ed226SJulian Elischer 		goto out;
947878ed226SJulian Elischer 	}
948878ed226SJulian Elischer 
949878ed226SJulian Elischer 	/*
9500986ab12SMaksim Yevmenkin 	 * Looks like we have successfuly disconnected channel, so notify
9510986ab12SMaksim Yevmenkin 	 * upper layer. If command timeout already happened then ignore
9520986ab12SMaksim Yevmenkin 	 * response.
953878ed226SJulian Elischer 	 */
954878ed226SJulian Elischer 
9550986ab12SMaksim Yevmenkin 	if ((error = ng_l2cap_command_untimeout(cmd)) != 0)
9560986ab12SMaksim Yevmenkin 		goto out;
9570986ab12SMaksim Yevmenkin 
958878ed226SJulian Elischer 	error = ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, NG_L2CAP_SUCCESS);
959878ed226SJulian Elischer 	ng_l2cap_free_chan(cmd->ch); /* this will free commands too */
960878ed226SJulian Elischer out:
961878ed226SJulian Elischer 	return (error);
962878ed226SJulian Elischer } /* ng_l2cap_process_discon_rsp */
963878ed226SJulian Elischer 
964878ed226SJulian Elischer /*
965878ed226SJulian Elischer  * Process L2CAP_EchoReq command
966878ed226SJulian Elischer  */
967878ed226SJulian Elischer 
968878ed226SJulian Elischer static int
969878ed226SJulian Elischer ng_l2cap_process_echo_req(ng_l2cap_con_p con, u_int8_t ident)
970878ed226SJulian Elischer {
971878ed226SJulian Elischer 	ng_l2cap_p		 l2cap = con->l2cap;
972878ed226SJulian Elischer 	ng_l2cap_cmd_hdr_t	*hdr = NULL;
973878ed226SJulian Elischer 	ng_l2cap_cmd_p		 cmd = NULL;
974878ed226SJulian Elischer 
975878ed226SJulian Elischer 	con->rx_pkt = ng_l2cap_prepend(con->rx_pkt, sizeof(*hdr));
976878ed226SJulian Elischer 	if (con->rx_pkt == NULL) {
977878ed226SJulian Elischer 		NG_L2CAP_ALERT(
9784ae439a3SMaksim Yevmenkin "%s: %s - ng_l2cap_prepend() failed, size=%zd\n",
979878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), sizeof(*hdr));
980878ed226SJulian Elischer 
981878ed226SJulian Elischer 		return (ENOBUFS);
982878ed226SJulian Elischer 	}
983878ed226SJulian Elischer 
984878ed226SJulian Elischer 	hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *);
985878ed226SJulian Elischer 	hdr->code = NG_L2CAP_ECHO_RSP;
986878ed226SJulian Elischer 	hdr->ident = ident;
987878ed226SJulian Elischer 	hdr->length = htole16(con->rx_pkt->m_pkthdr.len - sizeof(*hdr));
988878ed226SJulian Elischer 
989878ed226SJulian Elischer 	cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_ECHO_RSP, 0);
990878ed226SJulian Elischer 	if (cmd == NULL) {
991878ed226SJulian Elischer 		NG_FREE_M(con->rx_pkt);
992878ed226SJulian Elischer 
993878ed226SJulian Elischer 		return (ENOBUFS);
994878ed226SJulian Elischer 	}
995878ed226SJulian Elischer 
996878ed226SJulian Elischer 	/* Attach data and link command to the queue */
997878ed226SJulian Elischer 	cmd->aux = con->rx_pkt;
998878ed226SJulian Elischer 	con->rx_pkt = NULL;
999878ed226SJulian Elischer 	ng_l2cap_link_cmd(con, cmd);
1000878ed226SJulian Elischer 	ng_l2cap_lp_deliver(con);
1001878ed226SJulian Elischer 
1002878ed226SJulian Elischer 	return (0);
1003878ed226SJulian Elischer } /* ng_l2cap_process_echo_req */
1004878ed226SJulian Elischer 
1005878ed226SJulian Elischer /*
1006878ed226SJulian Elischer  * Process L2CAP_EchoRsp command
1007878ed226SJulian Elischer  */
1008878ed226SJulian Elischer 
1009878ed226SJulian Elischer static int
1010878ed226SJulian Elischer ng_l2cap_process_echo_rsp(ng_l2cap_con_p con, u_int8_t ident)
1011878ed226SJulian Elischer {
1012878ed226SJulian Elischer 	ng_l2cap_p	l2cap = con->l2cap;
1013878ed226SJulian Elischer 	ng_l2cap_cmd_p	cmd = NULL;
1014878ed226SJulian Elischer 	int		error = 0;
1015878ed226SJulian Elischer 
1016878ed226SJulian Elischer 	/* Check if we have this command */
1017878ed226SJulian Elischer 	cmd = ng_l2cap_cmd_by_ident(con, ident);
1018878ed226SJulian Elischer 	if (cmd != NULL) {
10190986ab12SMaksim Yevmenkin 		/* If command timeout already happened then ignore response */
10200986ab12SMaksim Yevmenkin 		if ((error = ng_l2cap_command_untimeout(cmd)) != 0) {
10210986ab12SMaksim Yevmenkin 			NG_FREE_M(con->rx_pkt);
10220986ab12SMaksim Yevmenkin 			return (error);
10230986ab12SMaksim Yevmenkin 		}
1024878ed226SJulian Elischer 
1025878ed226SJulian Elischer 		ng_l2cap_unlink_cmd(cmd);
1026878ed226SJulian Elischer 
1027878ed226SJulian Elischer 		error = ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
1028878ed226SJulian Elischer 				NG_L2CAP_SUCCESS, con->rx_pkt);
1029878ed226SJulian Elischer 
1030878ed226SJulian Elischer 		ng_l2cap_free_cmd(cmd);
1031878ed226SJulian Elischer 		con->rx_pkt = NULL;
1032878ed226SJulian Elischer 	} else {
1033878ed226SJulian Elischer 		NG_L2CAP_ERR(
1034878ed226SJulian Elischer "%s: %s - unexpected L2CAP_EchoRsp command. " \
1035878ed226SJulian Elischer "Requested ident does not exist, ident=%d\n",
1036878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), ident);
1037878ed226SJulian Elischer 		NG_FREE_M(con->rx_pkt);
1038878ed226SJulian Elischer 	}
1039878ed226SJulian Elischer 
1040878ed226SJulian Elischer 	return (error);
1041878ed226SJulian Elischer } /* ng_l2cap_process_echo_rsp */
1042878ed226SJulian Elischer 
1043878ed226SJulian Elischer /*
1044878ed226SJulian Elischer  * Process L2CAP_InfoReq command
1045878ed226SJulian Elischer  */
1046878ed226SJulian Elischer 
1047878ed226SJulian Elischer static int
1048878ed226SJulian Elischer ng_l2cap_process_info_req(ng_l2cap_con_p con, u_int8_t ident)
1049878ed226SJulian Elischer {
1050878ed226SJulian Elischer 	ng_l2cap_p	l2cap = con->l2cap;
1051878ed226SJulian Elischer 	ng_l2cap_cmd_p	cmd = NULL;
1052878ed226SJulian Elischer 	u_int16_t	type;
1053878ed226SJulian Elischer 
1054878ed226SJulian Elischer 	/* Get command parameters */
1055878ed226SJulian Elischer 	NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(ng_l2cap_info_req_cp));
1056878ed226SJulian Elischer 	if (con->rx_pkt == NULL)
1057878ed226SJulian Elischer 		return (ENOBUFS);
1058878ed226SJulian Elischer 
1059878ed226SJulian Elischer 	type = le16toh(mtod(con->rx_pkt, ng_l2cap_info_req_cp *)->type);
1060878ed226SJulian Elischer 	NG_FREE_M(con->rx_pkt);
1061878ed226SJulian Elischer 
1062878ed226SJulian Elischer 	cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_INFO_RSP, 0);
1063878ed226SJulian Elischer 	if (cmd == NULL)
1064878ed226SJulian Elischer 		return (ENOMEM);
1065878ed226SJulian Elischer 
1066878ed226SJulian Elischer 	switch (type) {
1067878ed226SJulian Elischer 	case NG_L2CAP_CONNLESS_MTU:
1068878ed226SJulian Elischer 		_ng_l2cap_info_rsp(cmd->aux, ident, NG_L2CAP_CONNLESS_MTU,
1069878ed226SJulian Elischer 				NG_L2CAP_SUCCESS, NG_L2CAP_MTU_DEFAULT);
1070878ed226SJulian Elischer 		break;
1071878ed226SJulian Elischer 
1072878ed226SJulian Elischer 	default:
1073878ed226SJulian Elischer 		_ng_l2cap_info_rsp(cmd->aux, ident, type,
1074878ed226SJulian Elischer 				NG_L2CAP_NOT_SUPPORTED, 0);
1075878ed226SJulian Elischer 		break;
1076878ed226SJulian Elischer 	}
1077878ed226SJulian Elischer 
1078878ed226SJulian Elischer 	if (cmd->aux == NULL) {
1079878ed226SJulian Elischer 		ng_l2cap_free_cmd(cmd);
1080878ed226SJulian Elischer 
1081878ed226SJulian Elischer 		return (ENOBUFS);
1082878ed226SJulian Elischer 	}
1083878ed226SJulian Elischer 
1084878ed226SJulian Elischer 	/* Link command to the queue */
1085878ed226SJulian Elischer 	ng_l2cap_link_cmd(con, cmd);
1086878ed226SJulian Elischer 	ng_l2cap_lp_deliver(con);
1087878ed226SJulian Elischer 
1088878ed226SJulian Elischer 	return (0);
1089878ed226SJulian Elischer } /* ng_l2cap_process_info_req */
1090878ed226SJulian Elischer 
1091878ed226SJulian Elischer /*
1092878ed226SJulian Elischer  * Process L2CAP_InfoRsp command
1093878ed226SJulian Elischer  */
1094878ed226SJulian Elischer 
1095878ed226SJulian Elischer static int
1096878ed226SJulian Elischer ng_l2cap_process_info_rsp(ng_l2cap_con_p con, u_int8_t ident)
1097878ed226SJulian Elischer {
1098878ed226SJulian Elischer 	ng_l2cap_p		 l2cap = con->l2cap;
1099878ed226SJulian Elischer 	ng_l2cap_info_rsp_cp	*cp = NULL;
1100878ed226SJulian Elischer 	ng_l2cap_cmd_p		 cmd = NULL;
1101878ed226SJulian Elischer 	int			 error = 0;
1102878ed226SJulian Elischer 
1103878ed226SJulian Elischer 	/* Get command parameters */
1104878ed226SJulian Elischer 	NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));
1105878ed226SJulian Elischer 	if (con->rx_pkt == NULL)
1106878ed226SJulian Elischer 		return (ENOBUFS);
1107878ed226SJulian Elischer 
1108878ed226SJulian Elischer 	cp = mtod(con->rx_pkt, ng_l2cap_info_rsp_cp *);
1109878ed226SJulian Elischer 	cp->type = le16toh(cp->type);
1110878ed226SJulian Elischer 	cp->result = le16toh(cp->result);
1111878ed226SJulian Elischer 	m_adj(con->rx_pkt, sizeof(*cp));
1112878ed226SJulian Elischer 
1113878ed226SJulian Elischer 	/* Check if we have pending command descriptor */
1114878ed226SJulian Elischer 	cmd = ng_l2cap_cmd_by_ident(con, ident);
1115878ed226SJulian Elischer 	if (cmd == NULL) {
1116878ed226SJulian Elischer 		NG_L2CAP_ERR(
1117878ed226SJulian Elischer "%s: %s - unexpected L2CAP_InfoRsp command. " \
1118878ed226SJulian Elischer "Requested ident does not exist, ident=%d\n",
1119878ed226SJulian Elischer 			__func__, NG_NODE_NAME(l2cap->node), ident);
1120878ed226SJulian Elischer 		NG_FREE_M(con->rx_pkt);
1121878ed226SJulian Elischer 
1122878ed226SJulian Elischer 		return (ENOENT);
1123878ed226SJulian Elischer 	}
1124878ed226SJulian Elischer 
11250986ab12SMaksim Yevmenkin 	/* If command timeout already happened then ignore response */
11260986ab12SMaksim Yevmenkin 	if ((error = ng_l2cap_command_untimeout(cmd)) != 0) {
11270986ab12SMaksim Yevmenkin 		NG_FREE_M(con->rx_pkt);
11280986ab12SMaksim Yevmenkin 		return (error);
11290986ab12SMaksim Yevmenkin 	}
1130878ed226SJulian Elischer 
1131878ed226SJulian Elischer 	ng_l2cap_unlink_cmd(cmd);
1132878ed226SJulian Elischer 
1133878ed226SJulian Elischer 	if (cp->result == NG_L2CAP_SUCCESS) {
1134878ed226SJulian Elischer 		switch (cp->type) {
1135878ed226SJulian Elischer 		case NG_L2CAP_CONNLESS_MTU:
1136878ed226SJulian Elischer 	    		if (con->rx_pkt->m_pkthdr.len == sizeof(u_int16_t))
1137878ed226SJulian Elischer 				*mtod(con->rx_pkt, u_int16_t *) =
1138878ed226SJulian Elischer 					le16toh(*mtod(con->rx_pkt,u_int16_t *));
1139878ed226SJulian Elischer 			else {
1140878ed226SJulian Elischer 				cp->result = NG_L2CAP_UNKNOWN; /* XXX */
1141878ed226SJulian Elischer 
1142878ed226SJulian Elischer 				NG_L2CAP_ERR(
1143878ed226SJulian Elischer "%s: %s - invalid L2CAP_InfoRsp command. " \
1144878ed226SJulian Elischer "Bad connectionless MTU parameter, len=%d\n",
1145878ed226SJulian Elischer 					__func__, NG_NODE_NAME(l2cap->node),
1146878ed226SJulian Elischer 					con->rx_pkt->m_pkthdr.len);
1147878ed226SJulian Elischer 			}
1148878ed226SJulian Elischer 			break;
1149878ed226SJulian Elischer 
1150878ed226SJulian Elischer 		default:
1151878ed226SJulian Elischer 			NG_L2CAP_WARN(
1152878ed226SJulian Elischer "%s: %s - invalid L2CAP_InfoRsp command. Unknown info type=%d\n",
1153878ed226SJulian Elischer 				__func__, NG_NODE_NAME(l2cap->node), cp->type);
1154878ed226SJulian Elischer 			break;
1155878ed226SJulian Elischer 		}
1156878ed226SJulian Elischer 	}
1157878ed226SJulian Elischer 
1158878ed226SJulian Elischer 	error = ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
1159878ed226SJulian Elischer 			cp->result, con->rx_pkt);
1160878ed226SJulian Elischer 
1161878ed226SJulian Elischer 	ng_l2cap_free_cmd(cmd);
1162878ed226SJulian Elischer 	con->rx_pkt = NULL;
1163878ed226SJulian Elischer 
1164878ed226SJulian Elischer 	return (error);
1165878ed226SJulian Elischer } /* ng_l2cap_process_info_rsp */
1166878ed226SJulian Elischer 
1167878ed226SJulian Elischer /*
1168878ed226SJulian Elischer  * Send L2CAP reject
1169878ed226SJulian Elischer  */
1170878ed226SJulian Elischer 
1171878ed226SJulian Elischer static int
1172878ed226SJulian Elischer send_l2cap_reject(ng_l2cap_con_p con, u_int8_t ident, u_int16_t reason,
1173878ed226SJulian Elischer 		u_int16_t mtu, u_int16_t scid, u_int16_t dcid)
1174878ed226SJulian Elischer {
1175878ed226SJulian Elischer 	ng_l2cap_cmd_p	cmd = NULL;
1176878ed226SJulian Elischer 
1177878ed226SJulian Elischer 	cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CMD_REJ, 0);
1178878ed226SJulian Elischer 	if (cmd == NULL)
1179878ed226SJulian Elischer 		return (ENOMEM);
1180878ed226SJulian Elischer 
1181878ed226SJulian Elischer 	 _ng_l2cap_cmd_rej(cmd->aux, cmd->ident, reason, mtu, scid, dcid);
1182878ed226SJulian Elischer 	if (cmd->aux == NULL) {
1183878ed226SJulian Elischer 		ng_l2cap_free_cmd(cmd);
1184878ed226SJulian Elischer 
1185878ed226SJulian Elischer 		return (ENOBUFS);
1186878ed226SJulian Elischer 	}
1187878ed226SJulian Elischer 
1188878ed226SJulian Elischer 	/* Link command to the queue */
1189878ed226SJulian Elischer 	ng_l2cap_link_cmd(con, cmd);
1190878ed226SJulian Elischer 	ng_l2cap_lp_deliver(con);
1191878ed226SJulian Elischer 
1192878ed226SJulian Elischer 	return (0);
1193878ed226SJulian Elischer } /* send_l2cap_reject */
1194878ed226SJulian Elischer 
1195878ed226SJulian Elischer /*
1196878ed226SJulian Elischer  * Send L2CAP connection reject
1197878ed226SJulian Elischer  */
1198878ed226SJulian Elischer 
1199878ed226SJulian Elischer static int
1200878ed226SJulian Elischer send_l2cap_con_rej(ng_l2cap_con_p con, u_int8_t ident, u_int16_t scid,
1201878ed226SJulian Elischer 		u_int16_t dcid, u_int16_t result)
1202878ed226SJulian Elischer {
1203878ed226SJulian Elischer 	ng_l2cap_cmd_p	cmd = NULL;
1204878ed226SJulian Elischer 
1205878ed226SJulian Elischer 	cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CON_RSP, 0);
1206878ed226SJulian Elischer 	if (cmd == NULL)
1207878ed226SJulian Elischer 		return (ENOMEM);
1208878ed226SJulian Elischer 
1209878ed226SJulian Elischer 	_ng_l2cap_con_rsp(cmd->aux, cmd->ident, scid, dcid, result, 0);
1210878ed226SJulian Elischer 	if (cmd->aux == NULL) {
1211878ed226SJulian Elischer 		ng_l2cap_free_cmd(cmd);
1212878ed226SJulian Elischer 
1213878ed226SJulian Elischer 		return (ENOBUFS);
1214878ed226SJulian Elischer 	}
1215878ed226SJulian Elischer 
1216878ed226SJulian Elischer 	/* Link command to the queue */
1217878ed226SJulian Elischer 	ng_l2cap_link_cmd(con, cmd);
1218878ed226SJulian Elischer 	ng_l2cap_lp_deliver(con);
1219878ed226SJulian Elischer 
1220878ed226SJulian Elischer 	return (0);
1221878ed226SJulian Elischer } /* send_l2cap_con_rej */
1222878ed226SJulian Elischer 
1223878ed226SJulian Elischer /*
1224878ed226SJulian Elischer  * Send L2CAP config response
1225878ed226SJulian Elischer  */
1226878ed226SJulian Elischer 
1227878ed226SJulian Elischer static int
1228878ed226SJulian Elischer send_l2cap_cfg_rsp(ng_l2cap_con_p con, u_int8_t ident, u_int16_t scid,
1229878ed226SJulian Elischer 		u_int16_t result, struct mbuf *opt)
1230878ed226SJulian Elischer {
1231878ed226SJulian Elischer 	ng_l2cap_cmd_p	cmd = NULL;
1232878ed226SJulian Elischer 
1233878ed226SJulian Elischer 	cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CFG_RSP, 0);
1234878ed226SJulian Elischer 	if (cmd == NULL) {
1235878ed226SJulian Elischer 		NG_FREE_M(opt);
1236878ed226SJulian Elischer 
1237878ed226SJulian Elischer 		return (ENOMEM);
1238878ed226SJulian Elischer 	}
1239878ed226SJulian Elischer 
1240878ed226SJulian Elischer 	_ng_l2cap_cfg_rsp(cmd->aux, cmd->ident, scid, 0, result, opt);
1241878ed226SJulian Elischer 	if (cmd->aux == NULL) {
1242878ed226SJulian Elischer 		ng_l2cap_free_cmd(cmd);
1243878ed226SJulian Elischer 
1244878ed226SJulian Elischer 		return (ENOBUFS);
1245878ed226SJulian Elischer 	}
1246878ed226SJulian Elischer 
1247878ed226SJulian Elischer 	/* Link command to the queue */
1248878ed226SJulian Elischer 	ng_l2cap_link_cmd(con, cmd);
1249878ed226SJulian Elischer 	ng_l2cap_lp_deliver(con);
1250878ed226SJulian Elischer 
1251878ed226SJulian Elischer 	return (0);
1252878ed226SJulian Elischer } /* send_l2cap_cfg_rsp */
1253878ed226SJulian Elischer 
1254878ed226SJulian Elischer /*
1255878ed226SJulian Elischer  * Get next L2CAP configuration option
1256878ed226SJulian Elischer  *
1257878ed226SJulian Elischer  * Return codes:
1258878ed226SJulian Elischer  *  0   no option
1259878ed226SJulian Elischer  *  1   we have got option
1260878ed226SJulian Elischer  * -1   header too short
1261878ed226SJulian Elischer  * -2   bad option value or length
1262878ed226SJulian Elischer  * -3   unknown option
1263878ed226SJulian Elischer  */
1264878ed226SJulian Elischer 
1265878ed226SJulian Elischer static int
1266878ed226SJulian Elischer get_next_l2cap_opt(struct mbuf *m, int *off, ng_l2cap_cfg_opt_p hdr,
1267878ed226SJulian Elischer 		ng_l2cap_cfg_opt_val_p val)
1268878ed226SJulian Elischer {
1269878ed226SJulian Elischer 	int	hint, len = m->m_pkthdr.len - (*off);
1270878ed226SJulian Elischer 
1271878ed226SJulian Elischer 	if (len == 0)
1272878ed226SJulian Elischer 		return (0);
1273878ed226SJulian Elischer 	if (len < 0 || len < sizeof(*hdr))
1274878ed226SJulian Elischer 		return (-1);
1275878ed226SJulian Elischer 
1276878ed226SJulian Elischer 	m_copydata(m, *off, sizeof(*hdr), (caddr_t) hdr);
1277878ed226SJulian Elischer 	*off += sizeof(*hdr);
1278878ed226SJulian Elischer 	len  -= sizeof(*hdr);
1279878ed226SJulian Elischer 
1280878ed226SJulian Elischer 	hint = NG_L2CAP_OPT_HINT(hdr->type);
1281878ed226SJulian Elischer 	hdr->type &= NG_L2CAP_OPT_HINT_MASK;
1282878ed226SJulian Elischer 
1283878ed226SJulian Elischer 	switch (hdr->type) {
1284878ed226SJulian Elischer 	case NG_L2CAP_OPT_MTU:
1285878ed226SJulian Elischer 		if (hdr->length != NG_L2CAP_OPT_MTU_SIZE || len < hdr->length)
1286878ed226SJulian Elischer 			return (-2);
1287878ed226SJulian Elischer 
1288878ed226SJulian Elischer 		m_copydata(m, *off, NG_L2CAP_OPT_MTU_SIZE, (caddr_t) val);
1289878ed226SJulian Elischer 		val->mtu = le16toh(val->mtu);
1290878ed226SJulian Elischer 		*off += NG_L2CAP_OPT_MTU_SIZE;
1291878ed226SJulian Elischer 		break;
1292878ed226SJulian Elischer 
1293878ed226SJulian Elischer 	case NG_L2CAP_OPT_FLUSH_TIMO:
1294878ed226SJulian Elischer 		if (hdr->length != NG_L2CAP_OPT_FLUSH_TIMO_SIZE ||
1295878ed226SJulian Elischer 		    len < hdr->length)
1296878ed226SJulian Elischer 			return (-2);
1297878ed226SJulian Elischer 
1298878ed226SJulian Elischer 		m_copydata(m, *off, NG_L2CAP_OPT_FLUSH_TIMO_SIZE, (caddr_t)val);
1299878ed226SJulian Elischer 		val->flush_timo = le16toh(val->flush_timo);
1300878ed226SJulian Elischer 		*off += NG_L2CAP_OPT_FLUSH_TIMO_SIZE;
1301878ed226SJulian Elischer 		break;
1302878ed226SJulian Elischer 
1303878ed226SJulian Elischer 	case NG_L2CAP_OPT_QOS:
1304878ed226SJulian Elischer 		if (hdr->length != NG_L2CAP_OPT_QOS_SIZE || len < hdr->length)
1305878ed226SJulian Elischer 			return (-2);
1306878ed226SJulian Elischer 
1307878ed226SJulian Elischer 		m_copydata(m, *off, NG_L2CAP_OPT_QOS_SIZE, (caddr_t) val);
1308878ed226SJulian Elischer 		val->flow.token_rate = le32toh(val->flow.token_rate);
1309878ed226SJulian Elischer 		val->flow.token_bucket_size =
1310878ed226SJulian Elischer 				le32toh(val->flow.token_bucket_size);
1311878ed226SJulian Elischer 		val->flow.peak_bandwidth = le32toh(val->flow.peak_bandwidth);
1312878ed226SJulian Elischer 		val->flow.latency = le32toh(val->flow.latency);
1313878ed226SJulian Elischer 		val->flow.delay_variation = le32toh(val->flow.delay_variation);
1314878ed226SJulian Elischer 		*off += NG_L2CAP_OPT_QOS_SIZE;
1315878ed226SJulian Elischer 		break;
1316878ed226SJulian Elischer 
1317878ed226SJulian Elischer 	default:
1318878ed226SJulian Elischer 		if (hint)
1319878ed226SJulian Elischer 			*off += hdr->length;
1320878ed226SJulian Elischer 		else
1321878ed226SJulian Elischer 			return (-3);
1322878ed226SJulian Elischer 		break;
1323878ed226SJulian Elischer 	}
1324878ed226SJulian Elischer 
1325878ed226SJulian Elischer 	return (1);
1326878ed226SJulian Elischer } /* get_next_l2cap_opt */
1327878ed226SJulian Elischer 
1328