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