kernel: Move us to using M_NOWAIT and M_WAITOK for mbuf functions.
[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 <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
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
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
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
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
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), (caddr_t) &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                         (caddr_t) &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
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
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
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
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
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
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
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
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
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), (caddr_t) &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), (caddr_t) &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
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
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
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
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
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
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