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
63void
64ng_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
228void
229ng_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 disconnect 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
325void
326ng_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		printf("%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