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