1107120Sjulian/*
2107120Sjulian * ng_l2cap_cmds.c
3139823Simp */
4139823Simp
5139823Simp/*-
6107120Sjulian * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
7107120Sjulian * All rights reserved.
8107120Sjulian *
9107120Sjulian * Redistribution and use in source and binary forms, with or without
10107120Sjulian * modification, are permitted provided that the following conditions
11107120Sjulian * are met:
12107120Sjulian * 1. Redistributions of source code must retain the above copyright
13107120Sjulian *    notice, this list of conditions and the following disclaimer.
14107120Sjulian * 2. Redistributions in binary form must reproduce the above copyright
15107120Sjulian *    notice, this list of conditions and the following disclaimer in the
16107120Sjulian *    documentation and/or other materials provided with the distribution.
17107120Sjulian *
18107120Sjulian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19107120Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20107120Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21107120Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22107120Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23107120Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24107120Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25107120Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26107120Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27107120Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28107120Sjulian * SUCH DAMAGE.
29107120Sjulian *
30121054Semax * $Id: ng_l2cap_cmds.c,v 1.2 2003/09/08 19:11:45 max Exp $
31107120Sjulian * $FreeBSD$
32107120Sjulian */
33107120Sjulian
34107120Sjulian#include <sys/param.h>
35107120Sjulian#include <sys/systm.h>
36107120Sjulian#include <sys/kernel.h>
37107120Sjulian#include <sys/endian.h>
38107120Sjulian#include <sys/malloc.h>
39107120Sjulian#include <sys/mbuf.h>
40107120Sjulian#include <sys/queue.h>
41107120Sjulian#include <netgraph/ng_message.h>
42107120Sjulian#include <netgraph/netgraph.h>
43128688Semax#include <netgraph/bluetooth/include/ng_bluetooth.h>
44128688Semax#include <netgraph/bluetooth/include/ng_hci.h>
45128688Semax#include <netgraph/bluetooth/include/ng_l2cap.h>
46128688Semax#include <netgraph/bluetooth/l2cap/ng_l2cap_var.h>
47128688Semax#include <netgraph/bluetooth/l2cap/ng_l2cap_cmds.h>
48128688Semax#include <netgraph/bluetooth/l2cap/ng_l2cap_evnt.h>
49128688Semax#include <netgraph/bluetooth/l2cap/ng_l2cap_llpi.h>
50128688Semax#include <netgraph/bluetooth/l2cap/ng_l2cap_ulpi.h>
51128688Semax#include <netgraph/bluetooth/l2cap/ng_l2cap_misc.h>
52107120Sjulian
53107120Sjulian/******************************************************************************
54107120Sjulian ******************************************************************************
55107120Sjulian **                    L2CAP commands processing module
56107120Sjulian ******************************************************************************
57107120Sjulian ******************************************************************************/
58107120Sjulian
59107120Sjulian/*
60107120Sjulian * Process L2CAP command queue on connection
61107120Sjulian */
62107120Sjulian
63107120Sjulianvoid
64107120Sjulianng_l2cap_con_wakeup(ng_l2cap_con_p con)
65107120Sjulian{
66107120Sjulian	ng_l2cap_cmd_p	 cmd = NULL;
67107120Sjulian	struct mbuf	*m = NULL;
68107120Sjulian	int		 error = 0;
69107120Sjulian
70107120Sjulian	/* Find first non-pending command in the queue */
71107120Sjulian	TAILQ_FOREACH(cmd, &con->cmd_list, next) {
72107120Sjulian		KASSERT((cmd->con == con),
73107120Sjulian("%s: %s - invalid connection pointer!\n",
74121054Semax			__func__, NG_NODE_NAME(con->l2cap->node)));
75107120Sjulian
76107120Sjulian		if (!(cmd->flags & NG_L2CAP_CMD_PENDING))
77107120Sjulian			break;
78107120Sjulian	}
79107120Sjulian
80107120Sjulian	if (cmd == NULL)
81107120Sjulian		return;
82107120Sjulian
83107120Sjulian	/* Detach command packet */
84107120Sjulian	m = cmd->aux;
85107120Sjulian	cmd->aux = NULL;
86107120Sjulian
87107120Sjulian	/* Process command */
88107120Sjulian	switch (cmd->code) {
89107120Sjulian	case NG_L2CAP_CMD_REJ:
90107120Sjulian	case NG_L2CAP_DISCON_RSP:
91107120Sjulian	case NG_L2CAP_ECHO_RSP:
92107120Sjulian	case NG_L2CAP_INFO_RSP:
93168009Semax		/*
94168009Semax		 * Do not check return ng_l2cap_lp_send() value, because
95168009Semax		 * in these cases we do not really have a graceful way out.
96168009Semax		 * ECHO and INFO responses are internal to the stack and not
97168009Semax		 * visible to user. REJect is just being nice to remote end
98168009Semax		 * (otherwise remote end will timeout anyway). DISCON is
99168009Semax		 * probably most interesting here, however, if it fails
100168009Semax		 * there is nothing we can do anyway.
101168009Semax		 */
102168009Semax
103168009Semax		(void) ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
104107120Sjulian		ng_l2cap_unlink_cmd(cmd);
105107120Sjulian		ng_l2cap_free_cmd(cmd);
106107120Sjulian		break;
107107120Sjulian
108107120Sjulian	case NG_L2CAP_CON_REQ:
109107120Sjulian		error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
110107120Sjulian		if (error != 0) {
111107120Sjulian			ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token,
112107120Sjulian				NG_L2CAP_NO_RESOURCES, 0);
113107120Sjulian			ng_l2cap_free_chan(cmd->ch); /* will free commands */
114107120Sjulian		} else
115107120Sjulian			ng_l2cap_command_timeout(cmd,
116107120Sjulian				bluetooth_l2cap_rtx_timeout());
117107120Sjulian		break;
118107120Sjulian
119107120Sjulian	case NG_L2CAP_CON_RSP:
120107120Sjulian		error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
121107120Sjulian		ng_l2cap_unlink_cmd(cmd);
122107120Sjulian		if (cmd->ch != NULL) {
123107120Sjulian			ng_l2cap_l2ca_con_rsp_rsp(cmd->ch, cmd->token,
124107120Sjulian				(error == 0)? NG_L2CAP_SUCCESS :
125107120Sjulian					NG_L2CAP_NO_RESOURCES);
126107120Sjulian			if (error != 0)
127107120Sjulian				ng_l2cap_free_chan(cmd->ch);
128107120Sjulian		}
129107120Sjulian		ng_l2cap_free_cmd(cmd);
130107120Sjulian		break;
131107120Sjulian
132107120Sjulian	case NG_L2CAP_CFG_REQ:
133107120Sjulian		error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
134107120Sjulian		if (error != 0) {
135107120Sjulian			ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token,
136107120Sjulian				NG_L2CAP_NO_RESOURCES);
137107120Sjulian			ng_l2cap_unlink_cmd(cmd);
138107120Sjulian			ng_l2cap_free_cmd(cmd);
139107120Sjulian		} else
140107120Sjulian			ng_l2cap_command_timeout(cmd,
141107120Sjulian				bluetooth_l2cap_rtx_timeout());
142107120Sjulian		break;
143107120Sjulian
144107120Sjulian	case NG_L2CAP_CFG_RSP:
145107120Sjulian		error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
146107120Sjulian		ng_l2cap_unlink_cmd(cmd);
147107120Sjulian		if (cmd->ch != NULL)
148107120Sjulian			ng_l2cap_l2ca_cfg_rsp_rsp(cmd->ch, cmd->token,
149107120Sjulian				(error == 0)? NG_L2CAP_SUCCESS :
150107120Sjulian					NG_L2CAP_NO_RESOURCES);
151107120Sjulian		ng_l2cap_free_cmd(cmd);
152107120Sjulian		break;
153107120Sjulian
154107120Sjulian	case NG_L2CAP_DISCON_REQ:
155107120Sjulian		error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
156107120Sjulian		ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token,
157107120Sjulian			(error == 0)? NG_L2CAP_SUCCESS : NG_L2CAP_NO_RESOURCES);
158107120Sjulian		if (error != 0)
159107120Sjulian			ng_l2cap_free_chan(cmd->ch); /* XXX free channel */
160107120Sjulian		else
161107120Sjulian			ng_l2cap_command_timeout(cmd,
162107120Sjulian				bluetooth_l2cap_rtx_timeout());
163107120Sjulian		break;
164107120Sjulian
165107120Sjulian	case NG_L2CAP_ECHO_REQ:
166107120Sjulian		error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
167107120Sjulian		if (error != 0) {
168107120Sjulian			ng_l2cap_l2ca_ping_rsp(con, cmd->token,
169107120Sjulian					NG_L2CAP_NO_RESOURCES, NULL);
170107120Sjulian			ng_l2cap_unlink_cmd(cmd);
171107120Sjulian			ng_l2cap_free_cmd(cmd);
172107120Sjulian		} else
173107120Sjulian			ng_l2cap_command_timeout(cmd,
174107120Sjulian				bluetooth_l2cap_rtx_timeout());
175107120Sjulian		break;
176107120Sjulian
177107120Sjulian	case NG_L2CAP_INFO_REQ:
178107120Sjulian		error = ng_l2cap_lp_send(con, NG_L2CAP_SIGNAL_CID, m);
179107120Sjulian		if (error != 0) {
180107120Sjulian			ng_l2cap_l2ca_get_info_rsp(con, cmd->token,
181107120Sjulian				NG_L2CAP_NO_RESOURCES, NULL);
182107120Sjulian			ng_l2cap_unlink_cmd(cmd);
183107120Sjulian			ng_l2cap_free_cmd(cmd);
184107120Sjulian		} else
185107120Sjulian			ng_l2cap_command_timeout(cmd,
186107120Sjulian				bluetooth_l2cap_rtx_timeout());
187107120Sjulian		break;
188107120Sjulian
189107120Sjulian	case NGM_L2CAP_L2CA_WRITE: {
190107120Sjulian		int	length = m->m_pkthdr.len;
191107120Sjulian
192107120Sjulian		if (cmd->ch->dcid == NG_L2CAP_CLT_CID) {
193107120Sjulian			m = ng_l2cap_prepend(m, sizeof(ng_l2cap_clt_hdr_t));
194107120Sjulian			if (m == NULL)
195107120Sjulian				error = ENOBUFS;
196107120Sjulian			else
197107120Sjulian                		mtod(m, ng_l2cap_clt_hdr_t *)->psm =
198107120Sjulian							htole16(cmd->ch->psm);
199107120Sjulian		}
200107120Sjulian
201107120Sjulian		if (error == 0)
202107120Sjulian			error = ng_l2cap_lp_send(con, cmd->ch->dcid, m);
203107120Sjulian
204107120Sjulian		ng_l2cap_l2ca_write_rsp(cmd->ch, cmd->token,
205107120Sjulian			(error == 0)? NG_L2CAP_SUCCESS : NG_L2CAP_NO_RESOURCES,
206107120Sjulian			length);
207107120Sjulian
208107120Sjulian		ng_l2cap_unlink_cmd(cmd);
209107120Sjulian		ng_l2cap_free_cmd(cmd);
210107120Sjulian		} break;
211107120Sjulian
212107120Sjulian	/* XXX FIXME add other commands */
213107120Sjulian
214107120Sjulian	default:
215121054Semax		panic(
216121054Semax"%s: %s - unknown command code=%d\n",
217121054Semax			__func__, NG_NODE_NAME(con->l2cap->node), cmd->code);
218107120Sjulian		break;
219107120Sjulian	}
220107120Sjulian} /* ng_l2cap_con_wakeup */
221107120Sjulian
222107120Sjulian/*
223107120Sjulian * We have failed to open ACL connection to the remote unit. Could be negative
224107120Sjulian * confirmation or timeout. So fail any "delayed" commands, notify upper layer,
225107120Sjulian * remove all channels and remove connection descriptor.
226107120Sjulian */
227107120Sjulian
228107120Sjulianvoid
229107120Sjulianng_l2cap_con_fail(ng_l2cap_con_p con, u_int16_t result)
230107120Sjulian{
231107120Sjulian	ng_l2cap_p	l2cap = con->l2cap;
232107120Sjulian	ng_l2cap_cmd_p	cmd = NULL;
233107120Sjulian	ng_l2cap_chan_p	ch = NULL;
234107120Sjulian
235107120Sjulian	NG_L2CAP_INFO(
236107120Sjulian"%s: %s - ACL connection failed, result=%d\n",
237107120Sjulian		__func__, NG_NODE_NAME(l2cap->node), result);
238107120Sjulian
239149679Semax	/* Connection is dying */
240149679Semax	con->flags |= NG_L2CAP_CON_DYING;
241149679Semax
242107120Sjulian	/* Clean command queue */
243107120Sjulian	while (!TAILQ_EMPTY(&con->cmd_list)) {
244107120Sjulian		cmd = TAILQ_FIRST(&con->cmd_list);
245107120Sjulian
246107120Sjulian		ng_l2cap_unlink_cmd(cmd);
247107120Sjulian		if(cmd->flags & NG_L2CAP_CMD_PENDING)
248107120Sjulian			ng_l2cap_command_untimeout(cmd);
249107120Sjulian
250107120Sjulian		KASSERT((cmd->con == con),
251107120Sjulian("%s: %s - invalid connection pointer!\n",
252121054Semax			__func__, NG_NODE_NAME(l2cap->node)));
253107120Sjulian
254107120Sjulian		switch (cmd->code) {
255107120Sjulian		case NG_L2CAP_CMD_REJ:
256107120Sjulian		case NG_L2CAP_DISCON_RSP:
257107120Sjulian		case NG_L2CAP_ECHO_RSP:
258107120Sjulian		case NG_L2CAP_INFO_RSP:
259107120Sjulian			break;
260107120Sjulian
261107120Sjulian		case NG_L2CAP_CON_REQ:
262107120Sjulian			ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, result, 0);
263107120Sjulian			break;
264107120Sjulian
265107120Sjulian		case NG_L2CAP_CON_RSP:
266107120Sjulian			if (cmd->ch != NULL)
267107120Sjulian				ng_l2cap_l2ca_con_rsp_rsp(cmd->ch, cmd->token,
268107120Sjulian					result);
269107120Sjulian			break;
270107120Sjulian
271107120Sjulian		case NG_L2CAP_CFG_REQ:
272107120Sjulian		case NG_L2CAP_CFG_RSP:
273107120Sjulian		case NGM_L2CAP_L2CA_WRITE:
274107120Sjulian			ng_l2cap_l2ca_discon_ind(cmd->ch);
275107120Sjulian			break;
276107120Sjulian
277107120Sjulian		case NG_L2CAP_DISCON_REQ:
278107120Sjulian			ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token,
279107120Sjulian				NG_L2CAP_SUCCESS);
280107120Sjulian			break;
281107120Sjulian
282107120Sjulian		case NG_L2CAP_ECHO_REQ:
283107120Sjulian			ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
284107120Sjulian				result, NULL);
285107120Sjulian			break;
286107120Sjulian
287107120Sjulian		case NG_L2CAP_INFO_REQ:
288107120Sjulian			ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
289107120Sjulian				result, NULL);
290107120Sjulian			break;
291107120Sjulian
292107120Sjulian		/* XXX FIXME add other commands */
293107120Sjulian
294107120Sjulian		default:
295121054Semax			panic(
296121054Semax"%s: %s - unexpected command code=%d\n",
297121054Semax				__func__, NG_NODE_NAME(l2cap->node), cmd->code);
298107120Sjulian			break;
299107120Sjulian		}
300107120Sjulian
301107120Sjulian		if (cmd->ch != NULL)
302107120Sjulian			ng_l2cap_free_chan(cmd->ch);
303107120Sjulian
304107120Sjulian		ng_l2cap_free_cmd(cmd);
305107120Sjulian	}
306107120Sjulian
307107120Sjulian	/*
308107120Sjulian	 * There still might be channels (in OPEN state?) that
309250460Seadler	 * did not submit any commands, so disconnect them
310107120Sjulian	 */
311107120Sjulian
312107120Sjulian	LIST_FOREACH(ch, &l2cap->chan_list, next)
313107120Sjulian		if (ch->con == con)
314107120Sjulian			ng_l2cap_l2ca_discon_ind(ch);
315107120Sjulian
316107120Sjulian	/* Free connection descriptor */
317107120Sjulian	ng_l2cap_free_con(con);
318107120Sjulian} /* ng_l2cap_con_fail */
319107120Sjulian
320107120Sjulian/*
321107120Sjulian * Process L2CAP command timeout. In general - notify upper layer and destroy
322107120Sjulian * channel. Do not pay much attension to return code, just do our best.
323107120Sjulian */
324107120Sjulian
325107120Sjulianvoid
326107120Sjulianng_l2cap_process_command_timeout(node_p node, hook_p hook, void *arg1, int arg2)
327107120Sjulian{
328121054Semax	ng_l2cap_p	l2cap = NULL;
329121054Semax	ng_l2cap_con_p	con = NULL;
330121054Semax	ng_l2cap_cmd_p	cmd = NULL;
331121054Semax	u_int16_t	con_handle = (arg2 & 0x0ffff);
332121054Semax	u_int8_t	ident = ((arg2 >> 16) & 0xff);
333107120Sjulian
334121054Semax	if (NG_NODE_NOT_VALID(node)) {
335121054Semax		printf("%s: Netgraph node is not valid\n", __func__);
336121054Semax		return;
337121054Semax	}
338107120Sjulian
339121054Semax	l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);
340121054Semax
341121054Semax	con = ng_l2cap_con_by_handle(l2cap, con_handle);
342121054Semax	if (con == NULL) {
343121054Semax		NG_L2CAP_ALERT(
344121054Semax"%s: %s - could not find connection, con_handle=%d\n",
345121054Semax			__func__, NG_NODE_NAME(node), con_handle);
346121054Semax		return;
347121054Semax	}
348121054Semax
349121054Semax	cmd = ng_l2cap_cmd_by_ident(con, ident);
350121054Semax	if (cmd == NULL) {
351121054Semax		NG_L2CAP_ALERT(
352121054Semax"%s: %s - could not find command, con_handle=%d, ident=%d\n",
353121054Semax			__func__, NG_NODE_NAME(node), con_handle, ident);
354121054Semax		return;
355121054Semax	}
356121054Semax
357107120Sjulian	cmd->flags &= ~NG_L2CAP_CMD_PENDING;
358107120Sjulian	ng_l2cap_unlink_cmd(cmd);
359107120Sjulian
360107120Sjulian	switch (cmd->code) {
361107120Sjulian 	case NG_L2CAP_CON_REQ:
362107120Sjulian		ng_l2cap_l2ca_con_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT, 0);
363107120Sjulian		ng_l2cap_free_chan(cmd->ch);
364107120Sjulian		break;
365107120Sjulian
366107120Sjulian	case NG_L2CAP_CFG_REQ:
367107120Sjulian		ng_l2cap_l2ca_cfg_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT);
368107120Sjulian		break;
369107120Sjulian
370107120Sjulian 	case NG_L2CAP_DISCON_REQ:
371107120Sjulian		ng_l2cap_l2ca_discon_rsp(cmd->ch, cmd->token, NG_L2CAP_TIMEOUT);
372107120Sjulian		ng_l2cap_free_chan(cmd->ch); /* XXX free channel */
373107120Sjulian		break;
374107120Sjulian
375107120Sjulian	case NG_L2CAP_ECHO_REQ:
376107120Sjulian		/* Echo request timed out. Let the upper layer know */
377121054Semax		ng_l2cap_l2ca_ping_rsp(cmd->con, cmd->token,
378107120Sjulian			NG_L2CAP_TIMEOUT, NULL);
379107120Sjulian		break;
380107120Sjulian
381107120Sjulian	case NG_L2CAP_INFO_REQ:
382107120Sjulian		/* Info request timed out. Let the upper layer know */
383107120Sjulian		ng_l2cap_l2ca_get_info_rsp(cmd->con, cmd->token,
384107120Sjulian			NG_L2CAP_TIMEOUT, NULL);
385107120Sjulian		break;
386107120Sjulian
387107120Sjulian	/* XXX FIXME add other commands */
388107120Sjulian
389107120Sjulian	default:
390121054Semax		panic(
391121054Semax"%s: %s - unexpected command code=%d\n",
392121054Semax			__func__, NG_NODE_NAME(l2cap->node), cmd->code);
393107120Sjulian		break;
394107120Sjulian	}
395107120Sjulian
396107120Sjulian	ng_l2cap_free_cmd(cmd);
397107120Sjulian} /* ng_l2cap_process_command_timeout */
398107120Sjulian
399