xref: /dflybsd-src/sys/netgraph7/bluetooth/hci/ng_hci_evnt.c (revision 05d02a3813e2bef176c69d68035311fd2efbd031)
1b06ebda0SMatthew Dillon /*
2b06ebda0SMatthew Dillon  * ng_hci_evnt.c
3b06ebda0SMatthew Dillon  */
4b06ebda0SMatthew Dillon 
5b06ebda0SMatthew Dillon /*-
6b06ebda0SMatthew Dillon  * Copyright (c) Maksim Yevmenkin <m_evmenkin@yahoo.com>
7b06ebda0SMatthew Dillon  * All rights reserved.
8b06ebda0SMatthew Dillon  *
9b06ebda0SMatthew Dillon  * Redistribution and use in source and binary forms, with or without
10b06ebda0SMatthew Dillon  * modification, are permitted provided that the following conditions
11b06ebda0SMatthew Dillon  * are met:
12b06ebda0SMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
13b06ebda0SMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
14b06ebda0SMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
15b06ebda0SMatthew Dillon  *    notice, this list of conditions and the following disclaimer in the
16b06ebda0SMatthew Dillon  *    documentation and/or other materials provided with the distribution.
17b06ebda0SMatthew Dillon  *
18b06ebda0SMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19b06ebda0SMatthew Dillon  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20b06ebda0SMatthew Dillon  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21b06ebda0SMatthew Dillon  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22b06ebda0SMatthew Dillon  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23b06ebda0SMatthew Dillon  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24b06ebda0SMatthew Dillon  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25b06ebda0SMatthew Dillon  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26b06ebda0SMatthew Dillon  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27b06ebda0SMatthew Dillon  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28b06ebda0SMatthew Dillon  * SUCH DAMAGE.
29b06ebda0SMatthew Dillon  *
30b06ebda0SMatthew Dillon  * $Id: ng_hci_evnt.c,v 1.6 2003/09/08 18:57:51 max Exp $
31b06ebda0SMatthew Dillon  * $FreeBSD: src/sys/netgraph/bluetooth/hci/ng_hci_evnt.c,v 1.8 2005/01/07 01:45:43 imp Exp $
32b06ebda0SMatthew Dillon  */
33b06ebda0SMatthew Dillon 
34b06ebda0SMatthew Dillon #include <sys/param.h>
35b06ebda0SMatthew Dillon #include <sys/systm.h>
36b06ebda0SMatthew Dillon #include <sys/kernel.h>
37b06ebda0SMatthew Dillon #include <sys/endian.h>
38b06ebda0SMatthew Dillon #include <sys/malloc.h>
39b06ebda0SMatthew Dillon #include <sys/mbuf.h>
40b06ebda0SMatthew Dillon #include <sys/queue.h>
41e85b99abSSascha Wildner #include <sys/refcount.h>
42e85b99abSSascha Wildner #include <netgraph7/ng_message.h>
43e85b99abSSascha Wildner #include <netgraph7/netgraph.h>
44e85b99abSSascha Wildner #include <netgraph7/netgraph2.h>
45e85b99abSSascha Wildner #include <netgraph7/bluetooth/include/ng_bluetooth.h>
46e85b99abSSascha Wildner #include <netgraph7/bluetooth/include/ng_hci.h>
47e85b99abSSascha Wildner #include <netgraph7/bluetooth/hci/ng_hci_var.h>
48e85b99abSSascha Wildner #include <netgraph7/bluetooth/hci/ng_hci_cmds.h>
49e85b99abSSascha Wildner #include <netgraph7/bluetooth/hci/ng_hci_evnt.h>
50e85b99abSSascha Wildner #include <netgraph7/bluetooth/hci/ng_hci_ulpi.h>
51e85b99abSSascha Wildner #include <netgraph7/bluetooth/hci/ng_hci_misc.h>
52b06ebda0SMatthew Dillon 
53b06ebda0SMatthew Dillon /******************************************************************************
54b06ebda0SMatthew Dillon  ******************************************************************************
55b06ebda0SMatthew Dillon  **                     HCI event processing module
56b06ebda0SMatthew Dillon  ******************************************************************************
57b06ebda0SMatthew Dillon  ******************************************************************************/
58b06ebda0SMatthew Dillon 
59b06ebda0SMatthew Dillon /*
60b06ebda0SMatthew Dillon  * Event processing routines
61b06ebda0SMatthew Dillon  */
62b06ebda0SMatthew Dillon 
63b06ebda0SMatthew Dillon static int inquiry_result             (ng_hci_unit_p, struct mbuf *);
64b06ebda0SMatthew Dillon static int con_compl                  (ng_hci_unit_p, struct mbuf *);
65b06ebda0SMatthew Dillon static int con_req                    (ng_hci_unit_p, struct mbuf *);
66b06ebda0SMatthew Dillon static int discon_compl               (ng_hci_unit_p, struct mbuf *);
67b06ebda0SMatthew Dillon static int encryption_change          (ng_hci_unit_p, struct mbuf *);
68b06ebda0SMatthew Dillon static int read_remote_features_compl (ng_hci_unit_p, struct mbuf *);
69b06ebda0SMatthew Dillon static int qos_setup_compl            (ng_hci_unit_p, struct mbuf *);
70b06ebda0SMatthew Dillon static int hardware_error             (ng_hci_unit_p, struct mbuf *);
71b06ebda0SMatthew Dillon static int role_change                (ng_hci_unit_p, struct mbuf *);
72b06ebda0SMatthew Dillon static int num_compl_pkts             (ng_hci_unit_p, struct mbuf *);
73b06ebda0SMatthew Dillon static int mode_change                (ng_hci_unit_p, struct mbuf *);
74b06ebda0SMatthew Dillon static int data_buffer_overflow       (ng_hci_unit_p, struct mbuf *);
75b06ebda0SMatthew Dillon static int read_clock_offset_compl    (ng_hci_unit_p, struct mbuf *);
76b06ebda0SMatthew Dillon static int qos_violation              (ng_hci_unit_p, struct mbuf *);
77b06ebda0SMatthew Dillon static int page_scan_mode_change      (ng_hci_unit_p, struct mbuf *);
78b06ebda0SMatthew Dillon static int page_scan_rep_mode_change  (ng_hci_unit_p, struct mbuf *);
79b06ebda0SMatthew Dillon static int sync_con_queue             (ng_hci_unit_p, ng_hci_unit_con_p, int);
80b06ebda0SMatthew Dillon static int send_data_packets          (ng_hci_unit_p, int, int);
81b06ebda0SMatthew Dillon 
82b06ebda0SMatthew Dillon /*
83b06ebda0SMatthew Dillon  * Process HCI event packet
84b06ebda0SMatthew Dillon  */
85b06ebda0SMatthew Dillon 
86b06ebda0SMatthew Dillon int
ng_hci_process_event(ng_hci_unit_p unit,struct mbuf * event)87b06ebda0SMatthew Dillon ng_hci_process_event(ng_hci_unit_p unit, struct mbuf *event)
88b06ebda0SMatthew Dillon {
89b06ebda0SMatthew Dillon 	ng_hci_event_pkt_t	*hdr = NULL;
90b06ebda0SMatthew Dillon 	int			 error = 0;
91b06ebda0SMatthew Dillon 
92b06ebda0SMatthew Dillon 	/* Get event packet header */
93b06ebda0SMatthew Dillon 	NG_HCI_M_PULLUP(event, sizeof(*hdr));
94b06ebda0SMatthew Dillon 	if (event == NULL)
95b06ebda0SMatthew Dillon 		return (ENOBUFS);
96b06ebda0SMatthew Dillon 
97b06ebda0SMatthew Dillon 	hdr = mtod(event, ng_hci_event_pkt_t *);
98b06ebda0SMatthew Dillon 
99b06ebda0SMatthew Dillon 	NG_HCI_INFO(
100b06ebda0SMatthew Dillon "%s: %s - got HCI event=%#x, length=%d\n",
101b06ebda0SMatthew Dillon 		__func__, NG_NODE_NAME(unit->node), hdr->event, hdr->length);
102b06ebda0SMatthew Dillon 
103b06ebda0SMatthew Dillon 	/* Get rid of event header and process event */
104b06ebda0SMatthew Dillon 	m_adj(event, sizeof(*hdr));
105b06ebda0SMatthew Dillon 
106b06ebda0SMatthew Dillon 	switch (hdr->event) {
107b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_INQUIRY_COMPL:
108b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_RETURN_LINK_KEYS:
109b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_PIN_CODE_REQ:
110b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_LINK_KEY_REQ:
111b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_LINK_KEY_NOTIFICATION:
112b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_LOOPBACK_COMMAND:
113b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_AUTH_COMPL:
114b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_CHANGE_CON_LINK_KEY_COMPL:
115b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_MASTER_LINK_KEY_COMPL:
116b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_FLUSH_OCCUR:	/* XXX Do we have to handle it? */
117b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_MAX_SLOT_CHANGE:
118b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_CON_PKT_TYPE_CHANGED:
119b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_BT_LOGO:
120b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_VENDOR:
121b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_REMOTE_NAME_REQ_COMPL:
122b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_READ_REMOTE_VER_INFO_COMPL:
123b06ebda0SMatthew Dillon 		/* These do not need post processing */
124b06ebda0SMatthew Dillon 		NG_FREE_M(event);
125b06ebda0SMatthew Dillon 		break;
126b06ebda0SMatthew Dillon 
127b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_INQUIRY_RESULT:
128b06ebda0SMatthew Dillon 		error = inquiry_result(unit, event);
129b06ebda0SMatthew Dillon 		break;
130b06ebda0SMatthew Dillon 
131b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_CON_COMPL:
132b06ebda0SMatthew Dillon 		error = con_compl(unit, event);
133b06ebda0SMatthew Dillon 		break;
134b06ebda0SMatthew Dillon 
135b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_CON_REQ:
136b06ebda0SMatthew Dillon 		error = con_req(unit, event);
137b06ebda0SMatthew Dillon 		break;
138b06ebda0SMatthew Dillon 
139b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_DISCON_COMPL:
140b06ebda0SMatthew Dillon 		error = discon_compl(unit, event);
141b06ebda0SMatthew Dillon 		break;
142b06ebda0SMatthew Dillon 
143b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_ENCRYPTION_CHANGE:
144b06ebda0SMatthew Dillon 		error = encryption_change(unit, event);
145b06ebda0SMatthew Dillon 		break;
146b06ebda0SMatthew Dillon 
147b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_READ_REMOTE_FEATURES_COMPL:
148b06ebda0SMatthew Dillon 		error = read_remote_features_compl(unit, event);
149b06ebda0SMatthew Dillon 		break;
150b06ebda0SMatthew Dillon 
151b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_QOS_SETUP_COMPL:
152b06ebda0SMatthew Dillon 		error = qos_setup_compl(unit, event);
153b06ebda0SMatthew Dillon 		break;
154b06ebda0SMatthew Dillon 
155b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_COMMAND_COMPL:
156b06ebda0SMatthew Dillon 		error = ng_hci_process_command_complete(unit, event);
157b06ebda0SMatthew Dillon 		break;
158b06ebda0SMatthew Dillon 
159b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_COMMAND_STATUS:
160b06ebda0SMatthew Dillon 		error = ng_hci_process_command_status(unit, event);
161b06ebda0SMatthew Dillon 		break;
162b06ebda0SMatthew Dillon 
163b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_HARDWARE_ERROR:
164b06ebda0SMatthew Dillon 		error = hardware_error(unit, event);
165b06ebda0SMatthew Dillon 		break;
166b06ebda0SMatthew Dillon 
167b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_ROLE_CHANGE:
168b06ebda0SMatthew Dillon 		error = role_change(unit, event);
169b06ebda0SMatthew Dillon 		break;
170b06ebda0SMatthew Dillon 
171b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_NUM_COMPL_PKTS:
172b06ebda0SMatthew Dillon 		error = num_compl_pkts(unit, event);
173b06ebda0SMatthew Dillon 		break;
174b06ebda0SMatthew Dillon 
175b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_MODE_CHANGE:
176b06ebda0SMatthew Dillon 		error = mode_change(unit, event);
177b06ebda0SMatthew Dillon 		break;
178b06ebda0SMatthew Dillon 
179b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_DATA_BUFFER_OVERFLOW:
180b06ebda0SMatthew Dillon 		error = data_buffer_overflow(unit, event);
181b06ebda0SMatthew Dillon 		break;
182b06ebda0SMatthew Dillon 
183b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_READ_CLOCK_OFFSET_COMPL:
184b06ebda0SMatthew Dillon 		error = read_clock_offset_compl(unit, event);
185b06ebda0SMatthew Dillon 		break;
186b06ebda0SMatthew Dillon 
187b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_QOS_VIOLATION:
188b06ebda0SMatthew Dillon 		error = qos_violation(unit, event);
189b06ebda0SMatthew Dillon 		break;
190b06ebda0SMatthew Dillon 
191b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_PAGE_SCAN_MODE_CHANGE:
192b06ebda0SMatthew Dillon 		error = page_scan_mode_change(unit, event);
193b06ebda0SMatthew Dillon 		break;
194b06ebda0SMatthew Dillon 
195b06ebda0SMatthew Dillon 	case NG_HCI_EVENT_PAGE_SCAN_REP_MODE_CHANGE:
196b06ebda0SMatthew Dillon 		error = page_scan_rep_mode_change(unit, event);
197b06ebda0SMatthew Dillon 		break;
198b06ebda0SMatthew Dillon 
199b06ebda0SMatthew Dillon 	default:
200b06ebda0SMatthew Dillon 		NG_FREE_M(event);
201b06ebda0SMatthew Dillon 		error = EINVAL;
202b06ebda0SMatthew Dillon 		break;
203b06ebda0SMatthew Dillon 	}
204b06ebda0SMatthew Dillon 
205b06ebda0SMatthew Dillon 	return (error);
206b06ebda0SMatthew Dillon } /* ng_hci_process_event */
207b06ebda0SMatthew Dillon 
208b06ebda0SMatthew Dillon /*
209b06ebda0SMatthew Dillon  * Send ACL and/or SCO data to the unit driver
210b06ebda0SMatthew Dillon  */
211b06ebda0SMatthew Dillon 
212b06ebda0SMatthew Dillon void
ng_hci_send_data(ng_hci_unit_p unit)213b06ebda0SMatthew Dillon ng_hci_send_data(ng_hci_unit_p unit)
214b06ebda0SMatthew Dillon {
215b06ebda0SMatthew Dillon 	int	count;
216b06ebda0SMatthew Dillon 
217b06ebda0SMatthew Dillon 	/* Send ACL data */
218b06ebda0SMatthew Dillon 	NG_HCI_BUFF_ACL_AVAIL(unit->buffer, count);
219b06ebda0SMatthew Dillon 
220b06ebda0SMatthew Dillon 	NG_HCI_INFO(
221b06ebda0SMatthew Dillon "%s: %s - sending ACL data packets, count=%d\n",
222b06ebda0SMatthew Dillon 		__func__, NG_NODE_NAME(unit->node), count);
223b06ebda0SMatthew Dillon 
224b06ebda0SMatthew Dillon 	if (count > 0) {
225b06ebda0SMatthew Dillon 		count = send_data_packets(unit, NG_HCI_LINK_ACL, count);
226b06ebda0SMatthew Dillon 		NG_HCI_STAT_ACL_SENT(unit->stat, count);
227b06ebda0SMatthew Dillon 		NG_HCI_BUFF_ACL_USE(unit->buffer, count);
228b06ebda0SMatthew Dillon 	}
229b06ebda0SMatthew Dillon 
230b06ebda0SMatthew Dillon 	/* Send SCO data */
231b06ebda0SMatthew Dillon 	NG_HCI_BUFF_SCO_AVAIL(unit->buffer, count);
232b06ebda0SMatthew Dillon 
233b06ebda0SMatthew Dillon 	NG_HCI_INFO(
234b06ebda0SMatthew Dillon "%s: %s - sending SCO data packets, count=%d\n",
235b06ebda0SMatthew Dillon 		__func__, NG_NODE_NAME(unit->node), count);
236b06ebda0SMatthew Dillon 
237b06ebda0SMatthew Dillon 	if (count > 0) {
238b06ebda0SMatthew Dillon 		count = send_data_packets(unit, NG_HCI_LINK_SCO, count);
239b06ebda0SMatthew Dillon 		NG_HCI_STAT_SCO_SENT(unit->stat, count);
240b06ebda0SMatthew Dillon 		NG_HCI_BUFF_SCO_USE(unit->buffer, count);
241b06ebda0SMatthew Dillon 	}
242b06ebda0SMatthew Dillon } /* ng_hci_send_data */
243b06ebda0SMatthew Dillon 
244b06ebda0SMatthew Dillon /*
245b06ebda0SMatthew Dillon  * Send data packets to the lower layer.
246b06ebda0SMatthew Dillon  */
247b06ebda0SMatthew Dillon 
248b06ebda0SMatthew Dillon static int
send_data_packets(ng_hci_unit_p unit,int link_type,int limit)249b06ebda0SMatthew Dillon send_data_packets(ng_hci_unit_p unit, int link_type, int limit)
250b06ebda0SMatthew Dillon {
251b06ebda0SMatthew Dillon 	ng_hci_unit_con_p	con = NULL, winner = NULL;
252e85b99abSSascha Wildner 	item_p			item = NULL, item2;
253b06ebda0SMatthew Dillon 	int			min_pending, total_sent, sent, error, v;
254b06ebda0SMatthew Dillon 
255b06ebda0SMatthew Dillon 	for (total_sent = 0; limit > 0; ) {
256b06ebda0SMatthew Dillon 		min_pending = 0x0fffffff;
257b06ebda0SMatthew Dillon 		winner = NULL;
258b06ebda0SMatthew Dillon 
259b06ebda0SMatthew Dillon 		/*
260b06ebda0SMatthew Dillon 		 * Find the connection that has has data to send
261b06ebda0SMatthew Dillon 		 * and the smallest number of pending packets
262b06ebda0SMatthew Dillon 		 */
263b06ebda0SMatthew Dillon 
264b06ebda0SMatthew Dillon 		LIST_FOREACH(con, &unit->con_list, next) {
265b06ebda0SMatthew Dillon 			if (con->link_type != link_type)
266b06ebda0SMatthew Dillon 				continue;
267b06ebda0SMatthew Dillon 			if (NG_BT_ITEMQ_LEN(&con->conq) == 0)
268b06ebda0SMatthew Dillon 				continue;
269b06ebda0SMatthew Dillon 
270b06ebda0SMatthew Dillon 			if (con->pending < min_pending) {
271b06ebda0SMatthew Dillon 				winner = con;
272b06ebda0SMatthew Dillon 				min_pending = con->pending;
273b06ebda0SMatthew Dillon 			}
274b06ebda0SMatthew Dillon 		}
275b06ebda0SMatthew Dillon 
276b06ebda0SMatthew Dillon 	        if (winner == NULL)
277b06ebda0SMatthew Dillon 			break;
278b06ebda0SMatthew Dillon 
279b06ebda0SMatthew Dillon 		/*
280b06ebda0SMatthew Dillon 		 * OK, we have a winner now send as much packets as we can
281b06ebda0SMatthew Dillon 		 * Count the number of packets we have sent and then sync
282b06ebda0SMatthew Dillon 		 * winner connection queue.
283b06ebda0SMatthew Dillon 		 */
284b06ebda0SMatthew Dillon 
285b06ebda0SMatthew Dillon 		for (sent = 0; limit > 0; limit --, total_sent ++, sent ++) {
286b06ebda0SMatthew Dillon 			NG_BT_ITEMQ_DEQUEUE(&winner->conq, item);
287b06ebda0SMatthew Dillon 			if (item == NULL)
288b06ebda0SMatthew Dillon 				break;
289e85b99abSSascha Wildner 			item2 = item;
290b06ebda0SMatthew Dillon 
291b06ebda0SMatthew Dillon 			NG_HCI_INFO(
292b06ebda0SMatthew Dillon "%s: %s - sending data packet, handle=%d, len=%d\n",
293b06ebda0SMatthew Dillon 				__func__, NG_NODE_NAME(unit->node),
294b06ebda0SMatthew Dillon 				winner->con_handle, NGI_M(item)->m_pkthdr.len);
295b06ebda0SMatthew Dillon 
296b06ebda0SMatthew Dillon 			/* Check if driver hook still there */
297b06ebda0SMatthew Dillon 			v = (unit->drv != NULL && NG_HOOK_IS_VALID(unit->drv));
298b06ebda0SMatthew Dillon 			if (!v || (unit->state & NG_HCI_UNIT_READY) !=
299b06ebda0SMatthew Dillon 					NG_HCI_UNIT_READY) {
300b06ebda0SMatthew Dillon 				NG_HCI_ERR(
301b06ebda0SMatthew Dillon "%s: %s - could not send data. Hook \"%s\" is %svalid, state=%#x\n",
302b06ebda0SMatthew Dillon 					__func__, NG_NODE_NAME(unit->node),
303b06ebda0SMatthew Dillon 					NG_HCI_HOOK_DRV, ((v)? "" : "not "),
304b06ebda0SMatthew Dillon 					unit->state);
305b06ebda0SMatthew Dillon 
306b06ebda0SMatthew Dillon 				NG_FREE_ITEM(item);
307b06ebda0SMatthew Dillon 				error = ENOTCONN;
308b06ebda0SMatthew Dillon 			} else {
309b06ebda0SMatthew Dillon 				v = NGI_M(item)->m_pkthdr.len;
310b06ebda0SMatthew Dillon 
311b06ebda0SMatthew Dillon 				/* Give packet to raw hook */
312b06ebda0SMatthew Dillon 				ng_hci_mtap(unit, NGI_M(item));
313b06ebda0SMatthew Dillon 
314b06ebda0SMatthew Dillon 				/* ... and forward item to the driver */
315b06ebda0SMatthew Dillon 				NG_FWD_ITEM_HOOK(error, item, unit->drv);
316b06ebda0SMatthew Dillon 			}
317e85b99abSSascha Wildner 			ng_unref_item(item2, error);
318b06ebda0SMatthew Dillon 
319b06ebda0SMatthew Dillon 			if (error != 0) {
320b06ebda0SMatthew Dillon 				NG_HCI_ERR(
321b06ebda0SMatthew Dillon "%s: %s - could not send data packet, handle=%d, error=%d\n",
322b06ebda0SMatthew Dillon 					__func__, NG_NODE_NAME(unit->node),
323b06ebda0SMatthew Dillon 					winner->con_handle, error);
324b06ebda0SMatthew Dillon 				break;
325b06ebda0SMatthew Dillon 			}
326b06ebda0SMatthew Dillon 
327b06ebda0SMatthew Dillon 			winner->pending ++;
328b06ebda0SMatthew Dillon 			NG_HCI_STAT_BYTES_SENT(unit->stat, v);
329b06ebda0SMatthew Dillon 		}
330b06ebda0SMatthew Dillon 
331b06ebda0SMatthew Dillon 		/*
332b06ebda0SMatthew Dillon 		 * Sync connection queue for the winner
333b06ebda0SMatthew Dillon 		 */
334b06ebda0SMatthew Dillon 
335b06ebda0SMatthew Dillon 		sync_con_queue(unit, winner, sent);
336b06ebda0SMatthew Dillon 	}
337b06ebda0SMatthew Dillon 
338b06ebda0SMatthew Dillon 	return (total_sent);
339b06ebda0SMatthew Dillon } /* send_data_packets */
340b06ebda0SMatthew Dillon 
341b06ebda0SMatthew Dillon /*
342b06ebda0SMatthew Dillon  * Send flow control messages to the upper layer
343b06ebda0SMatthew Dillon  */
344b06ebda0SMatthew Dillon 
345b06ebda0SMatthew Dillon static int
sync_con_queue(ng_hci_unit_p unit,ng_hci_unit_con_p con,int completed)346b06ebda0SMatthew Dillon sync_con_queue(ng_hci_unit_p unit, ng_hci_unit_con_p con, int completed)
347b06ebda0SMatthew Dillon {
348b06ebda0SMatthew Dillon 	hook_p				 hook = NULL;
349b06ebda0SMatthew Dillon 	struct ng_mesg			*msg = NULL;
350b06ebda0SMatthew Dillon 	ng_hci_sync_con_queue_ep	*state = NULL;
351b06ebda0SMatthew Dillon 	int				 error;
352b06ebda0SMatthew Dillon 
353b06ebda0SMatthew Dillon 	hook = (con->link_type == NG_HCI_LINK_ACL)? unit->acl : unit->sco;
354b06ebda0SMatthew Dillon 	if (hook == NULL || NG_HOOK_NOT_VALID(hook))
355b06ebda0SMatthew Dillon 		return (ENOTCONN);
356b06ebda0SMatthew Dillon 
357b06ebda0SMatthew Dillon 	NG_MKMESSAGE(msg, NGM_HCI_COOKIE, NGM_HCI_SYNC_CON_QUEUE,
3585a975a3dSMatthew Dillon 		sizeof(*state), M_WAITOK | M_NULLOK);
359b06ebda0SMatthew Dillon 	if (msg == NULL)
360b06ebda0SMatthew Dillon 		return (ENOMEM);
361b06ebda0SMatthew Dillon 
362b06ebda0SMatthew Dillon 	state = (ng_hci_sync_con_queue_ep *)(msg->data);
363b06ebda0SMatthew Dillon 	state->con_handle = con->con_handle;
364b06ebda0SMatthew Dillon 	state->completed = completed;
365b06ebda0SMatthew Dillon 
366b06ebda0SMatthew Dillon 	NG_SEND_MSG_HOOK(error, unit->node, msg, hook, 0);
367b06ebda0SMatthew Dillon 
368b06ebda0SMatthew Dillon 	return (error);
369b06ebda0SMatthew Dillon } /* sync_con_queue */
370b06ebda0SMatthew Dillon 
371b06ebda0SMatthew Dillon /* Inquiry result event */
372b06ebda0SMatthew Dillon static int
inquiry_result(ng_hci_unit_p unit,struct mbuf * event)373b06ebda0SMatthew Dillon inquiry_result(ng_hci_unit_p unit, struct mbuf *event)
374b06ebda0SMatthew Dillon {
375b06ebda0SMatthew Dillon 	ng_hci_inquiry_result_ep	*ep = NULL;
376b06ebda0SMatthew Dillon 	ng_hci_neighbor_p		 n = NULL;
377b06ebda0SMatthew Dillon 	bdaddr_t			 bdaddr;
378b06ebda0SMatthew Dillon 	int				 error = 0;
379b06ebda0SMatthew Dillon 
380b06ebda0SMatthew Dillon 	NG_HCI_M_PULLUP(event, sizeof(*ep));
381b06ebda0SMatthew Dillon 	if (event == NULL)
382b06ebda0SMatthew Dillon 		return (ENOBUFS);
383b06ebda0SMatthew Dillon 
384b06ebda0SMatthew Dillon 	ep = mtod(event, ng_hci_inquiry_result_ep *);
385b06ebda0SMatthew Dillon 	m_adj(event, sizeof(*ep));
386b06ebda0SMatthew Dillon 
387b06ebda0SMatthew Dillon 	for (; ep->num_responses > 0; ep->num_responses --) {
388b06ebda0SMatthew Dillon 		/* Get remote unit address */
389*05d02a38SAaron LI 		m_copydata(event, 0, sizeof(bdaddr), &bdaddr);
390b06ebda0SMatthew Dillon 		m_adj(event, sizeof(bdaddr));
391b06ebda0SMatthew Dillon 
392b06ebda0SMatthew Dillon 		/* Lookup entry in the cache */
393b06ebda0SMatthew Dillon 		n = ng_hci_get_neighbor(unit, &bdaddr);
394b06ebda0SMatthew Dillon 		if (n == NULL) {
395b06ebda0SMatthew Dillon 			/* Create new entry */
396b06ebda0SMatthew Dillon 			n = ng_hci_new_neighbor(unit);
397b06ebda0SMatthew Dillon 			if (n == NULL) {
398b06ebda0SMatthew Dillon 				error = ENOMEM;
399b06ebda0SMatthew Dillon 				break;
400b06ebda0SMatthew Dillon 			}
401b06ebda0SMatthew Dillon 		} else
402b06ebda0SMatthew Dillon 			getmicrotime(&n->updated);
403b06ebda0SMatthew Dillon 
404b06ebda0SMatthew Dillon 		bcopy(&bdaddr, &n->bdaddr, sizeof(n->bdaddr));
405b06ebda0SMatthew Dillon 
406b06ebda0SMatthew Dillon 		/* XXX call m_pullup here? */
407b06ebda0SMatthew Dillon 
408b06ebda0SMatthew Dillon 		n->page_scan_rep_mode = *mtod(event, u_int8_t *);
409b06ebda0SMatthew Dillon 		m_adj(event, sizeof(u_int8_t));
410b06ebda0SMatthew Dillon 
411b06ebda0SMatthew Dillon 		/* page_scan_period_mode */
412b06ebda0SMatthew Dillon 		m_adj(event, sizeof(u_int8_t));
413b06ebda0SMatthew Dillon 
414b06ebda0SMatthew Dillon 		n->page_scan_mode = *mtod(event, u_int8_t *);
415b06ebda0SMatthew Dillon 		m_adj(event, sizeof(u_int8_t));
416b06ebda0SMatthew Dillon 
417b06ebda0SMatthew Dillon 		/* class */
418b06ebda0SMatthew Dillon 		m_adj(event, NG_HCI_CLASS_SIZE);
419b06ebda0SMatthew Dillon 
420b06ebda0SMatthew Dillon 		/* clock offset */
421b06ebda0SMatthew Dillon 		m_copydata(event, 0, sizeof(n->clock_offset),
422*05d02a38SAaron LI 			&n->clock_offset);
423b06ebda0SMatthew Dillon 		n->clock_offset = le16toh(n->clock_offset);
424b06ebda0SMatthew Dillon 	}
425b06ebda0SMatthew Dillon 
426b06ebda0SMatthew Dillon 	NG_FREE_M(event);
427b06ebda0SMatthew Dillon 
428b06ebda0SMatthew Dillon 	return (error);
429b06ebda0SMatthew Dillon } /* inquiry_result */
430b06ebda0SMatthew Dillon 
431b06ebda0SMatthew Dillon /* Connection complete event */
432b06ebda0SMatthew Dillon static int
con_compl(ng_hci_unit_p unit,struct mbuf * event)433b06ebda0SMatthew Dillon con_compl(ng_hci_unit_p unit, struct mbuf *event)
434b06ebda0SMatthew Dillon {
435b06ebda0SMatthew Dillon 	ng_hci_con_compl_ep	*ep = NULL;
436b06ebda0SMatthew Dillon 	ng_hci_unit_con_p	 con = NULL;
437b06ebda0SMatthew Dillon 	int			 error = 0;
438b06ebda0SMatthew Dillon 
439b06ebda0SMatthew Dillon 	NG_HCI_M_PULLUP(event, sizeof(*ep));
440b06ebda0SMatthew Dillon 	if (event == NULL)
441b06ebda0SMatthew Dillon 		return (ENOBUFS);
442b06ebda0SMatthew Dillon 
443b06ebda0SMatthew Dillon 	ep = mtod(event, ng_hci_con_compl_ep *);
444b06ebda0SMatthew Dillon 
445b06ebda0SMatthew Dillon 	/*
446b06ebda0SMatthew Dillon 	 * Find the first connection descriptor that matches the following:
447b06ebda0SMatthew Dillon 	 *
448b06ebda0SMatthew Dillon 	 * 1) con->link_type == ep->link_type
449b06ebda0SMatthew Dillon 	 * 2) con->state == NG_HCI_CON_W4_CONN_COMPLETE
450b06ebda0SMatthew Dillon 	 * 3) con->bdaddr == ep->bdaddr
451b06ebda0SMatthew Dillon 	 */
452b06ebda0SMatthew Dillon 
453b06ebda0SMatthew Dillon 	LIST_FOREACH(con, &unit->con_list, next)
454b06ebda0SMatthew Dillon 		if (con->link_type == ep->link_type &&
455b06ebda0SMatthew Dillon 		    con->state == NG_HCI_CON_W4_CONN_COMPLETE &&
456b06ebda0SMatthew Dillon 		    bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
457b06ebda0SMatthew Dillon 			break;
458b06ebda0SMatthew Dillon 
459b06ebda0SMatthew Dillon 	/*
460b06ebda0SMatthew Dillon 	 * Two possible cases:
461b06ebda0SMatthew Dillon 	 *
462b06ebda0SMatthew Dillon 	 * 1) We have found connection descriptor. That means upper layer has
463b06ebda0SMatthew Dillon 	 *    requested this connection via LP_CON_REQ message. In this case
464b06ebda0SMatthew Dillon 	 *    connection must have timeout set. If ng_hci_con_untimeout() fails
465b06ebda0SMatthew Dillon 	 *    then timeout message already went into node's queue. In this case
466b06ebda0SMatthew Dillon 	 *    ignore Connection_Complete event and let timeout deal with it.
467b06ebda0SMatthew Dillon 	 *
468b06ebda0SMatthew Dillon 	 * 2) We do not have connection descriptor. That means upper layer
469b06ebda0SMatthew Dillon 	 *    nas not requested this connection or (less likely) we gave up
470b06ebda0SMatthew Dillon 	 *    on this connection (timeout). The most likely scenario is that
471b06ebda0SMatthew Dillon 	 *    we have received Create_Connection/Add_SCO_Connection command
472b06ebda0SMatthew Dillon 	 *    from the RAW hook
473b06ebda0SMatthew Dillon 	 */
474b06ebda0SMatthew Dillon 
475b06ebda0SMatthew Dillon 	if (con == NULL) {
476b06ebda0SMatthew Dillon 		if (ep->status != 0)
477b06ebda0SMatthew Dillon 			goto out;
478b06ebda0SMatthew Dillon 
479b06ebda0SMatthew Dillon 		con = ng_hci_new_con(unit, ep->link_type);
480b06ebda0SMatthew Dillon 		if (con == NULL) {
481b06ebda0SMatthew Dillon 			error = ENOMEM;
482b06ebda0SMatthew Dillon 			goto out;
483b06ebda0SMatthew Dillon 		}
484b06ebda0SMatthew Dillon 
485b06ebda0SMatthew Dillon 		bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));
486b06ebda0SMatthew Dillon 	} else if ((error = ng_hci_con_untimeout(con)) != 0)
487b06ebda0SMatthew Dillon 			goto out;
488b06ebda0SMatthew Dillon 
489b06ebda0SMatthew Dillon 	/*
490b06ebda0SMatthew Dillon 	 * Update connection descriptor and send notification
491b06ebda0SMatthew Dillon 	 * to the upper layers.
492b06ebda0SMatthew Dillon 	 */
493b06ebda0SMatthew Dillon 
494b06ebda0SMatthew Dillon 	con->con_handle = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
495b06ebda0SMatthew Dillon 	con->encryption_mode = ep->encryption_mode;
496b06ebda0SMatthew Dillon 
497b06ebda0SMatthew Dillon 	ng_hci_lp_con_cfm(con, ep->status);
498b06ebda0SMatthew Dillon 
499b06ebda0SMatthew Dillon 	/* Adjust connection state */
500b06ebda0SMatthew Dillon 	if (ep->status != 0)
501b06ebda0SMatthew Dillon 		ng_hci_free_con(con);
502b06ebda0SMatthew Dillon 	else {
503b06ebda0SMatthew Dillon 		con->state = NG_HCI_CON_OPEN;
504b06ebda0SMatthew Dillon 
505b06ebda0SMatthew Dillon 		/*
506b06ebda0SMatthew Dillon 		 * Change link policy for the ACL connections. Enable all
507b06ebda0SMatthew Dillon 		 * supported link modes. Enable Role switch as well if
508b06ebda0SMatthew Dillon 		 * device supports it.
509b06ebda0SMatthew Dillon 		 */
510b06ebda0SMatthew Dillon 
511b06ebda0SMatthew Dillon 		if (ep->link_type == NG_HCI_LINK_ACL) {
512b06ebda0SMatthew Dillon 			struct __link_policy {
513b06ebda0SMatthew Dillon 				ng_hci_cmd_pkt_t			 hdr;
514b06ebda0SMatthew Dillon 				ng_hci_write_link_policy_settings_cp	 cp;
515b06ebda0SMatthew Dillon 			} __attribute__ ((packed))			*lp;
516b06ebda0SMatthew Dillon 			struct mbuf					*m;
517b06ebda0SMatthew Dillon 
518b5523eacSSascha Wildner 			MGETHDR(m, M_NOWAIT, MT_DATA);
519b06ebda0SMatthew Dillon 			if (m != NULL) {
520b06ebda0SMatthew Dillon 				m->m_pkthdr.len = m->m_len = sizeof(*lp);
521b06ebda0SMatthew Dillon 				lp = mtod(m, struct __link_policy *);
522b06ebda0SMatthew Dillon 
523b06ebda0SMatthew Dillon 				lp->hdr.type = NG_HCI_CMD_PKT;
524b06ebda0SMatthew Dillon 				lp->hdr.opcode = htole16(NG_HCI_OPCODE(
525b06ebda0SMatthew Dillon 					NG_HCI_OGF_LINK_POLICY,
526b06ebda0SMatthew Dillon 					NG_HCI_OCF_WRITE_LINK_POLICY_SETTINGS));
527b06ebda0SMatthew Dillon 				lp->hdr.length = sizeof(lp->cp);
528b06ebda0SMatthew Dillon 
529b06ebda0SMatthew Dillon 				lp->cp.con_handle = ep->con_handle;
530b06ebda0SMatthew Dillon 
531b06ebda0SMatthew Dillon 				lp->cp.settings = 0;
532b06ebda0SMatthew Dillon 				if ((unit->features[0] & NG_HCI_LMP_SWITCH) &&
533b06ebda0SMatthew Dillon 				    unit->role_switch)
534b06ebda0SMatthew Dillon 					lp->cp.settings |= 0x1;
535b06ebda0SMatthew Dillon 				if (unit->features[0] & NG_HCI_LMP_HOLD_MODE)
536b06ebda0SMatthew Dillon 					lp->cp.settings |= 0x2;
537b06ebda0SMatthew Dillon 				if (unit->features[0] & NG_HCI_LMP_SNIFF_MODE)
538b06ebda0SMatthew Dillon 					lp->cp.settings |= 0x4;
539b06ebda0SMatthew Dillon 				if (unit->features[1] & NG_HCI_LMP_PARK_MODE)
540b06ebda0SMatthew Dillon 					lp->cp.settings |= 0x8;
541b06ebda0SMatthew Dillon 
542b06ebda0SMatthew Dillon 				lp->cp.settings &= unit->link_policy_mask;
543b06ebda0SMatthew Dillon 				lp->cp.settings = htole16(lp->cp.settings);
544b06ebda0SMatthew Dillon 
545b06ebda0SMatthew Dillon 				NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
546b06ebda0SMatthew Dillon 				if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
547b06ebda0SMatthew Dillon 					ng_hci_send_command(unit);
548b06ebda0SMatthew Dillon 			}
549b06ebda0SMatthew Dillon 		}
550b06ebda0SMatthew Dillon 	}
551b06ebda0SMatthew Dillon out:
552b06ebda0SMatthew Dillon 	NG_FREE_M(event);
553b06ebda0SMatthew Dillon 
554b06ebda0SMatthew Dillon 	return (error);
555b06ebda0SMatthew Dillon } /* con_compl */
556b06ebda0SMatthew Dillon 
557b06ebda0SMatthew Dillon /* Connection request event */
558b06ebda0SMatthew Dillon static int
con_req(ng_hci_unit_p unit,struct mbuf * event)559b06ebda0SMatthew Dillon con_req(ng_hci_unit_p unit, struct mbuf *event)
560b06ebda0SMatthew Dillon {
561b06ebda0SMatthew Dillon 	ng_hci_con_req_ep	*ep = NULL;
562b06ebda0SMatthew Dillon 	ng_hci_unit_con_p	 con = NULL;
563b06ebda0SMatthew Dillon 	int			 error = 0;
564b06ebda0SMatthew Dillon 
565b06ebda0SMatthew Dillon 	NG_HCI_M_PULLUP(event, sizeof(*ep));
566b06ebda0SMatthew Dillon 	if (event == NULL)
567b06ebda0SMatthew Dillon 		return (ENOBUFS);
568b06ebda0SMatthew Dillon 
569b06ebda0SMatthew Dillon 	ep = mtod(event, ng_hci_con_req_ep *);
570b06ebda0SMatthew Dillon 
571b06ebda0SMatthew Dillon 	/*
572b06ebda0SMatthew Dillon 	 * Find the first connection descriptor that matches the following:
573b06ebda0SMatthew Dillon 	 *
574b06ebda0SMatthew Dillon 	 * 1) con->link_type == ep->link_type
575b06ebda0SMatthew Dillon 	 *
576b06ebda0SMatthew Dillon 	 * 2) con->state == NG_HCI_CON_W4_LP_CON_RSP ||
577b06ebda0SMatthew Dillon 	 *    con->state == NG_HCI_CON_W4_CONN_COMPL
578b06ebda0SMatthew Dillon 	 *
579b06ebda0SMatthew Dillon 	 * 3) con->bdaddr == ep->bdaddr
580b06ebda0SMatthew Dillon 	 *
581b06ebda0SMatthew Dillon 	 * Possible cases:
582b06ebda0SMatthew Dillon 	 *
583b06ebda0SMatthew Dillon 	 * 1) We do not have connection descriptor. This is simple. Create
584b06ebda0SMatthew Dillon 	 *    new fresh connection descriptor and send notification to the
585b06ebda0SMatthew Dillon 	 *    appropriate upstream hook (based on link_type).
586b06ebda0SMatthew Dillon 	 *
587b06ebda0SMatthew Dillon 	 * 2) We found connection handle. This is more complicated.
588b06ebda0SMatthew Dillon 	 *
589b06ebda0SMatthew Dillon 	 * 2.1) ACL links
590b06ebda0SMatthew Dillon 	 *
591b06ebda0SMatthew Dillon 	 *      Since only one ACL link can exist between each pair of
592b06ebda0SMatthew Dillon 	 *      units then we have a race. Our upper layer has requested
593b06ebda0SMatthew Dillon 	 *      an ACL connection to the remote unit, but we did not send
594b06ebda0SMatthew Dillon 	 *      command yet. At the same time the remote unit has requested
595b06ebda0SMatthew Dillon 	 *      an ACL connection from us. In this case we will ignore
596b06ebda0SMatthew Dillon 	 *	Connection_Request event. This probably will cause connect
597b06ebda0SMatthew Dillon 	 *      failure	on both units.
598b06ebda0SMatthew Dillon 	 *
599b06ebda0SMatthew Dillon 	 * 2.2) SCO links
600b06ebda0SMatthew Dillon 	 *
601b06ebda0SMatthew Dillon 	 *      The spec on page 45 says :
602b06ebda0SMatthew Dillon 	 *
603b06ebda0SMatthew Dillon 	 *      "The master can support up to three SCO links to the same
604b06ebda0SMatthew Dillon 	 *       slave or to different slaves. A slave can support up to
605b06ebda0SMatthew Dillon 	 *       three SCO links from the same master, or two SCO links if
606b06ebda0SMatthew Dillon 	 *       the links originate from different masters."
607b06ebda0SMatthew Dillon 	 *
608b06ebda0SMatthew Dillon 	 *      The only problem is how to handle multiple SCO links between
609b06ebda0SMatthew Dillon 	 *      matster and slave. For now we will assume that multiple SCO
610b06ebda0SMatthew Dillon 	 *      links MUST be opened one after another.
611b06ebda0SMatthew Dillon 	 */
612b06ebda0SMatthew Dillon 
613b06ebda0SMatthew Dillon 	LIST_FOREACH(con, &unit->con_list, next)
614b06ebda0SMatthew Dillon 		if (con->link_type == ep->link_type &&
615b06ebda0SMatthew Dillon 		    (con->state == NG_HCI_CON_W4_LP_CON_RSP ||
616b06ebda0SMatthew Dillon 		     con->state == NG_HCI_CON_W4_CONN_COMPLETE) &&
617b06ebda0SMatthew Dillon 		    bcmp(&con->bdaddr, &ep->bdaddr, sizeof(bdaddr_t)) == 0)
618b06ebda0SMatthew Dillon 			break;
619b06ebda0SMatthew Dillon 
620b06ebda0SMatthew Dillon 	if (con == NULL) {
621b06ebda0SMatthew Dillon 		con = ng_hci_new_con(unit, ep->link_type);
622b06ebda0SMatthew Dillon 		if (con != NULL) {
623b06ebda0SMatthew Dillon 			bcopy(&ep->bdaddr, &con->bdaddr, sizeof(con->bdaddr));
624b06ebda0SMatthew Dillon 
625b06ebda0SMatthew Dillon 			con->state = NG_HCI_CON_W4_LP_CON_RSP;
626b06ebda0SMatthew Dillon 			ng_hci_con_timeout(con);
627b06ebda0SMatthew Dillon 
628b06ebda0SMatthew Dillon 			error = ng_hci_lp_con_ind(con, ep->uclass);
629b06ebda0SMatthew Dillon 			if (error != 0) {
630b06ebda0SMatthew Dillon 				ng_hci_con_untimeout(con);
631b06ebda0SMatthew Dillon 				ng_hci_free_con(con);
632b06ebda0SMatthew Dillon 			}
633b06ebda0SMatthew Dillon 		} else
634b06ebda0SMatthew Dillon 			error = ENOMEM;
635b06ebda0SMatthew Dillon 	}
636b06ebda0SMatthew Dillon 
637b06ebda0SMatthew Dillon 	NG_FREE_M(event);
638b06ebda0SMatthew Dillon 
639b06ebda0SMatthew Dillon 	return (error);
640b06ebda0SMatthew Dillon } /* con_req */
641b06ebda0SMatthew Dillon 
642b06ebda0SMatthew Dillon /* Disconnect complete event */
643b06ebda0SMatthew Dillon static int
discon_compl(ng_hci_unit_p unit,struct mbuf * event)644b06ebda0SMatthew Dillon discon_compl(ng_hci_unit_p unit, struct mbuf *event)
645b06ebda0SMatthew Dillon {
646b06ebda0SMatthew Dillon 	ng_hci_discon_compl_ep	*ep = NULL;
647b06ebda0SMatthew Dillon 	ng_hci_unit_con_p	 con = NULL;
648b06ebda0SMatthew Dillon 	int			 error = 0;
649b06ebda0SMatthew Dillon 	u_int16_t		 h;
650b06ebda0SMatthew Dillon 
651b06ebda0SMatthew Dillon 	NG_HCI_M_PULLUP(event, sizeof(*ep));
652b06ebda0SMatthew Dillon 	if (event == NULL)
653b06ebda0SMatthew Dillon 		return (ENOBUFS);
654b06ebda0SMatthew Dillon 
655b06ebda0SMatthew Dillon 	ep = mtod(event, ng_hci_discon_compl_ep *);
656b06ebda0SMatthew Dillon 
657b06ebda0SMatthew Dillon 	/*
658b06ebda0SMatthew Dillon 	 * XXX
659b06ebda0SMatthew Dillon 	 * Do we have to send notification if ep->status != 0?
660b06ebda0SMatthew Dillon 	 * For now we will send notification for both ACL and SCO connections
661b06ebda0SMatthew Dillon 	 * ONLY if ep->status == 0.
662b06ebda0SMatthew Dillon 	 */
663b06ebda0SMatthew Dillon 
664b06ebda0SMatthew Dillon 	if (ep->status == 0) {
665b06ebda0SMatthew Dillon 		h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
666b06ebda0SMatthew Dillon 		con = ng_hci_con_by_handle(unit, h);
667b06ebda0SMatthew Dillon 		if (con != NULL) {
668b06ebda0SMatthew Dillon 			error = ng_hci_lp_discon_ind(con, ep->reason);
669b06ebda0SMatthew Dillon 
670b06ebda0SMatthew Dillon 			/* Remove all timeouts (if any) */
671b06ebda0SMatthew Dillon 			if (con->flags & NG_HCI_CON_TIMEOUT_PENDING)
672b06ebda0SMatthew Dillon 				ng_hci_con_untimeout(con);
673b06ebda0SMatthew Dillon 
674b06ebda0SMatthew Dillon 			ng_hci_free_con(con);
675b06ebda0SMatthew Dillon 		} else {
676b06ebda0SMatthew Dillon 			NG_HCI_ALERT(
677b06ebda0SMatthew Dillon "%s: %s - invalid connection handle=%d\n",
678b06ebda0SMatthew Dillon 				__func__, NG_NODE_NAME(unit->node), h);
679b06ebda0SMatthew Dillon 			error = ENOENT;
680b06ebda0SMatthew Dillon 		}
681b06ebda0SMatthew Dillon 	}
682b06ebda0SMatthew Dillon 
683b06ebda0SMatthew Dillon 	NG_FREE_M(event);
684b06ebda0SMatthew Dillon 
685b06ebda0SMatthew Dillon 	return (error);
686b06ebda0SMatthew Dillon } /* discon_compl */
687b06ebda0SMatthew Dillon 
688b06ebda0SMatthew Dillon /* Encryption change event */
689b06ebda0SMatthew Dillon static int
encryption_change(ng_hci_unit_p unit,struct mbuf * event)690b06ebda0SMatthew Dillon encryption_change(ng_hci_unit_p unit, struct mbuf *event)
691b06ebda0SMatthew Dillon {
692b06ebda0SMatthew Dillon 	ng_hci_encryption_change_ep	*ep = NULL;
693b06ebda0SMatthew Dillon 	ng_hci_unit_con_p		 con = NULL;
694b06ebda0SMatthew Dillon 	int				 error = 0;
695b06ebda0SMatthew Dillon 
696b06ebda0SMatthew Dillon 	NG_HCI_M_PULLUP(event, sizeof(*ep));
697b06ebda0SMatthew Dillon 	if (event == NULL)
698b06ebda0SMatthew Dillon 		return (ENOBUFS);
699b06ebda0SMatthew Dillon 
700b06ebda0SMatthew Dillon 	ep = mtod(event, ng_hci_encryption_change_ep *);
701b06ebda0SMatthew Dillon 
702b06ebda0SMatthew Dillon 	if (ep->status == 0) {
703b06ebda0SMatthew Dillon 		u_int16_t	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
704b06ebda0SMatthew Dillon 
705b06ebda0SMatthew Dillon 		con = ng_hci_con_by_handle(unit, h);
706b06ebda0SMatthew Dillon 		if (con == NULL) {
707b06ebda0SMatthew Dillon 			NG_HCI_ALERT(
708b06ebda0SMatthew Dillon "%s: %s - invalid connection handle=%d\n",
709b06ebda0SMatthew Dillon 				__func__, NG_NODE_NAME(unit->node), h);
710b06ebda0SMatthew Dillon 			error = ENOENT;
711b06ebda0SMatthew Dillon 		} else if (con->link_type != NG_HCI_LINK_ACL) {
712b06ebda0SMatthew Dillon 			NG_HCI_ALERT(
713b06ebda0SMatthew Dillon "%s: %s - invalid link type=%d\n",
714b06ebda0SMatthew Dillon 				__func__, NG_NODE_NAME(unit->node),
715b06ebda0SMatthew Dillon 				con->link_type);
716b06ebda0SMatthew Dillon 			error = EINVAL;
717b06ebda0SMatthew Dillon 		} else if (ep->encryption_enable)
718b06ebda0SMatthew Dillon 			/* XXX is that true? */
719b06ebda0SMatthew Dillon 			con->encryption_mode = NG_HCI_ENCRYPTION_MODE_P2P;
720b06ebda0SMatthew Dillon 		else
721b06ebda0SMatthew Dillon 			con->encryption_mode = NG_HCI_ENCRYPTION_MODE_NONE;
722b06ebda0SMatthew Dillon 	} else
723b06ebda0SMatthew Dillon 		NG_HCI_ERR(
724b06ebda0SMatthew Dillon "%s: %s - failed to change encryption mode, status=%d\n",
725b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(unit->node), ep->status);
726b06ebda0SMatthew Dillon 
727b06ebda0SMatthew Dillon 	NG_FREE_M(event);
728b06ebda0SMatthew Dillon 
729b06ebda0SMatthew Dillon 	return (error);
730b06ebda0SMatthew Dillon } /* encryption_change */
731b06ebda0SMatthew Dillon 
732b06ebda0SMatthew Dillon /* Read remote feature complete event */
733b06ebda0SMatthew Dillon static int
read_remote_features_compl(ng_hci_unit_p unit,struct mbuf * event)734b06ebda0SMatthew Dillon read_remote_features_compl(ng_hci_unit_p unit, struct mbuf *event)
735b06ebda0SMatthew Dillon {
736b06ebda0SMatthew Dillon 	ng_hci_read_remote_features_compl_ep	*ep = NULL;
737b06ebda0SMatthew Dillon 	ng_hci_unit_con_p			 con = NULL;
738b06ebda0SMatthew Dillon 	ng_hci_neighbor_p			 n = NULL;
739b06ebda0SMatthew Dillon 	u_int16_t				 h;
740b06ebda0SMatthew Dillon 	int					 error = 0;
741b06ebda0SMatthew Dillon 
742b06ebda0SMatthew Dillon 	NG_HCI_M_PULLUP(event, sizeof(*ep));
743b06ebda0SMatthew Dillon 	if (event == NULL)
744b06ebda0SMatthew Dillon 		return (ENOBUFS);
745b06ebda0SMatthew Dillon 
746b06ebda0SMatthew Dillon 	ep = mtod(event, ng_hci_read_remote_features_compl_ep *);
747b06ebda0SMatthew Dillon 
748b06ebda0SMatthew Dillon 	if (ep->status == 0) {
749b06ebda0SMatthew Dillon 		/* Check if we have this connection handle */
750b06ebda0SMatthew Dillon 		h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
751b06ebda0SMatthew Dillon 		con = ng_hci_con_by_handle(unit, h);
752b06ebda0SMatthew Dillon 		if (con == NULL) {
753b06ebda0SMatthew Dillon 			NG_HCI_ALERT(
754b06ebda0SMatthew Dillon "%s: %s - invalid connection handle=%d\n",
755b06ebda0SMatthew Dillon 				__func__, NG_NODE_NAME(unit->node), h);
756b06ebda0SMatthew Dillon 			error = ENOENT;
757b06ebda0SMatthew Dillon 			goto out;
758b06ebda0SMatthew Dillon 		}
759b06ebda0SMatthew Dillon 
760b06ebda0SMatthew Dillon 		/* Update cache entry */
761b06ebda0SMatthew Dillon 		n = ng_hci_get_neighbor(unit, &con->bdaddr);
762b06ebda0SMatthew Dillon 		if (n == NULL) {
763b06ebda0SMatthew Dillon 			n = ng_hci_new_neighbor(unit);
764b06ebda0SMatthew Dillon 			if (n == NULL) {
765b06ebda0SMatthew Dillon 				error = ENOMEM;
766b06ebda0SMatthew Dillon 				goto out;
767b06ebda0SMatthew Dillon 			}
768b06ebda0SMatthew Dillon 
769b06ebda0SMatthew Dillon 			bcopy(&con->bdaddr, &n->bdaddr, sizeof(n->bdaddr));
770b06ebda0SMatthew Dillon 		} else
771b06ebda0SMatthew Dillon 			getmicrotime(&n->updated);
772b06ebda0SMatthew Dillon 
773b06ebda0SMatthew Dillon 		bcopy(ep->features, n->features, sizeof(n->features));
774b06ebda0SMatthew Dillon 	} else
775b06ebda0SMatthew Dillon 		NG_HCI_ERR(
776b06ebda0SMatthew Dillon "%s: %s - failed to read remote unit features, status=%d\n",
777b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(unit->node), ep->status);
778b06ebda0SMatthew Dillon out:
779b06ebda0SMatthew Dillon 	NG_FREE_M(event);
780b06ebda0SMatthew Dillon 
781b06ebda0SMatthew Dillon 	return (error);
782b06ebda0SMatthew Dillon } /* read_remote_features_compl */
783b06ebda0SMatthew Dillon 
784b06ebda0SMatthew Dillon /* QoS setup complete event */
785b06ebda0SMatthew Dillon static int
qos_setup_compl(ng_hci_unit_p unit,struct mbuf * event)786b06ebda0SMatthew Dillon qos_setup_compl(ng_hci_unit_p unit, struct mbuf *event)
787b06ebda0SMatthew Dillon {
788b06ebda0SMatthew Dillon 	ng_hci_qos_setup_compl_ep	*ep = NULL;
789b06ebda0SMatthew Dillon 	ng_hci_unit_con_p		 con = NULL;
790b06ebda0SMatthew Dillon 	u_int16_t			 h;
791b06ebda0SMatthew Dillon 	int				 error = 0;
792b06ebda0SMatthew Dillon 
793b06ebda0SMatthew Dillon 	NG_HCI_M_PULLUP(event, sizeof(*ep));
794b06ebda0SMatthew Dillon 	if (event == NULL)
795b06ebda0SMatthew Dillon 		return (ENOBUFS);
796b06ebda0SMatthew Dillon 
797b06ebda0SMatthew Dillon 	ep = mtod(event, ng_hci_qos_setup_compl_ep *);
798b06ebda0SMatthew Dillon 
799b06ebda0SMatthew Dillon 	/* Check if we have this connection handle */
800b06ebda0SMatthew Dillon 	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
801b06ebda0SMatthew Dillon 	con = ng_hci_con_by_handle(unit, h);
802b06ebda0SMatthew Dillon 	if (con == NULL) {
803b06ebda0SMatthew Dillon 		NG_HCI_ALERT(
804b06ebda0SMatthew Dillon "%s: %s - invalid connection handle=%d\n",
805b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(unit->node), h);
806b06ebda0SMatthew Dillon 		error = ENOENT;
807b06ebda0SMatthew Dillon 	} else if (con->link_type != NG_HCI_LINK_ACL) {
808b06ebda0SMatthew Dillon 		NG_HCI_ALERT(
809b06ebda0SMatthew Dillon "%s: %s - invalid link type=%d, handle=%d\n",
810b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(unit->node), con->link_type, h);
811b06ebda0SMatthew Dillon 		error = EINVAL;
812b06ebda0SMatthew Dillon 	} else if (con->state != NG_HCI_CON_OPEN) {
813b06ebda0SMatthew Dillon 		NG_HCI_ALERT(
814b06ebda0SMatthew Dillon "%s: %s - invalid connection state=%d, handle=%d\n",
815b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(unit->node),
816b06ebda0SMatthew Dillon 			con->state, h);
817b06ebda0SMatthew Dillon 		error = EINVAL;
818b06ebda0SMatthew Dillon 	} else /* Notify upper layer */
819b06ebda0SMatthew Dillon 		error = ng_hci_lp_qos_cfm(con, ep->status);
820b06ebda0SMatthew Dillon 
821b06ebda0SMatthew Dillon 	NG_FREE_M(event);
822b06ebda0SMatthew Dillon 
823b06ebda0SMatthew Dillon 	return (error);
824b06ebda0SMatthew Dillon } /* qos_setup_compl */
825b06ebda0SMatthew Dillon 
826b06ebda0SMatthew Dillon /* Hardware error event */
827b06ebda0SMatthew Dillon static int
hardware_error(ng_hci_unit_p unit,struct mbuf * event)828b06ebda0SMatthew Dillon hardware_error(ng_hci_unit_p unit, struct mbuf *event)
829b06ebda0SMatthew Dillon {
830b06ebda0SMatthew Dillon 	NG_HCI_ALERT(
831b06ebda0SMatthew Dillon "%s: %s - hardware error %#x\n",
832b06ebda0SMatthew Dillon 		__func__, NG_NODE_NAME(unit->node), *mtod(event, u_int8_t *));
833b06ebda0SMatthew Dillon 
834b06ebda0SMatthew Dillon 	NG_FREE_M(event);
835b06ebda0SMatthew Dillon 
836b06ebda0SMatthew Dillon 	return (0);
837b06ebda0SMatthew Dillon } /* hardware_error */
838b06ebda0SMatthew Dillon 
839b06ebda0SMatthew Dillon /* Role change event */
840b06ebda0SMatthew Dillon static int
role_change(ng_hci_unit_p unit,struct mbuf * event)841b06ebda0SMatthew Dillon role_change(ng_hci_unit_p unit, struct mbuf *event)
842b06ebda0SMatthew Dillon {
843b06ebda0SMatthew Dillon 	ng_hci_role_change_ep	*ep = NULL;
844b06ebda0SMatthew Dillon 	ng_hci_unit_con_p	 con = NULL;
845b06ebda0SMatthew Dillon 
846b06ebda0SMatthew Dillon 	NG_HCI_M_PULLUP(event, sizeof(*ep));
847b06ebda0SMatthew Dillon 	if (event == NULL)
848b06ebda0SMatthew Dillon 		return (ENOBUFS);
849b06ebda0SMatthew Dillon 
850b06ebda0SMatthew Dillon 	ep = mtod(event, ng_hci_role_change_ep *);
851b06ebda0SMatthew Dillon 
852b06ebda0SMatthew Dillon 	if (ep->status == 0) {
853b06ebda0SMatthew Dillon 		/* XXX shoud we also change "role" for SCO connections? */
854b06ebda0SMatthew Dillon 		con = ng_hci_con_by_bdaddr(unit, &ep->bdaddr, NG_HCI_LINK_ACL);
855b06ebda0SMatthew Dillon 		if (con != NULL)
856b06ebda0SMatthew Dillon 			con->role = ep->role;
857b06ebda0SMatthew Dillon 		else
858b06ebda0SMatthew Dillon 			NG_HCI_ALERT(
859b06ebda0SMatthew Dillon "%s: %s - ACL connection does not exist, bdaddr=%x:%x:%x:%x:%x:%x\n",
860b06ebda0SMatthew Dillon 				__func__, NG_NODE_NAME(unit->node),
861b06ebda0SMatthew Dillon 				ep->bdaddr.b[5], ep->bdaddr.b[4],
862b06ebda0SMatthew Dillon 				ep->bdaddr.b[3], ep->bdaddr.b[2],
863b06ebda0SMatthew Dillon 				ep->bdaddr.b[1], ep->bdaddr.b[0]);
864b06ebda0SMatthew Dillon 	} else
865b06ebda0SMatthew Dillon 		NG_HCI_ERR(
866b06ebda0SMatthew Dillon "%s: %s - failed to change role, status=%d, bdaddr=%x:%x:%x:%x:%x:%x\n",
867b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(unit->node), ep->status,
868b06ebda0SMatthew Dillon 			ep->bdaddr.b[5], ep->bdaddr.b[4], ep->bdaddr.b[3],
869b06ebda0SMatthew Dillon 			ep->bdaddr.b[2], ep->bdaddr.b[1], ep->bdaddr.b[0]);
870b06ebda0SMatthew Dillon 
871b06ebda0SMatthew Dillon 	NG_FREE_M(event);
872b06ebda0SMatthew Dillon 
873b06ebda0SMatthew Dillon 	return (0);
874b06ebda0SMatthew Dillon } /* role_change */
875b06ebda0SMatthew Dillon 
876b06ebda0SMatthew Dillon /* Number of completed packets event */
877b06ebda0SMatthew Dillon static int
num_compl_pkts(ng_hci_unit_p unit,struct mbuf * event)878b06ebda0SMatthew Dillon num_compl_pkts(ng_hci_unit_p unit, struct mbuf *event)
879b06ebda0SMatthew Dillon {
880b06ebda0SMatthew Dillon 	ng_hci_num_compl_pkts_ep	*ep = NULL;
881b06ebda0SMatthew Dillon 	ng_hci_unit_con_p		 con = NULL;
882b06ebda0SMatthew Dillon 	u_int16_t			 h, p;
883b06ebda0SMatthew Dillon 
884b06ebda0SMatthew Dillon 	NG_HCI_M_PULLUP(event, sizeof(*ep));
885b06ebda0SMatthew Dillon 	if (event == NULL)
886b06ebda0SMatthew Dillon 		return (ENOBUFS);
887b06ebda0SMatthew Dillon 
888b06ebda0SMatthew Dillon 	ep = mtod(event, ng_hci_num_compl_pkts_ep *);
889b06ebda0SMatthew Dillon 	m_adj(event, sizeof(*ep));
890b06ebda0SMatthew Dillon 
891b06ebda0SMatthew Dillon 	for (; ep->num_con_handles > 0; ep->num_con_handles --) {
892b06ebda0SMatthew Dillon 		/* Get connection handle */
893*05d02a38SAaron LI 		m_copydata(event, 0, sizeof(h), &h);
894b06ebda0SMatthew Dillon 		m_adj(event, sizeof(h));
895b06ebda0SMatthew Dillon 		h = NG_HCI_CON_HANDLE(le16toh(h));
896b06ebda0SMatthew Dillon 
897b06ebda0SMatthew Dillon 		/* Get number of completed packets */
898*05d02a38SAaron LI 		m_copydata(event, 0, sizeof(p), &p);
899b06ebda0SMatthew Dillon 		m_adj(event, sizeof(p));
900b06ebda0SMatthew Dillon 		p = le16toh(p);
901b06ebda0SMatthew Dillon 
902b06ebda0SMatthew Dillon 		/* Check if we have this connection handle */
903b06ebda0SMatthew Dillon 		con = ng_hci_con_by_handle(unit, h);
904b06ebda0SMatthew Dillon 		if (con != NULL) {
905b06ebda0SMatthew Dillon 			con->pending -= p;
906b06ebda0SMatthew Dillon 			if (con->pending < 0) {
907b06ebda0SMatthew Dillon 				NG_HCI_WARN(
908b06ebda0SMatthew Dillon "%s: %s - pending packet counter is out of sync! " \
909b06ebda0SMatthew Dillon "handle=%d, pending=%d, ncp=%d\n",	__func__, NG_NODE_NAME(unit->node),
910b06ebda0SMatthew Dillon 					con->con_handle, con->pending, p);
911b06ebda0SMatthew Dillon 
912b06ebda0SMatthew Dillon 				con->pending = 0;
913b06ebda0SMatthew Dillon 			}
914b06ebda0SMatthew Dillon 
915b06ebda0SMatthew Dillon 			/* Update buffer descriptor */
916b06ebda0SMatthew Dillon 			if (con->link_type == NG_HCI_LINK_ACL)
917b06ebda0SMatthew Dillon 				NG_HCI_BUFF_ACL_FREE(unit->buffer, p);
918b06ebda0SMatthew Dillon 			else
919b06ebda0SMatthew Dillon 				NG_HCI_BUFF_SCO_FREE(unit->buffer, p);
920b06ebda0SMatthew Dillon 		} else
921b06ebda0SMatthew Dillon 			NG_HCI_ALERT(
922b06ebda0SMatthew Dillon "%s: %s - invalid connection handle=%d\n",
923b06ebda0SMatthew Dillon 				__func__, NG_NODE_NAME(unit->node), h);
924b06ebda0SMatthew Dillon 	}
925b06ebda0SMatthew Dillon 
926b06ebda0SMatthew Dillon 	NG_FREE_M(event);
927b06ebda0SMatthew Dillon 
928b06ebda0SMatthew Dillon 	/* Send more data */
929b06ebda0SMatthew Dillon 	ng_hci_send_data(unit);
930b06ebda0SMatthew Dillon 
931b06ebda0SMatthew Dillon 	return (0);
932b06ebda0SMatthew Dillon } /* num_compl_pkts */
933b06ebda0SMatthew Dillon 
934b06ebda0SMatthew Dillon /* Mode change event */
935b06ebda0SMatthew Dillon static int
mode_change(ng_hci_unit_p unit,struct mbuf * event)936b06ebda0SMatthew Dillon mode_change(ng_hci_unit_p unit, struct mbuf *event)
937b06ebda0SMatthew Dillon {
938b06ebda0SMatthew Dillon 	ng_hci_mode_change_ep	*ep = NULL;
939b06ebda0SMatthew Dillon 	ng_hci_unit_con_p	 con = NULL;
940b06ebda0SMatthew Dillon 	int			 error = 0;
941b06ebda0SMatthew Dillon 
942b06ebda0SMatthew Dillon 	NG_HCI_M_PULLUP(event, sizeof(*ep));
943b06ebda0SMatthew Dillon 	if (event == NULL)
944b06ebda0SMatthew Dillon 		return (ENOBUFS);
945b06ebda0SMatthew Dillon 
946b06ebda0SMatthew Dillon 	ep = mtod(event, ng_hci_mode_change_ep *);
947b06ebda0SMatthew Dillon 
948b06ebda0SMatthew Dillon 	if (ep->status == 0) {
949b06ebda0SMatthew Dillon 		u_int16_t	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
950b06ebda0SMatthew Dillon 
951b06ebda0SMatthew Dillon 		con = ng_hci_con_by_handle(unit, h);
952b06ebda0SMatthew Dillon 		if (con == NULL) {
953b06ebda0SMatthew Dillon 			NG_HCI_ALERT(
954b06ebda0SMatthew Dillon "%s: %s - invalid connection handle=%d\n",
955b06ebda0SMatthew Dillon 				__func__, NG_NODE_NAME(unit->node), h);
956b06ebda0SMatthew Dillon 			error = ENOENT;
957b06ebda0SMatthew Dillon 		} else if (con->link_type != NG_HCI_LINK_ACL) {
958b06ebda0SMatthew Dillon 			NG_HCI_ALERT(
959b06ebda0SMatthew Dillon "%s: %s - invalid link type=%d\n",
960b06ebda0SMatthew Dillon 				__func__, NG_NODE_NAME(unit->node),
961b06ebda0SMatthew Dillon 				con->link_type);
962b06ebda0SMatthew Dillon 			error = EINVAL;
963b06ebda0SMatthew Dillon 		} else
964b06ebda0SMatthew Dillon 			con->mode = ep->unit_mode;
965b06ebda0SMatthew Dillon 	} else
966b06ebda0SMatthew Dillon 		NG_HCI_ERR(
967b06ebda0SMatthew Dillon "%s: %s - failed to change mode, status=%d\n",
968b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(unit->node), ep->status);
969b06ebda0SMatthew Dillon 
970b06ebda0SMatthew Dillon 	NG_FREE_M(event);
971b06ebda0SMatthew Dillon 
972b06ebda0SMatthew Dillon 	return (error);
973b06ebda0SMatthew Dillon } /* mode_change */
974b06ebda0SMatthew Dillon 
975b06ebda0SMatthew Dillon /* Data buffer overflow event */
976b06ebda0SMatthew Dillon static int
data_buffer_overflow(ng_hci_unit_p unit,struct mbuf * event)977b06ebda0SMatthew Dillon data_buffer_overflow(ng_hci_unit_p unit, struct mbuf *event)
978b06ebda0SMatthew Dillon {
979b06ebda0SMatthew Dillon 	NG_HCI_ALERT(
980b06ebda0SMatthew Dillon "%s: %s - %s data buffer overflow\n",
981b06ebda0SMatthew Dillon 		__func__, NG_NODE_NAME(unit->node),
982b06ebda0SMatthew Dillon 		(*mtod(event, u_int8_t *) == NG_HCI_LINK_ACL)? "ACL" : "SCO");
983b06ebda0SMatthew Dillon 
984b06ebda0SMatthew Dillon 	NG_FREE_M(event);
985b06ebda0SMatthew Dillon 
986b06ebda0SMatthew Dillon 	return (0);
987b06ebda0SMatthew Dillon } /* data_buffer_overflow */
988b06ebda0SMatthew Dillon 
989b06ebda0SMatthew Dillon /* Read clock offset complete event */
990b06ebda0SMatthew Dillon static int
read_clock_offset_compl(ng_hci_unit_p unit,struct mbuf * event)991b06ebda0SMatthew Dillon read_clock_offset_compl(ng_hci_unit_p unit, struct mbuf *event)
992b06ebda0SMatthew Dillon {
993b06ebda0SMatthew Dillon 	ng_hci_read_clock_offset_compl_ep	*ep = NULL;
994b06ebda0SMatthew Dillon 	ng_hci_unit_con_p			 con = NULL;
995b06ebda0SMatthew Dillon 	ng_hci_neighbor_p			 n = NULL;
996b06ebda0SMatthew Dillon 	int					 error = 0;
997b06ebda0SMatthew Dillon 
998b06ebda0SMatthew Dillon 	NG_HCI_M_PULLUP(event, sizeof(*ep));
999b06ebda0SMatthew Dillon 	if (event == NULL)
1000b06ebda0SMatthew Dillon 		return (ENOBUFS);
1001b06ebda0SMatthew Dillon 
1002b06ebda0SMatthew Dillon 	ep = mtod(event, ng_hci_read_clock_offset_compl_ep *);
1003b06ebda0SMatthew Dillon 
1004b06ebda0SMatthew Dillon 	if (ep->status == 0) {
1005b06ebda0SMatthew Dillon 		u_int16_t	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
1006b06ebda0SMatthew Dillon 
1007b06ebda0SMatthew Dillon 		con = ng_hci_con_by_handle(unit, h);
1008b06ebda0SMatthew Dillon 		if (con == NULL) {
1009b06ebda0SMatthew Dillon 			NG_HCI_ALERT(
1010b06ebda0SMatthew Dillon "%s: %s - invalid connection handle=%d\n",
1011b06ebda0SMatthew Dillon 				__func__, NG_NODE_NAME(unit->node), h);
1012b06ebda0SMatthew Dillon 			error = ENOENT;
1013b06ebda0SMatthew Dillon 			goto out;
1014b06ebda0SMatthew Dillon 		}
1015b06ebda0SMatthew Dillon 
1016b06ebda0SMatthew Dillon 		/* Update cache entry */
1017b06ebda0SMatthew Dillon 		n = ng_hci_get_neighbor(unit, &con->bdaddr);
1018b06ebda0SMatthew Dillon 		if (n == NULL) {
1019b06ebda0SMatthew Dillon 			n = ng_hci_new_neighbor(unit);
1020b06ebda0SMatthew Dillon 			if (n == NULL) {
1021b06ebda0SMatthew Dillon 				error = ENOMEM;
1022b06ebda0SMatthew Dillon 				goto out;
1023b06ebda0SMatthew Dillon 			}
1024b06ebda0SMatthew Dillon 
1025b06ebda0SMatthew Dillon 			bcopy(&con->bdaddr, &n->bdaddr, sizeof(n->bdaddr));
1026b06ebda0SMatthew Dillon 		} else
1027b06ebda0SMatthew Dillon 			getmicrotime(&n->updated);
1028b06ebda0SMatthew Dillon 
1029b06ebda0SMatthew Dillon 		n->clock_offset = le16toh(ep->clock_offset);
1030b06ebda0SMatthew Dillon 	} else
1031b06ebda0SMatthew Dillon 		NG_HCI_ERR(
1032b06ebda0SMatthew Dillon "%s: %s - failed to Read Remote Clock Offset, status=%d\n",
1033b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(unit->node), ep->status);
1034b06ebda0SMatthew Dillon out:
1035b06ebda0SMatthew Dillon 	NG_FREE_M(event);
1036b06ebda0SMatthew Dillon 
1037b06ebda0SMatthew Dillon 	return (error);
1038b06ebda0SMatthew Dillon } /* read_clock_offset_compl */
1039b06ebda0SMatthew Dillon 
1040b06ebda0SMatthew Dillon /* QoS violation event */
1041b06ebda0SMatthew Dillon static int
qos_violation(ng_hci_unit_p unit,struct mbuf * event)1042b06ebda0SMatthew Dillon qos_violation(ng_hci_unit_p unit, struct mbuf *event)
1043b06ebda0SMatthew Dillon {
1044b06ebda0SMatthew Dillon 	ng_hci_qos_violation_ep	*ep = NULL;
1045b06ebda0SMatthew Dillon 	ng_hci_unit_con_p	 con = NULL;
1046b06ebda0SMatthew Dillon 	u_int16_t		 h;
1047b06ebda0SMatthew Dillon 	int			 error = 0;
1048b06ebda0SMatthew Dillon 
1049b06ebda0SMatthew Dillon 	NG_HCI_M_PULLUP(event, sizeof(*ep));
1050b06ebda0SMatthew Dillon 	if (event == NULL)
1051b06ebda0SMatthew Dillon 		return (ENOBUFS);
1052b06ebda0SMatthew Dillon 
1053b06ebda0SMatthew Dillon 	ep = mtod(event, ng_hci_qos_violation_ep *);
1054b06ebda0SMatthew Dillon 
1055b06ebda0SMatthew Dillon 	/* Check if we have this connection handle */
1056b06ebda0SMatthew Dillon 	h = NG_HCI_CON_HANDLE(le16toh(ep->con_handle));
1057b06ebda0SMatthew Dillon 	con = ng_hci_con_by_handle(unit, h);
1058b06ebda0SMatthew Dillon 	if (con == NULL) {
1059b06ebda0SMatthew Dillon 		NG_HCI_ALERT(
1060b06ebda0SMatthew Dillon "%s: %s - invalid connection handle=%d\n",
1061b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(unit->node), h);
1062b06ebda0SMatthew Dillon 		error = ENOENT;
1063b06ebda0SMatthew Dillon 	} else if (con->link_type != NG_HCI_LINK_ACL) {
1064b06ebda0SMatthew Dillon 		NG_HCI_ALERT(
1065b06ebda0SMatthew Dillon "%s: %s - invalid link type=%d\n",
1066b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(unit->node), con->link_type);
1067b06ebda0SMatthew Dillon 		error = EINVAL;
1068b06ebda0SMatthew Dillon 	} else if (con->state != NG_HCI_CON_OPEN) {
1069b06ebda0SMatthew Dillon 		NG_HCI_ALERT(
1070b06ebda0SMatthew Dillon "%s: %s - invalid connection state=%d, handle=%d\n",
1071b06ebda0SMatthew Dillon 			__func__, NG_NODE_NAME(unit->node), con->state, h);
1072b06ebda0SMatthew Dillon 		error = EINVAL;
1073b06ebda0SMatthew Dillon 	} else /* Notify upper layer */
1074b06ebda0SMatthew Dillon 		error = ng_hci_lp_qos_ind(con);
1075b06ebda0SMatthew Dillon 
1076b06ebda0SMatthew Dillon 	NG_FREE_M(event);
1077b06ebda0SMatthew Dillon 
1078b06ebda0SMatthew Dillon 	return (error);
1079b06ebda0SMatthew Dillon } /* qos_violation */
1080b06ebda0SMatthew Dillon 
1081b06ebda0SMatthew Dillon /* Page scan mode change event */
1082b06ebda0SMatthew Dillon static int
page_scan_mode_change(ng_hci_unit_p unit,struct mbuf * event)1083b06ebda0SMatthew Dillon page_scan_mode_change(ng_hci_unit_p unit, struct mbuf *event)
1084b06ebda0SMatthew Dillon {
1085b06ebda0SMatthew Dillon 	ng_hci_page_scan_mode_change_ep	*ep = NULL;
1086b06ebda0SMatthew Dillon 	ng_hci_neighbor_p		 n = NULL;
1087b06ebda0SMatthew Dillon 	int				 error = 0;
1088b06ebda0SMatthew Dillon 
1089b06ebda0SMatthew Dillon 	NG_HCI_M_PULLUP(event, sizeof(*ep));
1090b06ebda0SMatthew Dillon 	if (event == NULL)
1091b06ebda0SMatthew Dillon 		return (ENOBUFS);
1092b06ebda0SMatthew Dillon 
1093b06ebda0SMatthew Dillon 	ep = mtod(event, ng_hci_page_scan_mode_change_ep *);
1094b06ebda0SMatthew Dillon 
1095b06ebda0SMatthew Dillon 	/* Update cache entry */
1096b06ebda0SMatthew Dillon 	n = ng_hci_get_neighbor(unit, &ep->bdaddr);
1097b06ebda0SMatthew Dillon 	if (n == NULL) {
1098b06ebda0SMatthew Dillon 		n = ng_hci_new_neighbor(unit);
1099b06ebda0SMatthew Dillon 		if (n == NULL) {
1100b06ebda0SMatthew Dillon 			error = ENOMEM;
1101b06ebda0SMatthew Dillon 			goto out;
1102b06ebda0SMatthew Dillon 		}
1103b06ebda0SMatthew Dillon 
1104b06ebda0SMatthew Dillon 		bcopy(&ep->bdaddr, &n->bdaddr, sizeof(n->bdaddr));
1105b06ebda0SMatthew Dillon 	} else
1106b06ebda0SMatthew Dillon 		getmicrotime(&n->updated);
1107b06ebda0SMatthew Dillon 
1108b06ebda0SMatthew Dillon 	n->page_scan_mode = ep->page_scan_mode;
1109b06ebda0SMatthew Dillon out:
1110b06ebda0SMatthew Dillon 	NG_FREE_M(event);
1111b06ebda0SMatthew Dillon 
1112b06ebda0SMatthew Dillon 	return (error);
1113b06ebda0SMatthew Dillon } /* page_scan_mode_change */
1114b06ebda0SMatthew Dillon 
1115b06ebda0SMatthew Dillon /* Page scan repetition mode change event */
1116b06ebda0SMatthew Dillon static int
page_scan_rep_mode_change(ng_hci_unit_p unit,struct mbuf * event)1117b06ebda0SMatthew Dillon page_scan_rep_mode_change(ng_hci_unit_p unit, struct mbuf *event)
1118b06ebda0SMatthew Dillon {
1119b06ebda0SMatthew Dillon 	ng_hci_page_scan_rep_mode_change_ep	*ep = NULL;
1120b06ebda0SMatthew Dillon 	ng_hci_neighbor_p			 n = NULL;
1121b06ebda0SMatthew Dillon 	int					 error = 0;
1122b06ebda0SMatthew Dillon 
1123b06ebda0SMatthew Dillon 	NG_HCI_M_PULLUP(event, sizeof(*ep));
1124b06ebda0SMatthew Dillon 	if (event == NULL)
1125b06ebda0SMatthew Dillon 		return (ENOBUFS);
1126b06ebda0SMatthew Dillon 
1127b06ebda0SMatthew Dillon 	ep = mtod(event, ng_hci_page_scan_rep_mode_change_ep *);
1128b06ebda0SMatthew Dillon 
1129b06ebda0SMatthew Dillon 	/* Update cache entry */
1130b06ebda0SMatthew Dillon 	n = ng_hci_get_neighbor(unit, &ep->bdaddr);
1131b06ebda0SMatthew Dillon 	if (n == NULL) {
1132b06ebda0SMatthew Dillon 		n = ng_hci_new_neighbor(unit);
1133b06ebda0SMatthew Dillon 		if (n == NULL) {
1134b06ebda0SMatthew Dillon 			error = ENOMEM;
1135b06ebda0SMatthew Dillon 			goto out;
1136b06ebda0SMatthew Dillon 		}
1137b06ebda0SMatthew Dillon 
1138b06ebda0SMatthew Dillon 		bcopy(&ep->bdaddr, &n->bdaddr, sizeof(n->bdaddr));
1139b06ebda0SMatthew Dillon 	} else
1140b06ebda0SMatthew Dillon 		getmicrotime(&n->updated);
1141b06ebda0SMatthew Dillon 
1142b06ebda0SMatthew Dillon 	n->page_scan_rep_mode = ep->page_scan_rep_mode;
1143b06ebda0SMatthew Dillon out:
1144b06ebda0SMatthew Dillon 	NG_FREE_M(event);
1145b06ebda0SMatthew Dillon 
1146b06ebda0SMatthew Dillon 	return (error);
1147b06ebda0SMatthew Dillon } /* page_scan_rep_mode_change */
1148b06ebda0SMatthew Dillon 
1149