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