xref: /freebsd/sys/netgraph/bluetooth/l2cap/ng_l2cap_cmds.c (revision fed1ca4b719c56c930f2259d80663cd34be812bb)
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
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
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 attention to return code, just do our best.
336  */
337 
338 void
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