1107120Sjulian/*
2107120Sjulian * ng_hci_ulpi.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_hci_ulpi.c,v 1.7 2003/09/08 18:57:51 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/hci/ng_hci_var.h>
46128688Semax#include <netgraph/bluetooth/hci/ng_hci_cmds.h>
47128688Semax#include <netgraph/bluetooth/hci/ng_hci_evnt.h>
48128688Semax#include <netgraph/bluetooth/hci/ng_hci_ulpi.h>
49128688Semax#include <netgraph/bluetooth/hci/ng_hci_misc.h>
50107120Sjulian
51107120Sjulian/******************************************************************************
52107120Sjulian ******************************************************************************
53107120Sjulian **                 Upper Layer Protocol Interface module
54107120Sjulian ******************************************************************************
55107120Sjulian ******************************************************************************/
56107120Sjulian
57107120Sjulianstatic int ng_hci_lp_acl_con_req (ng_hci_unit_p, item_p, hook_p);
58107120Sjulianstatic int ng_hci_lp_sco_con_req (ng_hci_unit_p, item_p, hook_p);
59107120Sjulian
60107120Sjulian/*
61107120Sjulian * Process LP_ConnectReq event from the upper layer protocol
62107120Sjulian */
63107120Sjulian
64107120Sjulianint
65107120Sjulianng_hci_lp_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
66107120Sjulian{
67107120Sjulian	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
68107120Sjulian		NG_HCI_WARN(
69107120Sjulian"%s: %s - unit is not ready, state=%#x\n",
70107120Sjulian			__func__, NG_NODE_NAME(unit->node), unit->state);
71107120Sjulian
72107120Sjulian		NG_FREE_ITEM(item);
73107120Sjulian
74107120Sjulian		return (ENXIO);
75107120Sjulian	}
76107120Sjulian
77107120Sjulian	if (NGI_MSG(item)->header.arglen != sizeof(ng_hci_lp_con_req_ep)) {
78107120Sjulian		NG_HCI_ALERT(
79107120Sjulian"%s: %s - invalid LP_ConnectReq message size=%d\n",
80107120Sjulian			__func__, NG_NODE_NAME(unit->node),
81107120Sjulian			NGI_MSG(item)->header.arglen);
82107120Sjulian
83107120Sjulian		NG_FREE_ITEM(item);
84107120Sjulian
85107120Sjulian		return (EMSGSIZE);
86107120Sjulian	}
87107120Sjulian
88107120Sjulian	if (((ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data))->link_type == NG_HCI_LINK_ACL)
89107120Sjulian		return (ng_hci_lp_acl_con_req(unit, item, hook));
90107120Sjulian
91107120Sjulian	if (hook != unit->sco) {
92107120Sjulian		NG_HCI_WARN(
93107120Sjulian"%s: %s - LP_ConnectReq for SCO connection came from wrong hook=%p\n",
94107120Sjulian			__func__, NG_NODE_NAME(unit->node), hook);
95107120Sjulian
96107120Sjulian		NG_FREE_ITEM(item);
97107120Sjulian
98107120Sjulian		return (EINVAL);
99107120Sjulian	}
100107120Sjulian
101107120Sjulian	return (ng_hci_lp_sco_con_req(unit, item, hook));
102107120Sjulian} /* ng_hci_lp_con_req */
103107120Sjulian
104107120Sjulian/*
105107120Sjulian * Request to create new ACL connection
106107120Sjulian */
107107120Sjulian
108107120Sjulianstatic int
109107120Sjulianng_hci_lp_acl_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
110107120Sjulian{
111107120Sjulian	struct acl_con_req {
112107120Sjulian		ng_hci_cmd_pkt_t	 hdr;
113107120Sjulian		ng_hci_create_con_cp	 cp;
114107120Sjulian	} __attribute__ ((packed))	*req = NULL;
115107120Sjulian	ng_hci_lp_con_req_ep		*ep = NULL;
116107120Sjulian	ng_hci_unit_con_p		 con = NULL;
117107120Sjulian	ng_hci_neighbor_t		*n = NULL;
118107120Sjulian	struct mbuf			*m = NULL;
119107120Sjulian	int				 error = 0;
120107120Sjulian
121107120Sjulian	ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);
122107120Sjulian
123107120Sjulian	/*
124107120Sjulian	 * Only one ACL connection can exist between each pair of units.
125107120Sjulian	 * So try to find ACL connection descriptor (in any state) that
126107120Sjulian	 * has requested remote BD_ADDR.
127107120Sjulian	 *
128107120Sjulian	 * Two cases:
129107120Sjulian	 *
130107120Sjulian	 * 1) We do not have connection to the remote unit. This is simple.
131107120Sjulian	 *    Just create new connection descriptor and send HCI command to
132107120Sjulian	 *    create new connection.
133107120Sjulian	 *
134107120Sjulian	 * 2) We do have connection descriptor. We need to check connection
135107120Sjulian	 *    state:
136107120Sjulian	 *
137114878Sjulian	 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means that we are in the middle of
138107120Sjulian	 *      accepting connection from the remote unit. This is a race
139107120Sjulian	 *      condition. We will ignore this message.
140107120Sjulian	 *
141114878Sjulian	 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that upper layer already
142107120Sjulian	 *      requested connection or we just accepted it. In any case
143107120Sjulian	 *      all we need to do here is set appropriate notification bit
144107120Sjulian	 *      and wait.
145107120Sjulian	 *
146114878Sjulian	 * 2.3) NG_HCI_CON_OPEN means connection is open. Just reply back
147107120Sjulian	 *      and let upper layer know that we have connection already.
148107120Sjulian	 */
149107120Sjulian
150107120Sjulian	con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL);
151107120Sjulian	if (con != NULL) {
152107120Sjulian		switch (con->state) {
153107120Sjulian		case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
154107120Sjulian			error = EALREADY;
155107120Sjulian			break;
156107120Sjulian
157107120Sjulian		case NG_HCI_CON_W4_CONN_COMPLETE:
158107120Sjulian			if (hook == unit->acl)
159107120Sjulian				con->flags |= NG_HCI_CON_NOTIFY_ACL;
160107120Sjulian			else
161107120Sjulian				con->flags |= NG_HCI_CON_NOTIFY_SCO;
162107120Sjulian			break;
163107120Sjulian
164107120Sjulian		case NG_HCI_CON_OPEN: {
165107120Sjulian			struct ng_mesg		*msg = NULL;
166107120Sjulian			ng_hci_lp_con_cfm_ep	*cfm = NULL;
167107120Sjulian
168107120Sjulian			if (hook != NULL && NG_HOOK_IS_VALID(hook)) {
169107120Sjulian				NGI_GET_MSG(item, msg);
170107120Sjulian				NG_FREE_MSG(msg);
171107120Sjulian
172107120Sjulian				NG_MKMESSAGE(msg, NGM_HCI_COOKIE,
173107120Sjulian					NGM_HCI_LP_CON_CFM, sizeof(*cfm),
174107120Sjulian					M_NOWAIT);
175107120Sjulian				if (msg != NULL) {
176107120Sjulian					cfm = (ng_hci_lp_con_cfm_ep *)msg->data;
177107120Sjulian					cfm->status = 0;
178107120Sjulian					cfm->link_type = con->link_type;
179107120Sjulian					cfm->con_handle = con->con_handle;
180107120Sjulian					bcopy(&con->bdaddr, &cfm->bdaddr,
181107120Sjulian						sizeof(cfm->bdaddr));
182107120Sjulian
183107120Sjulian					/*
184107120Sjulian					 * This will forward item back to
185107120Sjulian					 * sender and set item to NULL
186107120Sjulian					 */
187107120Sjulian
188107120Sjulian					_NGI_MSG(item) = msg;
189107120Sjulian					NG_FWD_ITEM_HOOK(error, item, hook);
190107120Sjulian				} else
191107120Sjulian					error = ENOMEM;
192107120Sjulian			} else
193107120Sjulian				NG_HCI_INFO(
194107120Sjulian"%s: %s - Source hook is not valid, hook=%p\n",
195107120Sjulian					__func__, NG_NODE_NAME(unit->node),
196107120Sjulian					hook);
197107120Sjulian			} break;
198107120Sjulian
199107120Sjulian		default:
200121054Semax			panic(
201121054Semax"%s: %s - Invalid connection state=%d\n",
202121054Semax				__func__, NG_NODE_NAME(unit->node), con->state);
203107120Sjulian			break;
204107120Sjulian		}
205107120Sjulian
206107120Sjulian		goto out;
207107120Sjulian	}
208107120Sjulian
209107120Sjulian	/*
210107120Sjulian	 * If we got here then we need to create new ACL connection descriptor
211107120Sjulian	 * and submit HCI command. First create new connection desriptor, set
212107120Sjulian	 * bdaddr and notification flags.
213107120Sjulian	 */
214107120Sjulian
215107120Sjulian	con = ng_hci_new_con(unit, NG_HCI_LINK_ACL);
216107120Sjulian	if (con == NULL) {
217107120Sjulian		error = ENOMEM;
218107120Sjulian		goto out;
219107120Sjulian	}
220107120Sjulian
221107120Sjulian	bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));
222107120Sjulian
223107120Sjulian	/*
224107120Sjulian	 * Create HCI command
225107120Sjulian	 */
226107120Sjulian
227243882Sglebius	MGETHDR(m, M_NOWAIT, MT_DATA);
228107120Sjulian	if (m == NULL) {
229107120Sjulian		ng_hci_free_con(con);
230107120Sjulian		error = ENOBUFS;
231107120Sjulian		goto out;
232107120Sjulian	}
233107120Sjulian
234107120Sjulian	m->m_pkthdr.len = m->m_len = sizeof(*req);
235107120Sjulian	req = mtod(m, struct acl_con_req *);
236107120Sjulian	req->hdr.type = NG_HCI_CMD_PKT;
237107120Sjulian	req->hdr.length = sizeof(req->cp);
238107120Sjulian	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
239107120Sjulian					NG_HCI_OCF_CREATE_CON));
240107120Sjulian
241107120Sjulian	bcopy(&ep->bdaddr, &req->cp.bdaddr, sizeof(req->cp.bdaddr));
242107120Sjulian
243107120Sjulian	req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1);
244107120Sjulian	if (unit->features[0] & NG_HCI_LMP_3SLOT)
245107120Sjulian		req->cp.pkt_type |= (NG_HCI_PKT_DM3|NG_HCI_PKT_DH3);
246107120Sjulian	if (unit->features[0] & NG_HCI_LMP_5SLOT)
247107120Sjulian		req->cp.pkt_type |= (NG_HCI_PKT_DM5|NG_HCI_PKT_DH5);
248107120Sjulian
249107120Sjulian	req->cp.pkt_type &= unit->packet_mask;
250114878Sjulian	if ((req->cp.pkt_type & (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1|
251114878Sjulian				 NG_HCI_PKT_DM3|NG_HCI_PKT_DH3|
252114878Sjulian				 NG_HCI_PKT_DM5|NG_HCI_PKT_DH5)) == 0)
253107120Sjulian		req->cp.pkt_type = (NG_HCI_PKT_DM1|NG_HCI_PKT_DH1);
254107120Sjulian
255107120Sjulian	req->cp.pkt_type = htole16(req->cp.pkt_type);
256107120Sjulian
257114878Sjulian	if ((unit->features[0] & NG_HCI_LMP_SWITCH) && unit->role_switch)
258107120Sjulian		req->cp.accept_role_switch = 1;
259107120Sjulian	else
260107120Sjulian		req->cp.accept_role_switch = 0;
261107120Sjulian
262107120Sjulian	/*
263107120Sjulian	 * We may speed up connect by specifying valid parameters.
264107120Sjulian	 * So check the neighbor cache.
265107120Sjulian	 */
266107120Sjulian
267107120Sjulian	n = ng_hci_get_neighbor(unit, &ep->bdaddr);
268107120Sjulian	if (n == NULL) {
269107120Sjulian		req->cp.page_scan_rep_mode = 0;
270107120Sjulian		req->cp.page_scan_mode = 0;
271107120Sjulian		req->cp.clock_offset = 0;
272107120Sjulian	} else {
273107120Sjulian		req->cp.page_scan_rep_mode = n->page_scan_rep_mode;
274107120Sjulian		req->cp.page_scan_mode = n->page_scan_mode;
275107120Sjulian		req->cp.clock_offset = htole16(n->clock_offset);
276107120Sjulian	}
277107120Sjulian
278107120Sjulian	/*
279107120Sjulian	 * Adust connection state
280107120Sjulian	 */
281107120Sjulian
282107120Sjulian	if (hook == unit->acl)
283107120Sjulian		con->flags |= NG_HCI_CON_NOTIFY_ACL;
284107120Sjulian	else
285107120Sjulian		con->flags |= NG_HCI_CON_NOTIFY_SCO;
286107120Sjulian
287107120Sjulian	con->state = NG_HCI_CON_W4_CONN_COMPLETE;
288107120Sjulian	ng_hci_con_timeout(con);
289107120Sjulian
290107120Sjulian	/*
291107120Sjulian	 * Queue and send HCI command
292107120Sjulian	 */
293107120Sjulian
294107120Sjulian	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
295107120Sjulian	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
296107120Sjulian		error = ng_hci_send_command(unit);
297107120Sjulianout:
298107120Sjulian	if (item != NULL)
299107120Sjulian		NG_FREE_ITEM(item);
300107120Sjulian
301107120Sjulian	return (error);
302107120Sjulian} /* ng_hci_lp_acl_con_req */
303107120Sjulian
304107120Sjulian/*
305107120Sjulian * Request to create new SCO connection
306107120Sjulian */
307107120Sjulian
308107120Sjulianstatic int
309107120Sjulianng_hci_lp_sco_con_req(ng_hci_unit_p unit, item_p item, hook_p hook)
310107120Sjulian{
311107120Sjulian	struct sco_con_req {
312107120Sjulian		ng_hci_cmd_pkt_t	 hdr;
313107120Sjulian		ng_hci_add_sco_con_cp	 cp;
314107120Sjulian	} __attribute__ ((packed))	*req = NULL;
315107120Sjulian	ng_hci_lp_con_req_ep		*ep = NULL;
316107120Sjulian	ng_hci_unit_con_p		 acl_con = NULL, sco_con = NULL;
317107120Sjulian	struct mbuf			*m = NULL;
318107120Sjulian	int				 error = 0;
319107120Sjulian
320107120Sjulian	ep = (ng_hci_lp_con_req_ep *)(NGI_MSG(item)->data);
321107120Sjulian
322107120Sjulian	/*
323107120Sjulian	 * SCO connection without ACL link
324107120Sjulian	 *
325107120Sjulian	 * If upper layer requests SCO connection and there is no open ACL
326107120Sjulian	 * connection to the desired remote unit, we will reject the request.
327107120Sjulian	 */
328107120Sjulian
329107120Sjulian	LIST_FOREACH(acl_con, &unit->con_list, next)
330107120Sjulian		if (acl_con->link_type == NG_HCI_LINK_ACL &&
331107120Sjulian		    acl_con->state == NG_HCI_CON_OPEN &&
332107120Sjulian		    bcmp(&acl_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
333107120Sjulian			break;
334107120Sjulian
335107120Sjulian	if (acl_con == NULL) {
336107120Sjulian		NG_HCI_INFO(
337107120Sjulian"%s: %s - No open ACL connection to bdaddr=%x:%x:%x:%x:%x:%x\n",
338107120Sjulian			__func__, NG_NODE_NAME(unit->node),
339107120Sjulian			ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],
340107120Sjulian			ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);
341107120Sjulian
342107120Sjulian		error = ENOENT;
343107120Sjulian		goto out;
344107120Sjulian	}
345107120Sjulian
346107120Sjulian	/*
347107120Sjulian	 * Multiple SCO connections can exist between the same pair of units.
348107120Sjulian	 * We assume that multiple SCO connections have to be opened one after
349107120Sjulian	 * another.
350107120Sjulian	 *
351107120Sjulian	 * Try to find SCO connection descriptor that matches the following:
352107120Sjulian	 *
353107120Sjulian	 * 1) sco_con->link_type == NG_HCI_LINK_SCO
354107120Sjulian	 *
355107120Sjulian	 * 2) sco_con->state == NG_HCI_CON_W4_LP_CON_RSP ||
356107120Sjulian	 *    sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE
357107120Sjulian	 *
358107120Sjulian	 * 3) sco_con->bdaddr == ep->bdaddr
359107120Sjulian	 *
360107120Sjulian	 * Two cases:
361107120Sjulian	 *
362107120Sjulian	 * 1) We do not have connection descriptor. This is simple. Just
363107120Sjulian	 *    create new connection and submit Add_SCO_Connection command.
364107120Sjulian	 *
365107120Sjulian	 * 2) We do have connection descriptor. We need to check the state.
366107120Sjulian	 *
367107120Sjulian	 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means we in the middle of accepting
368107120Sjulian	 *      connection from the remote unit. This is a race condition and
369107120Sjulian	 *      we will ignore the request.
370107120Sjulian	 *
371107120Sjulian	 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer already requested
372107120Sjulian	 *      connection or we just accepted it.
373107120Sjulian	 */
374107120Sjulian
375107120Sjulian	LIST_FOREACH(sco_con, &unit->con_list, next)
376107120Sjulian		if (sco_con->link_type == NG_HCI_LINK_SCO &&
377107120Sjulian		    (sco_con->state == NG_HCI_CON_W4_LP_CON_RSP ||
378107120Sjulian		     sco_con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
379107120Sjulian		    bcmp(&sco_con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
380107120Sjulian			break;
381107120Sjulian
382107120Sjulian	if (sco_con != NULL) {
383107120Sjulian		switch (sco_con->state) {
384107120Sjulian		case NG_HCI_CON_W4_LP_CON_RSP: /* XXX */
385107120Sjulian			error = EALREADY;
386107120Sjulian			break;
387107120Sjulian
388107120Sjulian		case NG_HCI_CON_W4_CONN_COMPLETE:
389107120Sjulian			sco_con->flags |= NG_HCI_CON_NOTIFY_SCO;
390107120Sjulian			break;
391107120Sjulian
392107120Sjulian		default:
393121054Semax			panic(
394250576Seadler"%s: %s - Invalid connection state=%d\n",
395107120Sjulian				__func__, NG_NODE_NAME(unit->node),
396121054Semax				sco_con->state);
397107120Sjulian			break;
398107120Sjulian		}
399107120Sjulian
400107120Sjulian		goto out;
401107120Sjulian	}
402107120Sjulian
403107120Sjulian	/*
404107120Sjulian	 * If we got here then we need to create new SCO connection descriptor
405107120Sjulian	 * and submit HCI command.
406107120Sjulian	 */
407107120Sjulian
408107120Sjulian	sco_con = ng_hci_new_con(unit, NG_HCI_LINK_SCO);
409107120Sjulian	if (sco_con == NULL) {
410107120Sjulian		error = ENOMEM;
411107120Sjulian		goto out;
412107120Sjulian	}
413107120Sjulian
414107120Sjulian	bcopy(&ep->bdaddr, &sco_con->bdaddr, sizeof(sco_con->bdaddr));
415107120Sjulian
416107120Sjulian	/*
417107120Sjulian	 * Create HCI command
418107120Sjulian	 */
419107120Sjulian
420243882Sglebius	MGETHDR(m, M_NOWAIT, MT_DATA);
421107120Sjulian	if (m == NULL) {
422107120Sjulian		ng_hci_free_con(sco_con);
423107120Sjulian		error = ENOBUFS;
424107120Sjulian		goto out;
425107120Sjulian	}
426107120Sjulian
427107120Sjulian	m->m_pkthdr.len = m->m_len = sizeof(*req);
428107120Sjulian	req = mtod(m, struct sco_con_req *);
429107120Sjulian	req->hdr.type = NG_HCI_CMD_PKT;
430107120Sjulian	req->hdr.length = sizeof(req->cp);
431107120Sjulian	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
432107120Sjulian					NG_HCI_OCF_ADD_SCO_CON));
433107120Sjulian
434107120Sjulian	req->cp.con_handle = htole16(acl_con->con_handle);
435107120Sjulian
436107120Sjulian	req->cp.pkt_type = NG_HCI_PKT_HV1;
437107120Sjulian	if (unit->features[1] & NG_HCI_LMP_HV2_PKT)
438107120Sjulian		req->cp.pkt_type |= NG_HCI_PKT_HV2;
439107120Sjulian	if (unit->features[1] & NG_HCI_LMP_HV3_PKT)
440107120Sjulian		req->cp.pkt_type |= NG_HCI_PKT_HV3;
441107120Sjulian
442107120Sjulian	req->cp.pkt_type &= unit->packet_mask;
443114878Sjulian	if ((req->cp.pkt_type & (NG_HCI_PKT_HV1|
444114878Sjulian				 NG_HCI_PKT_HV2|
445114878Sjulian				 NG_HCI_PKT_HV3)) == 0)
446107120Sjulian		req->cp.pkt_type = NG_HCI_PKT_HV1;
447107120Sjulian
448107120Sjulian	req->cp.pkt_type = htole16(req->cp.pkt_type);
449107120Sjulian
450107120Sjulian	/*
451107120Sjulian	 * Adust connection state
452107120Sjulian	 */
453107120Sjulian
454107120Sjulian	sco_con->flags |= NG_HCI_CON_NOTIFY_SCO;
455107120Sjulian
456107120Sjulian	sco_con->state = NG_HCI_CON_W4_CONN_COMPLETE;
457107120Sjulian	ng_hci_con_timeout(sco_con);
458107120Sjulian
459107120Sjulian	/*
460107120Sjulian	 * Queue and send HCI command
461107120Sjulian	 */
462107120Sjulian
463107120Sjulian	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
464107120Sjulian	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
465107120Sjulian		error = ng_hci_send_command(unit);
466107120Sjulianout:
467107120Sjulian	NG_FREE_ITEM(item);
468107120Sjulian
469107120Sjulian	return (error);
470107120Sjulian} /* ng_hci_lp_sco_con_req */
471107120Sjulian
472107120Sjulian/*
473107120Sjulian * Process LP_DisconnectReq event from the upper layer protocol
474107120Sjulian */
475107120Sjulian
476107120Sjulianint
477107120Sjulianng_hci_lp_discon_req(ng_hci_unit_p unit, item_p item, hook_p hook)
478107120Sjulian{
479107120Sjulian	struct discon_req {
480107120Sjulian		ng_hci_cmd_pkt_t	 hdr;
481107120Sjulian		ng_hci_discon_cp	 cp;
482107120Sjulian	} __attribute__ ((packed))	*req = NULL;
483107120Sjulian	ng_hci_lp_discon_req_ep		*ep = NULL;
484107120Sjulian	ng_hci_unit_con_p		 con = NULL;
485107120Sjulian	struct mbuf			*m = NULL;
486107120Sjulian	int				 error = 0;
487107120Sjulian
488107120Sjulian	/* Check if unit is ready */
489107120Sjulian	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
490107120Sjulian		NG_HCI_WARN(
491107120Sjulian"%s: %s - unit is not ready, state=%#x\n",
492107120Sjulian			__func__, NG_NODE_NAME(unit->node), unit->state);
493107120Sjulian
494107120Sjulian		error = ENXIO;
495107120Sjulian		goto out;
496107120Sjulian	}
497107120Sjulian
498107120Sjulian	if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
499107120Sjulian		NG_HCI_ALERT(
500107120Sjulian"%s: %s - invalid LP_DisconnectReq message size=%d\n",
501107120Sjulian			__func__, NG_NODE_NAME(unit->node),
502107120Sjulian			NGI_MSG(item)->header.arglen);
503107120Sjulian
504107120Sjulian		error = EMSGSIZE;
505107120Sjulian		goto out;
506107120Sjulian	}
507107120Sjulian
508107120Sjulian	ep = (ng_hci_lp_discon_req_ep *)(NGI_MSG(item)->data);
509107120Sjulian
510107120Sjulian	con = ng_hci_con_by_handle(unit, ep->con_handle);
511107120Sjulian	if (con == NULL) {
512107120Sjulian		NG_HCI_ERR(
513107120Sjulian"%s: %s - invalid connection handle=%d\n",
514107120Sjulian			__func__, NG_NODE_NAME(unit->node), ep->con_handle);
515107120Sjulian
516107120Sjulian		error = ENOENT;
517107120Sjulian		goto out;
518107120Sjulian	}
519107120Sjulian
520107120Sjulian	if (con->state != NG_HCI_CON_OPEN) {
521107120Sjulian		NG_HCI_ERR(
522107120Sjulian"%s: %s - invalid connection state=%d, handle=%d\n",
523107120Sjulian			__func__, NG_NODE_NAME(unit->node), con->state,
524107120Sjulian			ep->con_handle);
525107120Sjulian
526107120Sjulian		error = EINVAL;
527107120Sjulian		goto out;
528107120Sjulian	}
529107120Sjulian
530107120Sjulian	/*
531107120Sjulian	 * Create HCI command
532107120Sjulian	 */
533107120Sjulian
534243882Sglebius	MGETHDR(m, M_NOWAIT, MT_DATA);
535107120Sjulian	if (m == NULL) {
536107120Sjulian		error = ENOBUFS;
537107120Sjulian		goto out;
538107120Sjulian	}
539107120Sjulian
540107120Sjulian	m->m_pkthdr.len = m->m_len = sizeof(*req);
541107120Sjulian	req = mtod(m, struct discon_req *);
542107120Sjulian	req->hdr.type = NG_HCI_CMD_PKT;
543107120Sjulian	req->hdr.length = sizeof(req->cp);
544107120Sjulian	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
545107120Sjulian							NG_HCI_OCF_DISCON));
546107120Sjulian
547107120Sjulian	req->cp.con_handle = htole16(ep->con_handle);
548107120Sjulian	req->cp.reason = ep->reason;
549107120Sjulian
550107120Sjulian	/*
551107120Sjulian	 * Queue and send HCI command
552107120Sjulian	 */
553107120Sjulian
554107120Sjulian	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
555107120Sjulian	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
556107120Sjulian		error = ng_hci_send_command(unit);
557107120Sjulianout:
558107120Sjulian	NG_FREE_ITEM(item);
559107120Sjulian
560107120Sjulian	return (error);
561107120Sjulian} /* ng_hci_lp_discon_req */
562107120Sjulian
563107120Sjulian/*
564107120Sjulian * Send LP_ConnectCfm event to the upper layer protocol
565107120Sjulian */
566107120Sjulian
567107120Sjulianint
568107120Sjulianng_hci_lp_con_cfm(ng_hci_unit_con_p con, int status)
569107120Sjulian{
570107120Sjulian	ng_hci_unit_p		 unit = con->unit;
571107120Sjulian	struct ng_mesg		*msg = NULL;
572107120Sjulian	ng_hci_lp_con_cfm_ep	*ep = NULL;
573107120Sjulian	int			 error;
574107120Sjulian
575107120Sjulian	/*
576107120Sjulian	 * Check who wants to be notified. For ACL links both ACL and SCO
577107120Sjulian	 * upstream hooks will be notified (if required). For SCO links
578107120Sjulian	 * only SCO upstream hook will receive notification
579107120Sjulian	 */
580107120Sjulian
581107120Sjulian	if (con->link_type == NG_HCI_LINK_ACL &&
582107120Sjulian	    con->flags & NG_HCI_CON_NOTIFY_ACL) {
583107120Sjulian		if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
584107120Sjulian			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM,
585107120Sjulian				sizeof(*ep), M_NOWAIT);
586107120Sjulian			if (msg != NULL) {
587107120Sjulian				ep = (ng_hci_lp_con_cfm_ep *) msg->data;
588107120Sjulian				ep->status = status;
589107120Sjulian				ep->link_type = con->link_type;
590107120Sjulian				ep->con_handle = con->con_handle;
591107120Sjulian				bcopy(&con->bdaddr, &ep->bdaddr,
592107120Sjulian					sizeof(ep->bdaddr));
593107120Sjulian
594107120Sjulian				NG_SEND_MSG_HOOK(error, unit->node, msg,
595128076Semax					unit->acl, 0);
596107120Sjulian			}
597107120Sjulian		} else
598107120Sjulian			NG_HCI_INFO(
599107120Sjulian"%s: %s - ACL hook not valid, hook=%p\n",
600107120Sjulian				__func__, NG_NODE_NAME(unit->node), unit->acl);
601107120Sjulian
602107120Sjulian		con->flags &= ~NG_HCI_CON_NOTIFY_ACL;
603107120Sjulian	}
604107120Sjulian
605107120Sjulian	if (con->flags & NG_HCI_CON_NOTIFY_SCO) {
606107120Sjulian		if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
607107120Sjulian			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_CFM,
608107120Sjulian				sizeof(*ep), M_NOWAIT);
609107120Sjulian			if (msg != NULL) {
610107120Sjulian				ep = (ng_hci_lp_con_cfm_ep *) msg->data;
611107120Sjulian				ep->status = status;
612107120Sjulian				ep->link_type = con->link_type;
613107120Sjulian				ep->con_handle = con->con_handle;
614107120Sjulian				bcopy(&con->bdaddr, &ep->bdaddr,
615107120Sjulian					sizeof(ep->bdaddr));
616107120Sjulian
617107120Sjulian				NG_SEND_MSG_HOOK(error, unit->node, msg,
618128076Semax					unit->sco, 0);
619107120Sjulian			}
620107120Sjulian		} else
621107120Sjulian			NG_HCI_INFO(
622107120Sjulian"%s: %s - SCO hook not valid, hook=%p\n",
623107120Sjulian				__func__, NG_NODE_NAME(unit->node), unit->acl);
624107120Sjulian
625107120Sjulian		con->flags &= ~NG_HCI_CON_NOTIFY_SCO;
626107120Sjulian	}
627107120Sjulian
628107120Sjulian	return (0);
629107120Sjulian} /* ng_hci_lp_con_cfm */
630107120Sjulian
631107120Sjulian/*
632107120Sjulian * Send LP_ConnectInd event to the upper layer protocol
633107120Sjulian */
634107120Sjulian
635107120Sjulianint
636107120Sjulianng_hci_lp_con_ind(ng_hci_unit_con_p con, u_int8_t *uclass)
637107120Sjulian{
638107120Sjulian	ng_hci_unit_p		 unit = con->unit;
639107120Sjulian	struct ng_mesg		*msg = NULL;
640107120Sjulian	ng_hci_lp_con_ind_ep	*ep = NULL;
641107120Sjulian	hook_p			 hook = NULL;
642107120Sjulian	int			 error = 0;
643107120Sjulian
644107120Sjulian	/*
645107120Sjulian	 * Connection_Request event is generated for specific link type.
646107120Sjulian	 * Use link_type to select upstream hook.
647107120Sjulian	 */
648107120Sjulian
649107120Sjulian	if (con->link_type == NG_HCI_LINK_ACL)
650107120Sjulian		hook = unit->acl;
651107120Sjulian	else
652107120Sjulian		hook = unit->sco;
653107120Sjulian
654107120Sjulian	if (hook != NULL && NG_HOOK_IS_VALID(hook)) {
655107120Sjulian		NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_CON_IND,
656107120Sjulian			sizeof(*ep), M_NOWAIT);
657107120Sjulian		if (msg == NULL)
658107120Sjulian			return (ENOMEM);
659107120Sjulian
660107120Sjulian		ep = (ng_hci_lp_con_ind_ep *)(msg->data);
661107120Sjulian		ep->link_type = con->link_type;
662107120Sjulian		bcopy(uclass, ep->uclass, sizeof(ep->uclass));
663107120Sjulian		bcopy(&con->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr));
664107120Sjulian
665128076Semax		NG_SEND_MSG_HOOK(error, unit->node, msg, hook, 0);
666107120Sjulian	} else {
667107120Sjulian		NG_HCI_WARN(
668107120Sjulian"%s: %s - Upstream hook is not connected or not valid, hook=%p\n",
669107120Sjulian			__func__, NG_NODE_NAME(unit->node), hook);
670107120Sjulian
671107120Sjulian		error = ENOTCONN;
672107120Sjulian	}
673107120Sjulian
674107120Sjulian	return (error);
675107120Sjulian} /* ng_hci_lp_con_ind */
676107120Sjulian
677107120Sjulian/*
678107120Sjulian * Process LP_ConnectRsp event from the upper layer protocol
679107120Sjulian */
680107120Sjulian
681107120Sjulianint
682107120Sjulianng_hci_lp_con_rsp(ng_hci_unit_p unit, item_p item, hook_p hook)
683107120Sjulian{
684107120Sjulian	struct con_rsp_req {
685107120Sjulian		ng_hci_cmd_pkt_t		 hdr;
686107120Sjulian		union {
687107120Sjulian			ng_hci_accept_con_cp	 acc;
688107120Sjulian			ng_hci_reject_con_cp	 rej;
689107120Sjulian		} __attribute__ ((packed))	 cp;
690107120Sjulian	} __attribute__ ((packed))		*req = NULL;
691107120Sjulian	ng_hci_lp_con_rsp_ep			*ep = NULL;
692107120Sjulian	ng_hci_unit_con_p			 con = NULL;
693107120Sjulian	struct mbuf				*m = NULL;
694107120Sjulian	int					 error = 0;
695107120Sjulian
696107120Sjulian	/* Check if unit is ready */
697107120Sjulian	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
698107120Sjulian		NG_HCI_WARN(
699107120Sjulian"%s: %s - unit is not ready, state=%#x\n",
700107120Sjulian			__func__, NG_NODE_NAME(unit->node), unit->state);
701107120Sjulian
702107120Sjulian		error = ENXIO;
703107120Sjulian		goto out;
704107120Sjulian	}
705107120Sjulian
706107120Sjulian	if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
707107120Sjulian		NG_HCI_ALERT(
708107120Sjulian"%s: %s - invalid LP_ConnectRsp message size=%d\n",
709107120Sjulian			__func__, NG_NODE_NAME(unit->node),
710107120Sjulian			NGI_MSG(item)->header.arglen);
711107120Sjulian
712107120Sjulian		error = EMSGSIZE;
713107120Sjulian		goto out;
714107120Sjulian	}
715107120Sjulian
716107120Sjulian	ep = (ng_hci_lp_con_rsp_ep *)(NGI_MSG(item)->data);
717107120Sjulian
718107120Sjulian	/*
719107120Sjulian	 * Here we have to deal with race. Upper layers might send conflicting
720107120Sjulian	 * requests. One might send Accept and other Reject. We will not try
721107120Sjulian	 * to solve all the problems, so first request will always win.
722107120Sjulian	 *
723107120Sjulian	 * Try to find connection that matches the following:
724107120Sjulian	 *
725107120Sjulian	 * 1) con->link_type == ep->link_type
726107120Sjulian	 *
727107120Sjulian	 * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP ||
728107120Sjulian	 *    con->state == NG_HCI_CON_W4_CONN_COMPLETE
729107120Sjulian	 *
730107120Sjulian	 * 3) con->bdaddr == ep->bdaddr
731107120Sjulian	 *
732107120Sjulian	 * Two cases:
733107120Sjulian	 *
734107120Sjulian	 * 1) We do not have connection descriptor. Could be bogus request or
735107120Sjulian	 *    we have rejected connection already.
736107120Sjulian	 *
737107120Sjulian	 * 2) We do have connection descriptor. Then we need to check state:
738107120Sjulian	 *
739107120Sjulian	 * 2.1) NG_HCI_CON_W4_LP_CON_RSP means upper layer has requested
740107120Sjulian	 *      connection and it is a first response from the upper layer.
741107120Sjulian	 *      if "status == 0" (Accept) then we will send Accept_Connection
742107120Sjulian	 *      command and change connection state to W4_CONN_COMPLETE, else
743107120Sjulian	 *      send reject and delete connection.
744107120Sjulian	 *
745107120Sjulian	 * 2.2) NG_HCI_CON_W4_CONN_COMPLETE means that we already accepted
746107120Sjulian	 *      connection. If "status == 0" we just need to link request
747107120Sjulian	 *      and wait, else ignore Reject request.
748107120Sjulian	 */
749107120Sjulian
750107120Sjulian	LIST_FOREACH(con, &unit->con_list, next)
751107120Sjulian		if (con->link_type == ep->link_type &&
752107120Sjulian		    (con->state == NG_HCI_CON_W4_LP_CON_RSP ||
753107120Sjulian		     con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
754107120Sjulian		    bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
755107120Sjulian			break;
756107120Sjulian
757107120Sjulian	if (con == NULL) {
758107120Sjulian		/* Reject for non-existing connection is fine */
759107120Sjulian		error = (ep->status == 0)? ENOENT : 0;
760107120Sjulian		goto out;
761107120Sjulian	}
762107120Sjulian
763107120Sjulian	/*
764121054Semax	 * Remove connection timeout and check connection state.
765121054Semax	 * Note: if ng_hci_con_untimeout() fails (returns non-zero value) then
766121054Semax	 * timeout already happened and event went into node's queue.
767107120Sjulian	 */
768107120Sjulian
769121054Semax	if ((error = ng_hci_con_untimeout(con)) != 0)
770121054Semax		goto out;
771107120Sjulian
772107120Sjulian	switch (con->state) {
773107120Sjulian	case NG_HCI_CON_W4_LP_CON_RSP:
774107120Sjulian
775107120Sjulian		/*
776107120Sjulian		 * Create HCI command
777107120Sjulian		 */
778107120Sjulian
779243882Sglebius		MGETHDR(m, M_NOWAIT, MT_DATA);
780107120Sjulian		if (m == NULL) {
781107120Sjulian			error = ENOBUFS;
782107120Sjulian			goto out;
783107120Sjulian		}
784107120Sjulian
785107120Sjulian		req = mtod(m, struct con_rsp_req *);
786107120Sjulian		req->hdr.type = NG_HCI_CMD_PKT;
787107120Sjulian
788107120Sjulian		if (ep->status == 0) {
789107120Sjulian			req->hdr.length = sizeof(req->cp.acc);
790107120Sjulian			req->hdr.opcode = htole16(NG_HCI_OPCODE(
791107120Sjulian							NG_HCI_OGF_LINK_CONTROL,
792107120Sjulian							NG_HCI_OCF_ACCEPT_CON));
793107120Sjulian
794107120Sjulian			bcopy(&ep->bdaddr, &req->cp.acc.bdaddr,
795107120Sjulian				sizeof(req->cp.acc.bdaddr));
796107120Sjulian
797107120Sjulian			/*
798107120Sjulian			 * We are accepting connection, so if we support role
799114878Sjulian			 * switch and role switch was enabled then set role to
800114878Sjulian			 * NG_HCI_ROLE_MASTER and let LM peform role switch.
801114878Sjulian			 * Otherwise we remain slave. In this case LM WILL NOT
802114878Sjulian			 * perform role switch.
803107120Sjulian			 */
804107120Sjulian
805114878Sjulian			if ((unit->features[0] & NG_HCI_LMP_SWITCH) &&
806114878Sjulian			    unit->role_switch)
807107120Sjulian				req->cp.acc.role = NG_HCI_ROLE_MASTER;
808107120Sjulian			else
809107120Sjulian				req->cp.acc.role = NG_HCI_ROLE_SLAVE;
810107120Sjulian
811107120Sjulian			/*
812107120Sjulian			 * Adjust connection state
813107120Sjulian			 */
814107120Sjulian
815107120Sjulian			if (hook == unit->acl)
816107120Sjulian				con->flags |= NG_HCI_CON_NOTIFY_ACL;
817107120Sjulian			else
818107120Sjulian				con->flags |= NG_HCI_CON_NOTIFY_SCO;
819107120Sjulian
820107120Sjulian			con->state = NG_HCI_CON_W4_CONN_COMPLETE;
821107120Sjulian			ng_hci_con_timeout(con);
822107120Sjulian		} else {
823107120Sjulian			req->hdr.length = sizeof(req->cp.rej);
824107120Sjulian			req->hdr.opcode = htole16(NG_HCI_OPCODE(
825107120Sjulian							NG_HCI_OGF_LINK_CONTROL,
826107120Sjulian							NG_HCI_OCF_REJECT_CON));
827107120Sjulian
828107120Sjulian			bcopy(&ep->bdaddr, &req->cp.rej.bdaddr,
829107120Sjulian				sizeof(req->cp.rej.bdaddr));
830107120Sjulian
831107120Sjulian			req->cp.rej.reason = ep->status;
832107120Sjulian
833107120Sjulian			/*
834107120Sjulian			 * Free connection descritor
835107120Sjulian			 * Item will be deleted just before return.
836107120Sjulian			 */
837107120Sjulian
838107120Sjulian			ng_hci_free_con(con);
839107120Sjulian		}
840107120Sjulian
841107120Sjulian		m->m_pkthdr.len = m->m_len = sizeof(req->hdr) + req->hdr.length;
842107120Sjulian
843107120Sjulian		/* Queue and send HCI command */
844107120Sjulian		NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
845107120Sjulian		if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
846107120Sjulian			error = ng_hci_send_command(unit);
847107120Sjulian		break;
848107120Sjulian
849107120Sjulian	case NG_HCI_CON_W4_CONN_COMPLETE:
850107120Sjulian		if (ep->status == 0) {
851107120Sjulian			if (hook == unit->acl)
852107120Sjulian				con->flags |= NG_HCI_CON_NOTIFY_ACL;
853107120Sjulian			else
854107120Sjulian				con->flags |= NG_HCI_CON_NOTIFY_SCO;
855107120Sjulian		} else
856107120Sjulian			error = EPERM;
857107120Sjulian		break;
858107120Sjulian
859107120Sjulian	default:
860121054Semax		panic(
861121054Semax"%s: %s - Invalid connection state=%d\n",
862121054Semax			__func__, NG_NODE_NAME(unit->node), con->state);
863107120Sjulian		break;
864107120Sjulian	}
865107120Sjulianout:
866107120Sjulian	NG_FREE_ITEM(item);
867107120Sjulian
868107120Sjulian	return (error);
869107120Sjulian} /* ng_hci_lp_con_rsp */
870107120Sjulian
871107120Sjulian/*
872107120Sjulian * Send LP_DisconnectInd to the upper layer protocol
873107120Sjulian */
874107120Sjulian
875107120Sjulianint
876107120Sjulianng_hci_lp_discon_ind(ng_hci_unit_con_p con, int reason)
877107120Sjulian{
878107120Sjulian	ng_hci_unit_p		 unit = con->unit;
879107120Sjulian	struct ng_mesg		*msg = NULL;
880107120Sjulian	ng_hci_lp_discon_ind_ep	*ep = NULL;
881107120Sjulian	int			 error = 0;
882107120Sjulian
883107120Sjulian	/*
884107120Sjulian	 * Disconnect_Complete event is generated for specific connection
885107120Sjulian	 * handle. For ACL connection handles both ACL and SCO upstream
886107120Sjulian	 * hooks will receive notification. For SCO connection handles
887107120Sjulian	 * only SCO upstream hook will receive notification.
888107120Sjulian	 */
889107120Sjulian
890107120Sjulian	if (con->link_type == NG_HCI_LINK_ACL) {
891107120Sjulian		if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
892107120Sjulian			NG_MKMESSAGE(msg, NGM_HCI_COOKIE,
893107120Sjulian				NGM_HCI_LP_DISCON_IND, sizeof(*ep), M_NOWAIT);
894107120Sjulian			if (msg == NULL)
895107120Sjulian				return (ENOMEM);
896107120Sjulian
897107120Sjulian			ep = (ng_hci_lp_discon_ind_ep *) msg->data;
898107120Sjulian			ep->reason = reason;
899107120Sjulian			ep->link_type = con->link_type;
900107120Sjulian			ep->con_handle = con->con_handle;
901107120Sjulian
902128076Semax			NG_SEND_MSG_HOOK(error,unit->node,msg,unit->acl,0);
903107120Sjulian		} else
904107120Sjulian			NG_HCI_INFO(
905107120Sjulian"%s: %s - ACL hook is not connected or not valid, hook=%p\n",
906107120Sjulian				__func__, NG_NODE_NAME(unit->node), unit->acl);
907107120Sjulian	}
908107120Sjulian
909107120Sjulian	if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
910107120Sjulian		NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_DISCON_IND,
911107120Sjulian			sizeof(*ep), M_NOWAIT);
912107120Sjulian		if (msg == NULL)
913107120Sjulian			return (ENOMEM);
914107120Sjulian
915107120Sjulian		ep = (ng_hci_lp_discon_ind_ep *) msg->data;
916107120Sjulian		ep->reason = reason;
917107120Sjulian		ep->link_type = con->link_type;
918107120Sjulian		ep->con_handle = con->con_handle;
919107120Sjulian
920128076Semax		NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, 0);
921107120Sjulian	} else
922107120Sjulian		NG_HCI_INFO(
923107120Sjulian"%s: %s - SCO hook is not connected or not valid, hook=%p\n",
924107120Sjulian			__func__, NG_NODE_NAME(unit->node), unit->sco);
925107120Sjulian
926107120Sjulian	return (0);
927107120Sjulian} /* ng_hci_lp_discon_ind */
928107120Sjulian
929107120Sjulian/*
930107120Sjulian * Process LP_QoSReq action from the upper layer protocol
931107120Sjulian */
932107120Sjulian
933107120Sjulianint
934107120Sjulianng_hci_lp_qos_req(ng_hci_unit_p unit, item_p item, hook_p hook)
935107120Sjulian{
936107120Sjulian	struct qos_setup_req {
937107120Sjulian		ng_hci_cmd_pkt_t	 hdr;
938107120Sjulian		ng_hci_qos_setup_cp	 cp;
939107120Sjulian	} __attribute__ ((packed))	*req = NULL;
940107120Sjulian	ng_hci_lp_qos_req_ep		*ep = NULL;
941107120Sjulian	ng_hci_unit_con_p		 con = NULL;
942107120Sjulian	struct mbuf			*m = NULL;
943107120Sjulian	int				 error = 0;
944107120Sjulian
945107120Sjulian	/* Check if unit is ready */
946107120Sjulian	if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) {
947107120Sjulian		NG_HCI_WARN(
948107120Sjulian"%s: %s - unit is not ready, state=%#x\n",
949107120Sjulian			__func__, NG_NODE_NAME(unit->node), unit->state);
950107120Sjulian
951107120Sjulian		error = ENXIO;
952107120Sjulian		goto out;
953107120Sjulian	}
954107120Sjulian
955107120Sjulian	if (NGI_MSG(item)->header.arglen != sizeof(*ep)) {
956107120Sjulian		NG_HCI_ALERT(
957107120Sjulian"%s: %s - invalid LP_QoSSetupReq message size=%d\n",
958107120Sjulian			__func__, NG_NODE_NAME(unit->node),
959107120Sjulian			NGI_MSG(item)->header.arglen);
960107120Sjulian
961107120Sjulian		error = EMSGSIZE;
962107120Sjulian		goto out;
963107120Sjulian	}
964107120Sjulian
965107120Sjulian	ep = (ng_hci_lp_qos_req_ep *)(NGI_MSG(item)->data);
966107120Sjulian
967107120Sjulian	con = ng_hci_con_by_handle(unit, ep->con_handle);
968107120Sjulian	if (con == NULL) {
969107120Sjulian		NG_HCI_ERR(
970107120Sjulian"%s: %s - invalid connection handle=%d\n",
971107120Sjulian			__func__, NG_NODE_NAME(unit->node), ep->con_handle);
972107120Sjulian
973107120Sjulian		error = EINVAL;
974107120Sjulian		goto out;
975107120Sjulian	}
976107120Sjulian
977107120Sjulian	if (con->link_type != NG_HCI_LINK_ACL) {
978107120Sjulian		NG_HCI_ERR("%s: %s - invalid link type=%d\n",
979107120Sjulian			__func__, NG_NODE_NAME(unit->node), con->link_type);
980107120Sjulian
981107120Sjulian		error = EINVAL;
982107120Sjulian		goto out;
983107120Sjulian	}
984107120Sjulian
985107120Sjulian	if (con->state != NG_HCI_CON_OPEN) {
986107120Sjulian		NG_HCI_ERR(
987107120Sjulian"%s: %s - invalid connection state=%d, handle=%d\n",
988107120Sjulian			__func__, NG_NODE_NAME(unit->node), con->state,
989107120Sjulian			con->con_handle);
990107120Sjulian
991107120Sjulian		error = EINVAL;
992107120Sjulian		goto out;
993107120Sjulian	}
994107120Sjulian
995107120Sjulian	/*
996107120Sjulian	 * Create HCI command
997107120Sjulian	 */
998107120Sjulian
999243882Sglebius	MGETHDR(m, M_NOWAIT, MT_DATA);
1000107120Sjulian	if (m == NULL) {
1001107120Sjulian		error = ENOBUFS;
1002107120Sjulian		goto out;
1003107120Sjulian	}
1004107120Sjulian
1005107120Sjulian	m->m_pkthdr.len = m->m_len = sizeof(*req);
1006107120Sjulian	req = mtod(m, struct qos_setup_req *);
1007107120Sjulian	req->hdr.type = NG_HCI_CMD_PKT;
1008107120Sjulian	req->hdr.length = sizeof(req->cp);
1009107120Sjulian	req->hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY,
1010107120Sjulian			NG_HCI_OCF_QOS_SETUP));
1011107120Sjulian
1012107120Sjulian	req->cp.con_handle = htole16(ep->con_handle);
1013107120Sjulian	req->cp.flags = ep->flags;
1014107120Sjulian	req->cp.service_type = ep->service_type;
1015107120Sjulian	req->cp.token_rate = htole32(ep->token_rate);
1016107120Sjulian	req->cp.peak_bandwidth = htole32(ep->peak_bandwidth);
1017107120Sjulian	req->cp.latency = htole32(ep->latency);
1018107120Sjulian	req->cp.delay_variation = htole32(ep->delay_variation);
1019107120Sjulian
1020107120Sjulian	/*
1021107120Sjulian	 * Adjust connection state
1022107120Sjulian 	 */
1023107120Sjulian
1024107120Sjulian	if (hook == unit->acl)
1025107120Sjulian		con->flags |= NG_HCI_CON_NOTIFY_ACL;
1026107120Sjulian	else
1027107120Sjulian		con->flags |= NG_HCI_CON_NOTIFY_SCO;
1028107120Sjulian
1029107120Sjulian	/*
1030107120Sjulian	 * Queue and send HCI command
1031107120Sjulian	 */
1032107120Sjulian
1033107120Sjulian	NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
1034107120Sjulian	if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
1035107120Sjulian		error = ng_hci_send_command(unit);
1036107120Sjulianout:
1037107120Sjulian	NG_FREE_ITEM(item);
1038107120Sjulian
1039107120Sjulian	return (error);
1040107120Sjulian} /* ng_hci_lp_qos_req */
1041107120Sjulian
1042107120Sjulian/*
1043107120Sjulian * Send LP_QoSCfm event to the upper layer protocol
1044107120Sjulian */
1045107120Sjulian
1046107120Sjulianint
1047107120Sjulianng_hci_lp_qos_cfm(ng_hci_unit_con_p con, int status)
1048107120Sjulian{
1049107120Sjulian	ng_hci_unit_p		 unit = con->unit;
1050107120Sjulian	struct ng_mesg		*msg = NULL;
1051107120Sjulian	ng_hci_lp_qos_cfm_ep	*ep = NULL;
1052107120Sjulian	int			 error;
1053107120Sjulian
1054107120Sjulian	if (con->flags & NG_HCI_CON_NOTIFY_ACL) {
1055107120Sjulian		if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
1056107120Sjulian			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM,
1057107120Sjulian				sizeof(*ep), M_NOWAIT);
1058107120Sjulian			if (msg != NULL) {
1059107120Sjulian				ep = (ng_hci_lp_qos_cfm_ep *) msg->data;
1060107120Sjulian				ep->status = status;
1061107120Sjulian				ep->con_handle = con->con_handle;
1062107120Sjulian
1063107120Sjulian				NG_SEND_MSG_HOOK(error, unit->node, msg,
1064128076Semax					unit->acl, 0);
1065107120Sjulian			}
1066107120Sjulian		} else
1067107120Sjulian			NG_HCI_INFO(
1068107120Sjulian"%s: %s - ACL hook not valid, hook=%p\n",
1069107120Sjulian				__func__, NG_NODE_NAME(unit->node), unit->acl);
1070107120Sjulian
1071107120Sjulian		con->flags &= ~NG_HCI_CON_NOTIFY_ACL;
1072107120Sjulian	}
1073107120Sjulian
1074107120Sjulian	if (con->flags & NG_HCI_CON_NOTIFY_SCO) {
1075107120Sjulian		if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
1076107120Sjulian			NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_CFM,
1077107120Sjulian				sizeof(*ep), M_NOWAIT);
1078107120Sjulian			if (msg != NULL) {
1079107120Sjulian				ep = (ng_hci_lp_qos_cfm_ep *) msg->data;
1080107120Sjulian				ep->status = status;
1081107120Sjulian				ep->con_handle = con->con_handle;
1082107120Sjulian
1083107120Sjulian				NG_SEND_MSG_HOOK(error, unit->node, msg,
1084128076Semax					unit->sco, 0);
1085107120Sjulian			}
1086107120Sjulian		} else
1087107120Sjulian			NG_HCI_INFO(
1088107120Sjulian"%s: %s - SCO hook not valid, hook=%p\n",
1089107120Sjulian				 __func__, NG_NODE_NAME(unit->node), unit->sco);
1090107120Sjulian
1091107120Sjulian		con->flags &= ~NG_HCI_CON_NOTIFY_SCO;
1092107120Sjulian	}
1093107120Sjulian
1094107120Sjulian	return (0);
1095107120Sjulian} /* ng_hci_lp_qos_cfm */
1096107120Sjulian
1097107120Sjulian/*
1098107120Sjulian * Send LP_QoSViolationInd event to the upper layer protocol
1099107120Sjulian */
1100107120Sjulian
1101107120Sjulianint
1102107120Sjulianng_hci_lp_qos_ind(ng_hci_unit_con_p con)
1103107120Sjulian{
1104107120Sjulian	ng_hci_unit_p		 unit = con->unit;
1105107120Sjulian	struct ng_mesg		*msg = NULL;
1106107120Sjulian	ng_hci_lp_qos_ind_ep	*ep = NULL;
1107107120Sjulian	int			 error;
1108107120Sjulian
1109107120Sjulian	/*
1110107120Sjulian	 * QoS Violation can only be generated for ACL connection handles.
1111107120Sjulian	 * Both ACL and SCO upstream hooks will receive notification.
1112107120Sjulian	 */
1113107120Sjulian
1114107120Sjulian	if (unit->acl != NULL && NG_HOOK_IS_VALID(unit->acl)) {
1115107120Sjulian		NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND,
1116107120Sjulian			sizeof(*ep), M_NOWAIT);
1117107120Sjulian		if (msg == NULL)
1118107120Sjulian			return (ENOMEM);
1119107120Sjulian
1120107120Sjulian		ep = (ng_hci_lp_qos_ind_ep *) msg->data;
1121107120Sjulian		ep->con_handle = con->con_handle;
1122107120Sjulian
1123128076Semax		NG_SEND_MSG_HOOK(error, unit->node, msg, unit->acl, 0);
1124107120Sjulian	} else
1125107120Sjulian		NG_HCI_INFO(
1126107120Sjulian"%s: %s - ACL hook is not connected or not valid, hook=%p\n",
1127107120Sjulian			__func__, NG_NODE_NAME(unit->node), unit->acl);
1128107120Sjulian
1129107120Sjulian	if (unit->sco != NULL && NG_HOOK_IS_VALID(unit->sco)) {
1130107120Sjulian		NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_LP_QOS_IND,
1131107120Sjulian			sizeof(*ep), M_NOWAIT);
1132107120Sjulian		if (msg == NULL)
1133107120Sjulian			return (ENOMEM);
1134107120Sjulian
1135107120Sjulian		ep = (ng_hci_lp_qos_ind_ep *) msg->data;
1136107120Sjulian		ep->con_handle = con->con_handle;
1137107120Sjulian
1138128076Semax		NG_SEND_MSG_HOOK(error, unit->node, msg, unit->sco, 0);
1139107120Sjulian	} else
1140107120Sjulian		NG_HCI_INFO(
1141107120Sjulian"%s: %s - SCO hook is not connected or not valid, hook=%p\n",
1142107120Sjulian			__func__, NG_NODE_NAME(unit->node), unit->sco);
1143107120Sjulian
1144107120Sjulian	return (0);
1145107120Sjulian} /* ng_hci_lp_qos_ind */
1146107120Sjulian
1147107120Sjulian/*
1148107120Sjulian * Process connection timeout
1149107120Sjulian */
1150107120Sjulian
1151107120Sjulianvoid
1152121054Semaxng_hci_process_con_timeout(node_p node, hook_p hook, void *arg1, int con_handle)
1153107120Sjulian{
1154121054Semax	ng_hci_unit_p		unit = NULL;
1155121054Semax	ng_hci_unit_con_p	con = NULL;
1156107120Sjulian
1157121054Semax	if (NG_NODE_NOT_VALID(node)) {
1158121054Semax		printf("%s: Netgraph node is not valid\n", __func__);
1159121054Semax		return;
1160121054Semax	}
1161107120Sjulian
1162121054Semax	unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
1163121054Semax	con = ng_hci_con_by_handle(unit, con_handle);
1164121054Semax
1165121054Semax	if (con == NULL) {
1166121054Semax		NG_HCI_ALERT(
1167121054Semax"%s: %s - could not find connection, handle=%d\n",
1168121054Semax			__func__, NG_NODE_NAME(node), con_handle);
1169121054Semax		return;
1170121054Semax	}
1171121054Semax
1172121054Semax	if (!(con->flags & NG_HCI_CON_TIMEOUT_PENDING)) {
1173121054Semax		NG_HCI_ALERT(
1174121054Semax"%s: %s - no pending connection timeout, handle=%d, state=%d, flags=%#x\n",
1175121054Semax			__func__, NG_NODE_NAME(node), con_handle, con->state,
1176121054Semax			con->flags);
1177121054Semax		return;
1178121054Semax	}
1179121054Semax
1180107120Sjulian	con->flags &= ~NG_HCI_CON_TIMEOUT_PENDING;
1181107120Sjulian
1182107120Sjulian	/*
1183107120Sjulian	 * We expect to receive connection timeout in one of the following
1184107120Sjulian	 * states:
1185107120Sjulian	 *
1186114878Sjulian	 * 1) NG_HCI_CON_W4_LP_CON_RSP means that upper layer has not responded
1187107120Sjulian	 *    to our LP_CON_IND. Do nothing and destroy connection. Remote peer
1188107120Sjulian	 *    most likely already gave up on us.
1189107120Sjulian	 *
1190114878Sjulian	 * 2) NG_HCI_CON_W4_CONN_COMPLETE means upper layer requested connection
1191107120Sjulian	 *    (or we in the process of accepting it) and baseband has timedout
1192107120Sjulian	 *    on us. Inform upper layers and send LP_CON_CFM.
1193107120Sjulian	 */
1194107120Sjulian
1195107120Sjulian	switch (con->state) {
1196107120Sjulian	case NG_HCI_CON_W4_LP_CON_RSP:
1197107120Sjulian		break;
1198107120Sjulian
1199107120Sjulian	case NG_HCI_CON_W4_CONN_COMPLETE:
1200107120Sjulian		ng_hci_lp_con_cfm(con, 0xee);
1201107120Sjulian		break;
1202107120Sjulian
1203107120Sjulian	default:
1204121054Semax		panic(
1205121054Semax"%s: %s - Invalid connection state=%d\n",
1206121054Semax			__func__, NG_NODE_NAME(node), con->state);
1207107120Sjulian		break;
1208107120Sjulian	}
1209107120Sjulian
1210107120Sjulian	ng_hci_free_con(con);
1211107120Sjulian} /* ng_hci_process_con_timeout */
1212107120Sjulian
1213