6 * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
30 * $Id: ng_l2cap_evnt.c,v 1.5 2003/09/08 19:11:45 max Exp $
31 * $FreeBSD: src/sys/netgraph/bluetooth/l2cap/ng_l2cap_evnt.c,v 1.8 2005/01/07 01:45:43 imp Exp $
32 * $DragonFly: src/sys/netgraph7/bluetooth/l2cap/ng_l2cap_evnt.c,v 1.2 2008/06/26 23:05:40 dillon Exp $
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/endian.h>
39 #include <sys/malloc.h>
41 #include <sys/queue.h>
42 #include "ng_message.h"
44 #include "bluetooth/include/ng_bluetooth.h"
45 #include "bluetooth/include/ng_hci.h"
46 #include "bluetooth/include/ng_l2cap.h"
47 #include "bluetooth/l2cap/ng_l2cap_var.h"
48 #include "bluetooth/l2cap/ng_l2cap_cmds.h"
49 #include "bluetooth/l2cap/ng_l2cap_evnt.h"
50 #include "bluetooth/l2cap/ng_l2cap_llpi.h"
51 #include "bluetooth/l2cap/ng_l2cap_ulpi.h"
52 #include "bluetooth/l2cap/ng_l2cap_misc.h"
54 /******************************************************************************
55 ******************************************************************************
56 ** L2CAP events processing module
57 ******************************************************************************
58 ******************************************************************************/
60 static int ng_l2cap_process_signal_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_con_req (ng_l2cap_con_p, u_int8_t);
63 static int ng_l2cap_process_con_rsp (ng_l2cap_con_p, u_int8_t);
64 static int ng_l2cap_process_cfg_req (ng_l2cap_con_p, u_int8_t);
65 static int ng_l2cap_process_cfg_rsp (ng_l2cap_con_p, u_int8_t);
66 static int ng_l2cap_process_discon_req (ng_l2cap_con_p, u_int8_t);
67 static int ng_l2cap_process_discon_rsp (ng_l2cap_con_p, u_int8_t);
68 static int ng_l2cap_process_echo_req (ng_l2cap_con_p, u_int8_t);
69 static int ng_l2cap_process_echo_rsp (ng_l2cap_con_p, u_int8_t);
70 static int ng_l2cap_process_info_req (ng_l2cap_con_p, u_int8_t);
71 static int ng_l2cap_process_info_rsp (ng_l2cap_con_p, u_int8_t);
72 static int send_l2cap_reject
73 (ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, u_int16_t, u_int16_t);
74 static int send_l2cap_con_rej
75 (ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, u_int16_t);
76 static int send_l2cap_cfg_rsp
77 (ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, struct mbuf *);
78 static int get_next_l2cap_opt
79 (struct mbuf *, int *, ng_l2cap_cfg_opt_p, ng_l2cap_cfg_opt_val_p);
82 * Receive L2CAP packet. First get L2CAP header and verify packet. Than
83 * get destination channel and process packet.
87 ng_l2cap_receive(ng_l2cap_con_p con)
89 ng_l2cap_p l2cap = con->l2cap;
90 ng_l2cap_hdr_t *hdr = NULL;
94 if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) {
96 "%s: %s - invalid L2CAP packet. Packet too small, len=%d\n",
97 __func__, NG_NODE_NAME(l2cap->node),
98 con->rx_pkt->m_pkthdr.len);
103 /* Get L2CAP header */
104 NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));
105 if (con->rx_pkt == NULL)
108 hdr = mtod(con->rx_pkt, ng_l2cap_hdr_t *);
109 hdr->length = le16toh(hdr->length);
110 hdr->dcid = le16toh(hdr->dcid);
112 /* Check payload size */
113 if (hdr->length != con->rx_pkt->m_pkthdr.len - sizeof(*hdr)) {
115 "%s: %s - invalid L2CAP packet. Payload length mismatch, length=%d, len=%zd\n",
116 __func__, NG_NODE_NAME(l2cap->node), hdr->length,
117 con->rx_pkt->m_pkthdr.len - sizeof(*hdr));
124 case NG_L2CAP_SIGNAL_CID: /* L2CAP command */
125 m_adj(con->rx_pkt, sizeof(*hdr));
126 error = ng_l2cap_process_signal_cmd(con);
129 case NG_L2CAP_CLT_CID: /* Connectionless packet */
130 error = ng_l2cap_l2ca_clt_receive(con);
133 default: /* Data packet */
134 error = ng_l2cap_l2ca_receive(con);
140 NG_FREE_M(con->rx_pkt);
143 } /* ng_l2cap_receive */
146 * Process L2CAP signaling command. We already know that destination channel ID
147 * is 0x1 that means we have received signaling command from peer's L2CAP layer.
148 * So get command header, decode and process it.
150 * XXX do we need to check signaling MTU here?
154 ng_l2cap_process_signal_cmd(ng_l2cap_con_p con)
156 ng_l2cap_p l2cap = con->l2cap;
157 ng_l2cap_cmd_hdr_t *hdr = NULL;
158 struct mbuf *m = NULL;
160 while (con->rx_pkt != NULL) {
161 /* Verify packet length */
162 if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) {
164 "%s: %s - invalid L2CAP signaling command. Packet too small, len=%d\n",
165 __func__, NG_NODE_NAME(l2cap->node),
166 con->rx_pkt->m_pkthdr.len);
167 NG_FREE_M(con->rx_pkt);
172 /* Get signaling command */
173 NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));
174 if (con->rx_pkt == NULL)
177 hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *);
178 hdr->length = le16toh(hdr->length);
179 m_adj(con->rx_pkt, sizeof(*hdr));
181 /* Verify command length */
182 if (con->rx_pkt->m_pkthdr.len < hdr->length) {
184 "%s: %s - invalid L2CAP signaling command, code=%#x, ident=%d. " \
185 "Invalid command length=%d, m_pkthdr.len=%d\n",
186 __func__, NG_NODE_NAME(l2cap->node),
187 hdr->code, hdr->ident, hdr->length,
188 con->rx_pkt->m_pkthdr.len);
189 NG_FREE_M(con->rx_pkt);
194 /* Get the command, save the rest (if any) */
195 if (con->rx_pkt->m_pkthdr.len > hdr->length)
196 m = m_split(con->rx_pkt, hdr->length, MB_DONTWAIT);
200 /* Process command */
202 case NG_L2CAP_CMD_REJ:
203 ng_l2cap_process_cmd_rej(con, hdr->ident);
206 case NG_L2CAP_CON_REQ:
207 ng_l2cap_process_con_req(con, hdr->ident);
210 case NG_L2CAP_CON_RSP:
211 ng_l2cap_process_con_rsp(con, hdr->ident);
214 case NG_L2CAP_CFG_REQ:
215 ng_l2cap_process_cfg_req(con, hdr->ident);
218 case NG_L2CAP_CFG_RSP:
219 ng_l2cap_process_cfg_rsp(con, hdr->ident);
222 case NG_L2CAP_DISCON_REQ:
223 ng_l2cap_process_discon_req(con, hdr->ident);
226 case NG_L2CAP_DISCON_RSP:
227 ng_l2cap_process_discon_rsp(con, hdr->ident);
230 case NG_L2CAP_ECHO_REQ:
231 ng_l2cap_process_echo_req(con, hdr->ident);
234 case NG_L2CAP_ECHO_RSP:
235 ng_l2cap_process_echo_rsp(con, hdr->ident);
238 case NG_L2CAP_INFO_REQ:
239 ng_l2cap_process_info_req(con, hdr->ident);
242 case NG_L2CAP_INFO_RSP:
243 ng_l2cap_process_info_rsp(con, hdr->ident);
248 "%s: %s - unknown L2CAP signaling command, code=%#x, ident=%d, length=%d\n",
249 __func__, NG_NODE_NAME(l2cap->node),
250 hdr->code, hdr->ident, hdr->length);
253 * Send L2CAP_CommandRej. Do not really care
257 send_l2cap_reject(con, hdr->ident,
258 NG_L2CAP_REJ_NOT_UNDERSTOOD, 0, 0, 0);
259 NG_FREE_M(con->rx_pkt);
267 } /* ng_l2cap_process_signal_cmd */
270 * Process L2CAP_CommandRej command
274 ng_l2cap_process_cmd_rej(ng_l2cap_con_p con, u_int8_t ident)
276 ng_l2cap_p l2cap = con->l2cap;
277 ng_l2cap_cmd_rej_cp *cp = NULL;
278 ng_l2cap_cmd_p cmd = NULL;
280 /* Get command parameters */
281 NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));
282 if (con->rx_pkt == NULL)
285 cp = mtod(con->rx_pkt, ng_l2cap_cmd_rej_cp *);
286 cp->reason = le16toh(cp->reason);
288 /* Check if we have pending command descriptor */
289 cmd = ng_l2cap_cmd_by_ident(con, ident);
291 /* If command timeout already happened then ignore reject */
292 if (ng_l2cap_command_untimeout(cmd) != 0) {
293 NG_FREE_M(con->rx_pkt);
297 ng_l2cap_unlink_cmd(cmd);
300 case NG_L2CAP_CON_REQ:
301 ng_l2cap_l2ca_con_rsp(cmd->ch,cmd->token,cp->reason,0);
302 ng_l2cap_free_chan(cmd->ch);
305 case NG_L2CAP_CFG_REQ:
306 ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, cp->reason);
309 case NG_L2CAP_DISCON_REQ:
310 ng_l2cap_l2ca_discon_rsp(cmd->ch,cmd->token,cp->reason);
311 ng_l2cap_free_chan(cmd->ch); /* XXX free channel */
314 case NG_L2CAP_ECHO_REQ:
315 ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
319 case NG_L2CAP_INFO_REQ:
320 ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
326 "%s: %s - unexpected L2CAP_CommandRej. Unexpected L2CAP command opcode=%d\n",
327 __func__, NG_NODE_NAME(l2cap->node), cmd->code);
331 ng_l2cap_free_cmd(cmd);
334 "%s: %s - unexpected L2CAP_CommandRej command. " \
335 "Requested ident does not exist, ident=%d\n",
336 __func__, NG_NODE_NAME(l2cap->node), ident);
338 NG_FREE_M(con->rx_pkt);
341 } /* ng_l2cap_process_cmd_rej */
344 * Process L2CAP_ConnectReq command
348 ng_l2cap_process_con_req(ng_l2cap_con_p con, u_int8_t ident)
350 ng_l2cap_p l2cap = con->l2cap;
351 struct mbuf *m = con->rx_pkt;
352 ng_l2cap_con_req_cp *cp = NULL;
353 ng_l2cap_chan_p ch = NULL;
357 /* Get command parameters */
358 NG_L2CAP_M_PULLUP(m, sizeof(*cp));
362 cp = mtod(m, ng_l2cap_con_req_cp *);
363 psm = le16toh(cp->psm);
364 dcid = le16toh(cp->scid);
370 * Create new channel and send L2CA_ConnectInd notification
371 * to the upper layer protocol.
374 ch = ng_l2cap_new_chan(l2cap, con, psm);
376 return (send_l2cap_con_rej(con, ident, 0, dcid,
377 NG_L2CAP_NO_RESOURCES));
379 /* Update channel IDs */
382 /* Sent L2CA_ConnectInd notification to the upper layer */
384 ch->state = NG_L2CAP_W4_L2CA_CON_RSP;
386 error = ng_l2cap_l2ca_con_ind(ch);
388 send_l2cap_con_rej(con, ident, ch->scid, dcid,
389 (error == ENOMEM)? NG_L2CAP_NO_RESOURCES :
390 NG_L2CAP_PSM_NOT_SUPPORTED);
391 ng_l2cap_free_chan(ch);
395 } /* ng_l2cap_process_con_req */
398 * Process L2CAP_ConnectRsp command
402 ng_l2cap_process_con_rsp(ng_l2cap_con_p con, u_int8_t ident)
404 ng_l2cap_p l2cap = con->l2cap;
405 struct mbuf *m = con->rx_pkt;
406 ng_l2cap_con_rsp_cp *cp = NULL;
407 ng_l2cap_cmd_p cmd = NULL;
408 u_int16_t scid, dcid, result, status;
411 /* Get command parameters */
412 NG_L2CAP_M_PULLUP(m, sizeof(*cp));
416 cp = mtod(m, ng_l2cap_con_rsp_cp *);
417 dcid = le16toh(cp->dcid);
418 scid = le16toh(cp->scid);
419 result = le16toh(cp->result);
420 status = le16toh(cp->status);
425 /* Check if we have pending command descriptor */
426 cmd = ng_l2cap_cmd_by_ident(con, ident);
429 "%s: %s - unexpected L2CAP_ConnectRsp command. ident=%d, con_handle=%d\n",
430 __func__, NG_NODE_NAME(l2cap->node), ident,
436 /* Verify channel state, if invalid - do nothing */
437 if (cmd->ch->state != NG_L2CAP_W4_L2CAP_CON_RSP) {
439 "%s: %s - unexpected L2CAP_ConnectRsp. " \
440 "Invalid channel state, cid=%d, state=%d\n",
441 __func__, NG_NODE_NAME(l2cap->node), scid,
446 /* Verify CIDs and send reject if does not match */
447 if (cmd->ch->scid != scid) {
449 "%s: %s - unexpected L2CAP_ConnectRsp. Channel IDs do not match, scid=%d(%d)\n",
450 __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,
456 * Looks good. We got confirmation from our peer. Now process
457 * it. First disable RTX timer. Then check the result and send
458 * notification to the upper layer. If command timeout already
459 * happened then ignore response.
462 if ((error = ng_l2cap_command_untimeout(cmd)) != 0)
465 if (result == NG_L2CAP_PENDING) {
467 * Our peer wants more time to complete connection. We shall
468 * start ERTX timer and wait. Keep command in the list.
471 cmd->ch->dcid = dcid;
472 ng_l2cap_command_timeout(cmd, bluetooth_l2cap_ertx_timeout());
474 error = ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token,
477 ng_l2cap_free_chan(cmd->ch);
479 ng_l2cap_unlink_cmd(cmd);
481 if (result == NG_L2CAP_SUCCESS) {
483 * Channel is open. Complete command and move to CONFIG
484 * state. Since we have sent positive confirmation we
485 * expect to receive L2CA_Config request from the upper
489 cmd->ch->dcid = dcid;
490 cmd->ch->state = NG_L2CAP_CONFIG;
492 /* There was an error, so close the channel */
494 "%s: %s - failed to open L2CAP channel, result=%d, status=%d\n",
495 __func__, NG_NODE_NAME(l2cap->node), result,
498 error = ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token,
501 /* XXX do we have to remove the channel on error? */
502 if (error != 0 || result != NG_L2CAP_SUCCESS)
503 ng_l2cap_free_chan(cmd->ch);
505 ng_l2cap_free_cmd(cmd);
511 /* Send reject. Do not really care about the result */
512 send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, dcid);
515 } /* ng_l2cap_process_con_rsp */
518 * Process L2CAP_ConfigReq command
522 ng_l2cap_process_cfg_req(ng_l2cap_con_p con, u_int8_t ident)
524 ng_l2cap_p l2cap = con->l2cap;
525 struct mbuf *m = con->rx_pkt;
526 ng_l2cap_cfg_req_cp *cp = NULL;
527 ng_l2cap_chan_p ch = NULL;
528 u_int16_t dcid, respond, result;
529 ng_l2cap_cfg_opt_t hdr;
530 ng_l2cap_cfg_opt_val_t val;
533 /* Get command parameters */
535 NG_L2CAP_M_PULLUP(m, sizeof(*cp));
539 cp = mtod(m, ng_l2cap_cfg_req_cp *);
540 dcid = le16toh(cp->dcid);
541 respond = NG_L2CAP_OPT_CFLAG(le16toh(cp->flags));
542 m_adj(m, sizeof(*cp));
544 /* Check if we have this channel and it is in valid state */
545 ch = ng_l2cap_chan_by_scid(l2cap, dcid);
548 "%s: %s - unexpected L2CAP_ConfigReq command. " \
549 "Channel does not exist, cid=%d\n",
550 __func__, NG_NODE_NAME(l2cap->node), dcid);
554 /* Verify channel state */
555 if (ch->state != NG_L2CAP_CONFIG && ch->state != NG_L2CAP_OPEN) {
557 "%s: %s - unexpected L2CAP_ConfigReq. " \
558 "Invalid channel state, cid=%d, state=%d\n",
559 __func__, NG_NODE_NAME(l2cap->node), dcid, ch->state);
563 if (ch->state == NG_L2CAP_OPEN) { /* Re-configuration */
565 ch->state = NG_L2CAP_CONFIG;
568 for (result = 0, off = 0; ; ) {
569 error = get_next_l2cap_opt(m, &off, &hdr, &val);
570 if (error == 0) { /* We done with this packet */
573 } else if (error > 0) { /* Got option */
575 case NG_L2CAP_OPT_MTU:
579 case NG_L2CAP_OPT_FLUSH_TIMO:
580 ch->flush_timo = val.flush_timo;
583 case NG_L2CAP_OPT_QOS:
584 bcopy(&val.flow, &ch->iflow, sizeof(ch->iflow));
587 default: /* Ignore unknown hint option */
590 } else { /* Oops, something is wrong */
596 * Adjust mbuf so we can get to the start
597 * of the first option we did not like.
600 m_adj(m, off - sizeof(hdr));
601 m->m_pkthdr.len = sizeof(hdr) + hdr.length;
603 result = NG_L2CAP_UNKNOWN_OPTION;
605 /* XXX FIXME Send other reject codes? */
607 result = NG_L2CAP_REJECT;
615 * Now check and see if we have to respond. If everything was OK then
616 * respond contain "C flag" and (if set) we will respond with empty
617 * packet and will wait for more options.
619 * Other case is that we did not like peer's options and will respond
620 * with L2CAP_Config response command with Reject error code.
622 * When "respond == 0" than we have received all options and we will
623 * sent L2CA_ConfigInd event to the upper layer protocol.
627 error = send_l2cap_cfg_rsp(con, ident, ch->dcid, result, m);
629 ng_l2cap_l2ca_discon_ind(ch);
630 ng_l2cap_free_chan(ch);
633 /* Send L2CA_ConfigInd event to the upper layer protocol */
635 error = ng_l2cap_l2ca_cfg_ind(ch);
637 ng_l2cap_free_chan(ch);
643 /* Send reject. Do not really care about the result */
646 send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, 0, dcid);
649 } /* ng_l2cap_process_cfg_req */
652 * Process L2CAP_ConfigRsp command
656 ng_l2cap_process_cfg_rsp(ng_l2cap_con_p con, u_int8_t ident)
658 ng_l2cap_p l2cap = con->l2cap;
659 struct mbuf *m = con->rx_pkt;
660 ng_l2cap_cfg_rsp_cp *cp = NULL;
661 ng_l2cap_cmd_p cmd = NULL;
662 u_int16_t scid, cflag, result;
663 ng_l2cap_cfg_opt_t hdr;
664 ng_l2cap_cfg_opt_val_t val;
667 /* Get command parameters */
669 NG_L2CAP_M_PULLUP(m, sizeof(*cp));
673 cp = mtod(m, ng_l2cap_cfg_rsp_cp *);
674 scid = le16toh(cp->scid);
675 cflag = NG_L2CAP_OPT_CFLAG(le16toh(cp->flags));
676 result = le16toh(cp->result);
677 m_adj(m, sizeof(*cp));
679 /* Check if we have this command */
680 cmd = ng_l2cap_cmd_by_ident(con, ident);
683 "%s: %s - unexpected L2CAP_ConfigRsp command. ident=%d, con_handle=%d\n",
684 __func__, NG_NODE_NAME(l2cap->node), ident,
691 /* Verify CIDs and send reject if does not match */
692 if (cmd->ch->scid != scid) {
694 "%s: %s - unexpected L2CAP_ConfigRsp. " \
695 "Channel ID does not match, scid=%d(%d)\n",
696 __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,
701 /* Verify channel state and reject if invalid */
702 if (cmd->ch->state != NG_L2CAP_CONFIG) {
704 "%s: %s - unexpected L2CAP_ConfigRsp. " \
705 "Invalid channel state, scid=%d, state=%d\n",
706 __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,
712 * Looks like it is our response, so process it. First parse options,
713 * then verify C flag. If it is set then we shall expect more
714 * configuration options from the peer and we will wait. Otherwise we
715 * have received all options and we will send L2CA_ConfigRsp event to
716 * the upper layer protocol. If command timeout already happened then
720 if ((error = ng_l2cap_command_untimeout(cmd)) != 0) {
726 error = get_next_l2cap_opt(m, &off, &hdr, &val);
727 if (error == 0) /* We done with this packet */
729 else if (error > 0) { /* Got option */
731 case NG_L2CAP_OPT_MTU:
732 cmd->ch->imtu = val.mtu;
735 case NG_L2CAP_OPT_FLUSH_TIMO:
736 cmd->ch->flush_timo = val.flush_timo;
739 case NG_L2CAP_OPT_QOS:
740 bcopy(&val.flow, &cmd->ch->oflow,
741 sizeof(cmd->ch->oflow));
744 default: /* Ignore unknown hint option */
749 * XXX FIXME What to do here?
751 * This is really BAD :( options packet was broken, or
752 * peer sent us option that we did not understand. Let
753 * upper layer know and do not wait for more options.
757 "%s: %s - failed to parse configuration options, error=%d\n",
758 __func__, NG_NODE_NAME(l2cap->node), error);
760 result = NG_L2CAP_UNKNOWN;
769 if (cflag) /* Restart timer and wait for more options */
770 ng_l2cap_command_timeout(cmd, bluetooth_l2cap_rtx_timeout());
772 ng_l2cap_unlink_cmd(cmd);
774 /* Send L2CA_Config response to the upper layer protocol */
775 error = ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, result);
778 * XXX FIXME what to do here? we were not able to send
779 * response to the upper layer protocol, so for now
780 * just close the channel. Send L2CAP_Disconnect to
785 "%s: %s - failed to send L2CA_Config response, error=%d\n",
786 __func__, NG_NODE_NAME(l2cap->node), error);
788 ng_l2cap_free_chan(cmd->ch);
791 ng_l2cap_free_cmd(cmd);
797 /* Send reject. Do not really care about the result */
800 send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, 0);
803 } /* ng_l2cap_process_cfg_rsp */
806 * Process L2CAP_DisconnectReq command
810 ng_l2cap_process_discon_req(ng_l2cap_con_p con, u_int8_t ident)
812 ng_l2cap_p l2cap = con->l2cap;
813 ng_l2cap_discon_req_cp *cp = NULL;
814 ng_l2cap_chan_p ch = NULL;
815 ng_l2cap_cmd_p cmd = NULL;
816 u_int16_t scid, dcid;
818 /* Get command parameters */
819 NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));
820 if (con->rx_pkt == NULL)
823 cp = mtod(con->rx_pkt, ng_l2cap_discon_req_cp *);
824 dcid = le16toh(cp->dcid);
825 scid = le16toh(cp->scid);
827 NG_FREE_M(con->rx_pkt);
829 /* Check if we have this channel and it is in valid state */
830 ch = ng_l2cap_chan_by_scid(l2cap, dcid);
833 "%s: %s - unexpected L2CAP_DisconnectReq message. " \
834 "Channel does not exist, cid=%d\n",
835 __func__, NG_NODE_NAME(l2cap->node), dcid);
839 /* XXX Verify channel state and reject if invalid -- is that true? */
840 if (ch->state != NG_L2CAP_OPEN && ch->state != NG_L2CAP_CONFIG &&
841 ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) {
843 "%s: %s - unexpected L2CAP_DisconnectReq. " \
844 "Invalid channel state, cid=%d, state=%d\n",
845 __func__, NG_NODE_NAME(l2cap->node), dcid, ch->state);
849 /* Match destination channel ID */
850 if (ch->dcid != scid || ch->scid != dcid) {
852 "%s: %s - unexpected L2CAP_DisconnectReq. " \
853 "Channel IDs does not match, channel: scid=%d, dcid=%d, " \
854 "request: scid=%d, dcid=%d\n",
855 __func__, NG_NODE_NAME(l2cap->node), ch->scid, ch->dcid,
861 * Looks good, so notify upper layer protocol that channel is about
862 * to be disconnected and send L2CA_DisconnectInd message. Then respond
863 * with L2CAP_DisconnectRsp.
866 if (ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) {
867 ng_l2cap_l2ca_discon_ind(ch); /* do not care about result */
868 ng_l2cap_free_chan(ch);
871 /* Send L2CAP_DisconnectRsp */
872 cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_DISCON_RSP, 0);
876 _ng_l2cap_discon_rsp(cmd->aux, ident, dcid, scid);
877 if (cmd->aux == NULL) {
878 ng_l2cap_free_cmd(cmd);
883 /* Link command to the queue */
884 ng_l2cap_link_cmd(con, cmd);
885 ng_l2cap_lp_deliver(con);
890 /* Send reject. Do not really care about the result */
891 send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, dcid);
894 } /* ng_l2cap_process_discon_req */
897 * Process L2CAP_DisconnectRsp command
901 ng_l2cap_process_discon_rsp(ng_l2cap_con_p con, u_int8_t ident)
903 ng_l2cap_p l2cap = con->l2cap;
904 ng_l2cap_discon_rsp_cp *cp = NULL;
905 ng_l2cap_cmd_p cmd = NULL;
906 u_int16_t scid, dcid;
909 /* Get command parameters */
910 NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));
911 if (con->rx_pkt == NULL)
914 cp = mtod(con->rx_pkt, ng_l2cap_discon_rsp_cp *);
915 dcid = le16toh(cp->dcid);
916 scid = le16toh(cp->scid);
918 NG_FREE_M(con->rx_pkt);
920 /* Check if we have pending command descriptor */
921 cmd = ng_l2cap_cmd_by_ident(con, ident);
924 "%s: %s - unexpected L2CAP_DisconnectRsp command. ident=%d, con_handle=%d\n",
925 __func__, NG_NODE_NAME(l2cap->node), ident,
930 /* Verify channel state, do nothing if invalid */
931 if (cmd->ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) {
933 "%s: %s - unexpected L2CAP_DisconnectRsp. " \
934 "Invalid channel state, cid=%d, state=%d\n",
935 __func__, NG_NODE_NAME(l2cap->node), scid,
940 /* Verify CIDs and send reject if does not match */
941 if (cmd->ch->scid != scid || cmd->ch->dcid != dcid) {
943 "%s: %s - unexpected L2CAP_DisconnectRsp. " \
944 "Channel IDs do not match, scid=%d(%d), dcid=%d(%d)\n",
945 __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,
946 scid, cmd->ch->dcid, dcid);
951 * Looks like we have successfuly disconnected channel, so notify
952 * upper layer. If command timeout already happened then ignore
956 if ((error = ng_l2cap_command_untimeout(cmd)) != 0)
959 error = ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, NG_L2CAP_SUCCESS);
960 ng_l2cap_free_chan(cmd->ch); /* this will free commands too */
963 } /* ng_l2cap_process_discon_rsp */
966 * Process L2CAP_EchoReq command
970 ng_l2cap_process_echo_req(ng_l2cap_con_p con, u_int8_t ident)
972 ng_l2cap_p l2cap = con->l2cap;
973 ng_l2cap_cmd_hdr_t *hdr = NULL;
974 ng_l2cap_cmd_p cmd = NULL;
976 con->rx_pkt = ng_l2cap_prepend(con->rx_pkt, sizeof(*hdr));
977 if (con->rx_pkt == NULL) {
979 "%s: %s - ng_l2cap_prepend() failed, size=%zd\n",
980 __func__, NG_NODE_NAME(l2cap->node), sizeof(*hdr));
985 hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *);
986 hdr->code = NG_L2CAP_ECHO_RSP;
988 hdr->length = htole16(con->rx_pkt->m_pkthdr.len - sizeof(*hdr));
990 cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_ECHO_RSP, 0);
992 NG_FREE_M(con->rx_pkt);
997 /* Attach data and link command to the queue */
998 cmd->aux = con->rx_pkt;
1000 ng_l2cap_link_cmd(con, cmd);
1001 ng_l2cap_lp_deliver(con);
1004 } /* ng_l2cap_process_echo_req */
1007 * Process L2CAP_EchoRsp command
1011 ng_l2cap_process_echo_rsp(ng_l2cap_con_p con, u_int8_t ident)
1013 ng_l2cap_p l2cap = con->l2cap;
1014 ng_l2cap_cmd_p cmd = NULL;
1017 /* Check if we have this command */
1018 cmd = ng_l2cap_cmd_by_ident(con, ident);
1020 /* If command timeout already happened then ignore response */
1021 if ((error = ng_l2cap_command_untimeout(cmd)) != 0) {
1022 NG_FREE_M(con->rx_pkt);
1026 ng_l2cap_unlink_cmd(cmd);
1028 error = ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
1029 NG_L2CAP_SUCCESS, con->rx_pkt);
1031 ng_l2cap_free_cmd(cmd);
1035 "%s: %s - unexpected L2CAP_EchoRsp command. " \
1036 "Requested ident does not exist, ident=%d\n",
1037 __func__, NG_NODE_NAME(l2cap->node), ident);
1038 NG_FREE_M(con->rx_pkt);
1042 } /* ng_l2cap_process_echo_rsp */
1045 * Process L2CAP_InfoReq command
1049 ng_l2cap_process_info_req(ng_l2cap_con_p con, u_int8_t ident)
1051 ng_l2cap_p l2cap = con->l2cap;
1052 ng_l2cap_cmd_p cmd = NULL;
1055 /* Get command parameters */
1056 NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(ng_l2cap_info_req_cp));
1057 if (con->rx_pkt == NULL)
1060 type = le16toh(mtod(con->rx_pkt, ng_l2cap_info_req_cp *)->type);
1061 NG_FREE_M(con->rx_pkt);
1063 cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_INFO_RSP, 0);
1068 case NG_L2CAP_CONNLESS_MTU:
1069 _ng_l2cap_info_rsp(cmd->aux, ident, NG_L2CAP_CONNLESS_MTU,
1070 NG_L2CAP_SUCCESS, NG_L2CAP_MTU_DEFAULT);
1074 _ng_l2cap_info_rsp(cmd->aux, ident, type,
1075 NG_L2CAP_NOT_SUPPORTED, 0);
1079 if (cmd->aux == NULL) {
1080 ng_l2cap_free_cmd(cmd);
1085 /* Link command to the queue */
1086 ng_l2cap_link_cmd(con, cmd);
1087 ng_l2cap_lp_deliver(con);
1090 } /* ng_l2cap_process_info_req */
1093 * Process L2CAP_InfoRsp command
1097 ng_l2cap_process_info_rsp(ng_l2cap_con_p con, u_int8_t ident)
1099 ng_l2cap_p l2cap = con->l2cap;
1100 ng_l2cap_info_rsp_cp *cp = NULL;
1101 ng_l2cap_cmd_p cmd = NULL;
1104 /* Get command parameters */
1105 NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));
1106 if (con->rx_pkt == NULL)
1109 cp = mtod(con->rx_pkt, ng_l2cap_info_rsp_cp *);
1110 cp->type = le16toh(cp->type);
1111 cp->result = le16toh(cp->result);
1112 m_adj(con->rx_pkt, sizeof(*cp));
1114 /* Check if we have pending command descriptor */
1115 cmd = ng_l2cap_cmd_by_ident(con, ident);
1118 "%s: %s - unexpected L2CAP_InfoRsp command. " \
1119 "Requested ident does not exist, ident=%d\n",
1120 __func__, NG_NODE_NAME(l2cap->node), ident);
1121 NG_FREE_M(con->rx_pkt);
1126 /* If command timeout already happened then ignore response */
1127 if ((error = ng_l2cap_command_untimeout(cmd)) != 0) {
1128 NG_FREE_M(con->rx_pkt);
1132 ng_l2cap_unlink_cmd(cmd);
1134 if (cp->result == NG_L2CAP_SUCCESS) {
1136 case NG_L2CAP_CONNLESS_MTU:
1137 if (con->rx_pkt->m_pkthdr.len == sizeof(u_int16_t))
1138 *mtod(con->rx_pkt, u_int16_t *) =
1139 le16toh(*mtod(con->rx_pkt,u_int16_t *));
1141 cp->result = NG_L2CAP_UNKNOWN; /* XXX */
1144 "%s: %s - invalid L2CAP_InfoRsp command. " \
1145 "Bad connectionless MTU parameter, len=%d\n",
1146 __func__, NG_NODE_NAME(l2cap->node),
1147 con->rx_pkt->m_pkthdr.len);
1153 "%s: %s - invalid L2CAP_InfoRsp command. Unknown info type=%d\n",
1154 __func__, NG_NODE_NAME(l2cap->node), cp->type);
1159 error = ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
1160 cp->result, con->rx_pkt);
1162 ng_l2cap_free_cmd(cmd);
1166 } /* ng_l2cap_process_info_rsp */
1173 send_l2cap_reject(ng_l2cap_con_p con, u_int8_t ident, u_int16_t reason,
1174 u_int16_t mtu, u_int16_t scid, u_int16_t dcid)
1176 ng_l2cap_cmd_p cmd = NULL;
1178 cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CMD_REJ, 0);
1182 _ng_l2cap_cmd_rej(cmd->aux, cmd->ident, reason, mtu, scid, dcid);
1183 if (cmd->aux == NULL) {
1184 ng_l2cap_free_cmd(cmd);
1189 /* Link command to the queue */
1190 ng_l2cap_link_cmd(con, cmd);
1191 ng_l2cap_lp_deliver(con);
1194 } /* send_l2cap_reject */
1197 * Send L2CAP connection reject
1201 send_l2cap_con_rej(ng_l2cap_con_p con, u_int8_t ident, u_int16_t scid,
1202 u_int16_t dcid, u_int16_t result)
1204 ng_l2cap_cmd_p cmd = NULL;
1206 cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CON_RSP, 0);
1210 _ng_l2cap_con_rsp(cmd->aux, cmd->ident, scid, dcid, result, 0);
1211 if (cmd->aux == NULL) {
1212 ng_l2cap_free_cmd(cmd);
1217 /* Link command to the queue */
1218 ng_l2cap_link_cmd(con, cmd);
1219 ng_l2cap_lp_deliver(con);
1222 } /* send_l2cap_con_rej */
1225 * Send L2CAP config response
1229 send_l2cap_cfg_rsp(ng_l2cap_con_p con, u_int8_t ident, u_int16_t scid,
1230 u_int16_t result, struct mbuf *opt)
1232 ng_l2cap_cmd_p cmd = NULL;
1234 cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CFG_RSP, 0);
1241 _ng_l2cap_cfg_rsp(cmd->aux, cmd->ident, scid, 0, result, opt);
1242 if (cmd->aux == NULL) {
1243 ng_l2cap_free_cmd(cmd);
1248 /* Link command to the queue */
1249 ng_l2cap_link_cmd(con, cmd);
1250 ng_l2cap_lp_deliver(con);
1253 } /* send_l2cap_cfg_rsp */
1256 * Get next L2CAP configuration option
1260 * 1 we have got option
1261 * -1 header too short
1262 * -2 bad option value or length
1267 get_next_l2cap_opt(struct mbuf *m, int *off, ng_l2cap_cfg_opt_p hdr,
1268 ng_l2cap_cfg_opt_val_p val)
1270 int hint, len = m->m_pkthdr.len - (*off);
1274 if (len < 0 || len < sizeof(*hdr))
1277 m_copydata(m, *off, sizeof(*hdr), (caddr_t) hdr);
1278 *off += sizeof(*hdr);
1279 len -= sizeof(*hdr);
1281 hint = NG_L2CAP_OPT_HINT(hdr->type);
1282 hdr->type &= NG_L2CAP_OPT_HINT_MASK;
1284 switch (hdr->type) {
1285 case NG_L2CAP_OPT_MTU:
1286 if (hdr->length != NG_L2CAP_OPT_MTU_SIZE || len < hdr->length)
1289 m_copydata(m, *off, NG_L2CAP_OPT_MTU_SIZE, (caddr_t) val);
1290 val->mtu = le16toh(val->mtu);
1291 *off += NG_L2CAP_OPT_MTU_SIZE;
1294 case NG_L2CAP_OPT_FLUSH_TIMO:
1295 if (hdr->length != NG_L2CAP_OPT_FLUSH_TIMO_SIZE ||
1299 m_copydata(m, *off, NG_L2CAP_OPT_FLUSH_TIMO_SIZE, (caddr_t)val);
1300 val->flush_timo = le16toh(val->flush_timo);
1301 *off += NG_L2CAP_OPT_FLUSH_TIMO_SIZE;
1304 case NG_L2CAP_OPT_QOS:
1305 if (hdr->length != NG_L2CAP_OPT_QOS_SIZE || len < hdr->length)
1308 m_copydata(m, *off, NG_L2CAP_OPT_QOS_SIZE, (caddr_t) val);
1309 val->flow.token_rate = le32toh(val->flow.token_rate);
1310 val->flow.token_bucket_size =
1311 le32toh(val->flow.token_bucket_size);
1312 val->flow.peak_bandwidth = le32toh(val->flow.peak_bandwidth);
1313 val->flow.latency = le32toh(val->flow.latency);
1314 val->flow.delay_variation = le32toh(val->flow.delay_variation);
1315 *off += NG_L2CAP_OPT_QOS_SIZE;
1320 *off += hdr->length;
1327 } /* get_next_l2cap_opt */