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