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