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