xref: /freebsd/sys/netgraph/bluetooth/l2cap/ng_l2cap_cmds.c (revision 7660b554bc59a07be0431c17e0e33815818baa69)
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.1 2002/11/24 19:47:06 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 "ng_bluetooth.h"
42 #include "ng_hci.h"
43 #include "ng_l2cap.h"
44 #include "ng_l2cap_var.h"
45 #include "ng_l2cap_cmds.h"
46 #include "ng_l2cap_evnt.h"
47 #include "ng_l2cap_llpi.h"
48 #include "ng_l2cap_ulpi.h"
49 #include "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 		KASSERT(0,
204 ("%s: %s - unknown command code=%d\n",
205 			__func__, NG_NODE_NAME(con->l2cap->node), cmd->code));
206 
207 		ng_l2cap_unlink_cmd(cmd);
208 		ng_l2cap_free_cmd(cmd);
209 		break;
210 	}
211 } /* ng_l2cap_con_wakeup */
212 
213 /*
214  * We have failed to open ACL connection to the remote unit. Could be negative
215  * confirmation or timeout. So fail any "delayed" commands, notify upper layer,
216  * remove all channels and remove connection descriptor.
217  */
218 
219 void
220 ng_l2cap_con_fail(ng_l2cap_con_p con, u_int16_t result)
221 {
222 	ng_l2cap_p	l2cap = con->l2cap;
223 	ng_l2cap_cmd_p	cmd = NULL;
224 	ng_l2cap_chan_p	ch = NULL;
225 
226 	NG_L2CAP_INFO(
227 "%s: %s - ACL connection failed, result=%d\n",
228 		__func__, NG_NODE_NAME(l2cap->node), result);
229 
230 	/* Clean command queue */
231 	while (!TAILQ_EMPTY(&con->cmd_list)) {
232 		cmd = TAILQ_FIRST(&con->cmd_list);
233 
234 		ng_l2cap_unlink_cmd(cmd);
235 		if(cmd->flags & NG_L2CAP_CMD_PENDING)
236 			ng_l2cap_command_untimeout(cmd);
237 
238 		KASSERT((cmd->con == con),
239 ("%s: %s - invalid connection pointer!\n",
240 			__func__, NG_NODE_NAME(con->l2cap->node)));
241 
242 		switch (cmd->code) {
243 		case NG_L2CAP_CMD_REJ:
244 		case NG_L2CAP_DISCON_RSP:
245 		case NG_L2CAP_ECHO_RSP:
246 		case NG_L2CAP_INFO_RSP:
247 			break;
248 
249 		case NG_L2CAP_CON_REQ:
250 			ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, result, 0);
251 			break;
252 
253 		case NG_L2CAP_CON_RSP:
254 			if (cmd->ch != NULL)
255 				ng_l2cap_l2ca_con_rsp_rsp(cmd->ch, cmd->token,
256 					result);
257 			break;
258 
259 		case NG_L2CAP_CFG_REQ:
260 		case NG_L2CAP_CFG_RSP:
261 		case NGM_L2CAP_L2CA_WRITE:
262 			ng_l2cap_l2ca_discon_ind(cmd->ch);
263 			break;
264 
265 		case NG_L2CAP_DISCON_REQ:
266 			ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token,
267 				NG_L2CAP_SUCCESS);
268 			break;
269 
270 		case NG_L2CAP_ECHO_REQ:
271 			ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
272 				result, NULL);
273 			break;
274 
275 		case NG_L2CAP_INFO_REQ:
276 			ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
277 				result, NULL);
278 			break;
279 
280 		/* XXX FIXME add other commands */
281 
282 		default:
283 			KASSERT(0,
284 ("%s: %s - unexpected command code=%d\n",
285 				__func__, NG_NODE_NAME(con->l2cap->node),
286 				cmd->code));
287 			break;
288 		}
289 
290 		if (cmd->ch != NULL)
291 			ng_l2cap_free_chan(cmd->ch);
292 
293 		ng_l2cap_free_cmd(cmd);
294 	}
295 
296 	/*
297 	 * There still might be channels (in OPEN state?) that
298 	 * did not submit any commands, so diconnect them
299 	 */
300 
301 	LIST_FOREACH(ch, &l2cap->chan_list, next)
302 		if (ch->con == con)
303 			ng_l2cap_l2ca_discon_ind(ch);
304 
305 	/* Free connection descriptor */
306 	ng_l2cap_free_con(con);
307 } /* ng_l2cap_con_fail */
308 
309 /*
310  * Process L2CAP command timeout. In general - notify upper layer and destroy
311  * channel. Do not pay much attension to return code, just do our best.
312  */
313 
314 void
315 ng_l2cap_process_command_timeout(node_p node, hook_p hook, void *arg1, int arg2)
316 {
317 	ng_l2cap_cmd_p	cmd = (ng_l2cap_cmd_p) arg1;
318 
319 	KASSERT((cmd->flags & NG_L2CAP_CMD_PENDING),
320 ("%s: %s - invalid command flags flags=%#x!\n",
321 		__func__, NG_NODE_NAME(cmd->con->l2cap->node), cmd->flags));
322 
323 	cmd->flags &= ~NG_L2CAP_CMD_PENDING;
324 	ng_l2cap_unlink_cmd(cmd);
325 
326 	switch (cmd->code) {
327  	case NG_L2CAP_CON_REQ:
328 		ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT, 0);
329 		ng_l2cap_free_chan(cmd->ch);
330 		break;
331 
332 	case NG_L2CAP_CFG_REQ:
333 		ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT);
334 		break;
335 
336  	case NG_L2CAP_DISCON_REQ:
337 		ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT);
338 		ng_l2cap_free_chan(cmd->ch); /* XXX free channel */
339 		break;
340 
341 	case NG_L2CAP_ECHO_REQ:
342 		/* Echo request timed out. Let the upper layer know */
343 		ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
344 			NG_L2CAP_TIMEOUT, NULL);
345 		break;
346 
347 	case NG_L2CAP_INFO_REQ:
348 		/* Info request timed out. Let the upper layer know */
349 		ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
350 			NG_L2CAP_TIMEOUT, NULL);
351 		break;
352 
353 	/* XXX FIXME add other commands */
354 
355 	default:
356 		KASSERT(0,
357 ("%s: %s - unexpected command code=%d\n",
358 			__func__, NG_NODE_NAME(cmd->con->l2cap->node),
359 			cmd->code));
360 		break;
361 	}
362 
363 	ng_l2cap_free_cmd(cmd);
364 } /* ng_l2cap_process_command_timeout */
365 
366