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