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