1107120Sjulian/*
2107120Sjulian * ng_hci_evnt.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_evnt.c,v 1.6 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 **                     HCI event processing module
54107120Sjulian ******************************************************************************
55107120Sjulian ******************************************************************************/
56107120Sjulian
57107120Sjulian/*
58107120Sjulian * Event processing routines
59107120Sjulian */
60107120Sjulian
61107120Sjulianstatic int inquiry_result             (ng_hci_unit_p, struct mbuf *);
62107120Sjulianstatic int con_compl                  (ng_hci_unit_p, struct mbuf *);
63107120Sjulianstatic int con_req                    (ng_hci_unit_p, struct mbuf *);
64107120Sjulianstatic int discon_compl               (ng_hci_unit_p, struct mbuf *);
65114878Sjulianstatic int encryption_change          (ng_hci_unit_p, struct mbuf *);
66107120Sjulianstatic int read_remote_features_compl (ng_hci_unit_p, struct mbuf *);
67107120Sjulianstatic int qos_setup_compl            (ng_hci_unit_p, struct mbuf *);
68107120Sjulianstatic int hardware_error             (ng_hci_unit_p, struct mbuf *);
69107120Sjulianstatic int role_change                (ng_hci_unit_p, struct mbuf *);
70107120Sjulianstatic int num_compl_pkts             (ng_hci_unit_p, struct mbuf *);
71107120Sjulianstatic int mode_change                (ng_hci_unit_p, struct mbuf *);
72107120Sjulianstatic int data_buffer_overflow       (ng_hci_unit_p, struct mbuf *);
73107120Sjulianstatic int read_clock_offset_compl    (ng_hci_unit_p, struct mbuf *);
74107120Sjulianstatic int qos_violation              (ng_hci_unit_p, struct mbuf *);
75107120Sjulianstatic int page_scan_mode_change      (ng_hci_unit_p, struct mbuf *);
76107120Sjulianstatic int page_scan_rep_mode_change  (ng_hci_unit_p, struct mbuf *);
77107120Sjulianstatic int sync_con_queue             (ng_hci_unit_p, ng_hci_unit_con_p, int);
78107120Sjulianstatic int send_data_packets          (ng_hci_unit_p, int, int);
79107120Sjulian
80107120Sjulian/*
81107120Sjulian * Process HCI event packet
82107120Sjulian */
83107120Sjulian
84107120Sjulianint
85107120Sjulianng_hci_process_event(ng_hci_unit_p unit, struct mbuf *event)
86107120Sjulian{
87107120Sjulian	ng_hci_event_pkt_t	*hdr = NULL;
88107120Sjulian	int			 error = 0;
89107120Sjulian
90107120Sjulian	/* Get event packet header */
91107120Sjulian	NG_HCI_M_PULLUP(event, sizeof(*hdr));
92107120Sjulian	if (event == NULL)
93107120Sjulian		return (ENOBUFS);
94107120Sjulian
95107120Sjulian	hdr = mtod(event, ng_hci_event_pkt_t *);
96107120Sjulian
97107120Sjulian	NG_HCI_INFO(
98107120Sjulian"%s: %s - got HCI event=%#x, length=%d\n",
99107120Sjulian		__func__, NG_NODE_NAME(unit->node), hdr->event, hdr->length);
100107120Sjulian
101107120Sjulian	/* Get rid of event header and process event */
102107120Sjulian	m_adj(event, sizeof(*hdr));
103107120Sjulian
104107120Sjulian	switch (hdr->event) {
105107120Sjulian	case NG_HCI_EVENT_INQUIRY_COMPL:
106107120Sjulian	case NG_HCI_EVENT_RETURN_LINK_KEYS:
107107120Sjulian	case NG_HCI_EVENT_PIN_CODE_REQ:
108107120Sjulian	case NG_HCI_EVENT_LINK_KEY_REQ:
109107120Sjulian	case NG_HCI_EVENT_LINK_KEY_NOTIFICATION:
110107120Sjulian	case NG_HCI_EVENT_LOOPBACK_COMMAND:
111107120Sjulian	case NG_HCI_EVENT_AUTH_COMPL:
112107120Sjulian	case NG_HCI_EVENT_CHANGE_CON_LINK_KEY_COMPL:
113107120Sjulian	case NG_HCI_EVENT_MASTER_LINK_KEY_COMPL:
114107120Sjulian	case NG_HCI_EVENT_FLUSH_OCCUR:	/* XXX Do we have to handle it? */
115107120Sjulian	case NG_HCI_EVENT_MAX_SLOT_CHANGE:
116107120Sjulian	case NG_HCI_EVENT_CON_PKT_TYPE_CHANGED:
117107120Sjulian	case NG_HCI_EVENT_BT_LOGO:
118107120Sjulian	case NG_HCI_EVENT_VENDOR:
119107120Sjulian	case NG_HCI_EVENT_REMOTE_NAME_REQ_COMPL:
120107120Sjulian	case NG_HCI_EVENT_READ_REMOTE_VER_INFO_COMPL:
121107120Sjulian		/* These do not need post processing */
122107120Sjulian		NG_FREE_M(event);
123107120Sjulian		break;
124107120Sjulian
125107120Sjulian	case NG_HCI_EVENT_INQUIRY_RESULT:
126107120Sjulian		error = inquiry_result(unit, event);
127107120Sjulian		break;
128107120Sjulian
129107120Sjulian	case NG_HCI_EVENT_CON_COMPL:
130107120Sjulian		error = con_compl(unit, event);
131107120Sjulian		break;
132107120Sjulian
133107120Sjulian	case NG_HCI_EVENT_CON_REQ:
134107120Sjulian		error = con_req(unit, event);
135107120Sjulian		break;
136107120Sjulian
137107120Sjulian	case NG_HCI_EVENT_DISCON_COMPL:
138107120Sjulian		error = discon_compl(unit, event);
139107120Sjulian		break;
140107120Sjulian
141114878Sjulian	case NG_HCI_EVENT_ENCRYPTION_CHANGE:
142114878Sjulian		error = encryption_change(unit, event);
143114878Sjulian		break;
144114878Sjulian
145107120Sjulian	case NG_HCI_EVENT_READ_REMOTE_FEATURES_COMPL:
146107120Sjulian		error = read_remote_features_compl(unit, event);
147107120Sjulian		break;
148107120Sjulian
149107120Sjulian	case NG_HCI_EVENT_QOS_SETUP_COMPL:
150107120Sjulian		error = qos_setup_compl(unit, event);
151107120Sjulian		break;
152107120Sjulian
153107120Sjulian	case NG_HCI_EVENT_COMMAND_COMPL:
154107120Sjulian		error = ng_hci_process_command_complete(unit, event);
155107120Sjulian		break;
156107120Sjulian
157107120Sjulian	case NG_HCI_EVENT_COMMAND_STATUS:
158107120Sjulian		error = ng_hci_process_command_status(unit, event);
159107120Sjulian		break;
160107120Sjulian
161107120Sjulian	case NG_HCI_EVENT_HARDWARE_ERROR:
162107120Sjulian		error = hardware_error(unit, event);
163107120Sjulian		break;
164107120Sjulian
165107120Sjulian	case NG_HCI_EVENT_ROLE_CHANGE:
166107120Sjulian		error = role_change(unit, event);
167107120Sjulian		break;
168107120Sjulian
169107120Sjulian	case NG_HCI_EVENT_NUM_COMPL_PKTS:
170107120Sjulian		error = num_compl_pkts(unit, event);
171107120Sjulian		break;
172107120Sjulian
173107120Sjulian	case NG_HCI_EVENT_MODE_CHANGE:
174107120Sjulian		error = mode_change(unit, event);
175107120Sjulian		break;
176107120Sjulian
177107120Sjulian	case NG_HCI_EVENT_DATA_BUFFER_OVERFLOW:
178107120Sjulian		error = data_buffer_overflow(unit, event);
179107120Sjulian		break;
180107120Sjulian
181107120Sjulian	case NG_HCI_EVENT_READ_CLOCK_OFFSET_COMPL:
182107120Sjulian		error = read_clock_offset_compl(unit, event);
183107120Sjulian		break;
184107120Sjulian
185107120Sjulian	case NG_HCI_EVENT_QOS_VIOLATION:
186107120Sjulian		error = qos_violation(unit, event);
187107120Sjulian		break;
188107120Sjulian
189107120Sjulian	case NG_HCI_EVENT_PAGE_SCAN_MODE_CHANGE:
190107120Sjulian		error = page_scan_mode_change(unit, event);
191107120Sjulian		break;
192107120Sjulian
193107120Sjulian	case NG_HCI_EVENT_PAGE_SCAN_REP_MODE_CHANGE:
194107120Sjulian		error = page_scan_rep_mode_change(unit, event);
195107120Sjulian		break;
196107120Sjulian
197107120Sjulian	default:
198107120Sjulian		NG_FREE_M(event);
199107120Sjulian		error = EINVAL;
200107120Sjulian		break;
201107120Sjulian	}
202107120Sjulian
203107120Sjulian	return (error);
204107120Sjulian} /* ng_hci_process_event */
205107120Sjulian
206107120Sjulian/*
207107120Sjulian * Send ACL and/or SCO data to the unit driver
208107120Sjulian */
209107120Sjulian
210107120Sjulianvoid
211107120Sjulianng_hci_send_data(ng_hci_unit_p unit)
212107120Sjulian{
213107120Sjulian	int	count;
214107120Sjulian
215107120Sjulian	/* Send ACL data */
216107120Sjulian	NG_HCI_BUFF_ACL_AVAIL(unit->buffer, count);
217107120Sjulian
218107120Sjulian	NG_HCI_INFO(
219107120Sjulian"%s: %s - sending ACL data packets, count=%d\n",
220107120Sjulian		__func__, NG_NODE_NAME(unit->node), count);
221107120Sjulian
222107120Sjulian	if (count > 0) {
223107120Sjulian		count = send_data_packets(unit, NG_HCI_LINK_ACL, count);
224107120Sjulian		NG_HCI_STAT_ACL_SENT(unit->stat, count);
225107120Sjulian		NG_HCI_BUFF_ACL_USE(unit->buffer, count);
226107120Sjulian	}
227107120Sjulian
228107120Sjulian	/* Send SCO data */
229107120Sjulian	NG_HCI_BUFF_SCO_AVAIL(unit->buffer, count);
230107120Sjulian
231107120Sjulian	NG_HCI_INFO(
232107120Sjulian"%s: %s - sending SCO data packets, count=%d\n",
233107120Sjulian		__func__, NG_NODE_NAME(unit->node), count);
234107120Sjulian
235107120Sjulian	if (count > 0) {
236107120Sjulian		count = send_data_packets(unit, NG_HCI_LINK_SCO, count);
237107120Sjulian		NG_HCI_STAT_SCO_SENT(unit->stat, count);
238107120Sjulian		NG_HCI_BUFF_SCO_USE(unit->buffer, count);
239107120Sjulian	}
240107120Sjulian} /* ng_hci_send_data */
241107120Sjulian
242107120Sjulian/*
243107120Sjulian * Send data packets to the lower layer.
244107120Sjulian */
245107120Sjulian
246107120Sjulianstatic int
247107120Sjuliansend_data_packets(ng_hci_unit_p unit, int link_type, int limit)
248107120Sjulian{
249107120Sjulian	ng_hci_unit_con_p	con = NULL, winner = NULL;
250107120Sjulian	item_p			item = NULL;
251107120Sjulian	int			min_pending, total_sent, sent, error, v;
252107120Sjulian
253107120Sjulian	for (total_sent = 0; limit > 0; ) {
254107120Sjulian		min_pending = 0x0fffffff;
255107120Sjulian		winner = NULL;
256107120Sjulian
257107120Sjulian		/*
258107120Sjulian		 * Find the connection that has has data to send
259107120Sjulian		 * and the smallest number of pending packets
260107120Sjulian		 */
261107120Sjulian
262107120Sjulian		LIST_FOREACH(con, &unit->con_list, next) {
263107120Sjulian			if (con->link_type != link_type)
264107120Sjulian				continue;
265107120Sjulian			if (NG_BT_ITEMQ_LEN(&con->conq) == 0)
266107120Sjulian				continue;
267107120Sjulian
268107120Sjulian			if (con->pending < min_pending) {
269107120Sjulian				winner = con;
270107120Sjulian				min_pending = con->pending;
271107120Sjulian			}
272107120Sjulian		}
273107120Sjulian
274107120Sjulian	        if (winner == NULL)
275107120Sjulian			break;
276107120Sjulian
277107120Sjulian		/*
278107120Sjulian		 * OK, we have a winner now send as much packets as we can
279107120Sjulian		 * Count the number of packets we have sent and then sync
280107120Sjulian		 * winner connection queue.
281107120Sjulian		 */
282107120Sjulian
283107120Sjulian		for (sent = 0; limit > 0; limit --, total_sent ++, sent ++) {
284107120Sjulian			NG_BT_ITEMQ_DEQUEUE(&winner->conq, item);
285107120Sjulian			if (item == NULL)
286107120Sjulian				break;
287107120Sjulian
288107120Sjulian			NG_HCI_INFO(
289107120Sjulian"%s: %s - sending data packet, handle=%d, len=%d\n",
290107120Sjulian				__func__, NG_NODE_NAME(unit->node),
291107120Sjulian				winner->con_handle, NGI_M(item)->m_pkthdr.len);
292107120Sjulian
293107120Sjulian			/* Check if driver hook still there */
294107120Sjulian			v = (unit->drv != NULL && NG_HOOK_IS_VALID(unit->drv));
295107120Sjulian			if (!v || (unit->state & NG_HCI_UNIT_READY) !=
296107120Sjulian					NG_HCI_UNIT_READY) {
297107120Sjulian				NG_HCI_ERR(
298107120Sjulian"%s: %s - could not send data. Hook \"%s\" is %svalid, state=%#x\n",
299107120Sjulian					__func__, NG_NODE_NAME(unit->node),
300107120Sjulian					NG_HCI_HOOK_DRV, ((v)? "" : "not "),
301107120Sjulian					unit->state);
302107120Sjulian
303107120Sjulian				NG_FREE_ITEM(item);
304107120Sjulian				error = ENOTCONN;
305107120Sjulian			} else {
306107120Sjulian				v = NGI_M(item)->m_pkthdr.len;
307107120Sjulian
308107120Sjulian				/* Give packet to raw hook */
309107120Sjulian				ng_hci_mtap(unit, NGI_M(item));
310107120Sjulian
311107120Sjulian				/* ... and forward item to the driver */
312107120Sjulian				NG_FWD_ITEM_HOOK(error, item, unit->drv);
313107120Sjulian			}
314107120Sjulian
315107120Sjulian			if (error != 0) {
316107120Sjulian				NG_HCI_ERR(
317107120Sjulian"%s: %s - could not send data packet, handle=%d, error=%d\n",
318107120Sjulian					__func__, NG_NODE_NAME(unit->node),
319107120Sjulian					winner->con_handle, error);
320107120Sjulian				break;
321107120Sjulian			}
322107120Sjulian
323107120Sjulian			winner->pending ++;
324107120Sjulian			NG_HCI_STAT_BYTES_SENT(unit->stat, v);
325107120Sjulian		}
326107120Sjulian
327107120Sjulian		/*
328107120Sjulian		 * Sync connection queue for the winner
329107120Sjulian		 */
330107120Sjulian
331107120Sjulian		sync_con_queue(unit, winner, sent);
332107120Sjulian	}
333107120Sjulian
334107120Sjulian	return (total_sent);
335107120Sjulian} /* send_data_packets */
336107120Sjulian
337107120Sjulian/*
338107120Sjulian * Send flow control messages to the upper layer
339107120Sjulian */
340107120Sjulian
341107120Sjulianstatic int
342107120Sjuliansync_con_queue(ng_hci_unit_p unit, ng_hci_unit_con_p con, int completed)
343107120Sjulian{
344107120Sjulian	hook_p				 hook = NULL;
345107120Sjulian	struct ng_mesg			*msg = NULL;
346107120Sjulian	ng_hci_sync_con_queue_ep	*state = NULL;
347107120Sjulian	int				 error;
348107120Sjulian
349107120Sjulian	hook = (con->link_type == NG_HCI_LINK_ACL)? unit->acl : unit->sco;
350107120Sjulian	if (hook == NULL || NG_HOOK_NOT_VALID(hook))
351107120Sjulian		return (ENOTCONN);
352107120Sjulian
353107120Sjulian	NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_SYNC_CON_QUEUE,
354107120Sjulian		sizeof(*state), M_NOWAIT);
355107120Sjulian	if (msg == NULL)
356107120Sjulian		return (ENOMEM);
357107120Sjulian
358107120Sjulian	state = (ng_hci_sync_con_queue_ep *)(msg->data);
359107120Sjulian	state->con_handle = con->con_handle;
360107120Sjulian	state->completed = completed;
361107120Sjulian
362128076Semax	NG_SEND_MSG_HOOK(error, unit->node, msg, hook, 0);
363107120Sjulian
364107120Sjulian	return (error);
365107120Sjulian} /* sync_con_queue */
366107120Sjulian
367107120Sjulian/* Inquiry result event */
368107120Sjulianstatic int
369107120Sjulianinquiry_result(ng_hci_unit_p unit, struct mbuf *event)
370107120Sjulian{
371107120Sjulian	ng_hci_inquiry_result_ep	*ep = NULL;
372107120Sjulian	ng_hci_neighbor_p		 n = NULL;
373107120Sjulian	bdaddr_t			 bdaddr;
374107120Sjulian	int				 error = 0;
375107120Sjulian
376107120Sjulian	NG_HCI_M_PULLUP(event, sizeof(*ep));
377107120Sjulian	if (event == NULL)
378107120Sjulian		return (ENOBUFS);
379107120Sjulian
380107120Sjulian	ep = mtod(event, ng_hci_inquiry_result_ep *);
381107120Sjulian	m_adj(event, sizeof(*ep));
382107120Sjulian
383107120Sjulian	for (; ep->num_responses > 0; ep->num_responses --) {
384107120Sjulian		/* Get remote unit address */
385107120Sjulian		m_copydata(event, 0, sizeof(bdaddr), (caddr_t) &bdaddr);
386107120Sjulian		m_adj(event, sizeof(bdaddr));
387107120Sjulian
388107120Sjulian		/* Lookup entry in the cache */
389107120Sjulian		n = ng_hci_get_neighbor(unit, &bdaddr);
390107120Sjulian		if (n == NULL) {
391107120Sjulian			/* Create new entry */
392107120Sjulian			n = ng_hci_new_neighbor(unit);
393107120Sjulian			if (n == NULL) {
394107120Sjulian				error = ENOMEM;
395107120Sjulian				break;
396107120Sjulian			}
397107120Sjulian		} else
398107120Sjulian			getmicrotime(&n->updated);
399107120Sjulian
400107120Sjulian		bcopy(&bdaddr, &n->bdaddr, sizeof(n->bdaddr));
401107120Sjulian
402107120Sjulian		/* XXX call m_pullup here? */
403107120Sjulian
404107120Sjulian		n->page_scan_rep_mode = *mtod(event, u_int8_t *);
405107120Sjulian		m_adj(event, sizeof(u_int8_t));
406107120Sjulian
407107120Sjulian		/* page_scan_period_mode */
408107120Sjulian		m_adj(event, sizeof(u_int8_t));
409107120Sjulian
410107120Sjulian		n->page_scan_mode = *mtod(event, u_int8_t *);
411107120Sjulian		m_adj(event, sizeof(u_int8_t));
412107120Sjulian
413107120Sjulian		/* class */
414107120Sjulian		m_adj(event, NG_HCI_CLASS_SIZE);
415107120Sjulian
416107120Sjulian		/* clock offset */
417107120Sjulian		m_copydata(event, 0, sizeof(n->clock_offset),
418107120Sjulian			(caddr_t) &n->clock_offset);
419107120Sjulian		n->clock_offset = le16toh(n->clock_offset);
420107120Sjulian	}
421107120Sjulian
422107120Sjulian	NG_FREE_M(event);
423107120Sjulian
424107120Sjulian	return (error);
425107120Sjulian} /* inquiry_result */
426107120Sjulian
427107120Sjulian/* Connection complete event */
428107120Sjulianstatic int
429107120Sjuliancon_compl(ng_hci_unit_p unit, struct mbuf *event)
430107120Sjulian{
431107120Sjulian	ng_hci_con_compl_ep	*ep = NULL;
432107120Sjulian	ng_hci_unit_con_p	 con = NULL;
433107120Sjulian	int			 error = 0;
434107120Sjulian
435107120Sjulian	NG_HCI_M_PULLUP(event, sizeof(*ep));
436107120Sjulian	if (event == NULL)
437107120Sjulian		return (ENOBUFS);
438107120Sjulian
439107120Sjulian	ep = mtod(event, ng_hci_con_compl_ep *);
440107120Sjulian
441107120Sjulian	/*
442107120Sjulian	 * Find the first connection descriptor that matches the following:
443107120Sjulian	 *
444107120Sjulian	 * 1) con->link_type == ep->link_type
445107120Sjulian	 * 2) con->state == NG_HCI_CON_W4_CONN_COMPLETE
446107120Sjulian	 * 3) con->bdaddr == ep->bdaddr
447107120Sjulian	 */
448107120Sjulian
449107120Sjulian	LIST_FOREACH(con, &unit->con_list, next)
450107120Sjulian		if (con->link_type == ep->link_type &&
451107120Sjulian		    con->state == NG_HCI_CON_W4_CONN_COMPLETE &&
452107120Sjulian		    bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
453107120Sjulian			break;
454107120Sjulian
455107120Sjulian	/*
456107120Sjulian	 * Two possible cases:
457107120Sjulian	 *
458107120Sjulian	 * 1) We have found connection descriptor. That means upper layer has
459121054Semax	 *    requested this connection via LP_CON_REQ message. In this case
460121054Semax	 *    connection must have timeout set. If ng_hci_con_untimeout() fails
461121054Semax	 *    then timeout message already went into node's queue. In this case
462121054Semax	 *    ignore Connection_Complete event and let timeout deal with it.
463107120Sjulian	 *
464107120Sjulian	 * 2) We do not have connection descriptor. That means upper layer
465107120Sjulian	 *    nas not requested this connection or (less likely) we gave up
466107120Sjulian	 *    on this connection (timeout). The most likely scenario is that
467107120Sjulian	 *    we have received Create_Connection/Add_SCO_Connection command
468107120Sjulian	 *    from the RAW hook
469107120Sjulian	 */
470107120Sjulian
471107120Sjulian	if (con == NULL) {
472107120Sjulian		if (ep->status != 0)
473107120Sjulian			goto out;
474107120Sjulian
475107120Sjulian		con = ng_hci_new_con(unit, ep->link_type);
476107120Sjulian		if (con == NULL) {
477107120Sjulian			error = ENOMEM;
478107120Sjulian			goto out;
479107120Sjulian		}
480107120Sjulian
481107120Sjulian		bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));
482121054Semax	} else if ((error = ng_hci_con_untimeout(con)) != 0)
483121054Semax			goto out;
484107120Sjulian
485107120Sjulian	/*
486107120Sjulian	 * Update connection descriptor and send notification
487107120Sjulian	 * to the upper layers.
488107120Sjulian	 */
489107120Sjulian
490107120Sjulian	con->con_handle = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
491107120Sjulian	con->encryption_mode = ep->encryption_mode;
492107120Sjulian
493107120Sjulian	ng_hci_lp_con_cfm(con, ep->status);
494107120Sjulian
495107120Sjulian	/* Adjust connection state */
496107120Sjulian	if (ep->status != 0)
497107120Sjulian		ng_hci_free_con(con);
498107120Sjulian	else {
499107120Sjulian		con->state = NG_HCI_CON_OPEN;
500107120Sjulian
501107120Sjulian		/*
502107120Sjulian		 * Change link policy for the ACL connections. Enable all
503107120Sjulian		 * supported link modes. Enable Role switch as well if
504107120Sjulian		 * device supports it.
505107120Sjulian		 */
506107120Sjulian
507107120Sjulian		if (ep->link_type == NG_HCI_LINK_ACL) {
508107120Sjulian			struct __link_policy {
509107120Sjulian				ng_hci_cmd_pkt_t			 hdr;
510107120Sjulian				ng_hci_write_link_policy_settings_cp	 cp;
511107120Sjulian			} __attribute__ ((packed))			*lp;
512107120Sjulian			struct mbuf					*m;
513107120Sjulian
514243882Sglebius			MGETHDR(m, M_NOWAIT, MT_DATA);
515107120Sjulian			if (m != NULL) {
516107120Sjulian				m->m_pkthdr.len = m->m_len = sizeof(*lp);
517107120Sjulian				lp = mtod(m, struct __link_policy *);
518107120Sjulian
519107120Sjulian				lp->hdr.type = NG_HCI_CMD_PKT;
520107120Sjulian				lp->hdr.opcode = htole16(NG_HCI_OPCODE(
521107120Sjulian					NG_HCI_OGF_LINK_POLICY,
522107120Sjulian					NG_HCI_OCF_WRITE_LINK_POLICY_SETTINGS));
523107120Sjulian				lp->hdr.length = sizeof(lp->cp);
524107120Sjulian
525107120Sjulian				lp->cp.con_handle = ep->con_handle;
526107120Sjulian
527107120Sjulian				lp->cp.settings = 0;
528114878Sjulian				if ((unit->features[0] & NG_HCI_LMP_SWITCH) &&
529114878Sjulian				    unit->role_switch)
530107120Sjulian					lp->cp.settings |= 0x1;
531107120Sjulian				if (unit->features[0] & NG_HCI_LMP_HOLD_MODE)
532107120Sjulian					lp->cp.settings |= 0x2;
533107120Sjulian				if (unit->features[0] & NG_HCI_LMP_SNIFF_MODE)
534107120Sjulian					lp->cp.settings |= 0x4;
535107120Sjulian				if (unit->features[1] & NG_HCI_LMP_PARK_MODE)
536107120Sjulian					lp->cp.settings |= 0x8;
537107120Sjulian
538107120Sjulian				lp->cp.settings &= unit->link_policy_mask;
539107120Sjulian				lp->cp.settings = htole16(lp->cp.settings);
540107120Sjulian
541107120Sjulian				NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
542107120Sjulian				if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
543107120Sjulian					ng_hci_send_command(unit);
544107120Sjulian			}
545107120Sjulian		}
546107120Sjulian	}
547107120Sjulianout:
548107120Sjulian	NG_FREE_M(event);
549107120Sjulian
550107120Sjulian	return (error);
551121054Semax} /* con_compl */
552107120Sjulian
553107120Sjulian/* Connection request event */
554107120Sjulianstatic int
555107120Sjuliancon_req(ng_hci_unit_p unit, struct mbuf *event)
556107120Sjulian{
557107120Sjulian	ng_hci_con_req_ep	*ep = NULL;
558107120Sjulian	ng_hci_unit_con_p	 con = NULL;
559107120Sjulian	int			 error = 0;
560107120Sjulian
561107120Sjulian	NG_HCI_M_PULLUP(event, sizeof(*ep));
562107120Sjulian	if (event == NULL)
563107120Sjulian		return (ENOBUFS);
564107120Sjulian
565107120Sjulian	ep = mtod(event, ng_hci_con_req_ep *);
566107120Sjulian
567107120Sjulian	/*
568107120Sjulian	 * Find the first connection descriptor that matches the following:
569107120Sjulian	 *
570107120Sjulian	 * 1) con->link_type == ep->link_type
571107120Sjulian	 *
572107120Sjulian	 * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP ||
573107120Sjulian	 *    con->state == NG_HCI_CON_W4_CONN_COMPL
574107120Sjulian	 *
575107120Sjulian	 * 3) con->bdaddr == ep->bdaddr
576107120Sjulian	 *
577107120Sjulian	 * Possible cases:
578107120Sjulian	 *
579107120Sjulian	 * 1) We do not have connection descriptor. This is simple. Create
580107120Sjulian	 *    new fresh connection descriptor and send notification to the
581107120Sjulian	 *    appropriate upstream hook (based on link_type).
582107120Sjulian	 *
583107120Sjulian	 * 2) We found connection handle. This is more complicated.
584107120Sjulian	 *
585107120Sjulian	 * 2.1) ACL links
586107120Sjulian	 *
587107120Sjulian	 *      Since only one ACL link can exist between each pair of
588107120Sjulian	 *      units then we have a race. Our upper layer has requested
589107120Sjulian	 *      an ACL connection to the remote unit, but we did not send
590107120Sjulian	 *      command yet. At the same time the remote unit has requested
591107120Sjulian	 *      an ACL connection from us. In this case we will ignore
592107120Sjulian	 *	Connection_Request event. This probably will cause connect
593107120Sjulian	 *      failure	on both units.
594107120Sjulian	 *
595107120Sjulian	 * 2.2) SCO links
596107120Sjulian	 *
597107120Sjulian	 *      The spec on page 45 says :
598107120Sjulian	 *
599107120Sjulian	 *      "The master can support up to three SCO links to the same
600107120Sjulian	 *       slave or to different slaves. A slave can support up to
601107120Sjulian	 *       three SCO links from the same master, or two SCO links if
602107120Sjulian	 *       the links originate from different masters."
603107120Sjulian	 *
604107120Sjulian	 *      The only problem is how to handle multiple SCO links between
605107120Sjulian	 *      matster and slave. For now we will assume that multiple SCO
606107120Sjulian	 *      links MUST be opened one after another.
607107120Sjulian	 */
608107120Sjulian
609107120Sjulian	LIST_FOREACH(con, &unit->con_list, next)
610107120Sjulian		if (con->link_type == ep->link_type &&
611107120Sjulian		    (con->state == NG_HCI_CON_W4_LP_CON_RSP ||
612107120Sjulian		     con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
613107120Sjulian		    bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
614107120Sjulian			break;
615107120Sjulian
616107120Sjulian	if (con == NULL) {
617107120Sjulian		con = ng_hci_new_con(unit, ep->link_type);
618107120Sjulian		if (con != NULL) {
619107120Sjulian			bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));
620107120Sjulian
621107120Sjulian			con->state = NG_HCI_CON_W4_LP_CON_RSP;
622107120Sjulian			ng_hci_con_timeout(con);
623107120Sjulian
624107120Sjulian			error = ng_hci_lp_con_ind(con, ep->uclass);
625121054Semax			if (error != 0) {
626121054Semax				ng_hci_con_untimeout(con);
627107120Sjulian				ng_hci_free_con(con);
628121054Semax			}
629107120Sjulian		} else
630107120Sjulian			error = ENOMEM;
631107120Sjulian	}
632107120Sjulian
633107120Sjulian	NG_FREE_M(event);
634107120Sjulian
635107120Sjulian	return (error);
636107120Sjulian} /* con_req */
637107120Sjulian
638107120Sjulian/* Disconnect complete event */
639107120Sjulianstatic int
640107120Sjuliandiscon_compl(ng_hci_unit_p unit, struct mbuf *event)
641107120Sjulian{
642107120Sjulian	ng_hci_discon_compl_ep	*ep = NULL;
643107120Sjulian	ng_hci_unit_con_p	 con = NULL;
644107120Sjulian	int			 error = 0;
645107120Sjulian	u_int16_t		 h;
646107120Sjulian
647107120Sjulian	NG_HCI_M_PULLUP(event, sizeof(*ep));
648107120Sjulian	if (event == NULL)
649107120Sjulian		return (ENOBUFS);
650107120Sjulian
651107120Sjulian	ep = mtod(event, ng_hci_discon_compl_ep *);
652107120Sjulian
653107120Sjulian	/*
654107120Sjulian	 * XXX
655107120Sjulian	 * Do we have to send notification if ep->status != 0?
656107120Sjulian	 * For now we will send notification for both ACL and SCO connections
657107120Sjulian	 * ONLY if ep->status == 0.
658107120Sjulian	 */
659107120Sjulian
660107120Sjulian	if (ep->status == 0) {
661107120Sjulian		h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
662107120Sjulian		con = ng_hci_con_by_handle(unit, h);
663107120Sjulian		if (con != NULL) {
664107120Sjulian			error = ng_hci_lp_discon_ind(con, ep->reason);
665121054Semax
666121054Semax			/* Remove all timeouts (if any) */
667121054Semax			if (con->flags & NG_HCI_CON_TIMEOUT_PENDING)
668121054Semax				ng_hci_con_untimeout(con);
669121054Semax
670107120Sjulian			ng_hci_free_con(con);
671107120Sjulian		} else {
672107120Sjulian			NG_HCI_ALERT(
673107120Sjulian"%s: %s - invalid connection handle=%d\n",
674107120Sjulian				__func__, NG_NODE_NAME(unit->node), h);
675107120Sjulian			error = ENOENT;
676107120Sjulian		}
677107120Sjulian	}
678107120Sjulian
679107120Sjulian	NG_FREE_M(event);
680107120Sjulian
681107120Sjulian	return (error);
682107120Sjulian} /* discon_compl */
683107120Sjulian
684114878Sjulian/* Encryption change event */
685114878Sjulianstatic int
686114878Sjulianencryption_change(ng_hci_unit_p unit, struct mbuf *event)
687114878Sjulian{
688114878Sjulian	ng_hci_encryption_change_ep	*ep = NULL;
689114878Sjulian	ng_hci_unit_con_p		 con = NULL;
690114878Sjulian	int				 error = 0;
691114878Sjulian
692114878Sjulian	NG_HCI_M_PULLUP(event, sizeof(*ep));
693114878Sjulian	if (event == NULL)
694114878Sjulian		return (ENOBUFS);
695114878Sjulian
696114878Sjulian	ep = mtod(event, ng_hci_encryption_change_ep *);
697114878Sjulian
698114878Sjulian	if (ep->status == 0) {
699114878Sjulian		u_int16_t	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
700114878Sjulian
701114878Sjulian		con = ng_hci_con_by_handle(unit, h);
702114878Sjulian		if (con == NULL) {
703114878Sjulian			NG_HCI_ALERT(
704114878Sjulian"%s: %s - invalid connection handle=%d\n",
705114878Sjulian				__func__, NG_NODE_NAME(unit->node), h);
706114878Sjulian			error = ENOENT;
707114878Sjulian		} else if (con->link_type != NG_HCI_LINK_ACL) {
708114878Sjulian			NG_HCI_ALERT(
709114878Sjulian"%s: %s - invalid link type=%d\n",
710114878Sjulian				__func__, NG_NODE_NAME(unit->node),
711114878Sjulian				con->link_type);
712114878Sjulian			error = EINVAL;
713114878Sjulian		} else if (ep->encryption_enable)
714114878Sjulian			/* XXX is that true? */
715114878Sjulian			con->encryption_mode = NG_HCI_ENCRYPTION_MODE_P2P;
716114878Sjulian		else
717114878Sjulian			con->encryption_mode = NG_HCI_ENCRYPTION_MODE_NONE;
718114878Sjulian	} else
719114878Sjulian		NG_HCI_ERR(
720114878Sjulian"%s: %s - failed to change encryption mode, status=%d\n",
721114878Sjulian			__func__, NG_NODE_NAME(unit->node), ep->status);
722114878Sjulian
723114878Sjulian	NG_FREE_M(event);
724114878Sjulian
725114878Sjulian	return (error);
726114878Sjulian} /* encryption_change */
727114878Sjulian
728107120Sjulian/* Read remote feature complete event */
729107120Sjulianstatic int
730107120Sjulianread_remote_features_compl(ng_hci_unit_p unit, struct mbuf *event)
731107120Sjulian{
732107120Sjulian	ng_hci_read_remote_features_compl_ep	*ep = NULL;
733107120Sjulian	ng_hci_unit_con_p			 con = NULL;
734107120Sjulian	ng_hci_neighbor_p			 n = NULL;
735107120Sjulian	u_int16_t				 h;
736107120Sjulian	int					 error = 0;
737107120Sjulian
738107120Sjulian	NG_HCI_M_PULLUP(event, sizeof(*ep));
739107120Sjulian	if (event == NULL)
740107120Sjulian		return (ENOBUFS);
741107120Sjulian
742107120Sjulian	ep = mtod(event, ng_hci_read_remote_features_compl_ep *);
743107120Sjulian
744107120Sjulian	if (ep->status == 0) {
745107120Sjulian		/* Check if we have this connection handle */
746107120Sjulian		h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
747107120Sjulian		con = ng_hci_con_by_handle(unit, h);
748107120Sjulian		if (con == NULL) {
749107120Sjulian			NG_HCI_ALERT(
750107120Sjulian"%s: %s - invalid connection handle=%d\n",
751107120Sjulian				__func__, NG_NODE_NAME(unit->node), h);
752107120Sjulian			error = ENOENT;
753107120Sjulian			goto out;
754107120Sjulian		}
755107120Sjulian
756107120Sjulian		/* Update cache entry */
757107120Sjulian		n = ng_hci_get_neighbor(unit, &con->bdaddr);
758107120Sjulian		if (n == NULL) {
759107120Sjulian			n = ng_hci_new_neighbor(unit);
760107120Sjulian			if (n == NULL) {
761107120Sjulian				error = ENOMEM;
762107120Sjulian				goto out;
763107120Sjulian			}
764107120Sjulian
765107120Sjulian			bcopy(&con->bdaddr, &n->bdaddr, sizeof(n->bdaddr));
766107120Sjulian		} else
767107120Sjulian			getmicrotime(&n->updated);
768107120Sjulian
769107120Sjulian		bcopy(ep->features, n->features, sizeof(n->features));
770107120Sjulian	} else
771107120Sjulian		NG_HCI_ERR(
772107120Sjulian"%s: %s - failed to read remote unit features, status=%d\n",
773107120Sjulian			__func__, NG_NODE_NAME(unit->node), ep->status);
774107120Sjulianout:
775107120Sjulian	NG_FREE_M(event);
776107120Sjulian
777107120Sjulian	return (error);
778107120Sjulian} /* read_remote_features_compl */
779107120Sjulian
780107120Sjulian/* QoS setup complete event */
781107120Sjulianstatic int
782107120Sjulianqos_setup_compl(ng_hci_unit_p unit, struct mbuf *event)
783107120Sjulian{
784107120Sjulian	ng_hci_qos_setup_compl_ep	*ep = NULL;
785107120Sjulian	ng_hci_unit_con_p		 con = NULL;
786107120Sjulian	u_int16_t			 h;
787107120Sjulian	int				 error = 0;
788107120Sjulian
789107120Sjulian	NG_HCI_M_PULLUP(event, sizeof(*ep));
790107120Sjulian	if (event == NULL)
791107120Sjulian		return (ENOBUFS);
792107120Sjulian
793107120Sjulian	ep = mtod(event, ng_hci_qos_setup_compl_ep *);
794107120Sjulian
795107120Sjulian	/* Check if we have this connection handle */
796107120Sjulian	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
797107120Sjulian	con = ng_hci_con_by_handle(unit, h);
798107120Sjulian	if (con == NULL) {
799107120Sjulian		NG_HCI_ALERT(
800107120Sjulian"%s: %s - invalid connection handle=%d\n",
801107120Sjulian			__func__, NG_NODE_NAME(unit->node), h);
802107120Sjulian		error = ENOENT;
803107120Sjulian	} else if (con->link_type != NG_HCI_LINK_ACL) {
804107120Sjulian		NG_HCI_ALERT(
805107120Sjulian"%s: %s - invalid link type=%d, handle=%d\n",
806107120Sjulian			__func__, NG_NODE_NAME(unit->node), con->link_type, h);
807107120Sjulian		error = EINVAL;
808107120Sjulian	} else if (con->state != NG_HCI_CON_OPEN) {
809107120Sjulian		NG_HCI_ALERT(
810107120Sjulian"%s: %s - invalid connection state=%d, handle=%d\n",
811107120Sjulian			__func__, NG_NODE_NAME(unit->node),
812107120Sjulian			con->state, h);
813107120Sjulian		error = EINVAL;
814107120Sjulian	} else /* Notify upper layer */
815107120Sjulian		error = ng_hci_lp_qos_cfm(con, ep->status);
816107120Sjulian
817107120Sjulian	NG_FREE_M(event);
818107120Sjulian
819107120Sjulian	return (error);
820107120Sjulian} /* qos_setup_compl */
821107120Sjulian
822107120Sjulian/* Hardware error event */
823107120Sjulianstatic int
824107120Sjulianhardware_error(ng_hci_unit_p unit, struct mbuf *event)
825107120Sjulian{
826107120Sjulian	NG_HCI_ALERT(
827107120Sjulian"%s: %s - hardware error %#x\n",
828107120Sjulian		__func__, NG_NODE_NAME(unit->node), *mtod(event, u_int8_t *));
829107120Sjulian
830107120Sjulian	NG_FREE_M(event);
831107120Sjulian
832107120Sjulian	return (0);
833107120Sjulian} /* hardware_error */
834107120Sjulian
835107120Sjulian/* Role change event */
836107120Sjulianstatic int
837107120Sjulianrole_change(ng_hci_unit_p unit, struct mbuf *event)
838107120Sjulian{
839107120Sjulian	ng_hci_role_change_ep	*ep = NULL;
840107120Sjulian	ng_hci_unit_con_p	 con = NULL;
841107120Sjulian
842107120Sjulian	NG_HCI_M_PULLUP(event, sizeof(*ep));
843107120Sjulian	if (event == NULL)
844107120Sjulian		return (ENOBUFS);
845107120Sjulian
846107120Sjulian	ep = mtod(event, ng_hci_role_change_ep *);
847107120Sjulian
848107120Sjulian	if (ep->status == 0) {
849107120Sjulian		/* XXX shoud we also change "role" for SCO connections? */
850107120Sjulian		con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL);
851107120Sjulian		if (con != NULL)
852107120Sjulian			con->role = ep->role;
853107120Sjulian		else
854107120Sjulian			NG_HCI_ALERT(
855107120Sjulian"%s: %s - ACL connection does not exist, bdaddr=%x:%x:%x:%x:%x:%x\n",
856107120Sjulian				__func__, NG_NODE_NAME(unit->node),
857107120Sjulian				ep->bdaddr.b[5], ep->bdaddr.b[4],
858107120Sjulian				ep->bdaddr.b[3], ep->bdaddr.b[2],
859107120Sjulian				ep->bdaddr.b[1], ep->bdaddr.b[0]);
860107120Sjulian	} else
861107120Sjulian		NG_HCI_ERR(
862107120Sjulian"%s: %s - failed to change role, status=%d, bdaddr=%x:%x:%x:%x:%x:%x\n",
863107120Sjulian			__func__, NG_NODE_NAME(unit->node), ep->status,
864107120Sjulian			ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],
865107120Sjulian			ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);
866107120Sjulian
867107120Sjulian	NG_FREE_M(event);
868107120Sjulian
869107120Sjulian	return (0);
870107120Sjulian} /* role_change */
871107120Sjulian
872107120Sjulian/* Number of completed packets event */
873107120Sjulianstatic int
874107120Sjuliannum_compl_pkts(ng_hci_unit_p unit, struct mbuf *event)
875107120Sjulian{
876107120Sjulian	ng_hci_num_compl_pkts_ep	*ep = NULL;
877107120Sjulian	ng_hci_unit_con_p		 con = NULL;
878107120Sjulian	u_int16_t			 h, p;
879107120Sjulian
880107120Sjulian	NG_HCI_M_PULLUP(event, sizeof(*ep));
881107120Sjulian	if (event == NULL)
882107120Sjulian		return (ENOBUFS);
883107120Sjulian
884107120Sjulian	ep = mtod(event, ng_hci_num_compl_pkts_ep *);
885107120Sjulian	m_adj(event, sizeof(*ep));
886107120Sjulian
887107120Sjulian	for (; ep->num_con_handles > 0; ep->num_con_handles --) {
888107120Sjulian		/* Get connection handle */
889107120Sjulian		m_copydata(event, 0, sizeof(h), (caddr_t) &h);
890107120Sjulian		m_adj(event, sizeof(h));
891107120Sjulian		h = NG_HCI_CON_HANDLE(le16toh(h));
892107120Sjulian
893107120Sjulian		/* Get number of completed packets */
894107120Sjulian		m_copydata(event, 0, sizeof(p), (caddr_t) &p);
895107120Sjulian		m_adj(event, sizeof(p));
896107120Sjulian		p = le16toh(p);
897107120Sjulian
898107120Sjulian		/* Check if we have this connection handle */
899107120Sjulian		con = ng_hci_con_by_handle(unit, h);
900107120Sjulian		if (con != NULL) {
901107120Sjulian			con->pending -= p;
902107120Sjulian			if (con->pending < 0) {
903107120Sjulian				NG_HCI_WARN(
904107120Sjulian"%s: %s - pending packet counter is out of sync! " \
905107120Sjulian"handle=%d, pending=%d, ncp=%d\n",	__func__, NG_NODE_NAME(unit->node),
906107120Sjulian					con->con_handle, con->pending, p);
907107120Sjulian
908107120Sjulian				con->pending = 0;
909107120Sjulian			}
910107120Sjulian
911107120Sjulian			/* Update buffer descriptor */
912107120Sjulian			if (con->link_type == NG_HCI_LINK_ACL)
913107120Sjulian				NG_HCI_BUFF_ACL_FREE(unit->buffer, p);
914107120Sjulian			else
915107120Sjulian				NG_HCI_BUFF_SCO_FREE(unit->buffer, p);
916107120Sjulian		} else
917107120Sjulian			NG_HCI_ALERT(
918107120Sjulian"%s: %s - invalid connection handle=%d\n",
919107120Sjulian				__func__, NG_NODE_NAME(unit->node), h);
920107120Sjulian	}
921107120Sjulian
922107120Sjulian	NG_FREE_M(event);
923107120Sjulian
924107120Sjulian	/* Send more data */
925107120Sjulian	ng_hci_send_data(unit);
926107120Sjulian
927107120Sjulian	return (0);
928107120Sjulian} /* num_compl_pkts */
929107120Sjulian
930107120Sjulian/* Mode change event */
931107120Sjulianstatic int
932107120Sjulianmode_change(ng_hci_unit_p unit, struct mbuf *event)
933107120Sjulian{
934107120Sjulian	ng_hci_mode_change_ep	*ep = NULL;
935107120Sjulian	ng_hci_unit_con_p	 con = NULL;
936107120Sjulian	int			 error = 0;
937107120Sjulian
938107120Sjulian	NG_HCI_M_PULLUP(event, sizeof(*ep));
939107120Sjulian	if (event == NULL)
940107120Sjulian		return (ENOBUFS);
941107120Sjulian
942107120Sjulian	ep = mtod(event, ng_hci_mode_change_ep *);
943107120Sjulian
944107120Sjulian	if (ep->status == 0) {
945107120Sjulian		u_int16_t	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
946107120Sjulian
947107120Sjulian		con = ng_hci_con_by_handle(unit, h);
948107120Sjulian		if (con == NULL) {
949107120Sjulian			NG_HCI_ALERT(
950107120Sjulian"%s: %s - invalid connection handle=%d\n",
951107120Sjulian				__func__, NG_NODE_NAME(unit->node), h);
952107120Sjulian			error = ENOENT;
953107120Sjulian		} else if (con->link_type != NG_HCI_LINK_ACL) {
954107120Sjulian			NG_HCI_ALERT(
955107120Sjulian"%s: %s - invalid link type=%d\n",
956107120Sjulian				__func__, NG_NODE_NAME(unit->node),
957107120Sjulian				con->link_type);
958107120Sjulian			error = EINVAL;
959107120Sjulian		} else
960107120Sjulian			con->mode = ep->unit_mode;
961107120Sjulian	} else
962107120Sjulian		NG_HCI_ERR(
963107120Sjulian"%s: %s - failed to change mode, status=%d\n",
964107120Sjulian			__func__, NG_NODE_NAME(unit->node), ep->status);
965107120Sjulian
966107120Sjulian	NG_FREE_M(event);
967107120Sjulian
968107120Sjulian	return (error);
969107120Sjulian} /* mode_change */
970107120Sjulian
971107120Sjulian/* Data buffer overflow event */
972107120Sjulianstatic int
973107120Sjuliandata_buffer_overflow(ng_hci_unit_p unit, struct mbuf *event)
974107120Sjulian{
975107120Sjulian	NG_HCI_ALERT(
976107120Sjulian"%s: %s - %s data buffer overflow\n",
977107120Sjulian		__func__, NG_NODE_NAME(unit->node),
978107120Sjulian		(*mtod(event, u_int8_t *) == NG_HCI_LINK_ACL)? "ACL" : "SCO");
979107120Sjulian
980107120Sjulian	NG_FREE_M(event);
981107120Sjulian
982107120Sjulian	return (0);
983107120Sjulian} /* data_buffer_overflow */
984107120Sjulian
985107120Sjulian/* Read clock offset complete event */
986107120Sjulianstatic int
987107120Sjulianread_clock_offset_compl(ng_hci_unit_p unit, struct mbuf *event)
988107120Sjulian{
989107120Sjulian	ng_hci_read_clock_offset_compl_ep	*ep = NULL;
990107120Sjulian	ng_hci_unit_con_p			 con = NULL;
991107120Sjulian	ng_hci_neighbor_p			 n = NULL;
992107120Sjulian	int					 error = 0;
993107120Sjulian
994107120Sjulian	NG_HCI_M_PULLUP(event, sizeof(*ep));
995107120Sjulian	if (event == NULL)
996107120Sjulian		return (ENOBUFS);
997107120Sjulian
998107120Sjulian	ep = mtod(event, ng_hci_read_clock_offset_compl_ep *);
999107120Sjulian
1000107120Sjulian	if (ep->status == 0) {
1001107120Sjulian		u_int16_t	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
1002107120Sjulian
1003107120Sjulian		con = ng_hci_con_by_handle(unit, h);
1004107120Sjulian		if (con == NULL) {
1005107120Sjulian			NG_HCI_ALERT(
1006107120Sjulian"%s: %s - invalid connection handle=%d\n",
1007107120Sjulian				__func__, NG_NODE_NAME(unit->node), h);
1008107120Sjulian			error = ENOENT;
1009107120Sjulian			goto out;
1010107120Sjulian		}
1011107120Sjulian
1012107120Sjulian		/* Update cache entry */
1013107120Sjulian		n = ng_hci_get_neighbor(unit, &con->bdaddr);
1014107120Sjulian		if (n == NULL) {
1015107120Sjulian			n = ng_hci_new_neighbor(unit);
1016107120Sjulian			if (n == NULL) {
1017107120Sjulian				error = ENOMEM;
1018107120Sjulian				goto out;
1019107120Sjulian			}
1020107120Sjulian
1021107120Sjulian			bcopy(&con->bdaddr, &n->bdaddr, sizeof(n->bdaddr));
1022107120Sjulian		} else
1023107120Sjulian			getmicrotime(&n->updated);
1024107120Sjulian
1025107120Sjulian		n->clock_offset = le16toh(ep->clock_offset);
1026107120Sjulian	} else
1027107120Sjulian		NG_HCI_ERR(
1028107120Sjulian"%s: %s - failed to Read Remote Clock Offset, status=%d\n",
1029107120Sjulian			__func__, NG_NODE_NAME(unit->node), ep->status);
1030107120Sjulianout:
1031107120Sjulian	NG_FREE_M(event);
1032107120Sjulian
1033107120Sjulian	return (error);
1034107120Sjulian} /* read_clock_offset_compl */
1035107120Sjulian
1036107120Sjulian/* QoS violation event */
1037107120Sjulianstatic int
1038107120Sjulianqos_violation(ng_hci_unit_p unit, struct mbuf *event)
1039107120Sjulian{
1040107120Sjulian	ng_hci_qos_violation_ep	*ep = NULL;
1041107120Sjulian	ng_hci_unit_con_p	 con = NULL;
1042107120Sjulian	u_int16_t		 h;
1043107120Sjulian	int			 error = 0;
1044107120Sjulian
1045107120Sjulian	NG_HCI_M_PULLUP(event, sizeof(*ep));
1046107120Sjulian	if (event == NULL)
1047107120Sjulian		return (ENOBUFS);
1048107120Sjulian
1049107120Sjulian	ep = mtod(event, ng_hci_qos_violation_ep *);
1050107120Sjulian
1051107120Sjulian	/* Check if we have this connection handle */
1052107120Sjulian	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
1053107120Sjulian	con = ng_hci_con_by_handle(unit, h);
1054107120Sjulian	if (con == NULL) {
1055107120Sjulian		NG_HCI_ALERT(
1056107120Sjulian"%s: %s - invalid connection handle=%d\n",
1057107120Sjulian			__func__, NG_NODE_NAME(unit->node), h);
1058107120Sjulian		error = ENOENT;
1059107120Sjulian	} else if (con->link_type != NG_HCI_LINK_ACL) {
1060107120Sjulian		NG_HCI_ALERT(
1061107120Sjulian"%s: %s - invalid link type=%d\n",
1062107120Sjulian			__func__, NG_NODE_NAME(unit->node), con->link_type);
1063107120Sjulian		error = EINVAL;
1064107120Sjulian	} else if (con->state != NG_HCI_CON_OPEN) {
1065107120Sjulian		NG_HCI_ALERT(
1066107120Sjulian"%s: %s - invalid connection state=%d, handle=%d\n",
1067107120Sjulian			__func__, NG_NODE_NAME(unit->node), con->state, h);
1068107120Sjulian		error = EINVAL;
1069107120Sjulian	} else /* Notify upper layer */
1070107120Sjulian		error = ng_hci_lp_qos_ind(con);
1071107120Sjulian
1072107120Sjulian	NG_FREE_M(event);
1073107120Sjulian
1074107120Sjulian	return (error);
1075107120Sjulian} /* qos_violation */
1076107120Sjulian
1077107120Sjulian/* Page scan mode change event */
1078107120Sjulianstatic int
1079107120Sjulianpage_scan_mode_change(ng_hci_unit_p unit, struct mbuf *event)
1080107120Sjulian{
1081107120Sjulian	ng_hci_page_scan_mode_change_ep	*ep = NULL;
1082107120Sjulian	ng_hci_neighbor_p		 n = NULL;
1083107120Sjulian	int				 error = 0;
1084107120Sjulian
1085107120Sjulian	NG_HCI_M_PULLUP(event, sizeof(*ep));
1086107120Sjulian	if (event == NULL)
1087107120Sjulian		return (ENOBUFS);
1088107120Sjulian
1089107120Sjulian	ep = mtod(event, ng_hci_page_scan_mode_change_ep *);
1090107120Sjulian
1091107120Sjulian	/* Update cache entry */
1092107120Sjulian	n = ng_hci_get_neighbor(unit, &ep->bdaddr);
1093107120Sjulian	if (n == NULL) {
1094107120Sjulian		n = ng_hci_new_neighbor(unit);
1095107120Sjulian		if (n == NULL) {
1096107120Sjulian			error = ENOMEM;
1097107120Sjulian			goto out;
1098107120Sjulian		}
1099107120Sjulian
1100107120Sjulian		bcopy(&ep->bdaddr, &n->bdaddr, sizeof(n->bdaddr));
1101107120Sjulian	} else
1102107120Sjulian		getmicrotime(&n->updated);
1103107120Sjulian
1104107120Sjulian	n->page_scan_mode = ep->page_scan_mode;
1105107120Sjulianout:
1106107120Sjulian	NG_FREE_M(event);
1107107120Sjulian
1108107120Sjulian	return (error);
1109107120Sjulian} /* page_scan_mode_change */
1110107120Sjulian
1111107120Sjulian/* Page scan repetition mode change event */
1112107120Sjulianstatic int
1113107120Sjulianpage_scan_rep_mode_change(ng_hci_unit_p unit, struct mbuf *event)
1114107120Sjulian{
1115107120Sjulian	ng_hci_page_scan_rep_mode_change_ep	*ep = NULL;
1116107120Sjulian	ng_hci_neighbor_p			 n = NULL;
1117107120Sjulian	int					 error = 0;
1118107120Sjulian
1119107120Sjulian	NG_HCI_M_PULLUP(event, sizeof(*ep));
1120107120Sjulian	if (event == NULL)
1121107120Sjulian		return (ENOBUFS);
1122107120Sjulian
1123107120Sjulian	ep = mtod(event, ng_hci_page_scan_rep_mode_change_ep *);
1124107120Sjulian
1125107120Sjulian	/* Update cache entry */
1126107120Sjulian	n = ng_hci_get_neighbor(unit, &ep->bdaddr);
1127107120Sjulian	if (n == NULL) {
1128107120Sjulian		n = ng_hci_new_neighbor(unit);
1129107120Sjulian		if (n == NULL) {
1130107120Sjulian			error = ENOMEM;
1131107120Sjulian			goto out;
1132107120Sjulian		}
1133107120Sjulian
1134107120Sjulian		bcopy(&ep->bdaddr, &n->bdaddr, sizeof(n->bdaddr));
1135107120Sjulian	} else
1136107120Sjulian		getmicrotime(&n->updated);
1137107120Sjulian
1138107120Sjulian	n->page_scan_rep_mode = ep->page_scan_rep_mode;
1139107120Sjulianout:
1140107120Sjulian	NG_FREE_M(event);
1141107120Sjulian
1142107120Sjulian	return (error);
1143107120Sjulian} /* page_scan_rep_mode_change */
1144107120Sjulian
1145