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