| Commit | Line | Data |
|---|---|---|
| b06ebda0 MD |
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 $ | |
| 5a975a3d | 32 | * $DragonFly: src/sys/netgraph7/bluetooth/hci/ng_hci_main.c,v 1.2 2008/06/26 23:05:40 dillon Exp $ |
| b06ebda0 MD |
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> | |
| 5a975a3d MD |
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" | |
| b06ebda0 MD |
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 | ||
| fc025606 SW |
113 | unit = kmalloc(sizeof(*unit), M_NETGRAPH_HCI, |
| 114 | M_WAITOK | M_NULLOK | M_ZERO); | |
| b06ebda0 MD |
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)); | |
| fc025606 | 174 | kfree(unit, M_NETGRAPH_HCI); |
| b06ebda0 MD |
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 | ||
| 5a975a3d | 295 | NG_MKRESPONSE(rsp, msg, NG_TEXTRESPONSE, M_WAITOK | M_NULLOK); |
| b06ebda0 MD |
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: | |
| 5a975a3d | 341 | NG_MKRESPONSE(rsp, msg, sizeof(unit->state), M_WAITOK | M_NULLOK); |
| b06ebda0 MD |
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: | |
| 5a975a3d | 366 | NG_MKRESPONSE(rsp, msg, sizeof(unit->debug), M_WAITOK | M_NULLOK); |
| b06ebda0 MD |
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), | |
| 5a975a3d | 390 | M_WAITOK | M_NULLOK); |
| b06ebda0 MD |
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: | |
| 5a975a3d | 409 | NG_MKRESPONSE(rsp, msg, sizeof(bdaddr_t), M_WAITOK | M_NULLOK); |
| b06ebda0 MD |
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: | |
| 5a975a3d | 420 | NG_MKRESPONSE(rsp,msg,sizeof(unit->features),M_WAITOK | M_NULLOK); |
| b06ebda0 MD |
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: | |
| 5a975a3d | 431 | NG_MKRESPONSE(rsp, msg, sizeof(unit->stat), M_WAITOK | M_NULLOK); |
| b06ebda0 MD |
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), | |
| 5a975a3d | 473 | M_WAITOK | M_NULLOK); |
| b06ebda0 MD |
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), | |
| 5a975a3d | 514 | M_WAITOK | M_NULLOK); |
| b06ebda0 MD |
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), | |
| 5a975a3d | 549 | M_WAITOK | M_NULLOK); |
| b06ebda0 MD |
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), | |
| 5a975a3d | 575 | M_WAITOK | M_NULLOK); |
| b06ebda0 MD |
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), | |
| 5a975a3d | 600 | M_WAITOK | M_NULLOK); |
| b06ebda0 MD |
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 |