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  */
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 <netgraph7/ng_message.h>
42 #include <netgraph7/netgraph.h>
43 #include <netgraph7/bluetooth/include/ng_bluetooth.h>
44 #include <netgraph7/bluetooth/include/ng_hci.h>
45 #include <netgraph7/bluetooth/include/ng_l2cap.h>
46 #include <netgraph7/bluetooth/l2cap/ng_l2cap_var.h>
47 #include <netgraph7/bluetooth/l2cap/ng_l2cap_cmds.h>
48 #include <netgraph7/bluetooth/l2cap/ng_l2cap_evnt.h>
49 #include <netgraph7/bluetooth/l2cap/ng_l2cap_llpi.h>
50 #include <netgraph7/bluetooth/l2cap/ng_l2cap_ulpi.h>
51 #include <netgraph7/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
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_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 
228 void
229 ng_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
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 
325 void
326 ng_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 		kprintf("%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 
399