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