1 /*
2 * ng_l2cap_cmds.c
3 */
4
5 /*-
6 * SPDX-License-Identifier: BSD-2-Clause
7 *
8 * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 * $Id: ng_l2cap_cmds.c,v 1.2 2003/09/08 19:11:45 max Exp $
33 */
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/endian.h>
39 #include <sys/malloc.h>
40 #include <sys/mbuf.h>
41 #include <sys/queue.h>
42 #include <netgraph/ng_message.h>
43 #include <netgraph/netgraph.h>
44 #include <netgraph/bluetooth/include/ng_bluetooth.h>
45 #include <netgraph/bluetooth/include/ng_hci.h>
46 #include <netgraph/bluetooth/include/ng_l2cap.h>
47 #include <netgraph/bluetooth/l2cap/ng_l2cap_var.h>
48 #include <netgraph/bluetooth/l2cap/ng_l2cap_cmds.h>
49 #include <netgraph/bluetooth/l2cap/ng_l2cap_evnt.h>
50 #include <netgraph/bluetooth/l2cap/ng_l2cap_llpi.h>
51 #include <netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h>
52 #include <netgraph/bluetooth/l2cap/ng_l2cap_misc.h>
53
54 /******************************************************************************
55 ******************************************************************************
56 ** L2CAP commands processing module
57 ******************************************************************************
58 ******************************************************************************/
59
60 /*
61 * Process L2CAP command queue on connection
62 */
63
64 void
ng_l2cap_con_wakeup(ng_l2cap_con_p con)65 ng_l2cap_con_wakeup(ng_l2cap_con_p con)
66 {
67 ng_l2cap_cmd_p cmd = NULL;
68 struct mbuf *m = NULL;
69 int error = 0;
70
71 /* Find first non-pending command in the queue */
72 TAILQ_FOREACH(cmd, &con->cmd_list, next) {
73 KASSERT((cmd->con == con),
74 ("%s: %s - invalid connection pointer!\n",
75 __func__, NG_NODE_NAME(con->l2cap->node)));
76
77 if (!(cmd->flags & NG_L2CAP_CMD_PENDING))
78 break;
79 }
80
81 if (cmd == NULL)
82 return;
83
84 /* Detach command packet */
85 m = cmd->aux;
86 cmd->aux = NULL;
87
88 /* Process command */
89 switch (cmd->code) {
90 case NG_L2CAP_DISCON_RSP:
91 case NG_L2CAP_ECHO_RSP:
92 case NG_L2CAP_INFO_RSP:
93 /*
94 * Do not check return ng_l2cap_lp_send() value, because
95 * in these cases we do not really have a graceful way out.
96 * ECHO and INFO responses are internal to the stack and not
97 * visible to user. REJect is just being nice to remote end
98 * (otherwise remote end will timeout anyway). DISCON is
99 * probably most interesting here, however, if it fails
100 * there is nothing we can do anyway.
101 */
102
103 (void) ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
104 ng_l2cap_unlink_cmd(cmd);
105 ng_l2cap_free_cmd(cmd);
106 break;
107 case NG_L2CAP_CMD_REJ:
108 (void) ng_l2cap_lp_send(con,
109 (con->linktype == NG_HCI_LINK_ACL)?
110 NG_L2CAP_SIGNAL_CID:
111 NG_L2CAP_LESIGNAL_CID
112 , m);
113 ng_l2cap_unlink_cmd(cmd);
114 ng_l2cap_free_cmd(cmd);
115 break;
116
117 case NG_L2CAP_CON_REQ:
118 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
119 if (error != 0) {
120 ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token,
121 NG_L2CAP_NO_RESOURCES, 0);
122 ng_l2cap_free_chan(cmd->ch); /* will free commands */
123 } else
124 ng_l2cap_command_timeout(cmd,
125 bluetooth_l2cap_rtx_timeout());
126 break;
127 case NG_L2CAP_CON_RSP:
128 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
129 ng_l2cap_unlink_cmd(cmd);
130 if (cmd->ch != NULL) {
131 ng_l2cap_l2ca_con_rsp_rsp(cmd->ch, cmd->token,
132 (error == 0)? NG_L2CAP_SUCCESS :
133 NG_L2CAP_NO_RESOURCES);
134 if (error != 0)
135 ng_l2cap_free_chan(cmd->ch);
136 }
137 ng_l2cap_free_cmd(cmd);
138 break;
139
140 case NG_L2CAP_CFG_REQ:
141 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
142 if (error != 0) {
143 ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token,
144 NG_L2CAP_NO_RESOURCES);
145 ng_l2cap_unlink_cmd(cmd);
146 ng_l2cap_free_cmd(cmd);
147 } else
148 ng_l2cap_command_timeout(cmd,
149 bluetooth_l2cap_rtx_timeout());
150 break;
151
152 case NG_L2CAP_CFG_RSP:
153 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
154 ng_l2cap_unlink_cmd(cmd);
155 if (cmd->ch != NULL)
156 ng_l2cap_l2ca_cfg_rsp_rsp(cmd->ch, cmd->token,
157 (error == 0)? NG_L2CAP_SUCCESS :
158 NG_L2CAP_NO_RESOURCES);
159 ng_l2cap_free_cmd(cmd);
160 break;
161
162 case NG_L2CAP_DISCON_REQ:
163 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
164 ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token,
165 (error == 0)? NG_L2CAP_SUCCESS : NG_L2CAP_NO_RESOURCES);
166 if (error != 0)
167 ng_l2cap_free_chan(cmd->ch); /* XXX free channel */
168 else
169 ng_l2cap_command_timeout(cmd,
170 bluetooth_l2cap_rtx_timeout());
171 break;
172
173 case NG_L2CAP_ECHO_REQ:
174 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
175 if (error != 0) {
176 ng_l2cap_l2ca_ping_rsp(con, cmd->token,
177 NG_L2CAP_NO_RESOURCES, NULL);
178 ng_l2cap_unlink_cmd(cmd);
179 ng_l2cap_free_cmd(cmd);
180 } else
181 ng_l2cap_command_timeout(cmd,
182 bluetooth_l2cap_rtx_timeout());
183 break;
184
185 case NG_L2CAP_INFO_REQ:
186 error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
187 if (error != 0) {
188 ng_l2cap_l2ca_get_info_rsp(con, cmd->token,
189 NG_L2CAP_NO_RESOURCES, NULL);
190 ng_l2cap_unlink_cmd(cmd);
191 ng_l2cap_free_cmd(cmd);
192 } else
193 ng_l2cap_command_timeout(cmd,
194 bluetooth_l2cap_rtx_timeout());
195 break;
196
197 case NGM_L2CAP_L2CA_WRITE: {
198 int length = m->m_pkthdr.len;
199
200 if (cmd->ch->dcid == NG_L2CAP_CLT_CID) {
201 m = ng_l2cap_prepend(m, sizeof(ng_l2cap_clt_hdr_t));
202 if (m == NULL)
203 error = ENOBUFS;
204 else
205 mtod(m, ng_l2cap_clt_hdr_t *)->psm =
206 htole16(cmd->ch->psm);
207 }
208
209 if (error == 0)
210 error = ng_l2cap_lp_send(con, cmd->ch->dcid, m);
211
212 ng_l2cap_l2ca_write_rsp(cmd->ch, cmd->token,
213 (error == 0)? NG_L2CAP_SUCCESS : NG_L2CAP_NO_RESOURCES,
214 length);
215
216 ng_l2cap_unlink_cmd(cmd);
217 ng_l2cap_free_cmd(cmd);
218 } break;
219 case NG_L2CAP_CMD_PARAM_UPDATE_RESPONSE:
220 error = ng_l2cap_lp_send(con, NG_L2CAP_LESIGNAL_CID, m);
221 ng_l2cap_unlink_cmd(cmd);
222 ng_l2cap_free_cmd(cmd);
223 break;
224 case NG_L2CAP_CMD_PARAM_UPDATE_REQUEST:
225 /*TBD.*/
226 /* XXX FIXME add other commands */
227 default:
228 panic(
229 "%s: %s - unknown command code=%d\n",
230 __func__, NG_NODE_NAME(con->l2cap->node), cmd->code);
231 break;
232 }
233 } /* ng_l2cap_con_wakeup */
234
235 /*
236 * We have failed to open ACL connection to the remote unit. Could be negative
237 * confirmation or timeout. So fail any "delayed" commands, notify upper layer,
238 * remove all channels and remove connection descriptor.
239 */
240
241 void
ng_l2cap_con_fail(ng_l2cap_con_p con,u_int16_t result)242 ng_l2cap_con_fail(ng_l2cap_con_p con, u_int16_t result)
243 {
244 ng_l2cap_p l2cap = con->l2cap;
245 ng_l2cap_cmd_p cmd = NULL;
246 ng_l2cap_chan_p ch = NULL;
247
248 NG_L2CAP_INFO(
249 "%s: %s - ACL connection failed, result=%d\n",
250 __func__, NG_NODE_NAME(l2cap->node), result);
251
252 /* Connection is dying */
253 con->flags |= NG_L2CAP_CON_DYING;
254
255 /* Clean command queue */
256 while (!TAILQ_EMPTY(&con->cmd_list)) {
257 cmd = TAILQ_FIRST(&con->cmd_list);
258
259 ng_l2cap_unlink_cmd(cmd);
260 if(cmd->flags & NG_L2CAP_CMD_PENDING)
261 ng_l2cap_command_untimeout(cmd);
262
263 KASSERT((cmd->con == con),
264 ("%s: %s - invalid connection pointer!\n",
265 __func__, NG_NODE_NAME(l2cap->node)));
266
267 switch (cmd->code) {
268 case NG_L2CAP_CMD_REJ:
269 case NG_L2CAP_DISCON_RSP:
270 case NG_L2CAP_ECHO_RSP:
271 case NG_L2CAP_INFO_RSP:
272 case NG_L2CAP_CMD_PARAM_UPDATE_RESPONSE:
273 break;
274
275 case NG_L2CAP_CON_REQ:
276 ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, result, 0);
277 break;
278
279 case NG_L2CAP_CON_RSP:
280 if (cmd->ch != NULL)
281 ng_l2cap_l2ca_con_rsp_rsp(cmd->ch, cmd->token,
282 result);
283 break;
284
285 case NG_L2CAP_CFG_REQ:
286 case NG_L2CAP_CFG_RSP:
287 case NGM_L2CAP_L2CA_WRITE:
288 ng_l2cap_l2ca_discon_ind(cmd->ch);
289 break;
290
291 case NG_L2CAP_DISCON_REQ:
292 ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token,
293 NG_L2CAP_SUCCESS);
294 break;
295
296 case NG_L2CAP_ECHO_REQ:
297 ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
298 result, NULL);
299 break;
300
301 case NG_L2CAP_INFO_REQ:
302 ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
303 result, NULL);
304 break;
305
306 /* XXX FIXME add other commands */
307
308 default:
309 panic(
310 "%s: %s - unexpected command code=%d\n",
311 __func__, NG_NODE_NAME(l2cap->node), cmd->code);
312 break;
313 }
314
315 if (cmd->ch != NULL)
316 ng_l2cap_free_chan(cmd->ch);
317
318 ng_l2cap_free_cmd(cmd);
319 }
320
321 /*
322 * There still might be channels (in OPEN state?) that
323 * did not submit any commands, so disconnect them
324 */
325
326 LIST_FOREACH(ch, &l2cap->chan_list, next)
327 if (ch->con == con)
328 ng_l2cap_l2ca_discon_ind(ch);
329
330 /* Free connection descriptor */
331 ng_l2cap_free_con(con);
332 } /* ng_l2cap_con_fail */
333
334 /*
335 * Process L2CAP command timeout. In general - notify upper layer and destroy
336 * channel. Do not pay much attention to return code, just do our best.
337 */
338
339 void
ng_l2cap_process_command_timeout(node_p node,hook_p hook,void * arg1,int arg2)340 ng_l2cap_process_command_timeout(node_p node, hook_p hook, void *arg1, int arg2)
341 {
342 ng_l2cap_p l2cap = NULL;
343 ng_l2cap_con_p con = NULL;
344 ng_l2cap_cmd_p cmd = NULL;
345 u_int16_t con_handle = (arg2 & 0x0ffff);
346 u_int8_t ident = ((arg2 >> 16) & 0xff);
347
348 if (NG_NODE_NOT_VALID(node)) {
349 printf("%s: Netgraph node is not valid\n", __func__);
350 return;
351 }
352
353 l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);
354
355 con = ng_l2cap_con_by_handle(l2cap, con_handle);
356 if (con == NULL) {
357 NG_L2CAP_ALERT(
358 "%s: %s - could not find connection, con_handle=%d\n",
359 __func__, NG_NODE_NAME(node), con_handle);
360 return;
361 }
362
363 cmd = ng_l2cap_cmd_by_ident(con, ident);
364 if (cmd == NULL) {
365 NG_L2CAP_ALERT(
366 "%s: %s - could not find command, con_handle=%d, ident=%d\n",
367 __func__, NG_NODE_NAME(node), con_handle, ident);
368 return;
369 }
370
371 cmd->flags &= ~NG_L2CAP_CMD_PENDING;
372 ng_l2cap_unlink_cmd(cmd);
373
374 switch (cmd->code) {
375 case NG_L2CAP_CON_REQ:
376 ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT, 0);
377 ng_l2cap_free_chan(cmd->ch);
378 break;
379
380 case NG_L2CAP_CFG_REQ:
381 ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT);
382 break;
383
384 case NG_L2CAP_DISCON_REQ:
385 ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT);
386 ng_l2cap_free_chan(cmd->ch); /* XXX free channel */
387 break;
388
389 case NG_L2CAP_ECHO_REQ:
390 /* Echo request timed out. Let the upper layer know */
391 ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
392 NG_L2CAP_TIMEOUT, NULL);
393 break;
394
395 case NG_L2CAP_INFO_REQ:
396 /* Info request timed out. Let the upper layer know */
397 ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
398 NG_L2CAP_TIMEOUT, NULL);
399 break;
400
401 /* XXX FIXME add other commands */
402
403 default:
404 panic(
405 "%s: %s - unexpected command code=%d\n",
406 __func__, NG_NODE_NAME(l2cap->node), cmd->code);
407 break;
408 }
409
410 ng_l2cap_free_cmd(cmd);
411 } /* ng_l2cap_process_command_timeout */
412