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