xref: /freebsd/sys/netgraph/bluetooth/l2cap/ng_l2cap_ulpi.c (revision 1669d8afc64812c8d2d1d147ae1fd42ff441e1b1)
1 /*
2  * ng_l2cap_ulpi.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_ulpi.c,v 1.1 2002/11/24 19:47:06 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_hci.h>
44 #include <netgraph/bluetooth/include/ng_l2cap.h>
45 #include <netgraph/bluetooth/l2cap/ng_l2cap_var.h>
46 #include <netgraph/bluetooth/l2cap/ng_l2cap_cmds.h>
47 #include <netgraph/bluetooth/l2cap/ng_l2cap_evnt.h>
48 #include <netgraph/bluetooth/l2cap/ng_l2cap_llpi.h>
49 #include <netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h>
50 #include <netgraph/bluetooth/l2cap/ng_l2cap_misc.h>
51 
52 /******************************************************************************
53  ******************************************************************************
54  **                 Upper Layer Protocol Interface module
55  ******************************************************************************
56  ******************************************************************************/
57 
58 /*
59  * Process L2CA_Connect request from the upper layer protocol.
60  */
61 
62 int
63 ng_l2cap_l2ca_con_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
64 {
65 	ng_l2cap_l2ca_con_ip	*ip = NULL;
66 	ng_l2cap_con_p		 con = NULL;
67 	ng_l2cap_chan_p		 ch = NULL;
68 	ng_l2cap_cmd_p		 cmd = NULL;
69 	int			 error = 0;
70 
71 	/* Check message */
72 	if (msg->header.arglen != sizeof(*ip)) {
73 		NG_L2CAP_ALERT(
74 "%s: %s - invalid L2CA_Connect request message size, size=%d\n",
75 			__func__, NG_NODE_NAME(l2cap->node),
76 			msg->header.arglen);
77 		error = EMSGSIZE;
78 		goto out;
79 	}
80 
81 	ip = (ng_l2cap_l2ca_con_ip *)(msg->data);
82 
83 	/* Check if we have connection to the remote unit */
84 	con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr);
85 	if (con == NULL) {
86 		/* Submit LP_ConnectReq to the lower layer */
87 		error = ng_l2cap_lp_con_req(l2cap, &ip->bdaddr);
88 		if (error != 0) {
89 			NG_L2CAP_ERR(
90 "%s: %s - unable to send LP_ConnectReq message, error=%d\n",
91 				__func__, NG_NODE_NAME(l2cap->node), error);
92 			goto out;
93 		}
94 
95 		/* This should not fail */
96 		con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr);
97 		KASSERT((con != NULL),
98 ("%s: %s - could not find connection!\n", __func__, NG_NODE_NAME(l2cap->node)));
99 	}
100 
101 	/*
102 	 * Create new empty channel descriptor. In case of any failure do
103 	 * not touch connection descriptor.
104 	 */
105 
106 	ch = ng_l2cap_new_chan(l2cap, con, ip->psm);
107 	if (ch == NULL) {
108 		error = ENOMEM;
109 		goto out;
110 	}
111 
112 	/* Now create L2CAP_ConnectReq command */
113 	cmd = ng_l2cap_new_cmd(ch->con, ch, ng_l2cap_get_ident(con),
114 			NG_L2CAP_CON_REQ, msg->header.token);
115 	if (cmd == NULL) {
116 		ng_l2cap_free_chan(ch);
117 		error = ENOMEM;
118 		goto out;
119 	}
120 
121 	if (cmd->ident == NG_L2CAP_NULL_IDENT) {
122 		ng_l2cap_free_cmd(cmd);
123 		ng_l2cap_free_chan(ch);
124 		error = EIO;
125 		goto out;
126 	}
127 
128 	/* Create L2CAP command packet */
129 	_ng_l2cap_con_req(cmd->aux, cmd->ident, ch->psm, ch->scid);
130 	if (cmd->aux == NULL) {
131 		ng_l2cap_free_cmd(cmd);
132 		ng_l2cap_free_chan(ch);
133 		error = ENOBUFS;
134 		goto out;
135 	}
136 
137 	ch->state = NG_L2CAP_W4_L2CAP_CON_RSP;
138 
139 	/* Link command to the queue */
140 	ng_l2cap_link_cmd(ch->con, cmd);
141 	ng_l2cap_lp_deliver(ch->con);
142 out:
143 	return (error);
144 } /* ng_l2cap_l2ca_con_req */
145 
146 /*
147  * Send L2CA_Connect response to the upper layer protocol.
148  */
149 
150 int
151 ng_l2cap_l2ca_con_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result,
152 		u_int16_t status)
153 {
154 	ng_l2cap_p		 l2cap = ch->con->l2cap;
155 	struct ng_mesg		*msg = NULL;
156 	ng_l2cap_l2ca_con_op	*op = NULL;
157 	int			 error = 0;
158 
159 	/* Check if upstream hook is connected and valid */
160 	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
161 		NG_L2CAP_ERR(
162 "%s: %s - unable to send L2CA_Connect response message. " \
163 "Hook is not connected or valid, psm=%d\n",
164 			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
165 
166 		return (ENOTCONN);
167 	}
168 
169 	/* Create and send L2CA_Connect response message */
170 	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CON,
171 		sizeof(*op), M_NOWAIT);
172 	if (msg == NULL)
173 		error = ENOMEM;
174 	else {
175 		msg->header.token = token;
176 		msg->header.flags |= NGF_RESP;
177 
178 		op = (ng_l2cap_l2ca_con_op *)(msg->data);
179 
180 		/*
181 		 * XXX Spec. says we should only populate LCID when result == 0
182 		 * What about PENDING? What the heck, for now always populate
183 		 * LCID :)
184 		 */
185 
186 		op->lcid = ch->scid;
187 		op->result = result;
188 		op->status = status;
189 
190 		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
191 	}
192 
193 	return (error);
194 } /* ng_l2cap_l2ca_con_rsp */
195 
196 /*
197  * Process L2CA_ConnectRsp request from the upper layer protocol.
198  */
199 
200 int
201 ng_l2cap_l2ca_con_rsp_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
202 {
203 	ng_l2cap_l2ca_con_rsp_ip	*ip = NULL;
204 	ng_l2cap_con_p			 con = NULL;
205 	ng_l2cap_chan_p			 ch = NULL;
206 	ng_l2cap_cmd_p			 cmd = NULL;
207 	u_int16_t			 dcid;
208 	int				 error = 0;
209 
210 	/* Check message */
211 	if (msg->header.arglen != sizeof(*ip)) {
212 		NG_L2CAP_ALERT(
213 "%s: %s - invalid L2CA_ConnectRsp request message size, size=%d\n",
214 			__func__, NG_NODE_NAME(l2cap->node),
215 			msg->header.arglen);
216 		error = EMSGSIZE;
217 		goto out;
218 	}
219 
220 	ip = (ng_l2cap_l2ca_con_rsp_ip *)(msg->data);
221 
222 	/* Check if we have this channel */
223 	ch = ng_l2cap_chan_by_scid(l2cap, ip->lcid);
224 	if (ch == NULL) {
225 		NG_L2CAP_ALERT(
226 "%s: %s - unexpected L2CA_ConnectRsp request message. " \
227 "Channel does not exist, lcid=%d\n",
228 			__func__, NG_NODE_NAME(l2cap->node), ip->lcid);
229 		error = ENOENT;
230 		goto out;
231 	}
232 
233 	/* Check channel state */
234 	if (ch->state != NG_L2CAP_W4_L2CA_CON_RSP) {
235 		NG_L2CAP_ERR(
236 "%s: %s - unexpected L2CA_ConnectRsp request message. " \
237 "Invalid channel state, state=%d, lcid=%d\n",
238 			__func__, NG_NODE_NAME(l2cap->node), ch->state,
239 			ip->lcid);
240 		error = EINVAL;
241 		goto out;
242 	}
243 
244 	dcid = ch->dcid;
245 	con = ch->con;
246 
247 	/*
248 	 * Now we are pretty much sure it is our response. So create and send
249 	 * L2CAP_ConnectRsp message to our peer.
250 	 */
251 
252 	if (ch->ident != ip->ident)
253 		NG_L2CAP_WARN(
254 "%s: %s - channel ident and response ident do not match, scid=%d, ident=%d. " \
255 "Will use response ident=%d\n",
256 			__func__, NG_NODE_NAME(l2cap->node), ch->scid,
257 			ch->ident, ip->ident);
258 
259 	/* Check result */
260 	switch (ip->result) {
261 	case NG_L2CAP_SUCCESS:
262 		ch->state = NG_L2CAP_CONFIG;
263 		ch->cfg_state = 0;
264 		break;
265 
266 	case NG_L2CAP_PENDING:
267 		break;
268 
269 	default:
270 		ng_l2cap_free_chan(ch);
271 		ch = NULL;
272 		break;
273 	}
274 
275 	/* Create L2CAP command */
276 	cmd = ng_l2cap_new_cmd(con, ch, ip->ident, NG_L2CAP_CON_RSP,
277 			msg->header.token);
278 	if (cmd == NULL) {
279 		if (ch != NULL)
280 			ng_l2cap_free_chan(ch);
281 
282 		error = ENOMEM;
283 		goto out;
284 	}
285 
286 	_ng_l2cap_con_rsp(cmd->aux, cmd->ident, ip->lcid, dcid,
287 		ip->result, ip->status);
288 	if (cmd->aux == NULL) {
289 		if (ch != NULL)
290 			ng_l2cap_free_chan(ch);
291 
292 		ng_l2cap_free_cmd(cmd);
293 		error = ENOBUFS;
294 		goto out;
295 	}
296 
297 	/* Link command to the queue */
298 	ng_l2cap_link_cmd(con, cmd);
299 	ng_l2cap_lp_deliver(con);
300 out:
301 	return (error);
302 } /* ng_l2cap_l2ca_con_rsp_req */
303 
304 /*
305  * Send L2CAP_ConnectRsp response to the upper layer
306  */
307 
308 int
309 ng_l2cap_l2ca_con_rsp_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result)
310 {
311 	ng_l2cap_p			 l2cap = ch->con->l2cap;
312 	struct ng_mesg			*msg = NULL;
313 	ng_l2cap_l2ca_con_rsp_op	*op = NULL;
314 	int				 error = 0;
315 
316 	/* Check if upstream hook is connected and valid */
317 	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
318 		NG_L2CAP_ERR(
319 "%s: %s - unable to send L2CA_ConnectRsp response message. " \
320 "Hook is not connected or valid, psm=%d\n",
321 			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
322 
323 		return (ENOTCONN);
324 	}
325 
326 	/* Create and send L2CA_ConnectRsp response message */
327 	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CON_RSP,
328 		sizeof(*op), M_NOWAIT);
329 	if (msg == NULL)
330 		error = ENOMEM;
331 	else {
332 		msg->header.token = token;
333 		msg->header.flags |= NGF_RESP;
334 
335 		op = (ng_l2cap_l2ca_con_rsp_op *)(msg->data);
336 		op->result = result;
337 
338 		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
339 	}
340 
341 	return (error);
342 } /* ng_l2cap_l2ca_con_rsp_rsp */
343 
344 /*
345  * Send L2CA_ConnectInd message to the upper layer protocol.
346  */
347 
348 int
349 ng_l2cap_l2ca_con_ind(ng_l2cap_chan_p ch)
350 {
351 	ng_l2cap_p			 l2cap = ch->con->l2cap;
352 	struct ng_mesg			*msg = NULL;
353 	ng_l2cap_l2ca_con_ind_ip	*ip = NULL;
354 	int				 error = 0;
355 
356 	/* Check if upstream hook is connected and valid */
357 	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
358 		NG_L2CAP_ERR(
359 "%s: %s - unable to send L2CA_ConnectInd message. " \
360 "Hook is not connected or valid, psm=%d\n",
361 			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
362 
363 		return (ENOTCONN);
364 	}
365 
366 	/* Create and send L2CA_ConnectInd message */
367 	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CON_IND,
368 		sizeof(*ip), M_NOWAIT);
369 	if (msg == NULL)
370 		error = ENOMEM;
371 	else {
372 		ip = (ng_l2cap_l2ca_con_ind_ip *)(msg->data);
373 
374 		bcopy(&ch->con->remote, &ip->bdaddr, sizeof(ip->bdaddr));
375 		ip->lcid = ch->scid;
376 		ip->psm = ch->psm;
377 		ip->ident = ch->ident;
378 
379 		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
380 	}
381 
382 	return (error);
383 } /* ng_l2cap_l2ca_con_ind */
384 
385 /*
386  * Process L2CA_Config request from the upper layer protocol
387  */
388 
389 int
390 ng_l2cap_l2ca_cfg_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
391 {
392 	ng_l2cap_l2ca_cfg_ip	*ip = NULL;
393 	ng_l2cap_chan_p		 ch = NULL;
394 	ng_l2cap_cmd_p		 cmd = NULL;
395 	struct mbuf		*opt = NULL;
396         u_int16_t		*mtu = NULL, *flush_timo = NULL;
397         ng_l2cap_flow_p		 flow = NULL;
398 	int			 error = 0;
399 
400 	/* Check message */
401 	if (msg->header.arglen != sizeof(*ip)) {
402 		NG_L2CAP_ALERT(
403 "%s: %s - Invalid L2CA_Config request message size, size=%d\n",
404 			__func__, NG_NODE_NAME(l2cap->node),
405 			msg->header.arglen);
406 		error = EMSGSIZE;
407 		goto out;
408 	}
409 
410 	ip = (ng_l2cap_l2ca_cfg_ip *)(msg->data);
411 
412 	/* Check if we have this channel */
413 	ch = ng_l2cap_chan_by_scid(l2cap, ip->lcid);
414 	if (ch == NULL) {
415 		NG_L2CAP_ERR(
416 "%s: %s - unexpected L2CA_Config request message. " \
417 "Channel does not exist, lcid=%d\n",
418 			__func__, NG_NODE_NAME(l2cap->node), ip->lcid);
419 		error = ENOENT;
420 		goto out;
421 	}
422 
423 	/* Check channel state */
424 	if (ch->state != NG_L2CAP_OPEN && ch->state != NG_L2CAP_CONFIG) {
425 		NG_L2CAP_ERR(
426 "%s: %s - unexpected L2CA_Config request message. " \
427 "Invalid channel state, state=%d, lcid=%d\n",
428 			__func__, NG_NODE_NAME(l2cap->node), ch->state,
429 			ch->scid);
430 		error = EINVAL;
431 		goto out;
432 	}
433 
434 	/* Set requested channel configuration options */
435 	ch->imtu = ip->imtu;
436 	bcopy(&ip->oflow, &ch->oflow, sizeof(ch->oflow));
437 	ch->flush_timo = ip->flush_timo;
438 	ch->link_timo = ip->link_timo;
439 
440 	/* Compare channel settings with defaults */
441 	if (ch->imtu != NG_L2CAP_MTU_DEFAULT)
442 		mtu = &ch->imtu;
443 	if (ch->flush_timo != NG_L2CAP_FLUSH_TIMO_DEFAULT)
444 		flush_timo = &ch->flush_timo;
445 	if (bcmp(ng_l2cap_default_flow(), &ch->oflow, sizeof(ch->oflow)) != 0)
446 		flow = &ch->oflow;
447 
448 	/* Create configuration options */
449 	_ng_l2cap_build_cfg_options(opt, mtu, flush_timo, flow);
450 	if (opt == NULL) {
451                 error = ENOBUFS;
452 		goto out;
453 	}
454 
455 	/* Create L2CAP command descriptor */
456 	cmd = ng_l2cap_new_cmd(ch->con, ch, ng_l2cap_get_ident(ch->con),
457 			NG_L2CAP_CFG_REQ, msg->header.token);
458 	if (cmd == NULL) {
459 		NG_FREE_M(opt);
460 		error = ENOMEM;
461 		goto out;
462 	}
463 
464 	if (cmd->ident == NG_L2CAP_NULL_IDENT) {
465 		ng_l2cap_free_cmd(cmd);
466 		NG_FREE_M(opt);
467 		error = EIO;
468 		goto out;
469 	}
470 
471 	/* Create L2CAP command packet */
472 	_ng_l2cap_cfg_req(cmd->aux, cmd->ident, ch->dcid, 0, opt);
473 	if (cmd->aux == NULL) {
474 		ng_l2cap_free_cmd(cmd);
475 		error =  ENOBUFS;
476 		goto out;
477 	}
478 
479 	/* Adjust channel state for re-configuration */
480 	if (ch->state == NG_L2CAP_OPEN) {
481 		ch->state = NG_L2CAP_CONFIG;
482 		ch->cfg_state = 0;
483 	}
484 
485         /* Link command to the queue */
486 	ng_l2cap_link_cmd(ch->con, cmd);
487 	ng_l2cap_lp_deliver(ch->con);
488 out:
489 	return (error);
490 } /* ng_l2cap_l2ca_cfg_req */
491 
492 /*
493  * Send L2CA_Config response to the upper layer protocol
494  */
495 
496 int
497 ng_l2cap_l2ca_cfg_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result)
498 {
499 	ng_l2cap_p		 l2cap = ch->con->l2cap;
500 	struct ng_mesg		*msg = NULL;
501 	ng_l2cap_l2ca_cfg_op	*op = NULL;
502 	int			 error = 0;
503 
504 	/* Check if upstream hook is connected and valid */
505 	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
506 		NG_L2CAP_ERR(
507 "%s: %s - unable to send L2CA_Config response message. " \
508 "Hook is not connected or valid, psm=%d\n",
509 			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
510 
511 		return (ENOTCONN);
512 	}
513 
514 	/* Create and send L2CA_Config response message */
515 	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CFG,
516 		sizeof(*op), M_NOWAIT);
517 	if (msg == NULL)
518 		error = ENOMEM;
519 	else {
520 		msg->header.token = token;
521 		msg->header.flags |= NGF_RESP;
522 
523 		op = (ng_l2cap_l2ca_cfg_op *)(msg->data);
524 		op->result = result;
525 		op->imtu = ch->imtu;
526 		bcopy(&ch->oflow, &op->oflow, sizeof(op->oflow));
527 		op->flush_timo = ch->flush_timo;
528 
529 		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
530 
531 		if (error == 0 && result == NG_L2CAP_SUCCESS) {
532 			ch->cfg_state |= NG_L2CAP_CFG_IN;
533 
534 			if (ch->cfg_state == NG_L2CAP_CFG_BOTH)
535 				ch->state = NG_L2CAP_OPEN;
536 		}
537 	}
538 
539 	return (error);
540 } /* ng_l2cap_l2ca_cfg_rsp */
541 
542 /*
543  * Process L2CA_ConfigRsp request from the upper layer protocol
544  *
545  * XXX XXX XXX
546  *
547  * NOTE: The Bluetooth specification says that Configuration_Response
548  * (L2CA_ConfigRsp) should be used to issue response to configuration request
549  * indication. The minor problem here is L2CAP command ident. We should use
550  * ident from original L2CAP request to make sure our peer can match request
551  * and response. For some reason Bluetooth specification does not include
552  * ident field into L2CA_ConfigInd and L2CA_ConfigRsp messages. This seems
553  * strange to me, because L2CA_ConnectInd and L2CA_ConnectRsp do have ident
554  * field. So we should store last known L2CAP request command ident in channel.
555  * Also it seems that upper layer can not reject configuration request, as
556  * Configuration_Response message does not have status/reason field.
557  */
558 
559 int
560 ng_l2cap_l2ca_cfg_rsp_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
561 {
562 	ng_l2cap_l2ca_cfg_rsp_ip	*ip = NULL;
563 	ng_l2cap_chan_p			 ch = NULL;
564 	ng_l2cap_cmd_p			 cmd = NULL;
565 	struct mbuf			*opt = NULL;
566 	u_int16_t			*mtu = NULL;
567 	ng_l2cap_flow_p			 flow = NULL;
568 	int				 error = 0;
569 
570 	/* Check message */
571 	if (msg->header.arglen != sizeof(*ip)) {
572 		NG_L2CAP_ALERT(
573 "%s: %s - invalid L2CA_ConfigRsp request message size, size=%d\n",
574 			__func__, NG_NODE_NAME(l2cap->node),
575 			msg->header.arglen);
576 		error = EMSGSIZE;
577 		goto out;
578 	}
579 
580 	ip = (ng_l2cap_l2ca_cfg_rsp_ip *)(msg->data);
581 
582 	/* Check if we have this channel */
583 	ch = ng_l2cap_chan_by_scid(l2cap, ip->lcid);
584 	if (ch == NULL) {
585 		NG_L2CAP_ERR(
586 "%s: %s - unexpected L2CA_ConfigRsp request message. " \
587 "Channel does not exist, lcid=%d\n",
588 			__func__, NG_NODE_NAME(l2cap->node), ip->lcid);
589 		error = ENOENT;
590 		goto out;
591 	}
592 
593 	/* Check channel state */
594 	if (ch->state != NG_L2CAP_CONFIG) {
595 		NG_L2CAP_ERR(
596 "%s: %s - unexpected L2CA_ConfigRsp request message. " \
597 "Invalid channel state, state=%d, lcid=%d\n",
598 			__func__, NG_NODE_NAME(l2cap->node), ch->state,
599 			ch->scid);
600 		error = EINVAL;
601 		goto out;
602 	}
603 
604 	/* Set channel settings */
605 	if (ip->omtu != ch->omtu) {
606 		ch->omtu = ip->omtu;
607 		mtu = &ch->omtu;
608 	}
609 
610 	if (bcmp(&ip->iflow, &ch->iflow, sizeof(ch->iflow)) != 0) {
611 		bcopy(&ip->iflow, &ch->iflow, sizeof(ch->iflow));
612 		flow = &ch->iflow;
613 	}
614 
615 	if (mtu != NULL || flow != NULL) {
616 		_ng_l2cap_build_cfg_options(opt, mtu, NULL, flow);
617 		if (opt == NULL) {
618 			error = ENOBUFS;
619 			goto out;
620 		}
621 	}
622 
623 	/* Create L2CAP command */
624 	cmd = ng_l2cap_new_cmd(ch->con, ch, ch->ident, NG_L2CAP_CFG_RSP,
625 			msg->header.token);
626 	if (cmd == NULL) {
627 		NG_FREE_M(opt);
628 		error = ENOMEM;
629 		goto out;
630 	}
631 
632 	_ng_l2cap_cfg_rsp(cmd->aux,cmd->ident,ch->dcid,0,NG_L2CAP_SUCCESS,opt);
633 	if (cmd->aux == NULL) {
634 		ng_l2cap_free_cmd(cmd);
635 		error = ENOBUFS;
636 		goto out;
637 	}
638 
639 	/* XXX FIXME - not here ??? */
640 	ch->cfg_state |= NG_L2CAP_CFG_OUT;
641 	if (ch->cfg_state == NG_L2CAP_CFG_BOTH)
642 		ch->state = NG_L2CAP_OPEN;
643 
644 	/* Link command to the queue */
645 	ng_l2cap_link_cmd(ch->con, cmd);
646 	ng_l2cap_lp_deliver(ch->con);
647 out:
648 	return (error);
649 } /* ng_l2cap_l2ca_cfg_rsp_req */
650 
651 /*
652  * Send L2CA_ConfigRsp response to the upper layer protocol
653  */
654 
655 int
656 ng_l2cap_l2ca_cfg_rsp_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result)
657 {
658 	ng_l2cap_p			 l2cap = ch->con->l2cap;
659 	struct ng_mesg			*msg = NULL;
660 	ng_l2cap_l2ca_cfg_rsp_op	*op = NULL;
661 	int				 error = 0;
662 
663 	/* Check if upstream hook is connected and valid */
664 	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
665 		NG_L2CAP_ERR(
666 "%s: %s - unable to send L2CA_ConfigRsp response message. " \
667 "Hook is not connected or valid, psm=%d\n",
668 			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
669 
670 		return (ENOTCONN);
671 	}
672 
673 	/* Create and send L2CA_ConfigRsp response message */
674 	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CFG_RSP,
675 		sizeof(*op), M_NOWAIT);
676 	if (msg == NULL)
677 		error = ENOMEM;
678 	else {
679 		msg->header.token = token;
680 		msg->header.flags |= NGF_RESP;
681 
682 		op = (ng_l2cap_l2ca_cfg_rsp_op *)(msg->data);
683 		op->result = result;
684 
685 		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
686 	}
687 
688 	return (error);
689 } /* ng_l2cap_l2ca_cfg_rsp_rsp */
690 
691 /*
692  * Send L2CA_ConfigInd message to the upper layer protocol
693  *
694  * XXX XXX XXX
695  *
696  * NOTE: The Bluetooth specification says that Configuration_Response
697  * (L2CA_ConfigRsp) should be used to issue response to configuration request
698  * indication. The minor problem here is L2CAP command ident. We should use
699  * ident from original L2CAP request to make sure our peer can match request
700  * and response. For some reason Bluetooth specification does not include
701  * ident field into L2CA_ConfigInd and L2CA_ConfigRsp messages. This seems
702  * strange to me, because L2CA_ConnectInd and L2CA_ConnectRsp do have ident
703  * field. So we should store last known L2CAP request command ident in channel.
704  * Also it seems that upper layer can not reject configuration request, as
705  * Configuration_Response message does not have status/reason field.
706  */
707 
708 int
709 ng_l2cap_l2ca_cfg_ind(ng_l2cap_chan_p ch)
710 {
711 	ng_l2cap_p			 l2cap = ch->con->l2cap;
712 	struct ng_mesg			*msg = NULL;
713 	ng_l2cap_l2ca_cfg_ind_ip	*ip = NULL;
714 	int				 error = 0;
715 
716 	/* Check if upstream hook is connected and valid */
717 	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
718 		NG_L2CAP_ERR(
719 "%s: %s - Unable to send L2CA_ConfigInd message. " \
720 "Hook is not connected or valid, psm=%d\n",
721 			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
722 
723 		return (ENOTCONN);
724 	}
725 
726 	/* Create and send L2CA_ConnectInd message */
727 	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_CFG_IND,
728 			sizeof(*ip), M_NOWAIT);
729 	if (msg == NULL)
730 		error = ENOMEM;
731 	else {
732 		ip = (ng_l2cap_l2ca_cfg_ind_ip *)(msg->data);
733 		ip->lcid = ch->scid;
734 		ip->omtu = ch->omtu;
735 		bcopy(&ch->iflow, &ip->iflow, sizeof(ip->iflow));
736 		ip->flush_timo = ch->flush_timo;
737 
738 		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
739 	}
740 
741 	return (error);
742 } /* ng_l2cap_l2ca_cfg_ind */
743 
744 /*
745  * Process L2CA_Write event
746  */
747 
748 int
749 ng_l2cap_l2ca_write_req(ng_l2cap_p l2cap, struct mbuf *m)
750 {
751 	ng_l2cap_l2ca_hdr_t	*l2ca_hdr = NULL;
752 	ng_l2cap_chan_p		 ch = NULL;
753 	ng_l2cap_cmd_p		 cmd = NULL;
754 	int			 error = 0;
755 	u_int32_t		 token = 0;
756 
757 	/* Make sure we can access L2CA data packet header */
758 	if (m->m_pkthdr.len < sizeof(*l2ca_hdr)) {
759 		NG_L2CAP_ERR(
760 "%s: %s - L2CA Data packet too small, len=%d\n",
761 			__func__,NG_NODE_NAME(l2cap->node),m->m_pkthdr.len);
762 		error = EMSGSIZE;
763 		goto drop;
764 	}
765 
766 	/* Get L2CA data packet header */
767 	NG_L2CAP_M_PULLUP(m, sizeof(*l2ca_hdr));
768 	if (m == NULL)
769 		return (ENOBUFS);
770 
771 	l2ca_hdr = mtod(m, ng_l2cap_l2ca_hdr_t *);
772 	token = l2ca_hdr->token;
773 	m_adj(m, sizeof(*l2ca_hdr));
774 
775 	/* Verify payload size */
776 	if (l2ca_hdr->length != m->m_pkthdr.len) {
777 		NG_L2CAP_ERR(
778 "%s: %s - invalid L2CA Data packet. " \
779 "Payload length does not match, length=%d, len=%d\n",
780 			__func__, NG_NODE_NAME(l2cap->node), l2ca_hdr->length,
781 			m->m_pkthdr.len);
782 		error = EMSGSIZE;
783 		goto drop;
784 	}
785 
786 	/* Check channel ID */
787 	if (l2ca_hdr->lcid < NG_L2CAP_FIRST_CID) {
788 		NG_L2CAP_ERR(
789 "%s: %s - invalid L2CA Data packet. Inavlid channel ID, cid=%d\n",
790 			__func__, NG_NODE_NAME(l2cap->node), l2ca_hdr->lcid);
791 		error = EINVAL;
792 		goto drop;
793 	}
794 
795 	/* Verify that we have the channel and make sure it is open */
796 	ch = ng_l2cap_chan_by_scid(l2cap, l2ca_hdr->lcid);
797 	if (ch == NULL) {
798 		NG_L2CAP_ERR(
799 "%s: %s - invalid L2CA Data packet. Channel does not exist, cid=%d\n",
800 			__func__, NG_NODE_NAME(l2cap->node), l2ca_hdr->lcid);
801 		error = ENOENT;
802 		goto drop;
803 	}
804 
805 	if (ch->state != NG_L2CAP_OPEN) {
806 		NG_L2CAP_ERR(
807 "%s: %s - invalid L2CA Data packet. Invalid channel state, scid=%d, state=%d\n",
808 			 __func__, NG_NODE_NAME(l2cap->node), ch->scid,
809 			ch->state);
810 		error = EHOSTDOWN;
811 		goto drop; /* XXX not always - re-configure */
812 	}
813 
814 	/* Create L2CAP command descriptor */
815 	cmd = ng_l2cap_new_cmd(ch->con, ch, 0, NGM_L2CAP_L2CA_WRITE, token);
816 	if (cmd == NULL) {
817 		error = ENOMEM;
818 		goto drop;
819 	}
820 
821 	/* Attach data packet and link command to the queue */
822 	cmd->aux = m;
823 	ng_l2cap_link_cmd(ch->con, cmd);
824 	ng_l2cap_lp_deliver(ch->con);
825 
826 	return (error);
827 drop:
828 	NG_FREE_M(m);
829 
830 	return (error);
831 } /* ng_l2cap_l2ca_write_req */
832 
833 /*
834  * Send L2CA_Write response
835  */
836 
837 int
838 ng_l2cap_l2ca_write_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result,
839 		u_int16_t length)
840 {
841 	ng_l2cap_p		 l2cap = ch->con->l2cap;
842 	struct ng_mesg		*msg = NULL;
843 	ng_l2cap_l2ca_write_op	*op = NULL;
844 	int			 error = 0;
845 
846 	/* Check if upstream hook is connected and valid */
847 	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
848 		NG_L2CAP_ERR(
849 "%s: %s - unable to send L2CA_WriteRsp message. " \
850 "Hook is not connected or valid, psm=%d\n",
851 			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
852 
853 		return (ENOTCONN);
854 	}
855 
856 	/* Create and send L2CA_WriteRsp message */
857 	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_WRITE,
858 			sizeof(*op), M_NOWAIT);
859 	if (msg == NULL)
860 		error = ENOMEM;
861 	else {
862 		msg->header.token = token;
863 		msg->header.flags |= NGF_RESP;
864 
865 		op = (ng_l2cap_l2ca_write_op *)(msg->data);
866 		op->result = result;
867 		op->length = length;
868 		op->lcid   = ch->scid;
869 
870 		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
871 	}
872 
873 	return (error);
874 } /* ng_l2cap_l2ca_write_rsp */
875 
876 /*
877  * Receive packet from the lower layer protocol and send it to the upper
878  * layer protocol (L2CAP_Read)
879  */
880 
881 int
882 ng_l2cap_l2ca_receive(ng_l2cap_con_p con)
883 {
884 	ng_l2cap_p	 l2cap = con->l2cap;
885 	ng_l2cap_hdr_t	*hdr = NULL;
886 	ng_l2cap_chan_p  ch = NULL;
887 	int		 error = 0;
888 
889 	NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));
890 	if (con->rx_pkt == NULL)
891 		return (ENOBUFS);
892 
893 	hdr = mtod(con->rx_pkt, ng_l2cap_hdr_t *);
894 
895 	/* Check channel */
896 	ch = ng_l2cap_chan_by_scid(l2cap, hdr->dcid);
897 	if (ch == NULL) {
898 		NG_L2CAP_ERR(
899 "%s: %s - unexpected L2CAP data packet. Channel does not exist, cid=%d\n",
900 			__func__, NG_NODE_NAME(l2cap->node), hdr->dcid);
901 		error = ENOENT;
902 		goto drop;
903 	}
904 
905 	/* Check channel state */
906 	if (ch->state != NG_L2CAP_OPEN) {
907 		NG_L2CAP_WARN(
908 "%s: %s - unexpected L2CAP data packet. " \
909 "Invalid channel state, cid=%d, state=%d\n",
910 			__func__, NG_NODE_NAME(l2cap->node), ch->scid,
911 			ch->state);
912 		error = EHOSTDOWN; /* XXX not always - re-configuration */
913 		goto drop;
914 	}
915 
916 	/* Check payload size and channel's MTU */
917 	if (hdr->length > ch->imtu) {
918 		NG_L2CAP_ERR(
919 "%s: %s - invalid L2CAP data packet. " \
920 "Packet too big, length=%d, imtu=%d, cid=%d\n",
921 			__func__, NG_NODE_NAME(l2cap->node), hdr->length,
922 			ch->imtu, ch->scid);
923 		error = EMSGSIZE;
924 		goto drop;
925 	}
926 
927 	/*
928 	 * If we got here then everything looks good and we can sent packet
929 	 * to the upper layer protocol.
930 	 */
931 
932 	/* Check if upstream hook is connected and valid */
933 	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
934 		NG_L2CAP_ERR(
935 "%s: %s - unable to send L2CAP data packet. " \
936 "Hook is not connected or valid, psm=%d\n",
937 			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
938 		error = ENOTCONN;
939 		goto drop;
940 	}
941 
942 	NG_SEND_DATA_ONLY(error, l2cap->l2c, con->rx_pkt);
943 	con->rx_pkt = NULL;
944 drop:
945 	NG_FREE_M(con->rx_pkt); /* checks for != NULL */
946 
947 	return (error);
948 } /* ng_l2cap_receive */
949 
950 /*
951  * Receive connectioless (multicast) packet from the lower layer protocol and
952  * send it to the upper layer protocol
953  */
954 
955 int
956 ng_l2cap_l2ca_clt_receive(ng_l2cap_con_p con)
957 {
958 	struct _clt_pkt {
959 		ng_l2cap_hdr_t		 h;
960 		ng_l2cap_clt_hdr_t	 c_h;
961 	} __attribute__ ((packed))	*hdr = NULL;
962 	ng_l2cap_p			 l2cap = con->l2cap;
963 	int				 length, error = 0;
964 
965 	NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));
966 	if (con->rx_pkt == NULL)
967 		return (ENOBUFS);
968 
969 	hdr = mtod(con->rx_pkt, struct _clt_pkt *);
970 
971 	/* Check packet */
972 	length = con->rx_pkt->m_pkthdr.len - sizeof(*hdr);
973 	if (length < 0) {
974 		NG_L2CAP_ERR(
975 "%s: %s - invalid L2CAP CLT data packet. Packet too small, length=%d\n",
976 			__func__, NG_NODE_NAME(l2cap->node), length);
977 		error = EMSGSIZE;
978 		goto drop;
979 	}
980 
981 	/* Check payload size against CLT MTU */
982 	if (length > NG_L2CAP_MTU_DEFAULT) {
983 		NG_L2CAP_ERR(
984 "%s: %s - invalid L2CAP CLT data packet. Packet too big, length=%d, mtu=%d\n",
985 			__func__, NG_NODE_NAME(l2cap->node), length,
986 			NG_L2CAP_MTU_DEFAULT);
987 		error = EMSGSIZE;
988 		goto drop;
989 	}
990 
991 	hdr->c_h.psm = le16toh(hdr->c_h.psm);
992 
993 	/*
994 	 * If we got here then everything looks good and we can sent packet
995 	 * to the upper layer protocol.
996 	 */
997 
998 	/* Select upstream hook based on PSM */
999 	switch (hdr->c_h.psm) {
1000 	case NG_L2CAP_PSM_SDP:
1001 		if (l2cap->flags & NG_L2CAP_CLT_SDP_DISABLED)
1002 			goto drop;
1003 		break;
1004 
1005 	case NG_L2CAP_PSM_RFCOMM:
1006 		if (l2cap->flags & NG_L2CAP_CLT_RFCOMM_DISABLED)
1007 			goto drop;
1008 		break;
1009 
1010 	case NG_L2CAP_PSM_TCP:
1011 		if (l2cap->flags & NG_L2CAP_CLT_TCP_DISABLED)
1012 			goto drop;
1013 		break;
1014         }
1015 
1016 	/* Check if upstream hook is connected and valid */
1017 	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
1018 		NG_L2CAP_ERR(
1019 "%s: %s - unable to send L2CAP CLT data packet. " \
1020 "Hook is not connected or valid, psm=%d\n",
1021 			__func__, NG_NODE_NAME(l2cap->node), hdr->c_h.psm);
1022 		error = ENOTCONN;
1023 		goto drop;
1024 	}
1025 
1026 	NG_SEND_DATA_ONLY(error, l2cap->l2c, con->rx_pkt);
1027 	con->rx_pkt = NULL;
1028 drop:
1029 	NG_FREE_M(con->rx_pkt); /* checks for != NULL */
1030 
1031 	return (error);
1032 } /* ng_l2cap_l2ca_clt_receive */
1033 
1034 /*
1035  * Send L2CA_QoSViolationInd to the upper layer protocol
1036  */
1037 
1038 int
1039 ng_l2cap_l2ca_qos_ind(ng_l2cap_chan_p ch)
1040 {
1041 	ng_l2cap_p			 l2cap = ch->con->l2cap;
1042 	struct ng_mesg			*msg = NULL;
1043 	ng_l2cap_l2ca_qos_ind_ip	*ip = NULL;
1044 	int				 error = 0;
1045 
1046 	/* Check if upstream hook is connected and valid */
1047 	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
1048 		NG_L2CAP_ERR(
1049 "%s: %s - unable to send L2CA_QoSViolationInd message. " \
1050 "Hook is not connected or valid, psm=%d\n",
1051 			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
1052 
1053 		return (ENOTCONN);
1054 	}
1055 
1056 	/* Create and send L2CA_QoSViolationInd message */
1057 	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_QOS_IND,
1058 		sizeof(*ip), M_NOWAIT);
1059 	if (msg == NULL)
1060 		error = ENOMEM;
1061 	else {
1062 		ip = (ng_l2cap_l2ca_qos_ind_ip *)(msg->data);
1063 		bcopy(&ch->con->remote, &ip->bdaddr, sizeof(ip->bdaddr));
1064 		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
1065 	}
1066 
1067 	return (error);
1068 } /* ng_l2cap_l2ca_qos_ind */
1069 
1070 /*
1071  * Process L2CA_Disconnect request from the upper layer protocol.
1072  */
1073 
1074 int
1075 ng_l2cap_l2ca_discon_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
1076 {
1077 	ng_l2cap_l2ca_discon_ip	*ip = NULL;
1078 	ng_l2cap_chan_p		 ch = NULL;
1079 	ng_l2cap_cmd_p		 cmd = NULL;
1080 	int			 error = 0;
1081 
1082 	/* Check message */
1083 	if (msg->header.arglen != sizeof(*ip)) {
1084 		NG_L2CAP_ALERT(
1085 "%s: %s - invalid L2CA_Disconnect request message size, size=%d\n",
1086 			__func__, NG_NODE_NAME(l2cap->node),
1087 			msg->header.arglen);
1088 		error = EMSGSIZE;
1089 		goto out;
1090 	}
1091 
1092 	ip = (ng_l2cap_l2ca_discon_ip *)(msg->data);
1093 
1094 	/* Check if we have this channel */
1095 	ch = ng_l2cap_chan_by_scid(l2cap, ip->lcid);
1096 	if (ch == NULL) {
1097 		NG_L2CAP_ERR(
1098 "%s: %s - unexpected L2CA_Disconnect request message. " \
1099 "Channel does not exist, lcid=%d\n",
1100 			__func__, NG_NODE_NAME(l2cap->node), ip->lcid);
1101 		error = ENOENT;
1102 		goto out;
1103 	}
1104 
1105 	/* Check channel state */
1106 	if (ch->state != NG_L2CAP_CONFIG && ch->state != NG_L2CAP_OPEN &&
1107 	    ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) {
1108 		NG_L2CAP_ERR(
1109 "%s: %s - unexpected L2CA_Disconnect request message. " \
1110 "Invalid channel state, state=%d, lcid=%d\n",
1111 			__func__, NG_NODE_NAME(l2cap->node), ch->state,
1112 			ch->scid);
1113 		error = EINVAL;
1114 		goto out;
1115 	}
1116 
1117 	/* Create and send L2CAP_DisconReq message */
1118 	cmd = ng_l2cap_new_cmd(ch->con, ch, ng_l2cap_get_ident(ch->con),
1119 			NG_L2CAP_DISCON_REQ, msg->header.token);
1120 	if (cmd == NULL) {
1121 		ng_l2cap_free_chan(ch);
1122 		error = ENOMEM;
1123 		goto out;
1124 	}
1125 
1126 	if (cmd->ident == NG_L2CAP_NULL_IDENT) {
1127 		ng_l2cap_free_chan(ch);
1128 		ng_l2cap_free_cmd(cmd);
1129 		error = EIO;
1130 		goto out;
1131 	}
1132 
1133 	_ng_l2cap_discon_req(cmd->aux, cmd->ident, ch->dcid, ch->scid);
1134 	if (cmd->aux == NULL) {
1135 		ng_l2cap_free_chan(ch);
1136 		ng_l2cap_free_cmd(cmd);
1137 		error = ENOBUFS;
1138 		goto out;
1139 	}
1140 
1141 	ch->state = NG_L2CAP_W4_L2CAP_DISCON_RSP;
1142 
1143 	/* Link command to the queue */
1144 	ng_l2cap_link_cmd(ch->con, cmd);
1145 	ng_l2cap_lp_deliver(ch->con);
1146 out:
1147 	return (error);
1148 } /* ng_l2cap_l2ca_discon_req */
1149 
1150 /*
1151  * Send L2CA_Disconnect response to the upper layer protocol
1152  */
1153 
1154 int
1155 ng_l2cap_l2ca_discon_rsp(ng_l2cap_chan_p ch, u_int32_t token, u_int16_t result)
1156 {
1157 	ng_l2cap_p		 l2cap = ch->con->l2cap;
1158 	struct ng_mesg		*msg = NULL;
1159 	ng_l2cap_l2ca_discon_op	*op = NULL;
1160 	int			 error = 0;
1161 
1162 	/* Check if upstream hook is connected and valid */
1163 	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
1164 		NG_L2CAP_ERR(
1165 "%s: %s - unable to send L2CA_Disconnect response message. " \
1166 "Hook is not connected or valid, psm=%d\n",
1167 			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
1168 
1169 		return (ENOTCONN);
1170 	}
1171 
1172 	/* Create and send L2CA_Disconnect response message */
1173 	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_DISCON,
1174 		sizeof(*op), M_NOWAIT);
1175 	if (msg == NULL)
1176 		error = ENOMEM;
1177 	else {
1178 		msg->header.token = token;
1179 		msg->header.flags |= NGF_RESP;
1180 
1181 		op = (ng_l2cap_l2ca_discon_op *)(msg->data);
1182 		op->result = result;
1183 
1184 		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
1185 	}
1186 
1187 	return (error);
1188 } /* ng_l2cap_l2ca_discon_rsp */
1189 
1190 /*
1191  * Send L2CA_DisconnectInd message to the upper layer protocol.
1192  */
1193 
1194 int
1195 ng_l2cap_l2ca_discon_ind(ng_l2cap_chan_p ch)
1196 {
1197 	ng_l2cap_p			 l2cap = ch->con->l2cap;
1198 	struct ng_mesg			*msg = NULL;
1199 	ng_l2cap_l2ca_discon_ind_ip	*ip = NULL;
1200 	int				 error = 0;
1201 
1202 	/* Check if upstream hook is connected and valid */
1203 	if (l2cap->l2c == NULL || NG_HOOK_NOT_VALID(l2cap->l2c)) {
1204 		NG_L2CAP_ERR(
1205 "%s: %s - unable to send L2CA_DisconnectInd message. " \
1206 "Hook is not connected or valid, psm=%d\n",
1207 			__func__, NG_NODE_NAME(l2cap->node), ch->psm);
1208 
1209 		return (ENOTCONN);
1210 	}
1211 
1212 	/* Create and send L2CA_DisconnectInd message */
1213 	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_DISCON_IND,
1214 		sizeof(*ip), M_NOWAIT);
1215 	if (msg == NULL)
1216 		error = ENOMEM;
1217 	else {
1218 		ip = (ng_l2cap_l2ca_discon_ind_ip *)(msg->data);
1219 		ip->lcid = ch->scid;
1220 		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->l2c, 0);
1221 	}
1222 
1223 	return (error);
1224 } /* ng_l2cap_l2ca_discon_ind */
1225 
1226 /*
1227  * Process L2CA_GroupCreate request from the upper layer protocol.
1228  * XXX FIXME
1229  */
1230 
1231 int
1232 ng_l2cap_l2ca_grp_create(ng_l2cap_p l2cap, struct ng_mesg *msg)
1233 {
1234 	return (ENOTSUP);
1235 } /* ng_l2cap_l2ca_grp_create */
1236 
1237 /*
1238  * Process L2CA_GroupClose request from the upper layer protocol
1239  * XXX FIXME
1240  */
1241 
1242 int
1243 ng_l2cap_l2ca_grp_close(ng_l2cap_p l2cap, struct ng_mesg *msg)
1244 {
1245 	return (ENOTSUP);
1246 } /* ng_l2cap_l2ca_grp_close */
1247 
1248 /*
1249  * Process L2CA_GroupAddMember request from the upper layer protocol.
1250  * XXX FIXME
1251  */
1252 
1253 int
1254 ng_l2cap_l2ca_grp_add_member_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
1255 {
1256 	return (ENOTSUP);
1257 } /* ng_l2cap_l2ca_grp_add_member_req */
1258 
1259 /*
1260  * Send L2CA_GroupAddMember response to the upper layer protocol.
1261  * XXX FIXME
1262  */
1263 
1264 int
1265 ng_l2cap_l2ca_grp_add_member_rsp(ng_l2cap_chan_p ch, u_int32_t token,
1266 		u_int16_t result)
1267 {
1268 	return (0);
1269 } /* ng_l2cap_l2ca_grp_add_member_rsp */
1270 
1271 /*
1272  * Process L2CA_GroupDeleteMember request from the upper layer protocol
1273  * XXX FIXME
1274  */
1275 
1276 int
1277 ng_l2cap_l2ca_grp_rem_member(ng_l2cap_p l2cap, struct ng_mesg *msg)
1278 {
1279 	return (ENOTSUP);
1280 } /* ng_l2cap_l2ca_grp_rem_member */
1281 
1282 /*
1283  * Process L2CA_GroupGetMembers request from the upper layer protocol
1284  * XXX FIXME
1285  */
1286 
1287 int
1288 ng_l2cap_l2ca_grp_get_members(ng_l2cap_p l2cap, struct ng_mesg *msg)
1289 {
1290 	return (ENOTSUP);
1291 } /* ng_l2cap_l2ca_grp_get_members */
1292 
1293 /*
1294  * Process L2CA_Ping request from the upper layer protocol
1295  */
1296 
1297 int
1298 ng_l2cap_l2ca_ping_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
1299 {
1300 	ng_l2cap_l2ca_ping_ip	*ip = NULL;
1301 	ng_l2cap_con_p		 con = NULL;
1302 	ng_l2cap_cmd_p		 cmd = NULL;
1303 	int			 error = 0;
1304 
1305 	/* Verify message */
1306 	if (msg->header.arglen < sizeof(*ip)) {
1307 		NG_L2CAP_ALERT(
1308 "%s: %s - invalid L2CA_Ping request message size, size=%d\n",
1309 			__func__, NG_NODE_NAME(l2cap->node),
1310 			msg->header.arglen);
1311 		error = EMSGSIZE;
1312 		goto out;
1313 	}
1314 
1315 	ip = (ng_l2cap_l2ca_ping_ip *)(msg->data);
1316 	if (ip->echo_size > NG_L2CAP_MAX_ECHO_SIZE) {
1317 		NG_L2CAP_WARN(
1318 "%s: %s - invalid L2CA_Ping request. Echo size is too big, echo_size=%d\n",
1319 			__func__, NG_NODE_NAME(l2cap->node), ip->echo_size);
1320 		error = EMSGSIZE;
1321 		goto out;
1322 	}
1323 
1324 	/* Check if we have connection to the unit */
1325 	con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr);
1326 	if (con == NULL) {
1327 		/* Submit LP_ConnectReq to the lower layer */
1328 		error = ng_l2cap_lp_con_req(l2cap, &ip->bdaddr);
1329 		if (error != 0) {
1330 			NG_L2CAP_ERR(
1331 "%s: %s - unable to send LP_ConnectReq message, error=%d\n",
1332 				__func__, NG_NODE_NAME(l2cap->node), error);
1333 			goto out;
1334 		}
1335 
1336 		/* This should not fail */
1337 		con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr);
1338 		KASSERT((con != NULL),
1339 ("%s: %s - could not find connection!\n", __func__, NG_NODE_NAME(l2cap->node)));
1340 	}
1341 
1342 	/* Create L2CAP command descriptor */
1343 	cmd = ng_l2cap_new_cmd(con, NULL, ng_l2cap_get_ident(con),
1344 			NG_L2CAP_ECHO_REQ, msg->header.token);
1345 	if (cmd == NULL) {
1346 		error = ENOMEM;
1347 		goto out;
1348 	}
1349 
1350 	if (cmd->ident == NG_L2CAP_NULL_IDENT) {
1351 		ng_l2cap_free_cmd(cmd);
1352                 error = EIO;
1353 		goto out;
1354 	}
1355 
1356 	/* Create L2CAP command packet */
1357 	_ng_l2cap_echo_req(cmd->aux, cmd->ident,
1358 			msg->data + sizeof(*ip), ip->echo_size);
1359 	if (cmd->aux == NULL) {
1360 		ng_l2cap_free_cmd(cmd);
1361                 error = ENOBUFS;
1362 		goto out;
1363 	}
1364 
1365         /* Link command to the queue */
1366         ng_l2cap_link_cmd(con, cmd);
1367 	ng_l2cap_lp_deliver(con);
1368 out:
1369 	return (error);
1370 } /* ng_l2cap_l2ca_ping_req */
1371 
1372 /*
1373  * Send L2CA_Ping response to the upper layer protocol
1374  */
1375 
1376 int
1377 ng_l2cap_l2ca_ping_rsp(ng_l2cap_con_p con, u_int32_t token, u_int16_t result,
1378 		struct mbuf *data)
1379 {
1380 	ng_l2cap_p		 l2cap = con->l2cap;
1381 	struct ng_mesg		*msg = NULL;
1382 	ng_l2cap_l2ca_ping_op	*op = NULL;
1383 	int			 error = 0, size = 0;
1384 
1385 	/* Check if control hook is connected and valid */
1386 	if (l2cap->ctl == NULL || NG_HOOK_NOT_VALID(l2cap->ctl)) {
1387 		NG_L2CAP_WARN(
1388 "%s: %s - unable to send L2CA_Ping response message. " \
1389 "Hook is not connected or valid\n",
1390 			__func__, NG_NODE_NAME(l2cap->node));
1391 		error = ENOTCONN;
1392 		goto out;
1393 	}
1394 
1395 	size = (data == NULL)? 0 : data->m_pkthdr.len;
1396 
1397 	/* Create and send L2CA_Ping response message */
1398 	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_PING,
1399 		sizeof(*op) + size, M_NOWAIT);
1400 	if (msg == NULL)
1401 		error = ENOMEM;
1402 	else {
1403 		msg->header.token = token;
1404 		msg->header.flags |= NGF_RESP;
1405 
1406 		op = (ng_l2cap_l2ca_ping_op *)(msg->data);
1407 		op->result = result;
1408 		bcopy(&con->remote, &op->bdaddr, sizeof(op->bdaddr));
1409 		if (data != NULL && size > 0) {
1410 			op->echo_size = size;
1411 			m_copydata(data, 0, size, (caddr_t) op + sizeof(*op));
1412 		}
1413 
1414 		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->ctl, 0);
1415 	}
1416 out:
1417 	NG_FREE_M(data);
1418 
1419 	return (error);
1420 } /* ng_l2cap_l2ca_ping_rsp */
1421 
1422 /*
1423  * Process L2CA_GetInfo request from the upper layer protocol
1424  */
1425 
1426 int
1427 ng_l2cap_l2ca_get_info_req(ng_l2cap_p l2cap, struct ng_mesg *msg)
1428 {
1429 	ng_l2cap_l2ca_get_info_ip	*ip = NULL;
1430 	ng_l2cap_con_p			 con = NULL;
1431 	ng_l2cap_cmd_p			 cmd = NULL;
1432 	int				 error = 0;
1433 
1434 	/* Verify message */
1435 	if (msg->header.arglen != sizeof(*ip)) {
1436 		NG_L2CAP_ALERT(
1437 "%s: %s - invalid L2CA_GetInfo request message size, size=%d\n",
1438 			__func__, NG_NODE_NAME(l2cap->node),
1439 			msg->header.arglen);
1440 		error = EMSGSIZE;
1441 		goto out;
1442 	}
1443 
1444 	ip = (ng_l2cap_l2ca_get_info_ip *)(msg->data);
1445 
1446 	/* Check if we have connection to the unit */
1447 	con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr);
1448 	if (con == NULL) {
1449 		/* Submit LP_ConnectReq to the lower layer */
1450 		error = ng_l2cap_lp_con_req(l2cap, &ip->bdaddr);
1451 		if (error != 0) {
1452 			NG_L2CAP_ERR(
1453 "%s: %s - unable to send LP_ConnectReq message, error=%d\n",
1454 				__func__, NG_NODE_NAME(l2cap->node), error);
1455 			goto out;
1456 		}
1457 
1458 		/* This should not fail */
1459 		con = ng_l2cap_con_by_addr(l2cap, &ip->bdaddr);
1460 		KASSERT((con != NULL),
1461 ("%s: %s - could not find connection!\n", __func__, NG_NODE_NAME(l2cap->node)));
1462 	}
1463 
1464 	/* Create L2CAP command descriptor */
1465 	cmd = ng_l2cap_new_cmd(con, NULL, ng_l2cap_get_ident(con),
1466 			NG_L2CAP_INFO_REQ, msg->header.token);
1467 	if (cmd == NULL) {
1468 		error = ENOMEM;
1469 		goto out;
1470 	}
1471 
1472 	if (cmd->ident == NG_L2CAP_NULL_IDENT) {
1473 		ng_l2cap_free_cmd(cmd);
1474 		error = EIO;
1475 		goto out;
1476 	}
1477 
1478 	/* Create L2CAP command packet */
1479 	_ng_l2cap_info_req(cmd->aux, cmd->ident, ip->info_type);
1480 	if (cmd->aux == NULL) {
1481 		ng_l2cap_free_cmd(cmd);
1482 		error = ENOBUFS;
1483 		goto out;
1484 	}
1485 
1486         /* Link command to the queue */
1487 	ng_l2cap_link_cmd(con, cmd);
1488 	ng_l2cap_lp_deliver(con);
1489 out:
1490 	return (error);
1491 } /* ng_l2cap_l2ca_get_info_req */
1492 
1493 /*
1494  * Send L2CA_GetInfo response to the upper layer protocol
1495  */
1496 
1497 int
1498 ng_l2cap_l2ca_get_info_rsp(ng_l2cap_con_p con, u_int32_t token,
1499 		u_int16_t result, struct mbuf *data)
1500 {
1501 	ng_l2cap_p			 l2cap = con->l2cap;
1502 	struct ng_mesg			*msg = NULL;
1503 	ng_l2cap_l2ca_get_info_op	*op = NULL;
1504 	int				 error = 0, size;
1505 
1506 	/* Check if control hook is connected and valid */
1507 	if (l2cap->ctl == NULL || NG_HOOK_NOT_VALID(l2cap->ctl)) {
1508 		NG_L2CAP_WARN(
1509 "%s: %s - unable to send L2CA_GetInfo response message. " \
1510 "Hook is not connected or valid\n",
1511 			__func__, NG_NODE_NAME(l2cap->node));
1512 		error = ENOTCONN;
1513 		goto out;
1514 	}
1515 
1516 	size = (data == NULL)? 0 : data->m_pkthdr.len;
1517 
1518 	/* Create and send L2CA_GetInfo response message */
1519 	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_GET_INFO,
1520 		sizeof(*op) + size, M_NOWAIT);
1521 	if (msg == NULL)
1522 		error = ENOMEM;
1523 	else {
1524 		msg->header.token = token;
1525 		msg->header.flags |= NGF_RESP;
1526 
1527 		op = (ng_l2cap_l2ca_get_info_op *)(msg->data);
1528 		op->result = result;
1529 		if (data != NULL && size > 0) {
1530 			op->info_size = size;
1531 			m_copydata(data, 0, size, (caddr_t) op + sizeof(*op));
1532 		}
1533 
1534 		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->ctl, 0);
1535 	}
1536 out:
1537 	NG_FREE_M(data);
1538 
1539 	return (error);
1540 } /* ng_l2cap_l2ca_get_info_rsp */
1541 
1542 /*
1543  * Process L2CA_EnableCLT message from the upper layer protocol
1544  * XXX convert to NGN_L2CAP_NODE_SET_FLAGS?
1545  */
1546 
1547 int
1548 ng_l2cap_l2ca_enable_clt(ng_l2cap_p l2cap, struct ng_mesg *msg)
1549 {
1550 	ng_l2cap_l2ca_enable_clt_ip	*ip = NULL;
1551 	int				 error = 0;
1552 #if 0
1553  *	ng_l2cap_l2ca_enable_clt_op	*op = NULL;
1554  *	u_int16_t			 result;
1555  * 	u_int32_t			 token;
1556 #endif
1557 
1558 	/* Check message */
1559 	if (msg->header.arglen != sizeof(*ip)) {
1560 		NG_L2CAP_ALERT(
1561 "%s: %s - invalid L2CA_EnableCLT message size, size=%d\n",
1562 			__func__, NG_NODE_NAME(l2cap->node),
1563 			msg->header.arglen);
1564 
1565 		return (EMSGSIZE);
1566 	}
1567 
1568 	/* Process request */
1569 	ip = (ng_l2cap_l2ca_enable_clt_ip *) (msg->data);
1570 #if 0
1571  *	result = NG_L2CAP_SUCCESS;
1572 #endif
1573 
1574 	switch (ip->psm)
1575 	{
1576 	case 0:
1577 		/* Special case: disable/enable all PSM */
1578 		if (ip->enable)
1579 			l2cap->flags &= ~(NG_L2CAP_CLT_SDP_DISABLED    |
1580 					  NG_L2CAP_CLT_RFCOMM_DISABLED |
1581 					  NG_L2CAP_CLT_TCP_DISABLED);
1582 		else
1583 			l2cap->flags |= (NG_L2CAP_CLT_SDP_DISABLED    |
1584 					 NG_L2CAP_CLT_RFCOMM_DISABLED |
1585 					 NG_L2CAP_CLT_TCP_DISABLED);
1586 		break;
1587 
1588 	case NG_L2CAP_PSM_SDP:
1589 		if (ip->enable)
1590 			l2cap->flags &= ~NG_L2CAP_CLT_SDP_DISABLED;
1591 		else
1592 			l2cap->flags |= NG_L2CAP_CLT_SDP_DISABLED;
1593 		break;
1594 
1595 	case NG_L2CAP_PSM_RFCOMM:
1596 		if (ip->enable)
1597 			l2cap->flags &= ~NG_L2CAP_CLT_RFCOMM_DISABLED;
1598 		else
1599 			l2cap->flags |= NG_L2CAP_CLT_RFCOMM_DISABLED;
1600 		break;
1601 
1602 	case NG_L2CAP_PSM_TCP:
1603 		if (ip->enable)
1604 			l2cap->flags &= ~NG_L2CAP_CLT_TCP_DISABLED;
1605 		else
1606 			l2cap->flags |= NG_L2CAP_CLT_TCP_DISABLED;
1607 		break;
1608 
1609 	default:
1610 		NG_L2CAP_ERR(
1611 "%s: %s - unsupported PSM=%d\n", __func__, NG_NODE_NAME(l2cap->node), ip->psm);
1612 #if 0
1613  *		result = NG_L2CAP_PSM_NOT_SUPPORTED;
1614 #endif
1615 		error = ENOTSUP;
1616 		break;
1617 	}
1618 
1619 #if 0
1620  *	/* Create and send response message */
1621  * 	token = msg->header.token;
1622  * 	NG_FREE_MSG(msg);
1623  * 	NG_MKMESSAGE(msg, NGM_L2CAP_COOKIE, NGM_L2CAP_L2CA_ENABLE_CLT,
1624  * 		sizeof(*op), M_NOWAIT);
1625  * 	if (msg == NULL)
1626  * 		error = ENOMEM;
1627  * 	else {
1628  * 		msg->header.token = token;
1629  * 		msg->header.flags |= NGF_RESP;
1630  *
1631  * 		op = (ng_l2cap_l2ca_enable_clt_op *)(msg->data);
1632  * 		op->result = result;
1633  * 	}
1634  *
1635  * 	/* Send response to control hook */
1636  * 	if (l2cap->ctl != NULL && NG_HOOK_IS_VALID(l2cap->ctl))
1637  * 		NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->ctl, 0);
1638 #endif
1639 
1640 	return (error);
1641 } /* ng_l2cap_l2ca_enable_clt */
1642 
1643