1878ed226SJulian Elischer /*
2878ed226SJulian Elischer * ng_l2cap_evnt.c
3c398230bSWarner Losh */
4c398230bSWarner Losh
5c398230bSWarner Losh /*-
6*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
7fe267a55SPedro F. Giffuni *
8878ed226SJulian Elischer * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
9878ed226SJulian Elischer * All rights reserved.
10878ed226SJulian Elischer *
11878ed226SJulian Elischer * Redistribution and use in source and binary forms, with or without
12878ed226SJulian Elischer * modification, are permitted provided that the following conditions
13878ed226SJulian Elischer * are met:
14878ed226SJulian Elischer * 1. Redistributions of source code must retain the above copyright
15878ed226SJulian Elischer * notice, this list of conditions and the following disclaimer.
16878ed226SJulian Elischer * 2. Redistributions in binary form must reproduce the above copyright
17878ed226SJulian Elischer * notice, this list of conditions and the following disclaimer in the
18878ed226SJulian Elischer * documentation and/or other materials provided with the distribution.
19878ed226SJulian Elischer *
20878ed226SJulian Elischer * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21878ed226SJulian Elischer * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22878ed226SJulian Elischer * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23878ed226SJulian Elischer * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24878ed226SJulian Elischer * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25878ed226SJulian Elischer * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26878ed226SJulian Elischer * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27878ed226SJulian Elischer * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28878ed226SJulian Elischer * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29878ed226SJulian Elischer * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30878ed226SJulian Elischer * SUCH DAMAGE.
31878ed226SJulian Elischer *
320986ab12SMaksim Yevmenkin * $Id: ng_l2cap_evnt.c,v 1.5 2003/09/08 19:11:45 max Exp $
33878ed226SJulian Elischer */
34878ed226SJulian Elischer
35878ed226SJulian Elischer #include <sys/param.h>
36878ed226SJulian Elischer #include <sys/systm.h>
37878ed226SJulian Elischer #include <sys/kernel.h>
38878ed226SJulian Elischer #include <sys/endian.h>
39878ed226SJulian Elischer #include <sys/malloc.h>
40878ed226SJulian Elischer #include <sys/mbuf.h>
41878ed226SJulian Elischer #include <sys/queue.h>
42878ed226SJulian Elischer #include <netgraph/ng_message.h>
43878ed226SJulian Elischer #include <netgraph/netgraph.h>
44b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_bluetooth.h>
45b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_hci.h>
46b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/include/ng_l2cap.h>
47b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_var.h>
48b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_cmds.h>
49b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_evnt.h>
50b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_llpi.h>
51b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h>
52b84b10f9SMaksim Yevmenkin #include <netgraph/bluetooth/l2cap/ng_l2cap_misc.h>
53878ed226SJulian Elischer
54878ed226SJulian Elischer /******************************************************************************
55878ed226SJulian Elischer ******************************************************************************
56878ed226SJulian Elischer ** L2CAP events processing module
57878ed226SJulian Elischer ******************************************************************************
58878ed226SJulian Elischer ******************************************************************************/
59878ed226SJulian Elischer
60878ed226SJulian Elischer static int ng_l2cap_process_signal_cmd (ng_l2cap_con_p);
61fbc48c2bSTakanori Watanabe static int ng_l2cap_process_lesignal_cmd (ng_l2cap_con_p);
62878ed226SJulian Elischer static int ng_l2cap_process_cmd_rej (ng_l2cap_con_p, u_int8_t);
63fbc48c2bSTakanori Watanabe static int ng_l2cap_process_cmd_urq (ng_l2cap_con_p, u_int8_t);
64fbc48c2bSTakanori Watanabe static int ng_l2cap_process_cmd_urs (ng_l2cap_con_p, u_int8_t);
65878ed226SJulian Elischer static int ng_l2cap_process_con_req (ng_l2cap_con_p, u_int8_t);
66878ed226SJulian Elischer static int ng_l2cap_process_con_rsp (ng_l2cap_con_p, u_int8_t);
67878ed226SJulian Elischer static int ng_l2cap_process_cfg_req (ng_l2cap_con_p, u_int8_t);
68878ed226SJulian Elischer static int ng_l2cap_process_cfg_rsp (ng_l2cap_con_p, u_int8_t);
69878ed226SJulian Elischer static int ng_l2cap_process_discon_req (ng_l2cap_con_p, u_int8_t);
70878ed226SJulian Elischer static int ng_l2cap_process_discon_rsp (ng_l2cap_con_p, u_int8_t);
71878ed226SJulian Elischer static int ng_l2cap_process_echo_req (ng_l2cap_con_p, u_int8_t);
72878ed226SJulian Elischer static int ng_l2cap_process_echo_rsp (ng_l2cap_con_p, u_int8_t);
73878ed226SJulian Elischer static int ng_l2cap_process_info_req (ng_l2cap_con_p, u_int8_t);
74878ed226SJulian Elischer static int ng_l2cap_process_info_rsp (ng_l2cap_con_p, u_int8_t);
75878ed226SJulian Elischer static int send_l2cap_reject
76878ed226SJulian Elischer (ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, u_int16_t, u_int16_t);
77878ed226SJulian Elischer static int send_l2cap_con_rej
78878ed226SJulian Elischer (ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, u_int16_t);
79878ed226SJulian Elischer static int send_l2cap_cfg_rsp
80878ed226SJulian Elischer (ng_l2cap_con_p, u_int8_t, u_int16_t, u_int16_t, struct mbuf *);
81fbc48c2bSTakanori Watanabe static int send_l2cap_param_urs
82fbc48c2bSTakanori Watanabe (ng_l2cap_con_p , u_int8_t , u_int16_t);
83fbc48c2bSTakanori Watanabe
84878ed226SJulian Elischer static int get_next_l2cap_opt
85878ed226SJulian Elischer (struct mbuf *, int *, ng_l2cap_cfg_opt_p, ng_l2cap_cfg_opt_val_p);
86878ed226SJulian Elischer
87878ed226SJulian Elischer /*
88878ed226SJulian Elischer * Receive L2CAP packet. First get L2CAP header and verify packet. Than
89878ed226SJulian Elischer * get destination channel and process packet.
90878ed226SJulian Elischer */
91878ed226SJulian Elischer
92878ed226SJulian Elischer int
ng_l2cap_receive(ng_l2cap_con_p con)93878ed226SJulian Elischer ng_l2cap_receive(ng_l2cap_con_p con)
94878ed226SJulian Elischer {
95878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap;
96878ed226SJulian Elischer ng_l2cap_hdr_t *hdr = NULL;
97878ed226SJulian Elischer int error = 0;
98878ed226SJulian Elischer
99878ed226SJulian Elischer /* Check packet */
100878ed226SJulian Elischer if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) {
101878ed226SJulian Elischer NG_L2CAP_ERR(
102878ed226SJulian Elischer "%s: %s - invalid L2CAP packet. Packet too small, len=%d\n",
103878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node),
104878ed226SJulian Elischer con->rx_pkt->m_pkthdr.len);
105878ed226SJulian Elischer error = EMSGSIZE;
106878ed226SJulian Elischer goto drop;
107878ed226SJulian Elischer }
108878ed226SJulian Elischer
109878ed226SJulian Elischer /* Get L2CAP header */
110878ed226SJulian Elischer NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));
111878ed226SJulian Elischer if (con->rx_pkt == NULL)
112878ed226SJulian Elischer return (ENOBUFS);
113878ed226SJulian Elischer
114878ed226SJulian Elischer hdr = mtod(con->rx_pkt, ng_l2cap_hdr_t *);
115878ed226SJulian Elischer hdr->length = le16toh(hdr->length);
116878ed226SJulian Elischer hdr->dcid = le16toh(hdr->dcid);
117878ed226SJulian Elischer
118878ed226SJulian Elischer /* Check payload size */
119878ed226SJulian Elischer if (hdr->length != con->rx_pkt->m_pkthdr.len - sizeof(*hdr)) {
120878ed226SJulian Elischer NG_L2CAP_ERR(
1214ae439a3SMaksim Yevmenkin "%s: %s - invalid L2CAP packet. Payload length mismatch, length=%d, len=%zd\n",
122878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), hdr->length,
123878ed226SJulian Elischer con->rx_pkt->m_pkthdr.len - sizeof(*hdr));
124878ed226SJulian Elischer error = EMSGSIZE;
125878ed226SJulian Elischer goto drop;
126878ed226SJulian Elischer }
127878ed226SJulian Elischer
128878ed226SJulian Elischer /* Process packet */
129878ed226SJulian Elischer switch (hdr->dcid) {
130878ed226SJulian Elischer case NG_L2CAP_SIGNAL_CID: /* L2CAP command */
131878ed226SJulian Elischer m_adj(con->rx_pkt, sizeof(*hdr));
132878ed226SJulian Elischer error = ng_l2cap_process_signal_cmd(con);
133878ed226SJulian Elischer break;
134fbc48c2bSTakanori Watanabe case NG_L2CAP_LESIGNAL_CID:
135fbc48c2bSTakanori Watanabe m_adj(con->rx_pkt, sizeof(*hdr));
136fbc48c2bSTakanori Watanabe error = ng_l2cap_process_lesignal_cmd(con);
137fbc48c2bSTakanori Watanabe break;
138878ed226SJulian Elischer case NG_L2CAP_CLT_CID: /* Connectionless packet */
139878ed226SJulian Elischer error = ng_l2cap_l2ca_clt_receive(con);
140878ed226SJulian Elischer break;
141878ed226SJulian Elischer
142878ed226SJulian Elischer default: /* Data packet */
143878ed226SJulian Elischer error = ng_l2cap_l2ca_receive(con);
144878ed226SJulian Elischer break;
145878ed226SJulian Elischer }
146878ed226SJulian Elischer
147878ed226SJulian Elischer return (error);
148878ed226SJulian Elischer drop:
149878ed226SJulian Elischer NG_FREE_M(con->rx_pkt);
150878ed226SJulian Elischer
151878ed226SJulian Elischer return (error);
152878ed226SJulian Elischer } /* ng_l2cap_receive */
153878ed226SJulian Elischer
154878ed226SJulian Elischer /*
155878ed226SJulian Elischer * Process L2CAP signaling command. We already know that destination channel ID
156878ed226SJulian Elischer * is 0x1 that means we have received signaling command from peer's L2CAP layer.
157878ed226SJulian Elischer * So get command header, decode and process it.
158878ed226SJulian Elischer *
159878ed226SJulian Elischer * XXX do we need to check signaling MTU here?
160878ed226SJulian Elischer */
161878ed226SJulian Elischer
162878ed226SJulian Elischer static int
ng_l2cap_process_signal_cmd(ng_l2cap_con_p con)163878ed226SJulian Elischer ng_l2cap_process_signal_cmd(ng_l2cap_con_p con)
164878ed226SJulian Elischer {
165878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap;
166878ed226SJulian Elischer ng_l2cap_cmd_hdr_t *hdr = NULL;
167878ed226SJulian Elischer struct mbuf *m = NULL;
168878ed226SJulian Elischer
169878ed226SJulian Elischer while (con->rx_pkt != NULL) {
170878ed226SJulian Elischer /* Verify packet length */
171878ed226SJulian Elischer if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) {
172878ed226SJulian Elischer NG_L2CAP_ERR(
173878ed226SJulian Elischer "%s: %s - invalid L2CAP signaling command. Packet too small, len=%d\n",
174878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node),
175878ed226SJulian Elischer con->rx_pkt->m_pkthdr.len);
176878ed226SJulian Elischer NG_FREE_M(con->rx_pkt);
177878ed226SJulian Elischer
178878ed226SJulian Elischer return (EMSGSIZE);
179878ed226SJulian Elischer }
180878ed226SJulian Elischer
181878ed226SJulian Elischer /* Get signaling command */
182878ed226SJulian Elischer NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));
183878ed226SJulian Elischer if (con->rx_pkt == NULL)
184878ed226SJulian Elischer return (ENOBUFS);
185878ed226SJulian Elischer
186878ed226SJulian Elischer hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *);
187878ed226SJulian Elischer hdr->length = le16toh(hdr->length);
188878ed226SJulian Elischer m_adj(con->rx_pkt, sizeof(*hdr));
189878ed226SJulian Elischer
190878ed226SJulian Elischer /* Verify command length */
191878ed226SJulian Elischer if (con->rx_pkt->m_pkthdr.len < hdr->length) {
192878ed226SJulian Elischer NG_L2CAP_ERR(
193878ed226SJulian Elischer "%s: %s - invalid L2CAP signaling command, code=%#x, ident=%d. " \
194878ed226SJulian Elischer "Invalid command length=%d, m_pkthdr.len=%d\n",
195878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node),
196878ed226SJulian Elischer hdr->code, hdr->ident, hdr->length,
197878ed226SJulian Elischer con->rx_pkt->m_pkthdr.len);
198878ed226SJulian Elischer NG_FREE_M(con->rx_pkt);
199878ed226SJulian Elischer
200878ed226SJulian Elischer return (EMSGSIZE);
201878ed226SJulian Elischer }
202878ed226SJulian Elischer
203878ed226SJulian Elischer /* Get the command, save the rest (if any) */
204878ed226SJulian Elischer if (con->rx_pkt->m_pkthdr.len > hdr->length)
205eb1b1807SGleb Smirnoff m = m_split(con->rx_pkt, hdr->length, M_NOWAIT);
206878ed226SJulian Elischer else
207878ed226SJulian Elischer m = NULL;
208878ed226SJulian Elischer
209878ed226SJulian Elischer /* Process command */
210878ed226SJulian Elischer switch (hdr->code) {
211878ed226SJulian Elischer case NG_L2CAP_CMD_REJ:
212878ed226SJulian Elischer ng_l2cap_process_cmd_rej(con, hdr->ident);
213878ed226SJulian Elischer break;
214878ed226SJulian Elischer
215878ed226SJulian Elischer case NG_L2CAP_CON_REQ:
216878ed226SJulian Elischer ng_l2cap_process_con_req(con, hdr->ident);
217878ed226SJulian Elischer break;
218878ed226SJulian Elischer
219878ed226SJulian Elischer case NG_L2CAP_CON_RSP:
220878ed226SJulian Elischer ng_l2cap_process_con_rsp(con, hdr->ident);
221878ed226SJulian Elischer break;
222878ed226SJulian Elischer
223878ed226SJulian Elischer case NG_L2CAP_CFG_REQ:
224878ed226SJulian Elischer ng_l2cap_process_cfg_req(con, hdr->ident);
225878ed226SJulian Elischer break;
226878ed226SJulian Elischer
227878ed226SJulian Elischer case NG_L2CAP_CFG_RSP:
228878ed226SJulian Elischer ng_l2cap_process_cfg_rsp(con, hdr->ident);
229878ed226SJulian Elischer break;
230878ed226SJulian Elischer
231878ed226SJulian Elischer case NG_L2CAP_DISCON_REQ:
232878ed226SJulian Elischer ng_l2cap_process_discon_req(con, hdr->ident);
233878ed226SJulian Elischer break;
234878ed226SJulian Elischer
235878ed226SJulian Elischer case NG_L2CAP_DISCON_RSP:
236878ed226SJulian Elischer ng_l2cap_process_discon_rsp(con, hdr->ident);
237878ed226SJulian Elischer break;
238878ed226SJulian Elischer
239878ed226SJulian Elischer case NG_L2CAP_ECHO_REQ:
240878ed226SJulian Elischer ng_l2cap_process_echo_req(con, hdr->ident);
241878ed226SJulian Elischer break;
242878ed226SJulian Elischer
243878ed226SJulian Elischer case NG_L2CAP_ECHO_RSP:
244878ed226SJulian Elischer ng_l2cap_process_echo_rsp(con, hdr->ident);
245878ed226SJulian Elischer break;
246878ed226SJulian Elischer
247878ed226SJulian Elischer case NG_L2CAP_INFO_REQ:
248878ed226SJulian Elischer ng_l2cap_process_info_req(con, hdr->ident);
249878ed226SJulian Elischer break;
250878ed226SJulian Elischer
251878ed226SJulian Elischer case NG_L2CAP_INFO_RSP:
252878ed226SJulian Elischer ng_l2cap_process_info_rsp(con, hdr->ident);
253878ed226SJulian Elischer break;
254878ed226SJulian Elischer
255878ed226SJulian Elischer default:
256878ed226SJulian Elischer NG_L2CAP_ERR(
257878ed226SJulian Elischer "%s: %s - unknown L2CAP signaling command, code=%#x, ident=%d, length=%d\n",
258878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node),
259878ed226SJulian Elischer hdr->code, hdr->ident, hdr->length);
260878ed226SJulian Elischer
261878ed226SJulian Elischer /*
262878ed226SJulian Elischer * Send L2CAP_CommandRej. Do not really care
263878ed226SJulian Elischer * about the result
264878ed226SJulian Elischer */
265878ed226SJulian Elischer
266878ed226SJulian Elischer send_l2cap_reject(con, hdr->ident,
267878ed226SJulian Elischer NG_L2CAP_REJ_NOT_UNDERSTOOD, 0, 0, 0);
268878ed226SJulian Elischer NG_FREE_M(con->rx_pkt);
269878ed226SJulian Elischer break;
270878ed226SJulian Elischer }
271878ed226SJulian Elischer
272878ed226SJulian Elischer con->rx_pkt = m;
273878ed226SJulian Elischer }
274878ed226SJulian Elischer
275878ed226SJulian Elischer return (0);
276878ed226SJulian Elischer } /* ng_l2cap_process_signal_cmd */
277fbc48c2bSTakanori Watanabe static int
ng_l2cap_process_lesignal_cmd(ng_l2cap_con_p con)278fbc48c2bSTakanori Watanabe ng_l2cap_process_lesignal_cmd(ng_l2cap_con_p con)
279fbc48c2bSTakanori Watanabe {
280fbc48c2bSTakanori Watanabe ng_l2cap_p l2cap = con->l2cap;
281fbc48c2bSTakanori Watanabe ng_l2cap_cmd_hdr_t *hdr = NULL;
282fbc48c2bSTakanori Watanabe struct mbuf *m = NULL;
283fbc48c2bSTakanori Watanabe
284fbc48c2bSTakanori Watanabe while (con->rx_pkt != NULL) {
285fbc48c2bSTakanori Watanabe /* Verify packet length */
286fbc48c2bSTakanori Watanabe if (con->rx_pkt->m_pkthdr.len < sizeof(*hdr)) {
287fbc48c2bSTakanori Watanabe NG_L2CAP_ERR(
288fbc48c2bSTakanori Watanabe "%s: %s - invalid L2CAP signaling command. Packet too small, len=%d\n",
289fbc48c2bSTakanori Watanabe __func__, NG_NODE_NAME(l2cap->node),
290fbc48c2bSTakanori Watanabe con->rx_pkt->m_pkthdr.len);
291fbc48c2bSTakanori Watanabe NG_FREE_M(con->rx_pkt);
292fbc48c2bSTakanori Watanabe
293fbc48c2bSTakanori Watanabe return (EMSGSIZE);
294fbc48c2bSTakanori Watanabe }
295fbc48c2bSTakanori Watanabe
296fbc48c2bSTakanori Watanabe /* Get signaling command */
297fbc48c2bSTakanori Watanabe NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*hdr));
298fbc48c2bSTakanori Watanabe if (con->rx_pkt == NULL)
299fbc48c2bSTakanori Watanabe return (ENOBUFS);
300fbc48c2bSTakanori Watanabe
301fbc48c2bSTakanori Watanabe hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *);
302fbc48c2bSTakanori Watanabe hdr->length = le16toh(hdr->length);
303fbc48c2bSTakanori Watanabe m_adj(con->rx_pkt, sizeof(*hdr));
304fbc48c2bSTakanori Watanabe
305fbc48c2bSTakanori Watanabe /* Verify command length */
306fbc48c2bSTakanori Watanabe if (con->rx_pkt->m_pkthdr.len < hdr->length) {
307fbc48c2bSTakanori Watanabe NG_L2CAP_ERR(
308fbc48c2bSTakanori Watanabe "%s: %s - invalid L2CAP signaling command, code=%#x, ident=%d. " \
309fbc48c2bSTakanori Watanabe "Invalid command length=%d, m_pkthdr.len=%d\n",
310fbc48c2bSTakanori Watanabe __func__, NG_NODE_NAME(l2cap->node),
311fbc48c2bSTakanori Watanabe hdr->code, hdr->ident, hdr->length,
312fbc48c2bSTakanori Watanabe con->rx_pkt->m_pkthdr.len);
313fbc48c2bSTakanori Watanabe NG_FREE_M(con->rx_pkt);
314fbc48c2bSTakanori Watanabe
315fbc48c2bSTakanori Watanabe return (EMSGSIZE);
316fbc48c2bSTakanori Watanabe }
317fbc48c2bSTakanori Watanabe
318fbc48c2bSTakanori Watanabe /* Get the command, save the rest (if any) */
319fbc48c2bSTakanori Watanabe if (con->rx_pkt->m_pkthdr.len > hdr->length)
320fbc48c2bSTakanori Watanabe m = m_split(con->rx_pkt, hdr->length, M_NOWAIT);
321fbc48c2bSTakanori Watanabe else
322fbc48c2bSTakanori Watanabe m = NULL;
323fbc48c2bSTakanori Watanabe
324fbc48c2bSTakanori Watanabe /* Process command */
325fbc48c2bSTakanori Watanabe switch (hdr->code) {
326fbc48c2bSTakanori Watanabe case NG_L2CAP_CMD_REJ:
327fbc48c2bSTakanori Watanabe ng_l2cap_process_cmd_rej(con, hdr->ident);
328fbc48c2bSTakanori Watanabe break;
329fbc48c2bSTakanori Watanabe case NG_L2CAP_CMD_PARAM_UPDATE_REQUEST:
330fbc48c2bSTakanori Watanabe ng_l2cap_process_cmd_urq(con, hdr->ident);
331fbc48c2bSTakanori Watanabe break;
332fbc48c2bSTakanori Watanabe case NG_L2CAP_CMD_PARAM_UPDATE_RESPONSE:
333fbc48c2bSTakanori Watanabe ng_l2cap_process_cmd_urs(con, hdr->ident);
334fbc48c2bSTakanori Watanabe break;
335fbc48c2bSTakanori Watanabe
336fbc48c2bSTakanori Watanabe
337fbc48c2bSTakanori Watanabe default:
338fbc48c2bSTakanori Watanabe NG_L2CAP_ERR(
339fbc48c2bSTakanori Watanabe "%s: %s - unknown L2CAP signaling command, code=%#x, ident=%d, length=%d\n",
340fbc48c2bSTakanori Watanabe __func__, NG_NODE_NAME(l2cap->node),
341fbc48c2bSTakanori Watanabe hdr->code, hdr->ident, hdr->length);
342fbc48c2bSTakanori Watanabe
343fbc48c2bSTakanori Watanabe /*
344fbc48c2bSTakanori Watanabe * Send L2CAP_CommandRej. Do not really care
345fbc48c2bSTakanori Watanabe * about the result
346fbc48c2bSTakanori Watanabe */
347fbc48c2bSTakanori Watanabe
348fbc48c2bSTakanori Watanabe send_l2cap_reject(con, hdr->ident,
349fbc48c2bSTakanori Watanabe NG_L2CAP_REJ_NOT_UNDERSTOOD, 0, 0, 0);
350fbc48c2bSTakanori Watanabe NG_FREE_M(con->rx_pkt);
351fbc48c2bSTakanori Watanabe break;
352fbc48c2bSTakanori Watanabe }
353fbc48c2bSTakanori Watanabe
354fbc48c2bSTakanori Watanabe con->rx_pkt = m;
355fbc48c2bSTakanori Watanabe }
356fbc48c2bSTakanori Watanabe
357fbc48c2bSTakanori Watanabe return (0);
358fbc48c2bSTakanori Watanabe } /* ng_l2cap_process_signal_cmd */
359fbc48c2bSTakanori Watanabe /*Update Paramater Request*/
ng_l2cap_process_cmd_urq(ng_l2cap_con_p con,uint8_t ident)360fbc48c2bSTakanori Watanabe static int ng_l2cap_process_cmd_urq(ng_l2cap_con_p con, uint8_t ident)
361fbc48c2bSTakanori Watanabe {
362053359b7SPedro F. Giffuni /* We do not implement parameter negotiation for now. */
363fbc48c2bSTakanori Watanabe send_l2cap_param_urs(con, ident, NG_L2CAP_UPDATE_PARAM_ACCEPT);
364fbc48c2bSTakanori Watanabe NG_FREE_M(con->rx_pkt);
365fbc48c2bSTakanori Watanabe return 0;
366fbc48c2bSTakanori Watanabe }
367fbc48c2bSTakanori Watanabe
ng_l2cap_process_cmd_urs(ng_l2cap_con_p con,uint8_t ident)368fbc48c2bSTakanori Watanabe static int ng_l2cap_process_cmd_urs(ng_l2cap_con_p con, uint8_t ident)
369fbc48c2bSTakanori Watanabe {
370fbc48c2bSTakanori Watanabe /* We only support master side yet .*/
371fbc48c2bSTakanori Watanabe //send_l2cap_reject(con,ident ... );
372fbc48c2bSTakanori Watanabe
373fbc48c2bSTakanori Watanabe NG_FREE_M(con->rx_pkt);
374fbc48c2bSTakanori Watanabe return 0;
375fbc48c2bSTakanori Watanabe }
376878ed226SJulian Elischer
377878ed226SJulian Elischer /*
378878ed226SJulian Elischer * Process L2CAP_CommandRej command
379878ed226SJulian Elischer */
380878ed226SJulian Elischer
381878ed226SJulian Elischer static int
ng_l2cap_process_cmd_rej(ng_l2cap_con_p con,u_int8_t ident)382878ed226SJulian Elischer ng_l2cap_process_cmd_rej(ng_l2cap_con_p con, u_int8_t ident)
383878ed226SJulian Elischer {
384878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap;
385878ed226SJulian Elischer ng_l2cap_cmd_rej_cp *cp = NULL;
386878ed226SJulian Elischer ng_l2cap_cmd_p cmd = NULL;
387878ed226SJulian Elischer
388878ed226SJulian Elischer /* Get command parameters */
389878ed226SJulian Elischer NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));
390878ed226SJulian Elischer if (con->rx_pkt == NULL)
391878ed226SJulian Elischer return (ENOBUFS);
392878ed226SJulian Elischer
393878ed226SJulian Elischer cp = mtod(con->rx_pkt, ng_l2cap_cmd_rej_cp *);
394878ed226SJulian Elischer cp->reason = le16toh(cp->reason);
395878ed226SJulian Elischer
396878ed226SJulian Elischer /* Check if we have pending command descriptor */
397878ed226SJulian Elischer cmd = ng_l2cap_cmd_by_ident(con, ident);
398878ed226SJulian Elischer if (cmd != NULL) {
3990986ab12SMaksim Yevmenkin /* If command timeout already happened then ignore reject */
4000986ab12SMaksim Yevmenkin if (ng_l2cap_command_untimeout(cmd) != 0) {
4010986ab12SMaksim Yevmenkin NG_FREE_M(con->rx_pkt);
4020986ab12SMaksim Yevmenkin return (ETIMEDOUT);
4030986ab12SMaksim Yevmenkin }
404878ed226SJulian Elischer
405878ed226SJulian Elischer ng_l2cap_unlink_cmd(cmd);
406878ed226SJulian Elischer
407878ed226SJulian Elischer switch (cmd->code) {
408878ed226SJulian Elischer case NG_L2CAP_CON_REQ:
409878ed226SJulian Elischer ng_l2cap_l2ca_con_rsp(cmd->ch,cmd->token,cp->reason,0);
410878ed226SJulian Elischer ng_l2cap_free_chan(cmd->ch);
411878ed226SJulian Elischer break;
412878ed226SJulian Elischer
413878ed226SJulian Elischer case NG_L2CAP_CFG_REQ:
414878ed226SJulian Elischer ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, cp->reason);
415878ed226SJulian Elischer break;
416878ed226SJulian Elischer
417878ed226SJulian Elischer case NG_L2CAP_DISCON_REQ:
418878ed226SJulian Elischer ng_l2cap_l2ca_discon_rsp(cmd->ch,cmd->token,cp->reason);
419878ed226SJulian Elischer ng_l2cap_free_chan(cmd->ch); /* XXX free channel */
420878ed226SJulian Elischer break;
421878ed226SJulian Elischer
422878ed226SJulian Elischer case NG_L2CAP_ECHO_REQ:
423878ed226SJulian Elischer ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
424878ed226SJulian Elischer cp->reason, NULL);
425878ed226SJulian Elischer break;
426878ed226SJulian Elischer
427878ed226SJulian Elischer case NG_L2CAP_INFO_REQ:
428878ed226SJulian Elischer ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
429878ed226SJulian Elischer cp->reason, NULL);
430878ed226SJulian Elischer break;
431878ed226SJulian Elischer
432878ed226SJulian Elischer default:
433878ed226SJulian Elischer NG_L2CAP_ALERT(
434878ed226SJulian Elischer "%s: %s - unexpected L2CAP_CommandRej. Unexpected L2CAP command opcode=%d\n",
435878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), cmd->code);
436878ed226SJulian Elischer break;
437878ed226SJulian Elischer }
438878ed226SJulian Elischer
439878ed226SJulian Elischer ng_l2cap_free_cmd(cmd);
440878ed226SJulian Elischer } else
441878ed226SJulian Elischer NG_L2CAP_ERR(
442878ed226SJulian Elischer "%s: %s - unexpected L2CAP_CommandRej command. " \
443878ed226SJulian Elischer "Requested ident does not exist, ident=%d\n",
444878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), ident);
445878ed226SJulian Elischer
446878ed226SJulian Elischer NG_FREE_M(con->rx_pkt);
447878ed226SJulian Elischer
448878ed226SJulian Elischer return (0);
449878ed226SJulian Elischer } /* ng_l2cap_process_cmd_rej */
450878ed226SJulian Elischer
451878ed226SJulian Elischer /*
452878ed226SJulian Elischer * Process L2CAP_ConnectReq command
453878ed226SJulian Elischer */
454878ed226SJulian Elischer
455878ed226SJulian Elischer static int
ng_l2cap_process_con_req(ng_l2cap_con_p con,u_int8_t ident)456878ed226SJulian Elischer ng_l2cap_process_con_req(ng_l2cap_con_p con, u_int8_t ident)
457878ed226SJulian Elischer {
458878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap;
459878ed226SJulian Elischer struct mbuf *m = con->rx_pkt;
460878ed226SJulian Elischer ng_l2cap_con_req_cp *cp = NULL;
461878ed226SJulian Elischer ng_l2cap_chan_p ch = NULL;
462878ed226SJulian Elischer int error = 0;
463878ed226SJulian Elischer u_int16_t dcid, psm;
464fbc48c2bSTakanori Watanabe int idtype;
465878ed226SJulian Elischer
466878ed226SJulian Elischer /* Get command parameters */
467878ed226SJulian Elischer NG_L2CAP_M_PULLUP(m, sizeof(*cp));
468878ed226SJulian Elischer if (m == NULL)
469878ed226SJulian Elischer return (ENOBUFS);
470878ed226SJulian Elischer
471878ed226SJulian Elischer cp = mtod(m, ng_l2cap_con_req_cp *);
472878ed226SJulian Elischer psm = le16toh(cp->psm);
473878ed226SJulian Elischer dcid = le16toh(cp->scid);
474878ed226SJulian Elischer
475878ed226SJulian Elischer NG_FREE_M(m);
476878ed226SJulian Elischer con->rx_pkt = NULL;
477fbc48c2bSTakanori Watanabe if(dcid == NG_L2CAP_ATT_CID)
478fbc48c2bSTakanori Watanabe idtype = NG_L2CAP_L2CA_IDTYPE_ATT;
4793a601a23STakanori Watanabe else if(dcid == NG_L2CAP_SMP_CID)
4803a601a23STakanori Watanabe idtype = NG_L2CAP_L2CA_IDTYPE_SMP;
481fbc48c2bSTakanori Watanabe else if( con->linktype != NG_HCI_LINK_ACL)
482fbc48c2bSTakanori Watanabe idtype = NG_L2CAP_L2CA_IDTYPE_LE;
483fbc48c2bSTakanori Watanabe else
484fbc48c2bSTakanori Watanabe idtype = NG_L2CAP_L2CA_IDTYPE_BREDR;
485878ed226SJulian Elischer
486878ed226SJulian Elischer /*
487878ed226SJulian Elischer * Create new channel and send L2CA_ConnectInd notification
488878ed226SJulian Elischer * to the upper layer protocol.
489878ed226SJulian Elischer */
490878ed226SJulian Elischer
491fbc48c2bSTakanori Watanabe ch = ng_l2cap_new_chan(l2cap, con, psm, idtype);
492fbc48c2bSTakanori Watanabe
493878ed226SJulian Elischer if (ch == NULL)
494878ed226SJulian Elischer return (send_l2cap_con_rej(con, ident, 0, dcid,
495878ed226SJulian Elischer NG_L2CAP_NO_RESOURCES));
496878ed226SJulian Elischer
497878ed226SJulian Elischer /* Update channel IDs */
498878ed226SJulian Elischer ch->dcid = dcid;
499878ed226SJulian Elischer
500878ed226SJulian Elischer /* Sent L2CA_ConnectInd notification to the upper layer */
501878ed226SJulian Elischer ch->ident = ident;
502878ed226SJulian Elischer ch->state = NG_L2CAP_W4_L2CA_CON_RSP;
503878ed226SJulian Elischer
504878ed226SJulian Elischer error = ng_l2cap_l2ca_con_ind(ch);
505878ed226SJulian Elischer if (error != 0) {
506878ed226SJulian Elischer send_l2cap_con_rej(con, ident, ch->scid, dcid,
507878ed226SJulian Elischer (error == ENOMEM)? NG_L2CAP_NO_RESOURCES :
508878ed226SJulian Elischer NG_L2CAP_PSM_NOT_SUPPORTED);
509878ed226SJulian Elischer ng_l2cap_free_chan(ch);
510878ed226SJulian Elischer }
511878ed226SJulian Elischer
512878ed226SJulian Elischer return (error);
513878ed226SJulian Elischer } /* ng_l2cap_process_con_req */
514878ed226SJulian Elischer
515878ed226SJulian Elischer /*
516878ed226SJulian Elischer * Process L2CAP_ConnectRsp command
517878ed226SJulian Elischer */
518878ed226SJulian Elischer
519878ed226SJulian Elischer static int
ng_l2cap_process_con_rsp(ng_l2cap_con_p con,u_int8_t ident)520878ed226SJulian Elischer ng_l2cap_process_con_rsp(ng_l2cap_con_p con, u_int8_t ident)
521878ed226SJulian Elischer {
522878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap;
523878ed226SJulian Elischer struct mbuf *m = con->rx_pkt;
524878ed226SJulian Elischer ng_l2cap_con_rsp_cp *cp = NULL;
525878ed226SJulian Elischer ng_l2cap_cmd_p cmd = NULL;
526878ed226SJulian Elischer u_int16_t scid, dcid, result, status;
527878ed226SJulian Elischer int error = 0;
528878ed226SJulian Elischer
529878ed226SJulian Elischer /* Get command parameters */
530878ed226SJulian Elischer NG_L2CAP_M_PULLUP(m, sizeof(*cp));
531878ed226SJulian Elischer if (m == NULL)
532878ed226SJulian Elischer return (ENOBUFS);
533878ed226SJulian Elischer
534878ed226SJulian Elischer cp = mtod(m, ng_l2cap_con_rsp_cp *);
535878ed226SJulian Elischer dcid = le16toh(cp->dcid);
536878ed226SJulian Elischer scid = le16toh(cp->scid);
537878ed226SJulian Elischer result = le16toh(cp->result);
538878ed226SJulian Elischer status = le16toh(cp->status);
539878ed226SJulian Elischer
540878ed226SJulian Elischer NG_FREE_M(m);
541878ed226SJulian Elischer con->rx_pkt = NULL;
542878ed226SJulian Elischer
543878ed226SJulian Elischer /* Check if we have pending command descriptor */
544878ed226SJulian Elischer cmd = ng_l2cap_cmd_by_ident(con, ident);
545878ed226SJulian Elischer if (cmd == NULL) {
546878ed226SJulian Elischer NG_L2CAP_ERR(
547878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConnectRsp command. ident=%d, con_handle=%d\n",
548878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), ident,
549878ed226SJulian Elischer con->con_handle);
550878ed226SJulian Elischer
551878ed226SJulian Elischer return (ENOENT);
552878ed226SJulian Elischer }
553878ed226SJulian Elischer
554878ed226SJulian Elischer /* Verify channel state, if invalid - do nothing */
555878ed226SJulian Elischer if (cmd->ch->state != NG_L2CAP_W4_L2CAP_CON_RSP) {
556878ed226SJulian Elischer NG_L2CAP_ERR(
557878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConnectRsp. " \
558878ed226SJulian Elischer "Invalid channel state, cid=%d, state=%d\n",
559878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), scid,
560878ed226SJulian Elischer cmd->ch->state);
561878ed226SJulian Elischer goto reject;
562878ed226SJulian Elischer }
563878ed226SJulian Elischer
564878ed226SJulian Elischer /* Verify CIDs and send reject if does not match */
565878ed226SJulian Elischer if (cmd->ch->scid != scid) {
566878ed226SJulian Elischer NG_L2CAP_ERR(
567878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConnectRsp. Channel IDs do not match, scid=%d(%d)\n",
568878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,
569878ed226SJulian Elischer scid);
570878ed226SJulian Elischer goto reject;
571878ed226SJulian Elischer }
572878ed226SJulian Elischer
573878ed226SJulian Elischer /*
574878ed226SJulian Elischer * Looks good. We got confirmation from our peer. Now process
575878ed226SJulian Elischer * it. First disable RTX timer. Then check the result and send
5760986ab12SMaksim Yevmenkin * notification to the upper layer. If command timeout already
5770986ab12SMaksim Yevmenkin * happened then ignore response.
578878ed226SJulian Elischer */
579878ed226SJulian Elischer
5800986ab12SMaksim Yevmenkin if ((error = ng_l2cap_command_untimeout(cmd)) != 0)
5810986ab12SMaksim Yevmenkin return (error);
582878ed226SJulian Elischer
583878ed226SJulian Elischer if (result == NG_L2CAP_PENDING) {
584878ed226SJulian Elischer /*
585878ed226SJulian Elischer * Our peer wants more time to complete connection. We shall
586878ed226SJulian Elischer * start ERTX timer and wait. Keep command in the list.
587878ed226SJulian Elischer */
588878ed226SJulian Elischer
589878ed226SJulian Elischer cmd->ch->dcid = dcid;
590878ed226SJulian Elischer ng_l2cap_command_timeout(cmd, bluetooth_l2cap_ertx_timeout());
591878ed226SJulian Elischer
592878ed226SJulian Elischer error = ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token,
593878ed226SJulian Elischer result, status);
594878ed226SJulian Elischer if (error != 0)
595878ed226SJulian Elischer ng_l2cap_free_chan(cmd->ch);
596878ed226SJulian Elischer } else {
597878ed226SJulian Elischer ng_l2cap_unlink_cmd(cmd);
598878ed226SJulian Elischer
599878ed226SJulian Elischer if (result == NG_L2CAP_SUCCESS) {
600878ed226SJulian Elischer /*
601878ed226SJulian Elischer * Channel is open. Complete command and move to CONFIG
602878ed226SJulian Elischer * state. Since we have sent positive confirmation we
603878ed226SJulian Elischer * expect to receive L2CA_Config request from the upper
604878ed226SJulian Elischer * layer protocol.
605878ed226SJulian Elischer */
606878ed226SJulian Elischer
607878ed226SJulian Elischer cmd->ch->dcid = dcid;
6083a601a23STakanori Watanabe cmd->ch->state = ((cmd->ch->scid == NG_L2CAP_ATT_CID)||
6093a601a23STakanori Watanabe (cmd->ch->scid == NG_L2CAP_SMP_CID))
6103a601a23STakanori Watanabe ?
611fbc48c2bSTakanori Watanabe NG_L2CAP_OPEN : NG_L2CAP_CONFIG;
612878ed226SJulian Elischer } else
613878ed226SJulian Elischer /* There was an error, so close the channel */
614878ed226SJulian Elischer NG_L2CAP_INFO(
615878ed226SJulian Elischer "%s: %s - failed to open L2CAP channel, result=%d, status=%d\n",
616878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), result,
617878ed226SJulian Elischer status);
618878ed226SJulian Elischer
619878ed226SJulian Elischer error = ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token,
620878ed226SJulian Elischer result, status);
621878ed226SJulian Elischer
622878ed226SJulian Elischer /* XXX do we have to remove the channel on error? */
623878ed226SJulian Elischer if (error != 0 || result != NG_L2CAP_SUCCESS)
624878ed226SJulian Elischer ng_l2cap_free_chan(cmd->ch);
625878ed226SJulian Elischer
626878ed226SJulian Elischer ng_l2cap_free_cmd(cmd);
627878ed226SJulian Elischer }
628878ed226SJulian Elischer
629878ed226SJulian Elischer return (error);
630878ed226SJulian Elischer
631878ed226SJulian Elischer reject:
632878ed226SJulian Elischer /* Send reject. Do not really care about the result */
633878ed226SJulian Elischer send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, dcid);
634878ed226SJulian Elischer
635878ed226SJulian Elischer return (0);
636878ed226SJulian Elischer } /* ng_l2cap_process_con_rsp */
637878ed226SJulian Elischer
638878ed226SJulian Elischer /*
639878ed226SJulian Elischer * Process L2CAP_ConfigReq command
640878ed226SJulian Elischer */
641878ed226SJulian Elischer
642878ed226SJulian Elischer static int
ng_l2cap_process_cfg_req(ng_l2cap_con_p con,u_int8_t ident)643878ed226SJulian Elischer ng_l2cap_process_cfg_req(ng_l2cap_con_p con, u_int8_t ident)
644878ed226SJulian Elischer {
645878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap;
646878ed226SJulian Elischer struct mbuf *m = con->rx_pkt;
647878ed226SJulian Elischer ng_l2cap_cfg_req_cp *cp = NULL;
648878ed226SJulian Elischer ng_l2cap_chan_p ch = NULL;
649878ed226SJulian Elischer u_int16_t dcid, respond, result;
650878ed226SJulian Elischer ng_l2cap_cfg_opt_t hdr;
651878ed226SJulian Elischer ng_l2cap_cfg_opt_val_t val;
652878ed226SJulian Elischer int off, error = 0;
653878ed226SJulian Elischer
654878ed226SJulian Elischer /* Get command parameters */
655878ed226SJulian Elischer con->rx_pkt = NULL;
656878ed226SJulian Elischer NG_L2CAP_M_PULLUP(m, sizeof(*cp));
657878ed226SJulian Elischer if (m == NULL)
658878ed226SJulian Elischer return (ENOBUFS);
659878ed226SJulian Elischer
660878ed226SJulian Elischer cp = mtod(m, ng_l2cap_cfg_req_cp *);
661878ed226SJulian Elischer dcid = le16toh(cp->dcid);
662878ed226SJulian Elischer respond = NG_L2CAP_OPT_CFLAG(le16toh(cp->flags));
663878ed226SJulian Elischer m_adj(m, sizeof(*cp));
664878ed226SJulian Elischer
665878ed226SJulian Elischer /* Check if we have this channel and it is in valid state */
666fbc48c2bSTakanori Watanabe ch = ng_l2cap_chan_by_scid(l2cap, dcid, NG_L2CAP_L2CA_IDTYPE_BREDR);
667878ed226SJulian Elischer if (ch == NULL) {
668878ed226SJulian Elischer NG_L2CAP_ERR(
669878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConfigReq command. " \
670878ed226SJulian Elischer "Channel does not exist, cid=%d\n",
671878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), dcid);
672878ed226SJulian Elischer goto reject;
673878ed226SJulian Elischer }
674878ed226SJulian Elischer
675878ed226SJulian Elischer /* Verify channel state */
676878ed226SJulian Elischer if (ch->state != NG_L2CAP_CONFIG && ch->state != NG_L2CAP_OPEN) {
677878ed226SJulian Elischer NG_L2CAP_ERR(
678878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConfigReq. " \
679878ed226SJulian Elischer "Invalid channel state, cid=%d, state=%d\n",
680878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), dcid, ch->state);
681878ed226SJulian Elischer goto reject;
682878ed226SJulian Elischer }
683878ed226SJulian Elischer
684878ed226SJulian Elischer if (ch->state == NG_L2CAP_OPEN) { /* Re-configuration */
685878ed226SJulian Elischer ch->cfg_state = 0;
686878ed226SJulian Elischer ch->state = NG_L2CAP_CONFIG;
687878ed226SJulian Elischer }
688878ed226SJulian Elischer
689878ed226SJulian Elischer for (result = 0, off = 0; ; ) {
690878ed226SJulian Elischer error = get_next_l2cap_opt(m, &off, &hdr, &val);
691878ed226SJulian Elischer if (error == 0) { /* We done with this packet */
692878ed226SJulian Elischer NG_FREE_M(m);
693878ed226SJulian Elischer break;
694878ed226SJulian Elischer } else if (error > 0) { /* Got option */
695878ed226SJulian Elischer switch (hdr.type) {
696878ed226SJulian Elischer case NG_L2CAP_OPT_MTU:
697878ed226SJulian Elischer ch->omtu = val.mtu;
698878ed226SJulian Elischer break;
699878ed226SJulian Elischer
700878ed226SJulian Elischer case NG_L2CAP_OPT_FLUSH_TIMO:
701878ed226SJulian Elischer ch->flush_timo = val.flush_timo;
702878ed226SJulian Elischer break;
703878ed226SJulian Elischer
704878ed226SJulian Elischer case NG_L2CAP_OPT_QOS:
705878ed226SJulian Elischer bcopy(&val.flow, &ch->iflow, sizeof(ch->iflow));
706878ed226SJulian Elischer break;
707878ed226SJulian Elischer
7080986ab12SMaksim Yevmenkin default: /* Ignore unknown hint option */
709878ed226SJulian Elischer break;
710878ed226SJulian Elischer }
711878ed226SJulian Elischer } else { /* Oops, something is wrong */
712878ed226SJulian Elischer respond = 1;
713878ed226SJulian Elischer
714878ed226SJulian Elischer if (error == -3) {
715878ed226SJulian Elischer /*
716878ed226SJulian Elischer * Adjust mbuf so we can get to the start
717878ed226SJulian Elischer * of the first option we did not like.
718878ed226SJulian Elischer */
719878ed226SJulian Elischer
720878ed226SJulian Elischer m_adj(m, off - sizeof(hdr));
721878ed226SJulian Elischer m->m_pkthdr.len = sizeof(hdr) + hdr.length;
722878ed226SJulian Elischer
723878ed226SJulian Elischer result = NG_L2CAP_UNKNOWN_OPTION;
724878ed226SJulian Elischer } else {
725878ed226SJulian Elischer /* XXX FIXME Send other reject codes? */
726878ed226SJulian Elischer NG_FREE_M(m);
727878ed226SJulian Elischer result = NG_L2CAP_REJECT;
728878ed226SJulian Elischer }
729878ed226SJulian Elischer
730878ed226SJulian Elischer break;
731878ed226SJulian Elischer }
732878ed226SJulian Elischer }
733878ed226SJulian Elischer
734878ed226SJulian Elischer /*
735878ed226SJulian Elischer * Now check and see if we have to respond. If everything was OK then
736878ed226SJulian Elischer * respond contain "C flag" and (if set) we will respond with empty
737878ed226SJulian Elischer * packet and will wait for more options.
738878ed226SJulian Elischer *
739878ed226SJulian Elischer * Other case is that we did not like peer's options and will respond
740878ed226SJulian Elischer * with L2CAP_Config response command with Reject error code.
741878ed226SJulian Elischer *
742878ed226SJulian Elischer * When "respond == 0" than we have received all options and we will
743878ed226SJulian Elischer * sent L2CA_ConfigInd event to the upper layer protocol.
744878ed226SJulian Elischer */
745878ed226SJulian Elischer
746878ed226SJulian Elischer if (respond) {
747878ed226SJulian Elischer error = send_l2cap_cfg_rsp(con, ident, ch->dcid, result, m);
748878ed226SJulian Elischer if (error != 0) {
749878ed226SJulian Elischer ng_l2cap_l2ca_discon_ind(ch);
750878ed226SJulian Elischer ng_l2cap_free_chan(ch);
751878ed226SJulian Elischer }
752878ed226SJulian Elischer } else {
753878ed226SJulian Elischer /* Send L2CA_ConfigInd event to the upper layer protocol */
754878ed226SJulian Elischer ch->ident = ident;
755878ed226SJulian Elischer error = ng_l2cap_l2ca_cfg_ind(ch);
756878ed226SJulian Elischer if (error != 0)
757878ed226SJulian Elischer ng_l2cap_free_chan(ch);
758878ed226SJulian Elischer }
759878ed226SJulian Elischer
760878ed226SJulian Elischer return (error);
761878ed226SJulian Elischer
762878ed226SJulian Elischer reject:
763878ed226SJulian Elischer /* Send reject. Do not really care about the result */
764878ed226SJulian Elischer NG_FREE_M(m);
765878ed226SJulian Elischer
766878ed226SJulian Elischer send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, 0, dcid);
767878ed226SJulian Elischer
768878ed226SJulian Elischer return (0);
769878ed226SJulian Elischer } /* ng_l2cap_process_cfg_req */
770878ed226SJulian Elischer
771878ed226SJulian Elischer /*
772878ed226SJulian Elischer * Process L2CAP_ConfigRsp command
773878ed226SJulian Elischer */
774878ed226SJulian Elischer
775878ed226SJulian Elischer static int
ng_l2cap_process_cfg_rsp(ng_l2cap_con_p con,u_int8_t ident)776878ed226SJulian Elischer ng_l2cap_process_cfg_rsp(ng_l2cap_con_p con, u_int8_t ident)
777878ed226SJulian Elischer {
778878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap;
779878ed226SJulian Elischer struct mbuf *m = con->rx_pkt;
780878ed226SJulian Elischer ng_l2cap_cfg_rsp_cp *cp = NULL;
781878ed226SJulian Elischer ng_l2cap_cmd_p cmd = NULL;
782878ed226SJulian Elischer u_int16_t scid, cflag, result;
783878ed226SJulian Elischer ng_l2cap_cfg_opt_t hdr;
784878ed226SJulian Elischer ng_l2cap_cfg_opt_val_t val;
785878ed226SJulian Elischer int off, error = 0;
786878ed226SJulian Elischer
787878ed226SJulian Elischer /* Get command parameters */
788878ed226SJulian Elischer con->rx_pkt = NULL;
789878ed226SJulian Elischer NG_L2CAP_M_PULLUP(m, sizeof(*cp));
790878ed226SJulian Elischer if (m == NULL)
791878ed226SJulian Elischer return (ENOBUFS);
792878ed226SJulian Elischer
793878ed226SJulian Elischer cp = mtod(m, ng_l2cap_cfg_rsp_cp *);
794878ed226SJulian Elischer scid = le16toh(cp->scid);
795878ed226SJulian Elischer cflag = NG_L2CAP_OPT_CFLAG(le16toh(cp->flags));
796878ed226SJulian Elischer result = le16toh(cp->result);
797878ed226SJulian Elischer m_adj(m, sizeof(*cp));
798878ed226SJulian Elischer
799878ed226SJulian Elischer /* Check if we have this command */
800878ed226SJulian Elischer cmd = ng_l2cap_cmd_by_ident(con, ident);
801878ed226SJulian Elischer if (cmd == NULL) {
802878ed226SJulian Elischer NG_L2CAP_ERR(
803878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConfigRsp command. ident=%d, con_handle=%d\n",
804878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), ident,
805878ed226SJulian Elischer con->con_handle);
806878ed226SJulian Elischer NG_FREE_M(m);
807878ed226SJulian Elischer
808878ed226SJulian Elischer return (ENOENT);
809878ed226SJulian Elischer }
810878ed226SJulian Elischer
811878ed226SJulian Elischer /* Verify CIDs and send reject if does not match */
812878ed226SJulian Elischer if (cmd->ch->scid != scid) {
813878ed226SJulian Elischer NG_L2CAP_ERR(
814878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConfigRsp. " \
815878ed226SJulian Elischer "Channel ID does not match, scid=%d(%d)\n",
816878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,
817878ed226SJulian Elischer scid);
818878ed226SJulian Elischer goto reject;
819878ed226SJulian Elischer }
820878ed226SJulian Elischer
821878ed226SJulian Elischer /* Verify channel state and reject if invalid */
822878ed226SJulian Elischer if (cmd->ch->state != NG_L2CAP_CONFIG) {
823878ed226SJulian Elischer NG_L2CAP_ERR(
824878ed226SJulian Elischer "%s: %s - unexpected L2CAP_ConfigRsp. " \
825878ed226SJulian Elischer "Invalid channel state, scid=%d, state=%d\n",
826878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,
827878ed226SJulian Elischer cmd->ch->state);
828878ed226SJulian Elischer goto reject;
829878ed226SJulian Elischer }
830878ed226SJulian Elischer
831878ed226SJulian Elischer /*
832878ed226SJulian Elischer * Looks like it is our response, so process it. First parse options,
833878ed226SJulian Elischer * then verify C flag. If it is set then we shall expect more
834878ed226SJulian Elischer * configuration options from the peer and we will wait. Otherwise we
835878ed226SJulian Elischer * have received all options and we will send L2CA_ConfigRsp event to
8360986ab12SMaksim Yevmenkin * the upper layer protocol. If command timeout already happened then
8370986ab12SMaksim Yevmenkin * ignore response.
838878ed226SJulian Elischer */
839878ed226SJulian Elischer
8400986ab12SMaksim Yevmenkin if ((error = ng_l2cap_command_untimeout(cmd)) != 0) {
8410986ab12SMaksim Yevmenkin NG_FREE_M(m);
8420986ab12SMaksim Yevmenkin return (error);
8430986ab12SMaksim Yevmenkin }
844878ed226SJulian Elischer
845878ed226SJulian Elischer for (off = 0; ; ) {
846878ed226SJulian Elischer error = get_next_l2cap_opt(m, &off, &hdr, &val);
847878ed226SJulian Elischer if (error == 0) /* We done with this packet */
848878ed226SJulian Elischer break;
849878ed226SJulian Elischer else if (error > 0) { /* Got option */
850878ed226SJulian Elischer switch (hdr.type) {
851878ed226SJulian Elischer case NG_L2CAP_OPT_MTU:
852878ed226SJulian Elischer cmd->ch->imtu = val.mtu;
853878ed226SJulian Elischer break;
854878ed226SJulian Elischer
855878ed226SJulian Elischer case NG_L2CAP_OPT_FLUSH_TIMO:
856878ed226SJulian Elischer cmd->ch->flush_timo = val.flush_timo;
857878ed226SJulian Elischer break;
858878ed226SJulian Elischer
859878ed226SJulian Elischer case NG_L2CAP_OPT_QOS:
860878ed226SJulian Elischer bcopy(&val.flow, &cmd->ch->oflow,
861878ed226SJulian Elischer sizeof(cmd->ch->oflow));
862878ed226SJulian Elischer break;
863878ed226SJulian Elischer
8640986ab12SMaksim Yevmenkin default: /* Ignore unknown hint option */
865878ed226SJulian Elischer break;
866878ed226SJulian Elischer }
867878ed226SJulian Elischer } else {
868878ed226SJulian Elischer /*
869878ed226SJulian Elischer * XXX FIXME What to do here?
870878ed226SJulian Elischer *
8710986ab12SMaksim Yevmenkin * This is really BAD :( options packet was broken, or
8720986ab12SMaksim Yevmenkin * peer sent us option that we did not understand. Let
8730986ab12SMaksim Yevmenkin * upper layer know and do not wait for more options.
874878ed226SJulian Elischer */
875878ed226SJulian Elischer
876878ed226SJulian Elischer NG_L2CAP_ALERT(
877878ed226SJulian Elischer "%s: %s - failed to parse configuration options, error=%d\n",
878878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), error);
879878ed226SJulian Elischer
880878ed226SJulian Elischer result = NG_L2CAP_UNKNOWN;
881878ed226SJulian Elischer cflag = 0;
882878ed226SJulian Elischer
883878ed226SJulian Elischer break;
884878ed226SJulian Elischer }
885878ed226SJulian Elischer }
886878ed226SJulian Elischer
887878ed226SJulian Elischer NG_FREE_M(m);
888878ed226SJulian Elischer
889878ed226SJulian Elischer if (cflag) /* Restart timer and wait for more options */
890878ed226SJulian Elischer ng_l2cap_command_timeout(cmd, bluetooth_l2cap_rtx_timeout());
891878ed226SJulian Elischer else {
892878ed226SJulian Elischer ng_l2cap_unlink_cmd(cmd);
893878ed226SJulian Elischer
894878ed226SJulian Elischer /* Send L2CA_Config response to the upper layer protocol */
895878ed226SJulian Elischer error = ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, result);
896878ed226SJulian Elischer if (error != 0) {
897878ed226SJulian Elischer /*
898878ed226SJulian Elischer * XXX FIXME what to do here? we were not able to send
899878ed226SJulian Elischer * response to the upper layer protocol, so for now
900878ed226SJulian Elischer * just close the channel. Send L2CAP_Disconnect to
901878ed226SJulian Elischer * remote peer?
902878ed226SJulian Elischer */
903878ed226SJulian Elischer
904878ed226SJulian Elischer NG_L2CAP_ERR(
905878ed226SJulian Elischer "%s: %s - failed to send L2CA_Config response, error=%d\n",
906878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), error);
907878ed226SJulian Elischer
908878ed226SJulian Elischer ng_l2cap_free_chan(cmd->ch);
909878ed226SJulian Elischer }
910878ed226SJulian Elischer
911878ed226SJulian Elischer ng_l2cap_free_cmd(cmd);
912878ed226SJulian Elischer }
913878ed226SJulian Elischer
914878ed226SJulian Elischer return (error);
915878ed226SJulian Elischer
916878ed226SJulian Elischer reject:
917878ed226SJulian Elischer /* Send reject. Do not really care about the result */
918878ed226SJulian Elischer NG_FREE_M(m);
919878ed226SJulian Elischer
920878ed226SJulian Elischer send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, 0);
921878ed226SJulian Elischer
922878ed226SJulian Elischer return (0);
923878ed226SJulian Elischer } /* ng_l2cap_process_cfg_rsp */
924878ed226SJulian Elischer
925878ed226SJulian Elischer /*
926878ed226SJulian Elischer * Process L2CAP_DisconnectReq command
927878ed226SJulian Elischer */
928878ed226SJulian Elischer
929878ed226SJulian Elischer static int
ng_l2cap_process_discon_req(ng_l2cap_con_p con,u_int8_t ident)930878ed226SJulian Elischer ng_l2cap_process_discon_req(ng_l2cap_con_p con, u_int8_t ident)
931878ed226SJulian Elischer {
932878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap;
933878ed226SJulian Elischer ng_l2cap_discon_req_cp *cp = NULL;
934878ed226SJulian Elischer ng_l2cap_chan_p ch = NULL;
935878ed226SJulian Elischer ng_l2cap_cmd_p cmd = NULL;
936878ed226SJulian Elischer u_int16_t scid, dcid;
937878ed226SJulian Elischer
938878ed226SJulian Elischer /* Get command parameters */
939878ed226SJulian Elischer NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));
940878ed226SJulian Elischer if (con->rx_pkt == NULL)
941878ed226SJulian Elischer return (ENOBUFS);
942878ed226SJulian Elischer
943878ed226SJulian Elischer cp = mtod(con->rx_pkt, ng_l2cap_discon_req_cp *);
944878ed226SJulian Elischer dcid = le16toh(cp->dcid);
945878ed226SJulian Elischer scid = le16toh(cp->scid);
946878ed226SJulian Elischer
947878ed226SJulian Elischer NG_FREE_M(con->rx_pkt);
948878ed226SJulian Elischer
949878ed226SJulian Elischer /* Check if we have this channel and it is in valid state */
950fbc48c2bSTakanori Watanabe ch = ng_l2cap_chan_by_scid(l2cap, dcid, NG_L2CAP_L2CA_IDTYPE_BREDR);
951878ed226SJulian Elischer if (ch == NULL) {
952878ed226SJulian Elischer NG_L2CAP_ERR(
953878ed226SJulian Elischer "%s: %s - unexpected L2CAP_DisconnectReq message. " \
954878ed226SJulian Elischer "Channel does not exist, cid=%d\n",
955878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), dcid);
956878ed226SJulian Elischer goto reject;
957878ed226SJulian Elischer }
958878ed226SJulian Elischer
959878ed226SJulian Elischer /* XXX Verify channel state and reject if invalid -- is that true? */
960f2bb1caeSJulian Elischer if (ch->state != NG_L2CAP_OPEN && ch->state != NG_L2CAP_CONFIG &&
961f2bb1caeSJulian Elischer ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) {
962878ed226SJulian Elischer NG_L2CAP_ERR(
963878ed226SJulian Elischer "%s: %s - unexpected L2CAP_DisconnectReq. " \
964878ed226SJulian Elischer "Invalid channel state, cid=%d, state=%d\n",
965878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), dcid, ch->state);
966878ed226SJulian Elischer goto reject;
967878ed226SJulian Elischer }
968878ed226SJulian Elischer
969878ed226SJulian Elischer /* Match destination channel ID */
970878ed226SJulian Elischer if (ch->dcid != scid || ch->scid != dcid) {
971878ed226SJulian Elischer NG_L2CAP_ERR(
972878ed226SJulian Elischer "%s: %s - unexpected L2CAP_DisconnectReq. " \
973878ed226SJulian Elischer "Channel IDs does not match, channel: scid=%d, dcid=%d, " \
974878ed226SJulian Elischer "request: scid=%d, dcid=%d\n",
975878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), ch->scid, ch->dcid,
976878ed226SJulian Elischer scid, dcid);
977878ed226SJulian Elischer goto reject;
978878ed226SJulian Elischer }
979878ed226SJulian Elischer
980878ed226SJulian Elischer /*
981878ed226SJulian Elischer * Looks good, so notify upper layer protocol that channel is about
982878ed226SJulian Elischer * to be disconnected and send L2CA_DisconnectInd message. Then respond
983878ed226SJulian Elischer * with L2CAP_DisconnectRsp.
984878ed226SJulian Elischer */
985878ed226SJulian Elischer
986f2bb1caeSJulian Elischer if (ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) {
987878ed226SJulian Elischer ng_l2cap_l2ca_discon_ind(ch); /* do not care about result */
988878ed226SJulian Elischer ng_l2cap_free_chan(ch);
989f2bb1caeSJulian Elischer }
990878ed226SJulian Elischer
991878ed226SJulian Elischer /* Send L2CAP_DisconnectRsp */
992878ed226SJulian Elischer cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_DISCON_RSP, 0);
993878ed226SJulian Elischer if (cmd == NULL)
994878ed226SJulian Elischer return (ENOMEM);
995878ed226SJulian Elischer
996878ed226SJulian Elischer _ng_l2cap_discon_rsp(cmd->aux, ident, dcid, scid);
997878ed226SJulian Elischer if (cmd->aux == NULL) {
998878ed226SJulian Elischer ng_l2cap_free_cmd(cmd);
999878ed226SJulian Elischer
1000878ed226SJulian Elischer return (ENOBUFS);
1001878ed226SJulian Elischer }
1002878ed226SJulian Elischer
1003878ed226SJulian Elischer /* Link command to the queue */
1004878ed226SJulian Elischer ng_l2cap_link_cmd(con, cmd);
1005878ed226SJulian Elischer ng_l2cap_lp_deliver(con);
1006878ed226SJulian Elischer
1007878ed226SJulian Elischer return (0);
1008878ed226SJulian Elischer
1009878ed226SJulian Elischer reject:
1010878ed226SJulian Elischer /* Send reject. Do not really care about the result */
1011878ed226SJulian Elischer send_l2cap_reject(con, ident, NG_L2CAP_REJ_INVALID_CID, 0, scid, dcid);
1012878ed226SJulian Elischer
1013878ed226SJulian Elischer return (0);
1014878ed226SJulian Elischer } /* ng_l2cap_process_discon_req */
1015878ed226SJulian Elischer
1016878ed226SJulian Elischer /*
1017878ed226SJulian Elischer * Process L2CAP_DisconnectRsp command
1018878ed226SJulian Elischer */
1019878ed226SJulian Elischer
1020878ed226SJulian Elischer static int
ng_l2cap_process_discon_rsp(ng_l2cap_con_p con,u_int8_t ident)1021878ed226SJulian Elischer ng_l2cap_process_discon_rsp(ng_l2cap_con_p con, u_int8_t ident)
1022878ed226SJulian Elischer {
1023878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap;
1024878ed226SJulian Elischer ng_l2cap_discon_rsp_cp *cp = NULL;
1025878ed226SJulian Elischer ng_l2cap_cmd_p cmd = NULL;
1026878ed226SJulian Elischer u_int16_t scid, dcid;
1027878ed226SJulian Elischer int error = 0;
1028878ed226SJulian Elischer
1029878ed226SJulian Elischer /* Get command parameters */
1030878ed226SJulian Elischer NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));
1031878ed226SJulian Elischer if (con->rx_pkt == NULL)
1032878ed226SJulian Elischer return (ENOBUFS);
1033878ed226SJulian Elischer
1034878ed226SJulian Elischer cp = mtod(con->rx_pkt, ng_l2cap_discon_rsp_cp *);
1035878ed226SJulian Elischer dcid = le16toh(cp->dcid);
1036878ed226SJulian Elischer scid = le16toh(cp->scid);
1037878ed226SJulian Elischer
1038878ed226SJulian Elischer NG_FREE_M(con->rx_pkt);
1039878ed226SJulian Elischer
1040878ed226SJulian Elischer /* Check if we have pending command descriptor */
1041878ed226SJulian Elischer cmd = ng_l2cap_cmd_by_ident(con, ident);
1042878ed226SJulian Elischer if (cmd == NULL) {
1043878ed226SJulian Elischer NG_L2CAP_ERR(
1044878ed226SJulian Elischer "%s: %s - unexpected L2CAP_DisconnectRsp command. ident=%d, con_handle=%d\n",
1045878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), ident,
1046878ed226SJulian Elischer con->con_handle);
1047878ed226SJulian Elischer goto out;
1048878ed226SJulian Elischer }
1049878ed226SJulian Elischer
1050878ed226SJulian Elischer /* Verify channel state, do nothing if invalid */
1051878ed226SJulian Elischer if (cmd->ch->state != NG_L2CAP_W4_L2CAP_DISCON_RSP) {
1052878ed226SJulian Elischer NG_L2CAP_ERR(
1053878ed226SJulian Elischer "%s: %s - unexpected L2CAP_DisconnectRsp. " \
1054878ed226SJulian Elischer "Invalid channel state, cid=%d, state=%d\n",
1055878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), scid,
1056878ed226SJulian Elischer cmd->ch->state);
1057878ed226SJulian Elischer goto out;
1058878ed226SJulian Elischer }
1059878ed226SJulian Elischer
1060878ed226SJulian Elischer /* Verify CIDs and send reject if does not match */
1061878ed226SJulian Elischer if (cmd->ch->scid != scid || cmd->ch->dcid != dcid) {
1062878ed226SJulian Elischer NG_L2CAP_ERR(
1063878ed226SJulian Elischer "%s: %s - unexpected L2CAP_DisconnectRsp. " \
1064878ed226SJulian Elischer "Channel IDs do not match, scid=%d(%d), dcid=%d(%d)\n",
1065878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), cmd->ch->scid,
1066878ed226SJulian Elischer scid, cmd->ch->dcid, dcid);
1067878ed226SJulian Elischer goto out;
1068878ed226SJulian Elischer }
1069878ed226SJulian Elischer
1070878ed226SJulian Elischer /*
1071053359b7SPedro F. Giffuni * Looks like we have successfully disconnected channel, so notify
10720986ab12SMaksim Yevmenkin * upper layer. If command timeout already happened then ignore
10730986ab12SMaksim Yevmenkin * response.
1074878ed226SJulian Elischer */
1075878ed226SJulian Elischer
10760986ab12SMaksim Yevmenkin if ((error = ng_l2cap_command_untimeout(cmd)) != 0)
10770986ab12SMaksim Yevmenkin goto out;
10780986ab12SMaksim Yevmenkin
1079878ed226SJulian Elischer error = ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, NG_L2CAP_SUCCESS);
1080878ed226SJulian Elischer ng_l2cap_free_chan(cmd->ch); /* this will free commands too */
1081878ed226SJulian Elischer out:
1082878ed226SJulian Elischer return (error);
1083878ed226SJulian Elischer } /* ng_l2cap_process_discon_rsp */
1084878ed226SJulian Elischer
1085878ed226SJulian Elischer /*
1086878ed226SJulian Elischer * Process L2CAP_EchoReq command
1087878ed226SJulian Elischer */
1088878ed226SJulian Elischer
1089878ed226SJulian Elischer static int
ng_l2cap_process_echo_req(ng_l2cap_con_p con,u_int8_t ident)1090878ed226SJulian Elischer ng_l2cap_process_echo_req(ng_l2cap_con_p con, u_int8_t ident)
1091878ed226SJulian Elischer {
1092878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap;
1093878ed226SJulian Elischer ng_l2cap_cmd_hdr_t *hdr = NULL;
1094878ed226SJulian Elischer ng_l2cap_cmd_p cmd = NULL;
1095878ed226SJulian Elischer
1096878ed226SJulian Elischer con->rx_pkt = ng_l2cap_prepend(con->rx_pkt, sizeof(*hdr));
1097878ed226SJulian Elischer if (con->rx_pkt == NULL) {
1098878ed226SJulian Elischer NG_L2CAP_ALERT(
10994ae439a3SMaksim Yevmenkin "%s: %s - ng_l2cap_prepend() failed, size=%zd\n",
1100878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), sizeof(*hdr));
1101878ed226SJulian Elischer
1102878ed226SJulian Elischer return (ENOBUFS);
1103878ed226SJulian Elischer }
1104878ed226SJulian Elischer
1105878ed226SJulian Elischer hdr = mtod(con->rx_pkt, ng_l2cap_cmd_hdr_t *);
1106878ed226SJulian Elischer hdr->code = NG_L2CAP_ECHO_RSP;
1107878ed226SJulian Elischer hdr->ident = ident;
1108878ed226SJulian Elischer hdr->length = htole16(con->rx_pkt->m_pkthdr.len - sizeof(*hdr));
1109878ed226SJulian Elischer
1110878ed226SJulian Elischer cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_ECHO_RSP, 0);
1111878ed226SJulian Elischer if (cmd == NULL) {
1112878ed226SJulian Elischer NG_FREE_M(con->rx_pkt);
1113878ed226SJulian Elischer
1114878ed226SJulian Elischer return (ENOBUFS);
1115878ed226SJulian Elischer }
1116878ed226SJulian Elischer
1117878ed226SJulian Elischer /* Attach data and link command to the queue */
1118878ed226SJulian Elischer cmd->aux = con->rx_pkt;
1119878ed226SJulian Elischer con->rx_pkt = NULL;
1120878ed226SJulian Elischer ng_l2cap_link_cmd(con, cmd);
1121878ed226SJulian Elischer ng_l2cap_lp_deliver(con);
1122878ed226SJulian Elischer
1123878ed226SJulian Elischer return (0);
1124878ed226SJulian Elischer } /* ng_l2cap_process_echo_req */
1125878ed226SJulian Elischer
1126878ed226SJulian Elischer /*
1127878ed226SJulian Elischer * Process L2CAP_EchoRsp command
1128878ed226SJulian Elischer */
1129878ed226SJulian Elischer
1130878ed226SJulian Elischer static int
ng_l2cap_process_echo_rsp(ng_l2cap_con_p con,u_int8_t ident)1131878ed226SJulian Elischer ng_l2cap_process_echo_rsp(ng_l2cap_con_p con, u_int8_t ident)
1132878ed226SJulian Elischer {
1133878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap;
1134878ed226SJulian Elischer ng_l2cap_cmd_p cmd = NULL;
1135878ed226SJulian Elischer int error = 0;
1136878ed226SJulian Elischer
1137878ed226SJulian Elischer /* Check if we have this command */
1138878ed226SJulian Elischer cmd = ng_l2cap_cmd_by_ident(con, ident);
1139878ed226SJulian Elischer if (cmd != NULL) {
11400986ab12SMaksim Yevmenkin /* If command timeout already happened then ignore response */
11410986ab12SMaksim Yevmenkin if ((error = ng_l2cap_command_untimeout(cmd)) != 0) {
11420986ab12SMaksim Yevmenkin NG_FREE_M(con->rx_pkt);
11430986ab12SMaksim Yevmenkin return (error);
11440986ab12SMaksim Yevmenkin }
1145878ed226SJulian Elischer
1146878ed226SJulian Elischer ng_l2cap_unlink_cmd(cmd);
1147878ed226SJulian Elischer
1148878ed226SJulian Elischer error = ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
1149878ed226SJulian Elischer NG_L2CAP_SUCCESS, con->rx_pkt);
1150878ed226SJulian Elischer
1151878ed226SJulian Elischer ng_l2cap_free_cmd(cmd);
1152878ed226SJulian Elischer con->rx_pkt = NULL;
1153878ed226SJulian Elischer } else {
1154878ed226SJulian Elischer NG_L2CAP_ERR(
1155878ed226SJulian Elischer "%s: %s - unexpected L2CAP_EchoRsp command. " \
1156878ed226SJulian Elischer "Requested ident does not exist, ident=%d\n",
1157878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), ident);
1158878ed226SJulian Elischer NG_FREE_M(con->rx_pkt);
1159878ed226SJulian Elischer }
1160878ed226SJulian Elischer
1161878ed226SJulian Elischer return (error);
1162878ed226SJulian Elischer } /* ng_l2cap_process_echo_rsp */
1163878ed226SJulian Elischer
1164878ed226SJulian Elischer /*
1165878ed226SJulian Elischer * Process L2CAP_InfoReq command
1166878ed226SJulian Elischer */
1167878ed226SJulian Elischer
1168878ed226SJulian Elischer static int
ng_l2cap_process_info_req(ng_l2cap_con_p con,u_int8_t ident)1169878ed226SJulian Elischer ng_l2cap_process_info_req(ng_l2cap_con_p con, u_int8_t ident)
1170878ed226SJulian Elischer {
1171878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap;
1172878ed226SJulian Elischer ng_l2cap_cmd_p cmd = NULL;
1173878ed226SJulian Elischer u_int16_t type;
1174878ed226SJulian Elischer
1175878ed226SJulian Elischer /* Get command parameters */
1176878ed226SJulian Elischer NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(ng_l2cap_info_req_cp));
1177878ed226SJulian Elischer if (con->rx_pkt == NULL)
1178878ed226SJulian Elischer return (ENOBUFS);
1179878ed226SJulian Elischer
1180878ed226SJulian Elischer type = le16toh(mtod(con->rx_pkt, ng_l2cap_info_req_cp *)->type);
1181878ed226SJulian Elischer NG_FREE_M(con->rx_pkt);
1182878ed226SJulian Elischer
1183878ed226SJulian Elischer cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_INFO_RSP, 0);
1184878ed226SJulian Elischer if (cmd == NULL)
1185878ed226SJulian Elischer return (ENOMEM);
1186878ed226SJulian Elischer
1187878ed226SJulian Elischer switch (type) {
1188878ed226SJulian Elischer case NG_L2CAP_CONNLESS_MTU:
1189878ed226SJulian Elischer _ng_l2cap_info_rsp(cmd->aux, ident, NG_L2CAP_CONNLESS_MTU,
1190878ed226SJulian Elischer NG_L2CAP_SUCCESS, NG_L2CAP_MTU_DEFAULT);
1191878ed226SJulian Elischer break;
1192878ed226SJulian Elischer
1193878ed226SJulian Elischer default:
1194878ed226SJulian Elischer _ng_l2cap_info_rsp(cmd->aux, ident, type,
1195878ed226SJulian Elischer NG_L2CAP_NOT_SUPPORTED, 0);
1196878ed226SJulian Elischer break;
1197878ed226SJulian Elischer }
1198878ed226SJulian Elischer
1199878ed226SJulian Elischer if (cmd->aux == NULL) {
1200878ed226SJulian Elischer ng_l2cap_free_cmd(cmd);
1201878ed226SJulian Elischer
1202878ed226SJulian Elischer return (ENOBUFS);
1203878ed226SJulian Elischer }
1204878ed226SJulian Elischer
1205878ed226SJulian Elischer /* Link command to the queue */
1206878ed226SJulian Elischer ng_l2cap_link_cmd(con, cmd);
1207878ed226SJulian Elischer ng_l2cap_lp_deliver(con);
1208878ed226SJulian Elischer
1209878ed226SJulian Elischer return (0);
1210878ed226SJulian Elischer } /* ng_l2cap_process_info_req */
1211878ed226SJulian Elischer
1212878ed226SJulian Elischer /*
1213878ed226SJulian Elischer * Process L2CAP_InfoRsp command
1214878ed226SJulian Elischer */
1215878ed226SJulian Elischer
1216878ed226SJulian Elischer static int
ng_l2cap_process_info_rsp(ng_l2cap_con_p con,u_int8_t ident)1217878ed226SJulian Elischer ng_l2cap_process_info_rsp(ng_l2cap_con_p con, u_int8_t ident)
1218878ed226SJulian Elischer {
1219878ed226SJulian Elischer ng_l2cap_p l2cap = con->l2cap;
1220878ed226SJulian Elischer ng_l2cap_info_rsp_cp *cp = NULL;
1221878ed226SJulian Elischer ng_l2cap_cmd_p cmd = NULL;
1222878ed226SJulian Elischer int error = 0;
1223878ed226SJulian Elischer
1224878ed226SJulian Elischer /* Get command parameters */
1225878ed226SJulian Elischer NG_L2CAP_M_PULLUP(con->rx_pkt, sizeof(*cp));
1226878ed226SJulian Elischer if (con->rx_pkt == NULL)
1227878ed226SJulian Elischer return (ENOBUFS);
1228878ed226SJulian Elischer
1229878ed226SJulian Elischer cp = mtod(con->rx_pkt, ng_l2cap_info_rsp_cp *);
1230878ed226SJulian Elischer cp->type = le16toh(cp->type);
1231878ed226SJulian Elischer cp->result = le16toh(cp->result);
1232878ed226SJulian Elischer m_adj(con->rx_pkt, sizeof(*cp));
1233878ed226SJulian Elischer
1234878ed226SJulian Elischer /* Check if we have pending command descriptor */
1235878ed226SJulian Elischer cmd = ng_l2cap_cmd_by_ident(con, ident);
1236878ed226SJulian Elischer if (cmd == NULL) {
1237878ed226SJulian Elischer NG_L2CAP_ERR(
1238878ed226SJulian Elischer "%s: %s - unexpected L2CAP_InfoRsp command. " \
1239878ed226SJulian Elischer "Requested ident does not exist, ident=%d\n",
1240878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), ident);
1241878ed226SJulian Elischer NG_FREE_M(con->rx_pkt);
1242878ed226SJulian Elischer
1243878ed226SJulian Elischer return (ENOENT);
1244878ed226SJulian Elischer }
1245878ed226SJulian Elischer
12460986ab12SMaksim Yevmenkin /* If command timeout already happened then ignore response */
12470986ab12SMaksim Yevmenkin if ((error = ng_l2cap_command_untimeout(cmd)) != 0) {
12480986ab12SMaksim Yevmenkin NG_FREE_M(con->rx_pkt);
12490986ab12SMaksim Yevmenkin return (error);
12500986ab12SMaksim Yevmenkin }
1251878ed226SJulian Elischer
1252878ed226SJulian Elischer ng_l2cap_unlink_cmd(cmd);
1253878ed226SJulian Elischer
1254878ed226SJulian Elischer if (cp->result == NG_L2CAP_SUCCESS) {
1255878ed226SJulian Elischer switch (cp->type) {
1256878ed226SJulian Elischer case NG_L2CAP_CONNLESS_MTU:
1257878ed226SJulian Elischer if (con->rx_pkt->m_pkthdr.len == sizeof(u_int16_t))
1258878ed226SJulian Elischer *mtod(con->rx_pkt, u_int16_t *) =
1259878ed226SJulian Elischer le16toh(*mtod(con->rx_pkt,u_int16_t *));
1260878ed226SJulian Elischer else {
1261878ed226SJulian Elischer cp->result = NG_L2CAP_UNKNOWN; /* XXX */
1262878ed226SJulian Elischer
1263878ed226SJulian Elischer NG_L2CAP_ERR(
1264878ed226SJulian Elischer "%s: %s - invalid L2CAP_InfoRsp command. " \
1265878ed226SJulian Elischer "Bad connectionless MTU parameter, len=%d\n",
1266878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node),
1267878ed226SJulian Elischer con->rx_pkt->m_pkthdr.len);
1268878ed226SJulian Elischer }
1269878ed226SJulian Elischer break;
1270878ed226SJulian Elischer
1271878ed226SJulian Elischer default:
1272878ed226SJulian Elischer NG_L2CAP_WARN(
1273878ed226SJulian Elischer "%s: %s - invalid L2CAP_InfoRsp command. Unknown info type=%d\n",
1274878ed226SJulian Elischer __func__, NG_NODE_NAME(l2cap->node), cp->type);
1275878ed226SJulian Elischer break;
1276878ed226SJulian Elischer }
1277878ed226SJulian Elischer }
1278878ed226SJulian Elischer
1279878ed226SJulian Elischer error = ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
1280878ed226SJulian Elischer cp->result, con->rx_pkt);
1281878ed226SJulian Elischer
1282878ed226SJulian Elischer ng_l2cap_free_cmd(cmd);
1283878ed226SJulian Elischer con->rx_pkt = NULL;
1284878ed226SJulian Elischer
1285878ed226SJulian Elischer return (error);
1286878ed226SJulian Elischer } /* ng_l2cap_process_info_rsp */
1287878ed226SJulian Elischer
1288878ed226SJulian Elischer /*
1289878ed226SJulian Elischer * Send L2CAP reject
1290878ed226SJulian Elischer */
1291878ed226SJulian Elischer
1292878ed226SJulian Elischer static int
send_l2cap_reject(ng_l2cap_con_p con,u_int8_t ident,u_int16_t reason,u_int16_t mtu,u_int16_t scid,u_int16_t dcid)1293878ed226SJulian Elischer send_l2cap_reject(ng_l2cap_con_p con, u_int8_t ident, u_int16_t reason,
1294878ed226SJulian Elischer u_int16_t mtu, u_int16_t scid, u_int16_t dcid)
1295878ed226SJulian Elischer {
1296878ed226SJulian Elischer ng_l2cap_cmd_p cmd = NULL;
1297878ed226SJulian Elischer
1298878ed226SJulian Elischer cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CMD_REJ, 0);
1299878ed226SJulian Elischer if (cmd == NULL)
1300878ed226SJulian Elischer return (ENOMEM);
1301878ed226SJulian Elischer
1302878ed226SJulian Elischer _ng_l2cap_cmd_rej(cmd->aux, cmd->ident, reason, mtu, scid, dcid);
1303878ed226SJulian Elischer if (cmd->aux == NULL) {
1304878ed226SJulian Elischer ng_l2cap_free_cmd(cmd);
1305878ed226SJulian Elischer
1306878ed226SJulian Elischer return (ENOBUFS);
1307878ed226SJulian Elischer }
1308878ed226SJulian Elischer
1309878ed226SJulian Elischer /* Link command to the queue */
1310878ed226SJulian Elischer ng_l2cap_link_cmd(con, cmd);
1311878ed226SJulian Elischer ng_l2cap_lp_deliver(con);
1312878ed226SJulian Elischer
1313878ed226SJulian Elischer return (0);
1314878ed226SJulian Elischer } /* send_l2cap_reject */
1315878ed226SJulian Elischer
1316878ed226SJulian Elischer /*
1317878ed226SJulian Elischer * Send L2CAP connection reject
1318878ed226SJulian Elischer */
1319878ed226SJulian Elischer
1320878ed226SJulian Elischer static int
send_l2cap_con_rej(ng_l2cap_con_p con,u_int8_t ident,u_int16_t scid,u_int16_t dcid,u_int16_t result)1321878ed226SJulian Elischer send_l2cap_con_rej(ng_l2cap_con_p con, u_int8_t ident, u_int16_t scid,
1322878ed226SJulian Elischer u_int16_t dcid, u_int16_t result)
1323878ed226SJulian Elischer {
1324878ed226SJulian Elischer ng_l2cap_cmd_p cmd = NULL;
1325878ed226SJulian Elischer
1326878ed226SJulian Elischer cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CON_RSP, 0);
1327878ed226SJulian Elischer if (cmd == NULL)
1328878ed226SJulian Elischer return (ENOMEM);
1329878ed226SJulian Elischer
1330878ed226SJulian Elischer _ng_l2cap_con_rsp(cmd->aux, cmd->ident, scid, dcid, result, 0);
1331878ed226SJulian Elischer if (cmd->aux == NULL) {
1332878ed226SJulian Elischer ng_l2cap_free_cmd(cmd);
1333878ed226SJulian Elischer
1334878ed226SJulian Elischer return (ENOBUFS);
1335878ed226SJulian Elischer }
1336878ed226SJulian Elischer
1337878ed226SJulian Elischer /* Link command to the queue */
1338878ed226SJulian Elischer ng_l2cap_link_cmd(con, cmd);
1339878ed226SJulian Elischer ng_l2cap_lp_deliver(con);
1340878ed226SJulian Elischer
1341878ed226SJulian Elischer return (0);
1342878ed226SJulian Elischer } /* send_l2cap_con_rej */
1343878ed226SJulian Elischer
1344878ed226SJulian Elischer /*
1345878ed226SJulian Elischer * Send L2CAP config response
1346878ed226SJulian Elischer */
1347878ed226SJulian Elischer
1348878ed226SJulian Elischer static int
send_l2cap_cfg_rsp(ng_l2cap_con_p con,u_int8_t ident,u_int16_t scid,u_int16_t result,struct mbuf * opt)1349878ed226SJulian Elischer send_l2cap_cfg_rsp(ng_l2cap_con_p con, u_int8_t ident, u_int16_t scid,
1350878ed226SJulian Elischer u_int16_t result, struct mbuf *opt)
1351878ed226SJulian Elischer {
1352878ed226SJulian Elischer ng_l2cap_cmd_p cmd = NULL;
1353878ed226SJulian Elischer
1354878ed226SJulian Elischer cmd = ng_l2cap_new_cmd(con, NULL, ident, NG_L2CAP_CFG_RSP, 0);
1355878ed226SJulian Elischer if (cmd == NULL) {
1356878ed226SJulian Elischer NG_FREE_M(opt);
1357878ed226SJulian Elischer
1358878ed226SJulian Elischer return (ENOMEM);
1359878ed226SJulian Elischer }
1360878ed226SJulian Elischer
1361878ed226SJulian Elischer _ng_l2cap_cfg_rsp(cmd->aux, cmd->ident, scid, 0, result, opt);
1362878ed226SJulian Elischer if (cmd->aux == NULL) {
1363878ed226SJulian Elischer ng_l2cap_free_cmd(cmd);
1364878ed226SJulian Elischer
1365878ed226SJulian Elischer return (ENOBUFS);
1366878ed226SJulian Elischer }
1367878ed226SJulian Elischer
1368878ed226SJulian Elischer /* Link command to the queue */
1369878ed226SJulian Elischer ng_l2cap_link_cmd(con, cmd);
1370878ed226SJulian Elischer ng_l2cap_lp_deliver(con);
1371878ed226SJulian Elischer
1372878ed226SJulian Elischer return (0);
1373878ed226SJulian Elischer } /* send_l2cap_cfg_rsp */
1374878ed226SJulian Elischer
1375fbc48c2bSTakanori Watanabe static int
send_l2cap_param_urs(ng_l2cap_con_p con,u_int8_t ident,u_int16_t result)1376fbc48c2bSTakanori Watanabe send_l2cap_param_urs(ng_l2cap_con_p con, u_int8_t ident,
1377fbc48c2bSTakanori Watanabe u_int16_t result)
1378fbc48c2bSTakanori Watanabe {
1379fbc48c2bSTakanori Watanabe ng_l2cap_cmd_p cmd = NULL;
1380fbc48c2bSTakanori Watanabe
1381fbc48c2bSTakanori Watanabe cmd = ng_l2cap_new_cmd(con, NULL, ident,
1382fbc48c2bSTakanori Watanabe NG_L2CAP_CMD_PARAM_UPDATE_RESPONSE,
1383fbc48c2bSTakanori Watanabe 0);
1384fbc48c2bSTakanori Watanabe if (cmd == NULL) {
1385fbc48c2bSTakanori Watanabe return (ENOMEM);
1386fbc48c2bSTakanori Watanabe }
1387fbc48c2bSTakanori Watanabe
1388fbc48c2bSTakanori Watanabe _ng_l2cap_cmd_urs(cmd->aux, cmd->ident, result);
1389fbc48c2bSTakanori Watanabe if (cmd->aux == NULL) {
1390fbc48c2bSTakanori Watanabe ng_l2cap_free_cmd(cmd);
1391fbc48c2bSTakanori Watanabe
1392fbc48c2bSTakanori Watanabe return (ENOBUFS);
1393fbc48c2bSTakanori Watanabe }
1394fbc48c2bSTakanori Watanabe
1395fbc48c2bSTakanori Watanabe /* Link command to the queue */
1396fbc48c2bSTakanori Watanabe ng_l2cap_link_cmd(con, cmd);
1397fbc48c2bSTakanori Watanabe ng_l2cap_lp_deliver(con);
1398fbc48c2bSTakanori Watanabe
1399fbc48c2bSTakanori Watanabe return (0);
1400fbc48c2bSTakanori Watanabe } /* send_l2cap_cfg_rsp */
1401fbc48c2bSTakanori Watanabe
1402878ed226SJulian Elischer /*
1403878ed226SJulian Elischer * Get next L2CAP configuration option
1404878ed226SJulian Elischer *
1405878ed226SJulian Elischer * Return codes:
1406878ed226SJulian Elischer * 0 no option
1407878ed226SJulian Elischer * 1 we have got option
1408878ed226SJulian Elischer * -1 header too short
1409878ed226SJulian Elischer * -2 bad option value or length
1410878ed226SJulian Elischer * -3 unknown option
1411878ed226SJulian Elischer */
1412878ed226SJulian Elischer
1413878ed226SJulian Elischer static int
get_next_l2cap_opt(struct mbuf * m,int * off,ng_l2cap_cfg_opt_p hdr,ng_l2cap_cfg_opt_val_p val)1414878ed226SJulian Elischer get_next_l2cap_opt(struct mbuf *m, int *off, ng_l2cap_cfg_opt_p hdr,
1415878ed226SJulian Elischer ng_l2cap_cfg_opt_val_p val)
1416878ed226SJulian Elischer {
1417878ed226SJulian Elischer int hint, len = m->m_pkthdr.len - (*off);
1418878ed226SJulian Elischer
1419878ed226SJulian Elischer if (len == 0)
1420878ed226SJulian Elischer return (0);
1421878ed226SJulian Elischer if (len < 0 || len < sizeof(*hdr))
1422878ed226SJulian Elischer return (-1);
1423878ed226SJulian Elischer
1424878ed226SJulian Elischer m_copydata(m, *off, sizeof(*hdr), (caddr_t) hdr);
1425878ed226SJulian Elischer *off += sizeof(*hdr);
1426878ed226SJulian Elischer len -= sizeof(*hdr);
1427878ed226SJulian Elischer
1428878ed226SJulian Elischer hint = NG_L2CAP_OPT_HINT(hdr->type);
1429878ed226SJulian Elischer hdr->type &= NG_L2CAP_OPT_HINT_MASK;
1430878ed226SJulian Elischer
1431878ed226SJulian Elischer switch (hdr->type) {
1432878ed226SJulian Elischer case NG_L2CAP_OPT_MTU:
1433878ed226SJulian Elischer if (hdr->length != NG_L2CAP_OPT_MTU_SIZE || len < hdr->length)
1434878ed226SJulian Elischer return (-2);
1435878ed226SJulian Elischer
1436878ed226SJulian Elischer m_copydata(m, *off, NG_L2CAP_OPT_MTU_SIZE, (caddr_t) val);
1437878ed226SJulian Elischer val->mtu = le16toh(val->mtu);
1438878ed226SJulian Elischer *off += NG_L2CAP_OPT_MTU_SIZE;
1439878ed226SJulian Elischer break;
1440878ed226SJulian Elischer
1441878ed226SJulian Elischer case NG_L2CAP_OPT_FLUSH_TIMO:
1442878ed226SJulian Elischer if (hdr->length != NG_L2CAP_OPT_FLUSH_TIMO_SIZE ||
1443878ed226SJulian Elischer len < hdr->length)
1444878ed226SJulian Elischer return (-2);
1445878ed226SJulian Elischer
1446878ed226SJulian Elischer m_copydata(m, *off, NG_L2CAP_OPT_FLUSH_TIMO_SIZE, (caddr_t)val);
1447878ed226SJulian Elischer val->flush_timo = le16toh(val->flush_timo);
1448878ed226SJulian Elischer *off += NG_L2CAP_OPT_FLUSH_TIMO_SIZE;
1449878ed226SJulian Elischer break;
1450878ed226SJulian Elischer
1451878ed226SJulian Elischer case NG_L2CAP_OPT_QOS:
1452878ed226SJulian Elischer if (hdr->length != NG_L2CAP_OPT_QOS_SIZE || len < hdr->length)
1453878ed226SJulian Elischer return (-2);
1454878ed226SJulian Elischer
1455878ed226SJulian Elischer m_copydata(m, *off, NG_L2CAP_OPT_QOS_SIZE, (caddr_t) val);
1456878ed226SJulian Elischer val->flow.token_rate = le32toh(val->flow.token_rate);
1457878ed226SJulian Elischer val->flow.token_bucket_size =
1458878ed226SJulian Elischer le32toh(val->flow.token_bucket_size);
1459878ed226SJulian Elischer val->flow.peak_bandwidth = le32toh(val->flow.peak_bandwidth);
1460878ed226SJulian Elischer val->flow.latency = le32toh(val->flow.latency);
1461878ed226SJulian Elischer val->flow.delay_variation = le32toh(val->flow.delay_variation);
1462878ed226SJulian Elischer *off += NG_L2CAP_OPT_QOS_SIZE;
1463878ed226SJulian Elischer break;
1464878ed226SJulian Elischer
1465878ed226SJulian Elischer default:
1466878ed226SJulian Elischer if (hint)
1467878ed226SJulian Elischer *off += hdr->length;
1468878ed226SJulian Elischer else
1469878ed226SJulian Elischer return (-3);
1470878ed226SJulian Elischer break;
1471878ed226SJulian Elischer }
1472878ed226SJulian Elischer
1473878ed226SJulian Elischer return (1);
1474878ed226SJulian Elischer } /* get_next_l2cap_opt */
1475