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