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