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