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