1107120Sjulian/*
2107120Sjulian * ng_l2cap_llpi.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_llpi.c,v 1.5 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 **                 Lower Layer Protocol (HCI) Interface module
56107120Sjulian ******************************************************************************
57107120Sjulian ******************************************************************************/
58107120Sjulian
59107120Sjulian/*
60107120Sjulian * Send LP_ConnectReq event to the lower layer protocol. Create new connection
61107120Sjulian * descriptor and initialize it. Create LP_ConnectReq event and send it to the
62107120Sjulian * lower layer, then adjust connection state and start timer. The function WILL
63107120Sjulian * FAIL if connection to the remote unit already exists.
64107120Sjulian */
65107120Sjulian
66107120Sjulianint
67281198Stakawatang_l2cap_lp_con_req(ng_l2cap_p l2cap, bdaddr_p bdaddr, int type)
68107120Sjulian{
69107120Sjulian	struct ng_mesg		*msg = NULL;
70107120Sjulian	ng_hci_lp_con_req_ep	*ep = NULL;
71107120Sjulian	ng_l2cap_con_p		 con = NULL;
72107120Sjulian	int			 error = 0;
73107120Sjulian
74107120Sjulian	/* Verify that we DO NOT have connection to the remote unit */
75281198Stakawata	con = ng_l2cap_con_by_addr(l2cap, bdaddr, type);
76107120Sjulian	if (con != NULL) {
77107120Sjulian		NG_L2CAP_ALERT(
78107120Sjulian"%s: %s - unexpected LP_ConnectReq event. " \
79107120Sjulian"Connection already exists, state=%d, con_handle=%d\n",
80107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), con->state,
81107120Sjulian			con->con_handle);
82107120Sjulian
83107120Sjulian		return (EEXIST);
84107120Sjulian	}
85107120Sjulian
86107120Sjulian	/* Check if lower layer protocol is still connected */
87107120Sjulian	if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
88107120Sjulian		NG_L2CAP_ERR(
89107120Sjulian"%s: %s - hook \"%s\" is not connected or valid\n",
90107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
91107120Sjulian
92107120Sjulian		return (ENOTCONN);
93107120Sjulian	}
94107120Sjulian
95107120Sjulian	/* Create and intialize new connection descriptor */
96281198Stakawata	con = ng_l2cap_new_con(l2cap, bdaddr, type);
97107120Sjulian	if (con == NULL)
98107120Sjulian		return (ENOMEM);
99107120Sjulian
100107120Sjulian	/* Create and send LP_ConnectReq event */
101107120Sjulian	NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_REQ,
102107120Sjulian		sizeof(*ep), M_NOWAIT);
103107120Sjulian	if (msg == NULL) {
104107120Sjulian		ng_l2cap_free_con(con);
105107120Sjulian
106107120Sjulian		return (ENOMEM);
107107120Sjulian	}
108107120Sjulian
109107120Sjulian	ep = (ng_hci_lp_con_req_ep *) (msg->data);
110107120Sjulian	bcopy(bdaddr, &ep->bdaddr, sizeof(ep->bdaddr));
111281198Stakawata	ep->link_type = type;
112107120Sjulian
113114878Sjulian	con->flags |= NG_L2CAP_CON_OUTGOING;
114107120Sjulian	con->state = NG_L2CAP_W4_LP_CON_CFM;
115107120Sjulian	ng_l2cap_lp_timeout(con);
116107120Sjulian
117128076Semax	NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, 0);
118114878Sjulian	if (error != 0) {
119187454Semax		if (ng_l2cap_lp_untimeout(con) == 0)
120187454Semax			ng_l2cap_free_con(con);
121121054Semax
122187454Semax		/*
123187454Semax		 * Do not free connection if ng_l2cap_lp_untimeout() failed
124187454Semax		 * let timeout handler deal with it. Always return error to
125187454Semax		 * the caller.
126187454Semax		 */
127114878Sjulian	}
128107120Sjulian
129107120Sjulian	return (error);
130107120Sjulian} /* ng_l2cap_lp_con_req */
131107120Sjulian
132107120Sjulian/*
133107120Sjulian * Process LP_ConnectCfm event from the lower layer protocol. It could be
134107120Sjulian * positive or negative. Verify remote unit address then stop the timer and
135107120Sjulian * process event.
136107120Sjulian */
137107120Sjulian
138107120Sjulianint
139107120Sjulianng_l2cap_lp_con_cfm(ng_l2cap_p l2cap, struct ng_mesg *msg)
140107120Sjulian{
141107120Sjulian	ng_hci_lp_con_cfm_ep	*ep = NULL;
142107120Sjulian	ng_l2cap_con_p		 con = NULL;
143107120Sjulian	int			 error = 0;
144107120Sjulian
145107120Sjulian	/* Check message */
146107120Sjulian	if (msg->header.arglen != sizeof(*ep)) {
147107120Sjulian		NG_L2CAP_ALERT(
148107120Sjulian"%s: %s - invalid LP_ConnectCfm[Neg] message size\n",
149107120Sjulian			__func__, NG_NODE_NAME(l2cap->node));
150107120Sjulian		error = EMSGSIZE;
151107120Sjulian		goto out;
152107120Sjulian	}
153107120Sjulian
154107120Sjulian	ep = (ng_hci_lp_con_cfm_ep *) (msg->data);
155107120Sjulian	/* Check if we have requested/accepted this connection */
156281198Stakawata	con = ng_l2cap_con_by_addr(l2cap, &ep->bdaddr, ep->link_type);
157107120Sjulian	if (con == NULL) {
158107120Sjulian		NG_L2CAP_ERR(
159107120Sjulian"%s: %s - unexpected LP_ConnectCfm event. Connection does not exist\n",
160107120Sjulian			__func__, NG_NODE_NAME(l2cap->node));
161107120Sjulian		error = ENOENT;
162107120Sjulian		goto out;
163107120Sjulian	}
164107120Sjulian
165107120Sjulian	/* Check connection state */
166107120Sjulian	if (con->state != NG_L2CAP_W4_LP_CON_CFM) {
167107120Sjulian		NG_L2CAP_ALERT(
168107120Sjulian"%s: %s - unexpected LP_ConnectCfm event. " \
169107120Sjulian"Invalid connection state, state=%d, con_handle=%d\n",
170107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), con->state,
171107120Sjulian			con->con_handle);
172107120Sjulian		error = EINVAL;
173107120Sjulian		goto out;
174107120Sjulian	}
175107120Sjulian
176107120Sjulian	/*
177107120Sjulian	 * Looks like it is our confirmation. It is safe now to cancel
178121054Semax	 * connection timer and notify upper layer. If timeout already
179121054Semax	 * happened then ignore connection confirmation and let timeout
180121054Semax	 * handle that.
181107120Sjulian 	 */
182107120Sjulian
183121054Semax	if ((error = ng_l2cap_lp_untimeout(con)) != 0)
184121054Semax		goto out;
185107120Sjulian
186107120Sjulian	if (ep->status == 0) {
187107120Sjulian		con->state = NG_L2CAP_CON_OPEN;
188107120Sjulian		con->con_handle = ep->con_handle;
189107120Sjulian		ng_l2cap_lp_deliver(con);
190114878Sjulian	} else /* Negative confirmation - remove connection descriptor */
191107120Sjulian		ng_l2cap_con_fail(con, ep->status);
192107120Sjulianout:
193107120Sjulian	return (error);
194107120Sjulian} /* ng_l2cap_lp_con_cfm */
195107120Sjulian
196107120Sjulian/*
197107120Sjulian * Process LP_ConnectInd event from the lower layer protocol. This is a good
198107120Sjulian * place to put some extra check on remote unit address and/or class. We could
199107120Sjulian * even forward this information to control hook (or check against internal
200107120Sjulian * black list) and thus implement some kind of firewall. But for now be simple
201107120Sjulian * and create new connection descriptor, start timer and send LP_ConnectRsp
202107120Sjulian * event (i.e. accept connection).
203107120Sjulian */
204107120Sjulian
205107120Sjulianint
206107120Sjulianng_l2cap_lp_con_ind(ng_l2cap_p l2cap, struct ng_mesg *msg)
207107120Sjulian{
208107120Sjulian	ng_hci_lp_con_ind_ep	*ep = NULL;
209107120Sjulian	ng_hci_lp_con_rsp_ep	*rp = NULL;
210107120Sjulian	struct ng_mesg		*rsp = NULL;
211107120Sjulian	ng_l2cap_con_p		 con = NULL;
212107120Sjulian	int			 error = 0;
213107120Sjulian
214107120Sjulian	/* Check message */
215107120Sjulian	if (msg->header.arglen != sizeof(*ep)) {
216107120Sjulian		NG_L2CAP_ALERT(
217107120Sjulian"%s: %s - invalid LP_ConnectInd message size\n",
218107120Sjulian			__func__, NG_NODE_NAME(l2cap->node));
219187454Semax
220187454Semax		return (EMSGSIZE);
221107120Sjulian	}
222107120Sjulian
223107120Sjulian 	ep = (ng_hci_lp_con_ind_ep *) (msg->data);
224107120Sjulian
225107120Sjulian	/* Make sure we have only one connection to the remote unit */
226281198Stakawata	con = ng_l2cap_con_by_addr(l2cap, &ep->bdaddr, ep->link_type);
227107120Sjulian	if (con != NULL) {
228107120Sjulian		NG_L2CAP_ALERT(
229107120Sjulian"%s: %s - unexpected LP_ConnectInd event. " \
230107120Sjulian"Connection already exists, state=%d, con_handle=%d\n",
231107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), con->state,
232107120Sjulian			con->con_handle);
233187454Semax
234187454Semax		return (EEXIST);
235107120Sjulian	}
236107120Sjulian
237107120Sjulian	/* Check if lower layer protocol is still connected */
238107120Sjulian	if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
239107120Sjulian		NG_L2CAP_ERR(
240107120Sjulian"%s: %s - hook \"%s\" is not connected or valid",
241107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
242187454Semax
243187454Semax		return (ENOTCONN);
244107120Sjulian	}
245107120Sjulian
246107120Sjulian	/* Create and intialize new connection descriptor */
247281198Stakawata	con = ng_l2cap_new_con(l2cap, &ep->bdaddr, ep->link_type);
248187454Semax	if (con == NULL)
249187454Semax		return (ENOMEM);
250107120Sjulian
251107120Sjulian	/* Create and send LP_ConnectRsp event */
252107120Sjulian	NG_MKMESSAGE(rsp, NGM_HCI_COOKIE, NGM_HCI_LP_CON_RSP,
253107120Sjulian		sizeof(*rp), M_NOWAIT);
254148518Semax	if (rsp == NULL) {
255107120Sjulian		ng_l2cap_free_con(con);
256187454Semax
257187454Semax		return (ENOMEM);
258107120Sjulian	}
259107120Sjulian
260107120Sjulian	rp = (ng_hci_lp_con_rsp_ep *)(rsp->data);
261107120Sjulian	rp->status = 0x00; /* accept connection */
262107120Sjulian	rp->link_type = NG_HCI_LINK_ACL;
263107120Sjulian	bcopy(&ep->bdaddr, &rp->bdaddr, sizeof(rp->bdaddr));
264107120Sjulian
265107120Sjulian	con->state = NG_L2CAP_W4_LP_CON_CFM;
266107120Sjulian	ng_l2cap_lp_timeout(con);
267107120Sjulian
268128076Semax	NG_SEND_MSG_HOOK(error, l2cap->node, rsp, l2cap->hci, 0);
269114878Sjulian	if (error != 0) {
270187454Semax		if (ng_l2cap_lp_untimeout(con) == 0)
271187454Semax			ng_l2cap_free_con(con);
272121054Semax
273187454Semax		/*
274187454Semax		 * Do not free connection if ng_l2cap_lp_untimeout() failed
275187454Semax		 * let timeout handler deal with it. Always return error to
276187454Semax		 * the caller.
277187454Semax		 */
278114878Sjulian	}
279187454Semax
280107120Sjulian	return (error);
281187454Semax} /* ng_l2cap_lp_con_ind */
282107120Sjulian
283107120Sjulian/*
284107120Sjulian * Process LP_DisconnectInd event from the lower layer protocol. We have been
285107120Sjulian * disconnected from the remote unit. So notify the upper layer protocol.
286107120Sjulian */
287107120Sjulian
288107120Sjulianint
289107120Sjulianng_l2cap_lp_discon_ind(ng_l2cap_p l2cap, struct ng_mesg *msg)
290107120Sjulian{
291107120Sjulian	ng_hci_lp_discon_ind_ep	*ep = NULL;
292107120Sjulian	ng_l2cap_con_p		 con = NULL;
293107120Sjulian	int			 error = 0;
294107120Sjulian
295107120Sjulian	/* Check message */
296107120Sjulian	if (msg->header.arglen != sizeof(*ep)) {
297107120Sjulian		NG_L2CAP_ALERT(
298107120Sjulian"%s: %s - invalid LP_DisconnectInd message size\n",
299107120Sjulian			__func__, NG_NODE_NAME(l2cap->node));
300107120Sjulian		error = EMSGSIZE;
301107120Sjulian		goto out;
302107120Sjulian	}
303107120Sjulian
304107120Sjulian	ep = (ng_hci_lp_discon_ind_ep *) (msg->data);
305107120Sjulian
306107120Sjulian	/* Check if we have this connection */
307107120Sjulian	con = ng_l2cap_con_by_handle(l2cap, ep->con_handle);
308107120Sjulian	if (con == NULL) {
309107120Sjulian		NG_L2CAP_ERR(
310107120Sjulian"%s: %s - unexpected LP_DisconnectInd event. " \
311107120Sjulian"Connection does not exist, con_handle=%d\n",
312107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ep->con_handle);
313107120Sjulian		error = ENOENT;
314107120Sjulian		goto out;
315107120Sjulian	}
316107120Sjulian
317107120Sjulian	/* XXX Verify connection state -- do we need to check this? */
318107120Sjulian	if (con->state != NG_L2CAP_CON_OPEN) {
319107120Sjulian		NG_L2CAP_ERR(
320107120Sjulian"%s: %s - unexpected LP_DisconnectInd event. " \
321107120Sjulian"Invalid connection state, state=%d, con_handle=%d\n",
322107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), con->state,
323107120Sjulian			con->con_handle);
324107120Sjulian		error = EINVAL;
325107120Sjulian		goto out;
326107120Sjulian	}
327107120Sjulian
328121054Semax	/*
329121054Semax	 * Notify upper layer and remove connection
330121054Semax	 * Note: The connection could have auto disconnect timeout set. Try
331121054Semax	 * to remove it. If auto disconnect timeout happened then ignore
332121054Semax	 * disconnect indication and let timeout handle that.
333121054Semax	 */
334121054Semax
335114878Sjulian	if (con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO)
336121054Semax		if ((error = ng_l2cap_discon_untimeout(con)) != 0)
337121054Semax			return (error);
338114878Sjulian
339107120Sjulian	ng_l2cap_con_fail(con, ep->reason);
340107120Sjulianout:
341107120Sjulian	return (error);
342107120Sjulian} /* ng_l2cap_lp_discon_ind */
343107120Sjulian
344107120Sjulian/*
345107120Sjulian * Send LP_QoSSetupReq event to the lower layer protocol
346107120Sjulian */
347107120Sjulian
348107120Sjulianint
349107120Sjulianng_l2cap_lp_qos_req(ng_l2cap_p l2cap, u_int16_t con_handle,
350107120Sjulian		ng_l2cap_flow_p flow)
351107120Sjulian{
352107120Sjulian	struct ng_mesg		*msg = NULL;
353107120Sjulian	ng_hci_lp_qos_req_ep	*ep = NULL;
354107120Sjulian	ng_l2cap_con_p		 con = NULL;
355107120Sjulian	int			 error = 0;
356107120Sjulian
357107120Sjulian	/* Verify that we have this connection */
358107120Sjulian	con = ng_l2cap_con_by_handle(l2cap, con_handle);
359107120Sjulian	if (con == NULL) {
360107120Sjulian		NG_L2CAP_ERR(
361107120Sjulian"%s: %s - unexpected LP_QoSSetupReq event. " \
362107120Sjulian"Connection does not exist, con_handle=%d\n",
363107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), con_handle);
364107120Sjulian
365107120Sjulian		return (ENOENT);
366107120Sjulian	}
367107120Sjulian
368107120Sjulian	/* Verify connection state */
369107120Sjulian	if (con->state != NG_L2CAP_CON_OPEN) {
370107120Sjulian		NG_L2CAP_ERR(
371107120Sjulian"%s: %s - unexpected LP_QoSSetupReq event. " \
372107120Sjulian"Invalid connection state, state=%d, con_handle=%d\n",
373107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), con->state,
374107120Sjulian			con->con_handle);
375107120Sjulian
376107120Sjulian		return (EINVAL);
377107120Sjulian	}
378107120Sjulian
379107120Sjulian	/* Check if lower layer protocol is still connected */
380107120Sjulian	if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
381107120Sjulian		NG_L2CAP_ERR(
382107120Sjulian"%s: %s - hook \"%s\" is not connected or valid",
383107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
384107120Sjulian
385107120Sjulian		return (ENOTCONN);
386107120Sjulian	}
387107120Sjulian
388107120Sjulian	/* Create and send LP_QoSSetupReq event */
389107120Sjulian	NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_REQ,
390107120Sjulian		sizeof(*ep), M_NOWAIT);
391107120Sjulian	if (msg == NULL)
392107120Sjulian		return (ENOMEM);
393107120Sjulian
394107120Sjulian	ep = (ng_hci_lp_qos_req_ep *) (msg->data);
395107120Sjulian	ep->con_handle = con_handle;
396107120Sjulian	ep->flags = flow->flags;
397107120Sjulian	ep->service_type = flow->service_type;
398107120Sjulian	ep->token_rate = flow->token_rate;
399107120Sjulian	ep->peak_bandwidth = flow->peak_bandwidth;
400107120Sjulian	ep->latency = flow->latency;
401107120Sjulian	ep->delay_variation = flow->delay_variation;
402107120Sjulian
403128076Semax	NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, 0);
404107120Sjulian
405107120Sjulian	return (error);
406107120Sjulian} /* ng_l2cap_lp_con_req */
407107120Sjulian
408107120Sjulian/*
409107120Sjulian * Process LP_QoSSetupCfm from the lower layer protocol
410107120Sjulian */
411107120Sjulian
412107120Sjulianint
413107120Sjulianng_l2cap_lp_qos_cfm(ng_l2cap_p l2cap, struct ng_mesg *msg)
414107120Sjulian{
415107120Sjulian	ng_hci_lp_qos_cfm_ep	*ep = NULL;
416107120Sjulian	int			 error = 0;
417107120Sjulian
418107120Sjulian	/* Check message */
419107120Sjulian	if (msg->header.arglen != sizeof(*ep)) {
420107120Sjulian		NG_L2CAP_ALERT(
421107120Sjulian"%s: %s - invalid LP_QoSSetupCfm[Neg] message size\n",
422107120Sjulian			__func__, NG_NODE_NAME(l2cap->node));
423107120Sjulian		error = EMSGSIZE;
424107120Sjulian		goto out;
425107120Sjulian	}
426107120Sjulian
427107120Sjulian	ep = (ng_hci_lp_qos_cfm_ep *) (msg->data);
428107120Sjulian	/* XXX FIXME do something */
429107120Sjulianout:
430107120Sjulian	return (error);
431107120Sjulian} /* ng_l2cap_lp_qos_cfm */
432107120Sjulian
433107120Sjulian/*
434107120Sjulian * Process LP_QoSViolationInd event from the lower layer protocol. Lower
435107120Sjulian * layer protocol has detected QoS Violation, so we MUST notify the
436107120Sjulian * upper layer.
437107120Sjulian */
438107120Sjulian
439107120Sjulianint
440107120Sjulianng_l2cap_lp_qos_ind(ng_l2cap_p l2cap, struct ng_mesg *msg)
441107120Sjulian{
442107120Sjulian	ng_hci_lp_qos_ind_ep	*ep = NULL;
443107120Sjulian	ng_l2cap_con_p		 con = NULL;
444107120Sjulian	int			 error = 0;
445107120Sjulian
446107120Sjulian	/* Check message */
447107120Sjulian	if (msg->header.arglen != sizeof(*ep)) {
448107120Sjulian		NG_L2CAP_ALERT(
449107120Sjulian"%s: %s - invalid LP_QoSViolation message size\n",
450107120Sjulian			__func__, NG_NODE_NAME(l2cap->node));
451107120Sjulian		error = EMSGSIZE;
452107120Sjulian		goto out;
453107120Sjulian	}
454107120Sjulian
455107120Sjulian	ep = (ng_hci_lp_qos_ind_ep *) (msg->data);
456107120Sjulian
457107120Sjulian	/* Check if we have this connection */
458107120Sjulian	con = ng_l2cap_con_by_handle(l2cap, ep->con_handle);
459107120Sjulian	if (con == NULL) {
460107120Sjulian		NG_L2CAP_ERR(
461107120Sjulian"%s: %s - unexpected LP_QoSViolationInd event. " \
462107120Sjulian"Connection does not exist, con_handle=%d\n",
463107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), ep->con_handle);
464107120Sjulian		error = ENOENT;
465107120Sjulian		goto out;
466107120Sjulian	}
467107120Sjulian
468107120Sjulian	/* Verify connection state */
469107120Sjulian	if (con->state != NG_L2CAP_CON_OPEN) {
470107120Sjulian		NG_L2CAP_ERR(
471107120Sjulian"%s: %s - unexpected LP_QoSViolationInd event. " \
472107120Sjulian"Invalid connection state, state=%d, con_handle=%d\n",
473107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), con->state,
474107120Sjulian			con->con_handle);
475107120Sjulian		error = EINVAL;
476107120Sjulian		goto out;
477107120Sjulian	}
478107120Sjulian
479107120Sjulian	/* XXX FIXME Notify upper layer and terminate channels if required */
480107120Sjulianout:
481107120Sjulian	return (error);
482107120Sjulian} /* ng_l2cap_qos_ind */
483107120Sjulian
484290038Stakawataint
485290038Stakawatang_l2cap_lp_enc_change(ng_l2cap_p l2cap, struct ng_mesg *msg)
486290038Stakawata{
487290038Stakawata	ng_hci_lp_enc_change_ep	*ep = NULL;
488290038Stakawata	ng_l2cap_con_p		 con = NULL;
489290038Stakawata	int			 error = 0;
490290038Stakawata	ng_l2cap_chan_p 	 ch = NULL;
491290038Stakawata	/* Check message */
492290038Stakawata	if (msg->header.arglen != sizeof(*ep)) {
493290038Stakawata		NG_L2CAP_ALERT(
494290038Stakawata"%s: %s - invalid LP_ENCChange message size\n",
495290038Stakawata			__func__, NG_NODE_NAME(l2cap->node));
496290038Stakawata		error = EMSGSIZE;
497290038Stakawata		goto out;
498290038Stakawata	}
499290038Stakawata
500290038Stakawata	ep = (ng_hci_lp_enc_change_ep *) (msg->data);
501290038Stakawata
502290038Stakawata	/* Check if we have this connection */
503290038Stakawata	con = ng_l2cap_con_by_handle(l2cap, ep->con_handle);
504290038Stakawata	if (con == NULL) {
505290038Stakawata		NG_L2CAP_ERR(
506290038Stakawata"%s: %s - unexpected LP_Enc Change Event. " \
507290038Stakawata"Connection does not exist, con_handle=%d\n",
508290038Stakawata			__func__, NG_NODE_NAME(l2cap->node), ep->con_handle);
509290038Stakawata		error = ENOENT;
510290038Stakawata		goto out;
511290038Stakawata	}
512290038Stakawata
513290038Stakawata	/* Verify connection state */
514290038Stakawata	if (con->state != NG_L2CAP_CON_OPEN) {
515290038Stakawata		NG_L2CAP_ERR(
516290038Stakawata"%s: %s - unexpected ENC_CHANGE event. " \
517290038Stakawata"Invalid connection state, state=%d, con_handle=%d\n",
518290038Stakawata			__func__, NG_NODE_NAME(l2cap->node), con->state,
519290038Stakawata			con->con_handle);
520290038Stakawata		error = EINVAL;
521290038Stakawata		goto out;
522290038Stakawata	}
523290038Stakawata
524290038Stakawata	con->encryption = ep->status;
525290038Stakawata
526290038Stakawata	LIST_FOREACH(ch, &l2cap->chan_list, next){
527290038Stakawata		if((ch->con->con_handle == ep->con_handle) &&
528290038Stakawata		   (ch->con->linktype == ep->link_type))
529290038Stakawata			ng_l2cap_l2ca_encryption_change(ch, ep->status);
530290038Stakawata	}
531290038Stakawata
532290038Stakawataout:
533290038Stakawata	return (error);
534290038Stakawata} /* ng_l2cap_enc_change */
535290038Stakawata
536107120Sjulian/*
537107120Sjulian * Prepare L2CAP packet. Prepend packet with L2CAP packet header and then
538107120Sjulian * segment it according to HCI MTU.
539107120Sjulian */
540107120Sjulian
541107120Sjulianint
542107120Sjulianng_l2cap_lp_send(ng_l2cap_con_p con, u_int16_t dcid, struct mbuf *m0)
543107120Sjulian{
544107120Sjulian	ng_l2cap_p		 l2cap = con->l2cap;
545107120Sjulian	ng_l2cap_hdr_t		*l2cap_hdr = NULL;
546107120Sjulian        ng_hci_acldata_pkt_t	*acl_hdr = NULL;
547107120Sjulian        struct mbuf		*m_last = NULL, *m = NULL;
548107120Sjulian        int			 len, flag = NG_HCI_PACKET_START;
549107120Sjulian
550107120Sjulian	KASSERT((con->tx_pkt == NULL),
551107120Sjulian("%s: %s - another packet pending?!\n", __func__, NG_NODE_NAME(l2cap->node)));
552107120Sjulian	KASSERT((l2cap->pkt_size > 0),
553107120Sjulian("%s: %s - invalid l2cap->pkt_size?!\n", __func__, NG_NODE_NAME(l2cap->node)));
554107120Sjulian
555107120Sjulian	/* Prepend mbuf with L2CAP header */
556107120Sjulian	m0 = ng_l2cap_prepend(m0, sizeof(*l2cap_hdr));
557107120Sjulian	if (m0 == NULL) {
558107120Sjulian		NG_L2CAP_ALERT(
559128076Semax"%s: %s - ng_l2cap_prepend(%zd) failed\n",
560107120Sjulian			__func__, NG_NODE_NAME(l2cap->node),
561107120Sjulian			sizeof(*l2cap_hdr));
562107120Sjulian
563107120Sjulian		goto fail;
564107120Sjulian	}
565107120Sjulian
566107120Sjulian	l2cap_hdr = mtod(m0, ng_l2cap_hdr_t *);
567107120Sjulian	l2cap_hdr->length = htole16(m0->m_pkthdr.len - sizeof(*l2cap_hdr));
568107120Sjulian	l2cap_hdr->dcid = htole16(dcid);
569107120Sjulian
570107120Sjulian	/*
571107120Sjulian	 * Segment single L2CAP packet according to the HCI layer MTU. Convert
572107120Sjulian	 * each segment into ACL data packet and prepend it with ACL data packet
573107120Sjulian	 * header. Link all segments together via m_nextpkt link.
574107120Sjulian 	 *
575107120Sjulian	 * XXX BC (Broadcast flag) will always be 0 (zero).
576107120Sjulian	 */
577107120Sjulian
578107120Sjulian	while (m0 != NULL) {
579107120Sjulian		/* Check length of the packet against HCI MTU */
580107120Sjulian		len = m0->m_pkthdr.len;
581107120Sjulian		if (len > l2cap->pkt_size) {
582243882Sglebius			m = m_split(m0, l2cap->pkt_size, M_NOWAIT);
583107120Sjulian			if (m == NULL) {
584107120Sjulian				NG_L2CAP_ALERT(
585107120Sjulian"%s: %s - m_split(%d) failed\n",	__func__, NG_NODE_NAME(l2cap->node),
586107120Sjulian					l2cap->pkt_size);
587107120Sjulian				goto fail;
588107120Sjulian			}
589107120Sjulian
590107120Sjulian			len = l2cap->pkt_size;
591107120Sjulian		}
592107120Sjulian
593107120Sjulian		/* Convert packet fragment into ACL data packet */
594107120Sjulian		m0 = ng_l2cap_prepend(m0, sizeof(*acl_hdr));
595107120Sjulian		if (m0 == NULL) {
596107120Sjulian			NG_L2CAP_ALERT(
597128076Semax"%s: %s - ng_l2cap_prepend(%zd) failed\n",
598107120Sjulian				__func__, NG_NODE_NAME(l2cap->node),
599107120Sjulian				sizeof(*acl_hdr));
600107120Sjulian			goto fail;
601107120Sjulian		}
602107120Sjulian
603107120Sjulian		acl_hdr = mtod(m0, ng_hci_acldata_pkt_t *);
604107120Sjulian		acl_hdr->type = NG_HCI_ACL_DATA_PKT;
605107120Sjulian		acl_hdr->length = htole16(len);
606107120Sjulian		acl_hdr->con_handle = htole16(NG_HCI_MK_CON_HANDLE(
607107120Sjulian					con->con_handle, flag, 0));
608107120Sjulian
609107120Sjulian		/* Add fragment to the chain */
610107120Sjulian		m0->m_nextpkt = NULL;
611107120Sjulian
612107120Sjulian		if (con->tx_pkt == NULL)
613107120Sjulian			con->tx_pkt = m_last = m0;
614107120Sjulian		else {
615107120Sjulian			m_last->m_nextpkt = m0;
616107120Sjulian			m_last = m0;
617107120Sjulian		}
618107120Sjulian
619107120Sjulian		NG_L2CAP_INFO(
620107120Sjulian"%s: %s - attaching ACL packet, con_handle=%d, PB=%#x, length=%d\n",
621107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), con->con_handle,
622107120Sjulian			flag, len);
623107120Sjulian
624107120Sjulian		m0 = m;
625107120Sjulian		m = NULL;
626107120Sjulian		flag = NG_HCI_PACKET_FRAGMENT;
627107120Sjulian	}
628107120Sjulian
629107120Sjulian	return (0);
630107120Sjulianfail:
631107120Sjulian	NG_FREE_M(m0);
632107120Sjulian	NG_FREE_M(m);
633107120Sjulian
634107120Sjulian	while (con->tx_pkt != NULL) {
635107120Sjulian		m = con->tx_pkt->m_nextpkt;
636107120Sjulian		m_freem(con->tx_pkt);
637107120Sjulian		con->tx_pkt = m;
638107120Sjulian	}
639107120Sjulian
640107120Sjulian	return (ENOBUFS);
641107120Sjulian} /* ng_l2cap_lp_send */
642107120Sjulian
643107120Sjulian/*
644107120Sjulian * Receive ACL data packet from the HCI layer. First strip ACL packet header
645107120Sjulian * and get connection handle, PB (Packet Boundary) flag and payload length.
646107120Sjulian * Then find connection descriptor and verify its state. Then process ACL
647107120Sjulian * packet as follows.
648107120Sjulian *
649107120Sjulian * 1) If we got first segment (pb == NG_HCI_PACKET_START) then extract L2CAP
650107120Sjulian *    header and get total length of the L2CAP packet. Then start new L2CAP
651107120Sjulian *    packet.
652107120Sjulian *
653107120Sjulian * 2) If we got other (then first :) segment (pb == NG_HCI_PACKET_FRAGMENT)
654107120Sjulian *    then add segment to the packet.
655107120Sjulian */
656107120Sjulian
657107120Sjulianint
658107120Sjulianng_l2cap_lp_receive(ng_l2cap_p l2cap, struct mbuf *m)
659107120Sjulian{
660107120Sjulian	ng_hci_acldata_pkt_t	*acl_hdr = NULL;
661107120Sjulian	ng_l2cap_hdr_t		*l2cap_hdr = NULL;
662107120Sjulian	ng_l2cap_con_p		 con = NULL;
663107120Sjulian	u_int16_t		 con_handle, length, pb;
664107120Sjulian	int			 error = 0;
665107120Sjulian
666107120Sjulian	/* Check ACL data packet */
667107120Sjulian	if (m->m_pkthdr.len < sizeof(*acl_hdr)) {
668107120Sjulian		NG_L2CAP_ERR(
669107120Sjulian"%s: %s - invalid ACL data packet. Packet too small, length=%d\n",
670107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), m->m_pkthdr.len);
671107120Sjulian		error = EMSGSIZE;
672107120Sjulian		goto drop;
673107120Sjulian	}
674107120Sjulian
675107120Sjulian	/* Strip ACL data packet header */
676107120Sjulian	NG_L2CAP_M_PULLUP(m, sizeof(*acl_hdr));
677107120Sjulian	if (m == NULL)
678107120Sjulian		return (ENOBUFS);
679107120Sjulian
680107120Sjulian	acl_hdr = mtod(m, ng_hci_acldata_pkt_t *);
681107120Sjulian	m_adj(m, sizeof(*acl_hdr));
682107120Sjulian
683107120Sjulian	/* Get ACL connection handle, PB flag and payload length */
684107120Sjulian	acl_hdr->con_handle = le16toh(acl_hdr->con_handle);
685107120Sjulian	con_handle = NG_HCI_CON_HANDLE(acl_hdr->con_handle);
686107120Sjulian	pb = NG_HCI_PB_FLAG(acl_hdr->con_handle);
687107120Sjulian	length = le16toh(acl_hdr->length);
688107120Sjulian
689107120Sjulian	NG_L2CAP_INFO(
690107120Sjulian"%s: %s - got ACL data packet, con_handle=%d, PB=%#x, length=%d\n",
691107120Sjulian		__func__, NG_NODE_NAME(l2cap->node), con_handle, pb, length);
692107120Sjulian
693107120Sjulian	/* Get connection descriptor */
694107120Sjulian	con = ng_l2cap_con_by_handle(l2cap, con_handle);
695107120Sjulian	if (con == NULL) {
696107120Sjulian		NG_L2CAP_ERR(
697107120Sjulian"%s: %s - unexpected ACL data packet. " \
698107120Sjulian"Connection does not exist, con_handle=%d\n",
699107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), con_handle);
700107120Sjulian		error = ENOENT;
701107120Sjulian		goto drop;
702107120Sjulian	}
703107120Sjulian
704107120Sjulian	/* Verify connection state */
705107120Sjulian	if (con->state != NG_L2CAP_CON_OPEN) {
706107120Sjulian		NG_L2CAP_ERR(
707107120Sjulian"%s: %s - unexpected ACL data packet. Invalid connection state=%d\n",
708107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), con->state);
709107120Sjulian		error = EHOSTDOWN;
710107120Sjulian		goto drop;
711107120Sjulian	}
712107120Sjulian
713107120Sjulian	/* Process packet */
714107120Sjulian	if (pb == NG_HCI_PACKET_START) {
715107120Sjulian		if (con->rx_pkt != NULL) {
716107120Sjulian			NG_L2CAP_ERR(
717107120Sjulian"%s: %s - dropping incomplete L2CAP packet, got %d bytes, want %d bytes\n",
718107120Sjulian				__func__, NG_NODE_NAME(l2cap->node),
719107120Sjulian				con->rx_pkt->m_pkthdr.len, con->rx_pkt_len);
720107120Sjulian			NG_FREE_M(con->rx_pkt);
721107120Sjulian			con->rx_pkt_len = 0;
722107120Sjulian		}
723107120Sjulian
724107120Sjulian		/* Get L2CAP header */
725107120Sjulian		if (m->m_pkthdr.len < sizeof(*l2cap_hdr)) {
726107120Sjulian			NG_L2CAP_ERR(
727107120Sjulian"%s: %s - invalid L2CAP packet start fragment. Packet too small, length=%d\n",
728107120Sjulian				__func__, NG_NODE_NAME(l2cap->node),
729107120Sjulian				m->m_pkthdr.len);
730107120Sjulian			error = EMSGSIZE;
731107120Sjulian			goto drop;
732107120Sjulian		}
733107120Sjulian
734107120Sjulian		NG_L2CAP_M_PULLUP(m, sizeof(*l2cap_hdr));
735107120Sjulian		if (m == NULL)
736107120Sjulian			return (ENOBUFS);
737107120Sjulian
738107120Sjulian		l2cap_hdr = mtod(m, ng_l2cap_hdr_t *);
739107120Sjulian
740107120Sjulian		NG_L2CAP_INFO(
741107120Sjulian"%s: %s - staring new L2CAP packet, con_handle=%d, length=%d\n",
742107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), con_handle,
743107120Sjulian			le16toh(l2cap_hdr->length));
744107120Sjulian
745107120Sjulian		/* Start new L2CAP packet */
746107120Sjulian		con->rx_pkt = m;
747107120Sjulian		con->rx_pkt_len = le16toh(l2cap_hdr->length)+sizeof(*l2cap_hdr);
748107120Sjulian	} else if (pb == NG_HCI_PACKET_FRAGMENT) {
749107120Sjulian		if (con->rx_pkt == NULL) {
750107120Sjulian			NG_L2CAP_ERR(
751107120Sjulian"%s: %s - unexpected ACL data packet fragment, con_handle=%d\n",
752107120Sjulian				__func__, NG_NODE_NAME(l2cap->node),
753107120Sjulian				con->con_handle);
754107120Sjulian			goto drop;
755107120Sjulian		}
756107120Sjulian
757107120Sjulian		/* Add fragment to the L2CAP packet */
758107120Sjulian		m_cat(con->rx_pkt, m);
759107120Sjulian		con->rx_pkt->m_pkthdr.len += length;
760107120Sjulian	} else {
761107120Sjulian		NG_L2CAP_ERR(
762107120Sjulian"%s: %s - invalid ACL data packet. Invalid PB flag=%#x\n",
763107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), pb);
764107120Sjulian		error = EINVAL;
765107120Sjulian		goto drop;
766107120Sjulian	}
767107120Sjulian
768107120Sjulian	con->rx_pkt_len -= length;
769107120Sjulian	if (con->rx_pkt_len < 0) {
770107120Sjulian		NG_L2CAP_ALERT(
771107120Sjulian"%s: %s - packet length mismatch. Got %d bytes, offset %d bytes\n",
772107120Sjulian			__func__, NG_NODE_NAME(l2cap->node),
773107120Sjulian			con->rx_pkt->m_pkthdr.len, con->rx_pkt_len);
774107120Sjulian		NG_FREE_M(con->rx_pkt);
775107120Sjulian		con->rx_pkt_len = 0;
776107120Sjulian	} else if (con->rx_pkt_len == 0) {
777107120Sjulian		/* OK, we have got complete L2CAP packet, so process it */
778107120Sjulian		error = ng_l2cap_receive(con);
779107120Sjulian		con->rx_pkt = NULL;
780107120Sjulian		con->rx_pkt_len = 0;
781107120Sjulian	}
782107120Sjulian
783107120Sjulian	return (error);
784107120Sjulian
785107120Sjuliandrop:
786107120Sjulian	NG_FREE_M(m);
787107120Sjulian
788107120Sjulian	return (error);
789107120Sjulian} /* ng_l2cap_lp_receive */
790107120Sjulian
791107120Sjulian/*
792107120Sjulian * Send queued ACL packets to the HCI layer
793107120Sjulian */
794107120Sjulian
795107120Sjulianvoid
796107120Sjulianng_l2cap_lp_deliver(ng_l2cap_con_p con)
797107120Sjulian{
798107120Sjulian	ng_l2cap_p	 l2cap = con->l2cap;
799107120Sjulian	struct mbuf	*m = NULL;
800107120Sjulian	int		 error;
801107120Sjulian
802107120Sjulian	/* Check connection */
803107120Sjulian	if (con->state != NG_L2CAP_CON_OPEN)
804107120Sjulian		return;
805107120Sjulian
806107120Sjulian	if (con->tx_pkt == NULL)
807107120Sjulian		ng_l2cap_con_wakeup(con);
808107120Sjulian
809107120Sjulian	if (con->tx_pkt == NULL)
810107120Sjulian		return;
811107120Sjulian
812107120Sjulian	/* Check if lower layer protocol is still connected */
813107120Sjulian	if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
814107120Sjulian		NG_L2CAP_ERR(
815107120Sjulian"%s: %s - hook \"%s\" is not connected or valid",
816107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
817107120Sjulian
818107120Sjulian		goto drop; /* XXX what to do with "pending"? */
819107120Sjulian	}
820107120Sjulian
821107120Sjulian	/* Send ACL data packets */
822107120Sjulian	while (con->pending < con->l2cap->num_pkts && con->tx_pkt != NULL) {
823107120Sjulian		m = con->tx_pkt;
824107120Sjulian		con->tx_pkt = con->tx_pkt->m_nextpkt;
825107120Sjulian		m->m_nextpkt = NULL;
826107120Sjulian
827281198Stakawata		if(m->m_flags &M_PROTO2){
828281198Stakawata			ng_l2cap_lp_receive(con->l2cap, m);
829281198Stakawata			continue;
830281198Stakawata		}
831107120Sjulian		NG_L2CAP_INFO(
832107120Sjulian"%s: %s - sending ACL packet, con_handle=%d, len=%d\n",
833107120Sjulian			__func__, NG_NODE_NAME(l2cap->node), con->con_handle,
834107120Sjulian			m->m_pkthdr.len);
835107120Sjulian
836107120Sjulian		NG_SEND_DATA_ONLY(error, l2cap->hci, m);
837107120Sjulian		if (error != 0) {
838107120Sjulian			NG_L2CAP_ERR(
839107120Sjulian"%s: %s - could not send ACL data packet, con_handle=%d, error=%d\n",
840107120Sjulian				__func__, NG_NODE_NAME(l2cap->node),
841107120Sjulian				con->con_handle, error);
842107120Sjulian
843107120Sjulian			goto drop; /* XXX what to do with "pending"? */
844107120Sjulian		}
845107120Sjulian
846107120Sjulian		con->pending ++;
847107120Sjulian	}
848107120Sjulian
849107120Sjulian	NG_L2CAP_INFO(
850107120Sjulian"%s: %s - %d ACL packets have been sent, con_handle=%d\n",
851107120Sjulian		__func__, NG_NODE_NAME(l2cap->node), con->pending,
852107120Sjulian		con->con_handle);
853107120Sjulian
854107120Sjulian	return;
855107120Sjulian
856107120Sjuliandrop:
857107120Sjulian	while (con->tx_pkt != NULL) {
858107120Sjulian		m = con->tx_pkt->m_nextpkt;
859107120Sjulian		m_freem(con->tx_pkt);
860107120Sjulian		con->tx_pkt = m;
861107120Sjulian	}
862107120Sjulian} /* ng_l2cap_lp_deliver */
863107120Sjulian
864107120Sjulian/*
865107120Sjulian * Process connection timeout. Remove connection from the list. If there
866107120Sjulian * are any channels that wait for the connection then notify them. Free
867107120Sjulian * connection descriptor.
868107120Sjulian */
869107120Sjulian
870107120Sjulianvoid
871121054Semaxng_l2cap_process_lp_timeout(node_p node, hook_p hook, void *arg1, int con_handle)
872107120Sjulian{
873121054Semax	ng_l2cap_p	l2cap = NULL;
874121054Semax	ng_l2cap_con_p	con = NULL;
875107120Sjulian
876121054Semax	if (NG_NODE_NOT_VALID(node)) {
877121054Semax		printf("%s: Netgraph node is not valid\n", __func__);
878121054Semax		return;
879121054Semax	}
880107120Sjulian
881121054Semax	l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);
882121054Semax	con = ng_l2cap_con_by_handle(l2cap, con_handle);
883121054Semax
884121054Semax	if (con == NULL) {
885121054Semax		NG_L2CAP_ALERT(
886121054Semax"%s: %s - could not find connection, con_handle=%d\n",
887121054Semax			__func__, NG_NODE_NAME(node), con_handle);
888121054Semax		return;
889121054Semax	}
890121054Semax
891121054Semax	if (!(con->flags & NG_L2CAP_CON_LP_TIMO)) {
892121054Semax		NG_L2CAP_ALERT(
893121054Semax"%s: %s - no pending LP timeout, con_handle=%d, state=%d, flags=%#x\n",
894121054Semax			__func__, NG_NODE_NAME(node), con_handle, con->state,
895121054Semax			con->flags);
896121054Semax		return;
897121054Semax	}
898121054Semax
899107120Sjulian	/*
900107120Sjulian	 * Notify channels that connection has timed out. This will remove
901107120Sjulian	 * connection, channels and pending commands.
902107120Sjulian	 */
903107120Sjulian
904114878Sjulian	con->flags &= ~NG_L2CAP_CON_LP_TIMO;
905107120Sjulian	ng_l2cap_con_fail(con, NG_L2CAP_TIMEOUT);
906107120Sjulian} /* ng_l2cap_process_lp_timeout */
907107120Sjulian
908114878Sjulian/*
909114878Sjulian * Process auto disconnect timeout and send LP_DisconReq event to the
910114878Sjulian * lower layer protocol
911114878Sjulian */
912114878Sjulian
913114878Sjulianvoid
914121054Semaxng_l2cap_process_discon_timeout(node_p node, hook_p hook, void *arg1, int con_handle)
915114878Sjulian{
916121054Semax	ng_l2cap_p		 l2cap = NULL;
917121054Semax	ng_l2cap_con_p		 con = NULL;
918114878Sjulian	struct ng_mesg		*msg = NULL;
919114878Sjulian	ng_hci_lp_discon_req_ep	*ep = NULL;
920114878Sjulian	int			 error;
921114878Sjulian
922121054Semax	if (NG_NODE_NOT_VALID(node)) {
923121054Semax		printf("%s: Netgraph node is not valid\n", __func__);
924121054Semax		return;
925121054Semax	}
926121054Semax
927121054Semax	l2cap = (ng_l2cap_p) NG_NODE_PRIVATE(node);
928121054Semax	con = ng_l2cap_con_by_handle(l2cap, con_handle);
929121054Semax
930121054Semax	if (con == NULL) {
931121054Semax		NG_L2CAP_ALERT(
932121054Semax"%s: %s - could not find connection, con_handle=%d\n",
933121054Semax			__func__, NG_NODE_NAME(node), con_handle);
934121054Semax		return;
935121054Semax	}
936121054Semax
937121054Semax	if (!(con->flags & NG_L2CAP_CON_AUTO_DISCON_TIMO)) {
938121054Semax		NG_L2CAP_ALERT(
939121054Semax"%s: %s - no pending disconnect timeout, con_handle=%d, state=%d, flags=%#x\n",
940121054Semax			__func__, NG_NODE_NAME(node), con_handle, con->state,
941121054Semax			con->flags);
942121054Semax		return;
943121054Semax	}
944121054Semax
945114878Sjulian	con->flags &= ~NG_L2CAP_CON_AUTO_DISCON_TIMO;
946114878Sjulian
947114878Sjulian	/* Check if lower layer protocol is still connected */
948114878Sjulian	if (l2cap->hci == NULL || NG_HOOK_NOT_VALID(l2cap->hci)) {
949114878Sjulian		NG_L2CAP_ERR(
950114878Sjulian"%s: %s - hook \"%s\" is not connected or valid\n",
951114878Sjulian			__func__, NG_NODE_NAME(l2cap->node), NG_L2CAP_HOOK_HCI);
952114878Sjulian		return;
953114878Sjulian	}
954114878Sjulian
955114878Sjulian	/* Create and send LP_DisconReq event */
956114878Sjulian	NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_REQ,
957114878Sjulian		sizeof(*ep), M_NOWAIT);
958114878Sjulian	if (msg == NULL)
959114878Sjulian		return;
960114878Sjulian
961114878Sjulian	ep = (ng_hci_lp_discon_req_ep *) (msg->data);
962114878Sjulian	ep->con_handle = con->con_handle;
963114878Sjulian	ep->reason = 0x13; /* User Ended Connection */
964114878Sjulian
965128076Semax	NG_SEND_MSG_HOOK(error, l2cap->node, msg, l2cap->hci, 0);
966114878Sjulian} /* ng_l2cap_process_discon_timeout */
967114878Sjulian
968