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