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