a29515ca4b7aeb2883dd5aef81ef0f63cc9e1ef9
[dragonfly.git] / sys / netgraph7 / bluetooth / hci / ng_hci_main.c
1 /*
2  * ng_hci_main.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_main.c,v 1.2 2003/03/18 00:09:36 max Exp $
31  * $FreeBSD: src/sys/netgraph/bluetooth/hci/ng_hci_main.c,v 1.6 2005/01/07 01:45:43 imp Exp $
32  * $DragonFly: src/sys/netgraph7/bluetooth/hci/ng_hci_main.c,v 1.2 2008/06/26 23:05:40 dillon Exp $
33  */
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/endian.h>
39 #include <sys/malloc.h>
40 #include <sys/mbuf.h>
41 #include <sys/queue.h>
42 #include "ng_message.h"
43 #include "netgraph.h"
44 #include "ng_parse.h"
45 #include "bluetooth/include/ng_bluetooth.h"
46 #include "bluetooth/include/ng_hci.h"
47 #include "bluetooth/hci/ng_hci_var.h"
48 #include "bluetooth/hci/ng_hci_prse.h"
49 #include "bluetooth/hci/ng_hci_cmds.h"
50 #include "bluetooth/hci/ng_hci_evnt.h"
51 #include "bluetooth/hci/ng_hci_ulpi.h"
52 #include "bluetooth/hci/ng_hci_misc.h"
53
54 /******************************************************************************
55  ******************************************************************************
56  **     This node implements Bluetooth Host Controller Interface (HCI)
57  ******************************************************************************
58  ******************************************************************************/
59
60 /* MALLOC define */
61 #ifdef NG_SEPARATE_MALLOC
62 MALLOC_DEFINE(M_NETGRAPH_HCI, "netgraph_hci", "Netgraph Bluetooth HCI node");
63 #else
64 #define M_NETGRAPH_HCI M_NETGRAPH
65 #endif /* NG_SEPARATE_MALLOC */
66
67 /* Netgraph node methods */
68 static  ng_constructor_t        ng_hci_constructor;
69 static  ng_shutdown_t           ng_hci_shutdown;
70 static  ng_newhook_t            ng_hci_newhook;
71 static  ng_connect_t            ng_hci_connect;
72 static  ng_disconnect_t         ng_hci_disconnect;
73 static  ng_rcvmsg_t             ng_hci_default_rcvmsg;
74 static  ng_rcvmsg_t             ng_hci_upper_rcvmsg;
75 static  ng_rcvdata_t            ng_hci_drv_rcvdata;
76 static  ng_rcvdata_t            ng_hci_acl_rcvdata;
77 static  ng_rcvdata_t            ng_hci_sco_rcvdata;
78 static  ng_rcvdata_t            ng_hci_raw_rcvdata;
79
80 /* Netgraph node type descriptor */
81 static  struct ng_type          typestruct = {
82         .version =      NG_ABI_VERSION,
83         .name =         NG_HCI_NODE_TYPE,
84         .constructor =  ng_hci_constructor,
85         .rcvmsg =       ng_hci_default_rcvmsg,
86         .shutdown =     ng_hci_shutdown,
87         .newhook =      ng_hci_newhook,
88         .connect =      ng_hci_connect,
89         .rcvdata =      ng_hci_drv_rcvdata,
90         .disconnect =   ng_hci_disconnect,
91         .cmdlist =      ng_hci_cmdlist,
92 };
93 NETGRAPH_INIT(hci, &typestruct);
94 MODULE_VERSION(ng_hci, NG_BLUETOOTH_VERSION);
95 MODULE_DEPEND(ng_hci, ng_bluetooth, NG_BLUETOOTH_VERSION,
96         NG_BLUETOOTH_VERSION, NG_BLUETOOTH_VERSION);
97
98 /*****************************************************************************
99  *****************************************************************************
100  **                   Netgraph methods implementation
101  *****************************************************************************
102  *****************************************************************************/
103
104 /*
105  * Create new instance of HCI node (new unit)
106  */
107
108 static int
109 ng_hci_constructor(node_p node)
110 {
111         ng_hci_unit_p   unit = NULL;
112
113         MALLOC(unit, ng_hci_unit_p, sizeof(*unit), M_NETGRAPH_HCI,
114                 M_WAITOK | M_NULLOK | M_ZERO);
115         if (unit == NULL)
116                 return (ENOMEM);
117
118         unit->node = node;
119         unit->debug = NG_HCI_WARN_LEVEL;
120
121         unit->link_policy_mask = 0xffff; /* Enable all supported modes */
122         unit->packet_mask = 0xffff; /* Enable all packet types */
123         unit->role_switch = 1; /* Enable role switch (if device supports it) */
124
125         /*
126          * Set default buffer info
127          *
128          * One HCI command
129          * One ACL packet with max. size of 17 bytes (1 DM1 packet)
130          * One SCO packet with max. size of 10 bytes (1 HV1 packet)
131          */
132
133         NG_HCI_BUFF_CMD_SET(unit->buffer, 1);
134         NG_HCI_BUFF_ACL_SET(unit->buffer, 1, 17, 1);  
135         NG_HCI_BUFF_SCO_SET(unit->buffer, 1, 10, 1);
136
137         /* Init command queue & command timeout handler */
138         ng_callout_init(&unit->cmd_timo);
139         NG_BT_MBUFQ_INIT(&unit->cmdq, NG_HCI_CMD_QUEUE_LEN);
140
141         /* Init lists */
142         LIST_INIT(&unit->con_list);
143         LIST_INIT(&unit->neighbors);
144
145         /*
146          * This node has to be a WRITER because both data and messages
147          * can change node state. 
148          */
149
150         NG_NODE_FORCE_WRITER(node);
151         NG_NODE_SET_PRIVATE(node, unit);
152
153         return (0);
154 } /* ng_hci_constructor */
155
156 /*
157  * Destroy the node
158  */
159
160 static int
161 ng_hci_shutdown(node_p node)
162 {
163         ng_hci_unit_p   unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
164
165         NG_NODE_SET_PRIVATE(node, NULL);
166         NG_NODE_UNREF(node);
167
168         unit->node = NULL;
169         ng_hci_unit_clean(unit, 0x16 /* Connection terminated by local host */);
170
171         NG_BT_MBUFQ_DESTROY(&unit->cmdq);
172
173         bzero(unit, sizeof(*unit));
174         FREE(unit, M_NETGRAPH_HCI);
175
176         return (0);
177 } /* ng_hci_shutdown */
178
179 /*
180  * Give our OK for a hook to be added. Unit driver is connected to the driver 
181  * (NG_HCI_HOOK_DRV) hook. Upper layer protocols are connected to appropriate
182  * (NG_HCI_HOOK_ACL or NG_HCI_HOOK_SCO) hooks.
183  */
184
185 static int
186 ng_hci_newhook(node_p node, hook_p hook, char const *name)
187 {
188         ng_hci_unit_p    unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
189         hook_p          *h = NULL;
190
191         if (strcmp(name, NG_HCI_HOOK_DRV) == 0)
192                 h = &unit->drv;
193         else if (strcmp(name, NG_HCI_HOOK_ACL) == 0)
194                 h = &unit->acl;
195         else if (strcmp(name, NG_HCI_HOOK_SCO) == 0)
196                 h = &unit->sco;
197         else if (strcmp(name, NG_HCI_HOOK_RAW) == 0)
198                 h = &unit->raw;
199         else
200                 return (EINVAL);
201
202         if (*h != NULL)
203                 return (EISCONN);
204
205         *h = hook;
206
207         return (0);
208 } /* ng_hci_newhook */
209
210 /*
211  * Give our final OK to connect hook
212  */
213
214 static int
215 ng_hci_connect(hook_p hook)
216 {
217         ng_hci_unit_p   unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
218
219         if (hook != unit->drv) {
220                 if (hook == unit->acl) {
221                         NG_HOOK_SET_RCVMSG(hook, ng_hci_upper_rcvmsg);
222                         NG_HOOK_SET_RCVDATA(hook, ng_hci_acl_rcvdata);
223                 } else if (hook == unit->sco) {
224                         NG_HOOK_SET_RCVMSG(hook, ng_hci_upper_rcvmsg);
225                         NG_HOOK_SET_RCVDATA(hook, ng_hci_sco_rcvdata);
226                 } else
227                         NG_HOOK_SET_RCVDATA(hook, ng_hci_raw_rcvdata);
228
229                 /* Send delayed notification to the upper layers */
230                 if (hook != unit->raw) 
231                         ng_send_fn(unit->node, hook, ng_hci_node_is_up, NULL,0);
232         } else
233                 unit->state |= NG_HCI_UNIT_CONNECTED;
234
235         return (0);
236 } /* ng_hci_connect */
237
238 /*
239  * Disconnect the hook
240  */
241
242 static int
243 ng_hci_disconnect(hook_p hook)
244 {
245         ng_hci_unit_p    unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
246
247         if (hook == unit->acl)
248                 unit->acl = NULL;
249         else if (hook == unit->sco)
250                 unit->sco = NULL;
251         else if (hook == unit->raw)
252                 unit->raw = NULL;
253         else if (hook == unit->drv) {
254                 unit->drv = NULL;
255
256                 /* Connection terminated by local host */
257                 ng_hci_unit_clean(unit, 0x16);
258                 unit->state &= ~(NG_HCI_UNIT_CONNECTED|NG_HCI_UNIT_INITED);
259         } else
260                 return (EINVAL);
261
262         /* Shutdown when all hooks are disconnected */
263         if ((NG_NODE_NUMHOOKS(NG_HOOK_NODE(hook)) == 0) &&
264             (NG_NODE_IS_VALID(NG_HOOK_NODE(hook))))
265                 ng_rmnode_self(NG_HOOK_NODE(hook));
266
267         return (0);
268 } /* ng_hci_disconnect */
269
270 /*
271  * Default control message processing routine. Control message could be:
272  *
273  * 1) GENERIC Netgraph messages
274  *
275  * 2) Control message directed to the node itself.
276  */
277
278 static int
279 ng_hci_default_rcvmsg(node_p node, item_p item, hook_p lasthook)
280 {
281         ng_hci_unit_p    unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
282         struct ng_mesg  *msg = NULL, *rsp = NULL;
283         int              error = 0;
284
285         NGI_GET_MSG(item, msg); 
286
287         switch (msg->header.typecookie) {
288         case NGM_GENERIC_COOKIE:
289                 switch (msg->header.cmd) {
290                 case NGM_TEXT_STATUS: {
291                         int     cmd_avail,
292                                 acl_total, acl_avail, acl_size,
293                                 sco_total, sco_avail, sco_size;
294
295                         NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_WAITOK | M_NULLOK);
296                         if (rsp == NULL) {
297                                 error = ENOMEM;
298                                 break;
299                         }
300
301                         NG_HCI_BUFF_CMD_GET(unit->buffer, cmd_avail);
302
303                         NG_HCI_BUFF_ACL_AVAIL(unit->buffer, acl_avail);
304                         NG_HCI_BUFF_ACL_TOTAL(unit->buffer, acl_total);
305                         NG_HCI_BUFF_ACL_SIZE(unit->buffer, acl_size);
306
307                         NG_HCI_BUFF_SCO_AVAIL(unit->buffer, sco_avail);
308                         NG_HCI_BUFF_SCO_TOTAL(unit->buffer, sco_total);
309                         NG_HCI_BUFF_SCO_SIZE(unit->buffer, sco_size);
310
311                         snprintf(rsp->data, NG_TEXTRESPONSE,
312                                 "bdaddr %x:%x:%x:%x:%x:%x\n" \
313                                 "Hooks  %s %s %s %s\n" \
314                                 "State  %#x\n" \
315                                 "Queue  cmd:%d\n" \
316                                 "Buffer cmd:%d,acl:%d,%d,%d,sco:%d,%d,%d",
317                                 unit->bdaddr.b[5], unit->bdaddr.b[4],
318                                 unit->bdaddr.b[3], unit->bdaddr.b[2],
319                                 unit->bdaddr.b[1], unit->bdaddr.b[0],
320                                 (unit->drv != NULL)? NG_HCI_HOOK_DRV : "",
321                                 (unit->acl != NULL)? NG_HCI_HOOK_ACL : "",
322                                 (unit->sco != NULL)? NG_HCI_HOOK_SCO : "",
323                                 (unit->raw != NULL)? NG_HCI_HOOK_RAW : "",
324                                 unit->state,
325                                 NG_BT_MBUFQ_LEN(&unit->cmdq),
326                                 cmd_avail,
327                                 acl_avail, acl_total, acl_size, 
328                                 sco_avail, sco_total, sco_size);
329                         } break;
330
331                 default:
332                         error = EINVAL;
333                         break;
334                 }
335                 break;
336
337         case NGM_HCI_COOKIE:
338                 switch (msg->header.cmd) {
339                 /* Get current node state */
340                 case NGM_HCI_NODE_GET_STATE:
341                         NG_MKRESPONSE(rsp, msg, sizeof(unit->state), M_WAITOK | M_NULLOK);
342                         if (rsp == NULL) {
343                                 error = ENOMEM;
344                                 break;
345                         }
346
347                         *((ng_hci_node_state_ep *)(rsp->data)) = unit->state;
348                         break;
349
350                 /* Turn INITED bit - node initialized */
351                 case NGM_HCI_NODE_INIT:
352                         if (bcmp(&unit->bdaddr, NG_HCI_BDADDR_ANY,
353                                         sizeof(bdaddr_t)) == 0) {
354                                 error = ENXIO;
355                                 break;
356                         }
357                                 
358                         unit->state |= NG_HCI_UNIT_INITED;
359
360                         ng_hci_node_is_up(unit->node, unit->acl, NULL, 0);
361                         ng_hci_node_is_up(unit->node, unit->sco, NULL, 0);
362                         break;
363
364                 /* Get node debug level */
365                 case NGM_HCI_NODE_GET_DEBUG:
366                         NG_MKRESPONSE(rsp, msg, sizeof(unit->debug), M_WAITOK | M_NULLOK);
367                         if (rsp == NULL) {
368                                 error = ENOMEM;
369                                 break;
370                         }
371
372                         *((ng_hci_node_debug_ep *)(rsp->data)) = unit->debug;
373                         break;
374
375                 /* Set node debug level */
376                 case NGM_HCI_NODE_SET_DEBUG:
377                         if (msg->header.arglen != sizeof(ng_hci_node_debug_ep)){
378                                 error = EMSGSIZE;
379                                 break;
380                         }
381
382                         unit->debug = *((ng_hci_node_debug_ep *)(msg->data));
383                         break;
384
385                 /* Get buffer info */
386                 case NGM_HCI_NODE_GET_BUFFER: {
387                         ng_hci_node_buffer_ep   *ep = NULL;
388
389                         NG_MKRESPONSE(rsp, msg, sizeof(ng_hci_node_buffer_ep),
390                                 M_WAITOK | M_NULLOK);
391                         if (rsp == NULL) {
392                                 error = ENOMEM;
393                                 break;
394                         }
395
396                         ep = (ng_hci_node_buffer_ep *)(rsp->data);
397
398                         NG_HCI_BUFF_CMD_GET(unit->buffer, ep->cmd_free);
399                         NG_HCI_BUFF_ACL_AVAIL(unit->buffer, ep->acl_free);
400                         NG_HCI_BUFF_ACL_TOTAL(unit->buffer, ep->acl_pkts);
401                         NG_HCI_BUFF_ACL_SIZE(unit->buffer, ep->acl_size);
402                         NG_HCI_BUFF_SCO_AVAIL(unit->buffer, ep->sco_free);
403                         NG_HCI_BUFF_SCO_TOTAL(unit->buffer, ep->sco_pkts);
404                         NG_HCI_BUFF_SCO_SIZE(unit->buffer, ep->sco_size);
405                         } break;
406
407                 /* Get BDADDR */
408                 case NGM_HCI_NODE_GET_BDADDR:
409                         NG_MKRESPONSE(rsp, msg, sizeof(bdaddr_t), M_WAITOK | M_NULLOK);
410                         if (rsp == NULL) {
411                                 error = ENOMEM;
412                                 break;
413                         }
414
415                         bcopy(&unit->bdaddr, rsp->data, sizeof(bdaddr_t));
416                         break;
417
418                 /* Get features */
419                 case NGM_HCI_NODE_GET_FEATURES:
420                         NG_MKRESPONSE(rsp,msg,sizeof(unit->features),M_WAITOK | M_NULLOK);
421                         if (rsp == NULL) {
422                                 error = ENOMEM;
423                                 break;
424                         }
425
426                         bcopy(&unit->features,rsp->data,sizeof(unit->features));
427                         break;
428
429                 /* Get stat */
430                 case NGM_HCI_NODE_GET_STAT:
431                         NG_MKRESPONSE(rsp, msg, sizeof(unit->stat), M_WAITOK | M_NULLOK);
432                         if (rsp == NULL) {
433                                 error = ENOMEM;
434                                 break;
435                         }
436
437                         bcopy(&unit->stat, rsp->data, sizeof(unit->stat));
438                         break;
439
440                 /* Reset stat */
441                 case NGM_HCI_NODE_RESET_STAT:
442                         NG_HCI_STAT_RESET(unit->stat);
443                         break;
444
445                 /* Clean up neighbors list */
446                 case NGM_HCI_NODE_FLUSH_NEIGHBOR_CACHE:
447                         ng_hci_flush_neighbor_cache(unit);
448                         break;
449
450                 /* Get neighbor cache entries */
451                 case NGM_HCI_NODE_GET_NEIGHBOR_CACHE: {
452                         ng_hci_neighbor_p                        n = NULL;
453                         ng_hci_node_get_neighbor_cache_ep       *e1 = NULL;
454                         ng_hci_node_neighbor_cache_entry_ep     *e2 = NULL;
455                         int                                      s = 0;
456
457                         /* Look for the fresh entries in the cache */
458                         for (n = LIST_FIRST(&unit->neighbors); n != NULL; ) {
459                                 ng_hci_neighbor_p       nn = LIST_NEXT(n, next);
460
461                                 if (ng_hci_neighbor_stale(n))
462                                         ng_hci_free_neighbor(n);
463                                 else
464                                         s ++;
465
466                                 n = nn;
467                         }
468                         if (s > NG_HCI_MAX_NEIGHBOR_NUM)
469                                 s = NG_HCI_MAX_NEIGHBOR_NUM;
470
471                         /* Prepare response */
472                         NG_MKRESPONSE(rsp, msg, sizeof(*e1) + s * sizeof(*e2),
473                                 M_WAITOK | M_NULLOK);
474                         if (rsp == NULL) {
475                                 error = ENOMEM;
476                                 break;
477                         }
478
479                         e1 = (ng_hci_node_get_neighbor_cache_ep *)(rsp->data);
480                         e2 = (ng_hci_node_neighbor_cache_entry_ep *)(e1 + 1);
481
482                         e1->num_entries = s;
483
484                         LIST_FOREACH(n, &unit->neighbors, next) {
485                                 e2->page_scan_rep_mode = n->page_scan_rep_mode;
486                                 e2->page_scan_mode = n->page_scan_mode;
487                                 e2->clock_offset = n->clock_offset;
488                                 bcopy(&n->bdaddr, &e2->bdaddr, 
489                                         sizeof(e2->bdaddr));
490                                 bcopy(&n->features, &e2->features,
491                                         sizeof(e2->features));
492
493                                 e2 ++;
494                                 if (--s <= 0)
495                                         break;
496                         }
497                         } break;
498
499                 /* Get connection list */
500                 case NGM_HCI_NODE_GET_CON_LIST: {
501                         ng_hci_unit_con_p        c = NULL;
502                         ng_hci_node_con_list_ep *e1 = NULL;
503                         ng_hci_node_con_ep      *e2 = NULL;
504                         int                      s = 0;
505
506                         /* Count number of connections in the list */
507                         LIST_FOREACH(c, &unit->con_list, next)
508                                 s ++;
509                         if (s > NG_HCI_MAX_CON_NUM)
510                                 s = NG_HCI_MAX_CON_NUM;
511
512                         /* Prepare response */
513                         NG_MKRESPONSE(rsp, msg, sizeof(*e1) + s * sizeof(*e2), 
514                                 M_WAITOK | M_NULLOK);
515                         if (rsp == NULL) {
516                                 error = ENOMEM;
517                                 break;
518                         }
519
520                         e1 = (ng_hci_node_con_list_ep *)(rsp->data);
521                         e2 = (ng_hci_node_con_ep *)(e1 + 1);
522
523                         e1->num_connections = s;
524
525                         LIST_FOREACH(c, &unit->con_list, next) {
526                                 e2->link_type = c->link_type;
527                                 e2->encryption_mode= c->encryption_mode;
528                                 e2->mode = c->mode;
529                                 e2->role = c->role;
530
531                                 e2->state = c->state;
532
533                                 e2->pending = c->pending;
534                                 e2->queue_len = NG_BT_ITEMQ_LEN(&c->conq);
535
536                                 e2->con_handle = c->con_handle;
537                                 bcopy(&c->bdaddr, &e2->bdaddr, 
538                                         sizeof(e2->bdaddr));
539
540                                 e2 ++;
541                                 if (--s <= 0)
542                                         break;
543                         }
544                         } break;
545
546                 /* Get link policy settings mask */
547                 case NGM_HCI_NODE_GET_LINK_POLICY_SETTINGS_MASK:
548                         NG_MKRESPONSE(rsp, msg, sizeof(unit->link_policy_mask),
549                                 M_WAITOK | M_NULLOK);
550                         if (rsp == NULL) {
551                                 error = ENOMEM;
552                                 break;
553                         }
554
555                         *((ng_hci_node_link_policy_mask_ep *)(rsp->data)) = 
556                                 unit->link_policy_mask;
557                         break;
558
559                 /* Set link policy settings mask */
560                 case NGM_HCI_NODE_SET_LINK_POLICY_SETTINGS_MASK:
561                         if (msg->header.arglen !=
562                                 sizeof(ng_hci_node_link_policy_mask_ep)) {
563                                 error = EMSGSIZE;
564                                 break;
565                         }
566
567                         unit->link_policy_mask = 
568                                 *((ng_hci_node_link_policy_mask_ep *)
569                                         (msg->data));
570                         break;
571
572                 /* Get packet mask */
573                 case NGM_HCI_NODE_GET_PACKET_MASK:
574                         NG_MKRESPONSE(rsp, msg, sizeof(unit->packet_mask),
575                                 M_WAITOK | M_NULLOK);
576                         if (rsp == NULL) {
577                                 error = ENOMEM;
578                                 break;
579                         }
580
581                         *((ng_hci_node_packet_mask_ep *)(rsp->data)) = 
582                                 unit->packet_mask;
583                         break;
584
585                 /* Set packet mask */
586                 case NGM_HCI_NODE_SET_PACKET_MASK:
587                         if (msg->header.arglen !=
588                                         sizeof(ng_hci_node_packet_mask_ep)) {
589                                 error = EMSGSIZE;
590                                 break;
591                         }
592
593                         unit->packet_mask = 
594                                 *((ng_hci_node_packet_mask_ep *)(msg->data));
595                         break;
596
597                 /* Get role switch */
598                 case NGM_HCI_NODE_GET_ROLE_SWITCH:
599                         NG_MKRESPONSE(rsp, msg, sizeof(unit->role_switch),
600                                 M_WAITOK | M_NULLOK);
601                         if (rsp == NULL) {
602                                 error = ENOMEM;
603                                 break;
604                         }
605
606                         *((ng_hci_node_role_switch_ep *)(rsp->data)) = 
607                                 unit->role_switch;
608                         break;
609
610                 /* Set role switch */
611                 case NGM_HCI_NODE_SET_ROLE_SWITCH:
612                         if (msg->header.arglen !=
613                                         sizeof(ng_hci_node_role_switch_ep)) {
614                                 error = EMSGSIZE;
615                                 break;
616                         }
617
618                         unit->role_switch = 
619                                 *((ng_hci_node_role_switch_ep *)(msg->data));
620                         break;
621
622                 default:
623                         error = EINVAL;
624                         break;
625                 }
626                 break;
627
628         default:
629                 error = EINVAL;
630                 break;
631         }
632
633         /* NG_RESPOND_MSG should take care of "item" and "rsp" */
634         NG_RESPOND_MSG(error, node, item, rsp);
635         NG_FREE_MSG(msg);
636
637         return (error);
638 } /* ng_hci_default_rcvmsg */
639
640 /*
641  * Process control message from upstream hooks (ACL and SCO).
642  * Handle LP_xxx messages here, give everything else to default routine.
643  */
644
645 static int
646 ng_hci_upper_rcvmsg(node_p node, item_p item, hook_p lasthook)
647 {
648         ng_hci_unit_p   unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node);
649         int             error = 0;
650
651         switch (NGI_MSG(item)->header.typecookie) {
652         case NGM_HCI_COOKIE:
653                 switch (NGI_MSG(item)->header.cmd) {
654                 case NGM_HCI_LP_CON_REQ:
655                         error = ng_hci_lp_con_req(unit, item, lasthook);
656                         break;
657
658                 case NGM_HCI_LP_DISCON_REQ: /* XXX not defined by specs */
659                         error = ng_hci_lp_discon_req(unit, item, lasthook);
660                         break;
661
662                 case NGM_HCI_LP_CON_RSP:
663                         error = ng_hci_lp_con_rsp(unit, item, lasthook);
664                         break;
665
666                 case NGM_HCI_LP_QOS_REQ:
667                         error = ng_hci_lp_qos_req(unit, item, lasthook);
668                         break;
669
670                 default:
671                         error = ng_hci_default_rcvmsg(node, item, lasthook);
672                         break;
673                 }
674                 break;
675
676         default:
677                 error = ng_hci_default_rcvmsg(node, item, lasthook);
678                 break;
679         }
680
681         return (error);
682 } /* ng_hci_upper_rcvmsg */
683
684 /*
685  * Process data packet from the driver hook. 
686  * We expect HCI events, ACL or SCO data packets.
687  */
688
689 static int
690 ng_hci_drv_rcvdata(hook_p hook, item_p item)
691 {
692         ng_hci_unit_p    unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
693         struct mbuf     *m = NULL;
694         int              error = 0;
695
696         /* Process packet */
697         m = NGI_M(item); /* item still has mbuf, just peeking */
698         m->m_flags |= M_PROTO1; /* mark as incoming packet */
699
700         NG_HCI_STAT_BYTES_RECV(unit->stat, m->m_pkthdr.len);
701
702         /* Give copy packet to RAW hook */
703         ng_hci_mtap(unit, m);
704
705         /*
706          * XXX XXX XXX
707          * Lower layer drivers MUST NOT send mbuf chain with empty mbuf at
708          * the beginning of the chain. HCI layer WILL NOT call m_pullup() here.
709          */
710
711         switch (*mtod(m, u_int8_t *)) {
712         case NG_HCI_ACL_DATA_PKT:
713                 NG_HCI_STAT_ACL_RECV(unit->stat);
714
715                 if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY ||
716                     unit->acl == NULL || NG_HOOK_NOT_VALID(unit->acl)) {
717                         NG_HCI_WARN(
718 "%s: %s - could not forward HCI ACL data packet, state=%#x, hook=%p\n",
719                                 __func__, NG_NODE_NAME(unit->node), 
720                                 unit->state, unit->acl);
721
722                         NG_FREE_ITEM(item);
723                 } else
724                         NG_FWD_ITEM_HOOK(error, item, unit->acl);
725                 break;
726
727         case NG_HCI_SCO_DATA_PKT:
728                 NG_HCI_STAT_SCO_RECV(unit->stat);
729
730                 if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY ||
731                     unit->sco == NULL || NG_HOOK_NOT_VALID(unit->sco)) {
732                         NG_HCI_WARN(
733 "%s: %s - could not forward HCI SCO data packet, state=%#x, hook=%p\n",
734                                 __func__, NG_NODE_NAME(unit->node), 
735                                 unit->state, unit->sco);
736
737                         NG_FREE_ITEM(item);
738                 } else
739                         NG_FWD_ITEM_HOOK(error, item, unit->sco);
740                 break;
741         
742         case NG_HCI_EVENT_PKT:
743                 NG_HCI_STAT_EVNT_RECV(unit->stat);
744
745                 /* Detach mbuf, discard item and process event */
746                 NGI_GET_M(item, m);
747                 NG_FREE_ITEM(item);
748
749                 error = ng_hci_process_event(unit, m);
750                 break;
751                 
752         default:
753                 NG_HCI_ALERT(
754 "%s: %s - got unknown HCI packet type=%#x\n",
755                         __func__, NG_NODE_NAME(unit->node),
756                         *mtod(m, u_int8_t *));
757
758                 NG_FREE_ITEM(item);
759
760                 error = EINVAL;
761                 break;
762         }
763
764         return (error);
765 } /* ng_hci_drv_rcvdata */
766
767 /*
768  * Process data packet from ACL upstream hook.
769  * We expect valid HCI ACL data packets.
770  */
771
772 static int
773 ng_hci_acl_rcvdata(hook_p hook, item_p item)
774 {
775         ng_hci_unit_p            unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
776         struct mbuf             *m = NULL;
777         ng_hci_unit_con_p        con = NULL;
778         u_int16_t                con_handle;
779         int                      size, error = 0;
780
781         NG_HCI_BUFF_ACL_SIZE(unit->buffer, size);
782
783         /* Check packet */
784         NGI_GET_M(item, m);
785
786         if (*mtod(m, u_int8_t *) != NG_HCI_ACL_DATA_PKT) {
787                 NG_HCI_ALERT(
788 "%s: %s - invalid HCI data packet type=%#x\n",
789                         __func__, NG_NODE_NAME(unit->node),
790                         *mtod(m, u_int8_t *));
791
792                 error = EINVAL;
793                 goto drop;
794         }
795
796         if (m->m_pkthdr.len < sizeof(ng_hci_acldata_pkt_t) ||
797             m->m_pkthdr.len > sizeof(ng_hci_acldata_pkt_t) + size) {
798                 NG_HCI_ALERT(
799 "%s: %s - invalid HCI ACL data packet, len=%d, mtu=%d\n",
800                         __func__, NG_NODE_NAME(unit->node), 
801                         m->m_pkthdr.len, size);
802
803                 error = EMSGSIZE;
804                 goto drop;
805         }
806
807         NG_HCI_M_PULLUP(m, sizeof(ng_hci_acldata_pkt_t));
808         if (m == NULL) {
809                 error = ENOBUFS;
810                 goto drop;
811         }
812
813         con_handle = NG_HCI_CON_HANDLE(le16toh(
814                         mtod(m, ng_hci_acldata_pkt_t *)->con_handle));
815         size = le16toh(mtod(m, ng_hci_acldata_pkt_t *)->length);
816
817         if (m->m_pkthdr.len != sizeof(ng_hci_acldata_pkt_t) + size) {
818                 NG_HCI_ALERT(
819 "%s: %s - invalid HCI ACL data packet size, len=%d, length=%d\n",
820                         __func__, NG_NODE_NAME(unit->node), 
821                         m->m_pkthdr.len, size);
822
823                 error = EMSGSIZE;
824                 goto drop;
825         }
826
827         /* Queue packet */
828         con = ng_hci_con_by_handle(unit, con_handle);
829         if (con == NULL) {
830                 NG_HCI_ERR(
831 "%s: %s - unexpected HCI ACL data packet. Connection does not exists, " \
832 "con_handle=%d\n",      __func__, NG_NODE_NAME(unit->node), con_handle);
833
834                 error = ENOENT;
835                 goto drop;
836         }
837
838         if (con->link_type != NG_HCI_LINK_ACL) {
839                 NG_HCI_ERR(
840 "%s: %s - unexpected HCI ACL data packet. Not ACL link, con_handle=%d, " \
841 "link_type=%d\n",       __func__, NG_NODE_NAME(unit->node), 
842                         con_handle, con->link_type);
843
844                 error = EINVAL;
845                 goto drop;
846         }
847
848         if (con->state != NG_HCI_CON_OPEN) {
849                 NG_HCI_ERR(
850 "%s: %s - unexpected HCI ACL data packet. Invalid connection state=%d, " \
851 "con_handle=%d\n",       __func__, NG_NODE_NAME(unit->node), 
852                         con->state, con_handle);
853
854                 error = EHOSTDOWN;
855                 goto drop;
856         }
857
858         if (NG_BT_ITEMQ_FULL(&con->conq)) {
859                 NG_HCI_ALERT(
860 "%s: %s - dropping HCI ACL data packet, con_handle=%d, len=%d, queue_len=%d\n",
861                          __func__, NG_NODE_NAME(unit->node), con_handle, 
862                         m->m_pkthdr.len, NG_BT_ITEMQ_LEN(&con->conq));
863
864                 NG_BT_ITEMQ_DROP(&con->conq);
865
866                 error = ENOBUFS;
867                 goto drop;
868         }
869
870         /* Queue item and schedule data transfer */
871         NGI_M(item) = m;
872         NG_BT_ITEMQ_ENQUEUE(&con->conq, item);
873         item = NULL;
874         m = NULL;
875
876         ng_hci_send_data(unit);
877 drop:
878         if (item != NULL)
879                 NG_FREE_ITEM(item);
880
881         NG_FREE_M(m); /* NG_FREE_M() checks for m != NULL */
882
883         return (error);
884 } /* ng_hci_acl_rcvdata */
885
886 /*
887  * Process data packet from SCO upstream hook.
888  * We expect valid HCI SCO data packets
889  */
890
891 static int
892 ng_hci_sco_rcvdata(hook_p hook, item_p item)
893 {
894         ng_hci_unit_p            unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
895         struct mbuf             *m = NULL;
896         ng_hci_unit_con_p        con = NULL;
897         u_int16_t                con_handle;
898         int                      size, error = 0;
899
900         NG_HCI_BUFF_SCO_SIZE(unit->buffer, size);
901
902         /* Check packet */
903         NGI_GET_M(item, m);
904
905         if (*mtod(m, u_int8_t *) != NG_HCI_SCO_DATA_PKT) {
906                 NG_HCI_ALERT(
907 "%s: %s - invalid HCI data packet type=%#x\n",
908                         __func__, NG_NODE_NAME(unit->node),
909                         *mtod(m, u_int8_t *));
910
911                 error = EINVAL;
912                 goto drop;
913         }
914
915         if (m->m_pkthdr.len < sizeof(ng_hci_scodata_pkt_t) ||
916             m->m_pkthdr.len > sizeof(ng_hci_scodata_pkt_t) + size) {
917                 NG_HCI_ALERT(
918 "%s: %s - invalid HCI SCO data packet, len=%d, mtu=%d\n",
919                         __func__, NG_NODE_NAME(unit->node), 
920                         m->m_pkthdr.len, size);
921
922                 error = EMSGSIZE;
923                 goto drop;
924         }
925
926         NG_HCI_M_PULLUP(m, sizeof(ng_hci_scodata_pkt_t));
927         if (m == NULL) {
928                 error = ENOBUFS;
929                 goto drop;
930         }
931
932         con_handle = NG_HCI_CON_HANDLE(le16toh(
933                         mtod(m, ng_hci_scodata_pkt_t *)->con_handle));
934         size = mtod(m, ng_hci_scodata_pkt_t *)->length;
935
936         if (m->m_pkthdr.len != sizeof(ng_hci_scodata_pkt_t) + size) {
937                 NG_HCI_ALERT(
938 "%s: %s - invalid HCI SCO data packet size, len=%d, length=%d\n",
939                         __func__, NG_NODE_NAME(unit->node), 
940                         m->m_pkthdr.len, size);
941
942                 error = EMSGSIZE;
943                 goto drop;
944         }
945
946         /* Queue packet */
947         con = ng_hci_con_by_handle(unit, con_handle);
948         if (con == NULL) {
949                 NG_HCI_ERR(
950 "%s: %s - unexpected HCI SCO data packet. Connection does not exists, " \
951 "con_handle=%d\n",      __func__, NG_NODE_NAME(unit->node), con_handle);
952
953                 error = ENOENT;
954                 goto drop;
955         }
956
957         if (con->link_type != NG_HCI_LINK_SCO) {
958                 NG_HCI_ERR(
959 "%s: %s - unexpected HCI SCO data packet. Not SCO link, con_handle=%d, " \
960 "link_type=%d\n",       __func__, NG_NODE_NAME(unit->node), 
961                         con_handle, con->link_type);
962
963                 error = EINVAL;
964                 goto drop;
965         }
966
967         if (con->state != NG_HCI_CON_OPEN) {
968                 NG_HCI_ERR(
969 "%s: %s - unexpected HCI SCO data packet. Invalid connection state=%d, " \
970 "con_handle=%d\n",      __func__, NG_NODE_NAME(unit->node), 
971                         con->state, con_handle);
972
973                 error = EHOSTDOWN;
974                 goto drop;
975         }
976
977         if (NG_BT_ITEMQ_FULL(&con->conq)) {
978                 NG_HCI_ALERT(
979 "%s: %s - dropping HCI SCO data packet, con_handle=%d, len=%d, queue_len=%d\n",
980                         __func__, NG_NODE_NAME(unit->node), con_handle, 
981                         m->m_pkthdr.len, NG_BT_ITEMQ_LEN(&con->conq));
982
983                 NG_BT_ITEMQ_DROP(&con->conq);
984
985                 error = ENOBUFS;
986                 goto drop;
987         }
988
989         /* Queue item and schedule data transfer */
990         NGI_M(item) = m;
991         NG_BT_ITEMQ_ENQUEUE(&con->conq, item);
992         item = NULL;
993         m = NULL;
994
995         ng_hci_send_data(unit);
996 drop:
997         if (item != NULL)
998                 NG_FREE_ITEM(item);
999
1000         NG_FREE_M(m); /* NG_FREE_M() checks for m != NULL */
1001
1002         return (error);
1003 } /* ng_hci_sco_rcvdata */
1004
1005 /*
1006  * Process data packet from uptream RAW hook.
1007  * We expect valid HCI command packets.
1008  */
1009
1010 static int
1011 ng_hci_raw_rcvdata(hook_p hook, item_p item)
1012 {
1013         ng_hci_unit_p    unit = (ng_hci_unit_p) NG_NODE_PRIVATE(NG_HOOK_NODE(hook));
1014         struct mbuf     *m = NULL;
1015         int              error = 0;
1016
1017         NGI_GET_M(item, m); 
1018         NG_FREE_ITEM(item);
1019
1020         /* Check packet */
1021         if (*mtod(m, u_int8_t *) != NG_HCI_CMD_PKT) {
1022                 NG_HCI_ALERT(
1023 "%s: %s - invalid HCI command packet type=%#x\n",
1024                         __func__, NG_NODE_NAME(unit->node),
1025                         *mtod(m, u_int8_t *));
1026
1027                 error = EINVAL;
1028                 goto drop;
1029         }
1030
1031         if (m->m_pkthdr.len < sizeof(ng_hci_cmd_pkt_t)) {
1032                 NG_HCI_ALERT(
1033 "%s: %s - invalid HCI command packet len=%d\n",
1034                         __func__, NG_NODE_NAME(unit->node), m->m_pkthdr.len);
1035
1036                 error = EMSGSIZE;
1037                 goto drop;
1038         }
1039
1040         NG_HCI_M_PULLUP(m, sizeof(ng_hci_cmd_pkt_t));
1041         if (m == NULL) {
1042                 error = ENOBUFS;
1043                 goto drop;
1044         }
1045
1046         if (m->m_pkthdr.len != 
1047             mtod(m, ng_hci_cmd_pkt_t *)->length + sizeof(ng_hci_cmd_pkt_t)) {
1048                 NG_HCI_ALERT(
1049 "%s: %s - invalid HCI command packet size, len=%d, length=%d\n",
1050                         __func__, NG_NODE_NAME(unit->node), m->m_pkthdr.len,
1051                         mtod(m, ng_hci_cmd_pkt_t *)->length);
1052
1053                 error = EMSGSIZE;
1054                 goto drop;
1055         }
1056
1057         if (mtod(m, ng_hci_cmd_pkt_t *)->opcode == 0) {
1058                 NG_HCI_ALERT(
1059 "%s: %s - invalid HCI command opcode\n", 
1060                         __func__, NG_NODE_NAME(unit->node));
1061
1062                 error = EINVAL;
1063                 goto drop;
1064         }
1065
1066         if (NG_BT_MBUFQ_FULL(&unit->cmdq)) {
1067                 NG_HCI_ALERT(
1068 "%s: %s - dropping HCI command packet, len=%d, queue_len=%d\n",
1069                         __func__, NG_NODE_NAME(unit->node), m->m_pkthdr.len, 
1070                         NG_BT_MBUFQ_LEN(&unit->cmdq));
1071
1072                 NG_BT_MBUFQ_DROP(&unit->cmdq);
1073
1074                 error = ENOBUFS;
1075                 goto drop;
1076         }
1077
1078         /* Queue and send command */
1079         NG_BT_MBUFQ_ENQUEUE(&unit->cmdq, m);
1080         m = NULL;
1081
1082         if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING))
1083                 error = ng_hci_send_command(unit);
1084 drop:
1085         NG_FREE_M(m); /* NG_FREE_M() checks for m != NULL */
1086
1087         return (error);
1088 } /* ng_hci_raw_rcvdata */
1089