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