| Commit | Line | Data |
|---|---|---|
| b06ebda0 MD |
1 | /* |
| 2 | * ng_hci_misc.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_misc.c,v 1.5 2003/09/08 18:57:51 max Exp $ | |
| 31 | * $FreeBSD: src/sys/netgraph/bluetooth/hci/ng_hci_misc.c,v 1.10 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/malloc.h> | |
| 38 | #include <sys/mbuf.h> | |
| 39 | #include <sys/queue.h> | |
| 5a975a3d MD |
40 | #include "ng_message.h" |
| 41 | #include "netgraph.h" | |
| 42 | #include "bluetooth/include/ng_bluetooth.h" | |
| 43 | #include "bluetooth/include/ng_hci.h" | |
| 44 | #include "bluetooth/hci/ng_hci_var.h" | |
| 45 | #include "bluetooth/hci/ng_hci_cmds.h" | |
| 46 | #include "bluetooth/hci/ng_hci_evnt.h" | |
| 47 | #include "bluetooth/hci/ng_hci_ulpi.h" | |
| 48 | #include "bluetooth/hci/ng_hci_misc.h" | |
| b06ebda0 MD |
49 | |
| 50 | /****************************************************************************** | |
| 51 | ****************************************************************************** | |
| 52 | ** Utility routines | |
| 53 | ****************************************************************************** | |
| 54 | ******************************************************************************/ | |
| 55 | ||
| 56 | /* | |
| 57 | * Give packet to RAW hook | |
| 58 | * Assumes input mbuf is read only. | |
| 59 | */ | |
| 60 | ||
| 61 | void | |
| 62 | ng_hci_mtap(ng_hci_unit_p unit, struct mbuf *m0) | |
| 63 | { | |
| 64 | struct mbuf *m = NULL; | |
| 65 | int error = 0; | |
| 66 | ||
| 67 | if (unit->raw != NULL && NG_HOOK_IS_VALID(unit->raw)) { | |
| 5a975a3d | 68 | m = m_dup(m0, MB_DONTWAIT); |
| b06ebda0 MD |
69 | if (m != NULL) |
| 70 | NG_SEND_DATA_ONLY(error, unit->raw, m); | |
| 71 | ||
| 72 | if (error != 0) | |
| 73 | NG_HCI_INFO( | |
| 74 | "%s: %s - Could not forward packet, error=%d\n", | |
| 75 | __func__, NG_NODE_NAME(unit->node), error); | |
| 76 | } | |
| 77 | } /* ng_hci_mtap */ | |
| 78 | ||
| 79 | /* | |
| 80 | * Send notification to the upper layer's | |
| 81 | */ | |
| 82 | ||
| 83 | void | |
| 84 | ng_hci_node_is_up(node_p node, hook_p hook, void *arg1, int arg2) | |
| 85 | { | |
| 86 | ng_hci_unit_p unit = NULL; | |
| 87 | struct ng_mesg *msg = NULL; | |
| 88 | ng_hci_node_up_ep *ep = NULL; | |
| 89 | int error; | |
| 90 | ||
| 91 | if (node == NULL || NG_NODE_NOT_VALID(node) || | |
| 92 | hook == NULL || NG_HOOK_NOT_VALID(hook)) | |
| 93 | return; | |
| 94 | ||
| 95 | unit = (ng_hci_unit_p) NG_NODE_PRIVATE(node); | |
| 96 | if ((unit->state & NG_HCI_UNIT_READY) != NG_HCI_UNIT_READY) | |
| 97 | return; | |
| 98 | ||
| 99 | if (hook != unit->acl && hook != unit->sco) | |
| 100 | return; | |
| 101 | ||
| 5a975a3d | 102 | NG_MKMESSAGE(msg,NGM_HCI_COOKIE,NGM_HCI_NODE_UP,sizeof(*ep),M_WAITOK | M_NULLOK); |
| b06ebda0 MD |
103 | if (msg != NULL) { |
| 104 | ep = (ng_hci_node_up_ep *)(msg->data); | |
| 105 | ||
| 106 | if (hook == unit->acl) { | |
| 107 | NG_HCI_BUFF_ACL_SIZE(unit->buffer, ep->pkt_size); | |
| 108 | NG_HCI_BUFF_ACL_TOTAL(unit->buffer, ep->num_pkts); | |
| 109 | } else { | |
| 110 | NG_HCI_BUFF_SCO_SIZE(unit->buffer, ep->pkt_size); | |
| 111 | NG_HCI_BUFF_SCO_TOTAL(unit->buffer, ep->num_pkts); | |
| 112 | } | |
| 113 | ||
| 114 | bcopy(&unit->bdaddr, &ep->bdaddr, sizeof(ep->bdaddr)); | |
| 115 | ||
| 116 | NG_SEND_MSG_HOOK(error, node, msg, hook, 0); | |
| 117 | } else | |
| 118 | error = ENOMEM; | |
| 119 | ||
| 120 | if (error != 0) | |
| 121 | NG_HCI_INFO( | |
| 122 | "%s: %s - failed to send NODE_UP message to hook \"%s\", error=%d\n", | |
| 123 | __func__, NG_NODE_NAME(unit->node), | |
| 124 | NG_HOOK_NAME(hook), error); | |
| 125 | } /* ng_hci_node_is_up */ | |
| 126 | ||
| 127 | /* | |
| 128 | * Clean unit (helper) | |
| 129 | */ | |
| 130 | ||
| 131 | void | |
| 132 | ng_hci_unit_clean(ng_hci_unit_p unit, int reason) | |
| 133 | { | |
| 134 | int size; | |
| 135 | ||
| 136 | /* Drain command queue */ | |
| 137 | if (unit->state & NG_HCI_UNIT_COMMAND_PENDING) | |
| 138 | ng_hci_command_untimeout(unit); | |
| 139 | ||
| 140 | NG_BT_MBUFQ_DRAIN(&unit->cmdq); | |
| 141 | NG_HCI_BUFF_CMD_SET(unit->buffer, 1); | |
| 142 | ||
| 143 | /* Clean up connection list */ | |
| 144 | while (!LIST_EMPTY(&unit->con_list)) { | |
| 145 | ng_hci_unit_con_p con = LIST_FIRST(&unit->con_list); | |
| 146 | ||
| 147 | /* Remove all timeouts (if any) */ | |
| 148 | if (con->flags & NG_HCI_CON_TIMEOUT_PENDING) | |
| 149 | ng_hci_con_untimeout(con); | |
| 150 | ||
| 151 | /* | |
| 152 | * Notify upper layer protocol and destroy connection | |
| 153 | * descriptor. Do not really care about the result. | |
| 154 | */ | |
| 155 | ||
| 156 | ng_hci_lp_discon_ind(con, reason); | |
| 157 | ng_hci_free_con(con); | |
| 158 | } | |
| 159 | ||
| 160 | NG_HCI_BUFF_ACL_TOTAL(unit->buffer, size); | |
| 161 | NG_HCI_BUFF_ACL_FREE(unit->buffer, size); | |
| 162 | ||
| 163 | NG_HCI_BUFF_SCO_TOTAL(unit->buffer, size); | |
| 164 | NG_HCI_BUFF_SCO_FREE(unit->buffer, size); | |
| 165 | ||
| 166 | /* Clean up neighbors list */ | |
| 167 | ng_hci_flush_neighbor_cache(unit); | |
| 168 | } /* ng_hci_unit_clean */ | |
| 169 | ||
| 170 | /* | |
| 171 | * Allocate and link new unit neighbor cache entry | |
| 172 | */ | |
| 173 | ||
| 174 | ng_hci_neighbor_p | |
| 175 | ng_hci_new_neighbor(ng_hci_unit_p unit) | |
| 176 | { | |
| 177 | ng_hci_neighbor_p n = NULL; | |
| 178 | ||
| fc025606 | 179 | n = kmalloc(sizeof(*n), M_NETGRAPH_HCI, M_WAITOK | M_NULLOK | M_ZERO); |
| b06ebda0 MD |
180 | if (n != NULL) { |
| 181 | getmicrotime(&n->updated); | |
| 182 | LIST_INSERT_HEAD(&unit->neighbors, n, next); | |
| 183 | } | |
| 184 | ||
| 185 | return (n); | |
| 186 | } /* ng_hci_new_neighbor */ | |
| 187 | ||
| 188 | /* | |
| 189 | * Free unit neighbor cache entry | |
| 190 | */ | |
| 191 | ||
| 192 | void | |
| 193 | ng_hci_free_neighbor(ng_hci_neighbor_p n) | |
| 194 | { | |
| 195 | LIST_REMOVE(n, next); | |
| 196 | bzero(n, sizeof(*n)); | |
| fc025606 | 197 | kfree(n, M_NETGRAPH_HCI); |
| b06ebda0 MD |
198 | } /* ng_hci_free_neighbor */ |
| 199 | ||
| 200 | /* | |
| 201 | * Flush neighbor cache | |
| 202 | */ | |
| 203 | ||
| 204 | void | |
| 205 | ng_hci_flush_neighbor_cache(ng_hci_unit_p unit) | |
| 206 | { | |
| 207 | while (!LIST_EMPTY(&unit->neighbors)) | |
| 208 | ng_hci_free_neighbor(LIST_FIRST(&unit->neighbors)); | |
| 209 | } /* ng_hci_flush_neighbor_cache */ | |
| 210 | ||
| 211 | /* | |
| 212 | * Lookup unit in neighbor cache | |
| 213 | */ | |
| 214 | ||
| 215 | ng_hci_neighbor_p | |
| 216 | ng_hci_get_neighbor(ng_hci_unit_p unit, bdaddr_p bdaddr) | |
| 217 | { | |
| 218 | ng_hci_neighbor_p n = NULL; | |
| 219 | ||
| 220 | for (n = LIST_FIRST(&unit->neighbors); n != NULL; ) { | |
| 221 | ng_hci_neighbor_p nn = LIST_NEXT(n, next); | |
| 222 | ||
| 223 | if (!ng_hci_neighbor_stale(n)) { | |
| 224 | if (bcmp(&n->bdaddr, bdaddr, sizeof(*bdaddr)) == 0) | |
| 225 | break; | |
| 226 | } else | |
| 227 | ng_hci_free_neighbor(n); /* remove old entry */ | |
| 228 | ||
| 229 | n = nn; | |
| 230 | } | |
| 231 | ||
| 232 | return (n); | |
| 233 | } /* ng_hci_get_neighbor */ | |
| 234 | ||
| 235 | /* | |
| 236 | * Check if neighbor entry is stale | |
| 237 | */ | |
| 238 | ||
| 239 | int | |
| 240 | ng_hci_neighbor_stale(ng_hci_neighbor_p n) | |
| 241 | { | |
| 242 | struct timeval now; | |
| 243 | ||
| 244 | getmicrotime(&now); | |
| 245 | ||
| 246 | return (now.tv_sec - n->updated.tv_sec > bluetooth_hci_max_neighbor_age()); | |
| 247 | } /* ng_hci_neighbor_stale */ | |
| 248 | ||
| 249 | /* | |
| 250 | * Allocate and link new connection descriptor | |
| 251 | */ | |
| 252 | ||
| 253 | ng_hci_unit_con_p | |
| 254 | ng_hci_new_con(ng_hci_unit_p unit, int link_type) | |
| 255 | { | |
| 256 | ng_hci_unit_con_p con = NULL; | |
| 257 | int num_pkts; | |
| 258 | static int fake_con_handle = 0x0f00; | |
| 259 | ||
| fc025606 SW |
260 | con = kmalloc(sizeof(*con), M_NETGRAPH_HCI, |
| 261 | M_WAITOK | M_NULLOK | M_ZERO); | |
| b06ebda0 MD |
262 | if (con != NULL) { |
| 263 | con->unit = unit; | |
| 264 | con->state = NG_HCI_CON_CLOSED; | |
| 265 | ||
| 266 | /* | |
| 267 | * XXX | |
| 268 | * | |
| 269 | * Assign fake connection handle to the connection descriptor. | |
| 270 | * Bluetooth specification marks 0x0f00 - 0x0fff connection | |
| 271 | * handles as reserved. We need this fake connection handles | |
| 272 | * for timeouts. Connection handle will be passed as argument | |
| 273 | * to timeout so when timeout happens we can find the right | |
| 274 | * connection descriptor. We can not pass pointers, because | |
| 275 | * timeouts are external (to Netgraph) events and there might | |
| 276 | * be a race when node/hook goes down and timeout event already | |
| 277 | * went into node's queue | |
| 278 | */ | |
| 279 | ||
| 280 | con->con_handle = fake_con_handle ++; | |
| 281 | if (fake_con_handle > 0x0fff) | |
| 282 | fake_con_handle = 0x0f00; | |
| 283 | ||
| 284 | con->link_type = link_type; | |
| 285 | ||
| 286 | if (con->link_type == NG_HCI_LINK_ACL) | |
| 287 | NG_HCI_BUFF_ACL_TOTAL(unit->buffer, num_pkts); | |
| 288 | else | |
| 289 | NG_HCI_BUFF_SCO_TOTAL(unit->buffer, num_pkts); | |
| 290 | ||
| 291 | NG_BT_ITEMQ_INIT(&con->conq, num_pkts); | |
| 292 | ||
| 293 | ng_callout_init(&con->con_timo); | |
| 294 | ||
| 295 | LIST_INSERT_HEAD(&unit->con_list, con, next); | |
| 296 | } | |
| 297 | ||
| 298 | return (con); | |
| 299 | } /* ng_hci_new_con */ | |
| 300 | ||
| 301 | /* | |
| 302 | * Free connection descriptor | |
| 303 | */ | |
| 304 | ||
| 305 | void | |
| 306 | ng_hci_free_con(ng_hci_unit_con_p con) | |
| 307 | { | |
| 308 | LIST_REMOVE(con, next); | |
| 309 | ||
| 310 | /* | |
| 311 | * If we have pending packets then assume that Host Controller has | |
| 312 | * flushed these packets and we can free them too | |
| 313 | */ | |
| 314 | ||
| 315 | if (con->link_type == NG_HCI_LINK_ACL) | |
| 316 | NG_HCI_BUFF_ACL_FREE(con->unit->buffer, con->pending); | |
| 317 | else | |
| 318 | NG_HCI_BUFF_SCO_FREE(con->unit->buffer, con->pending); | |
| 319 | ||
| 320 | NG_BT_ITEMQ_DESTROY(&con->conq); | |
| 321 | ||
| 322 | bzero(con, sizeof(*con)); | |
| fc025606 | 323 | kfree(con, M_NETGRAPH_HCI); |
| b06ebda0 MD |
324 | } /* ng_hci_free_con */ |
| 325 | ||
| 326 | /* | |
| 327 | * Lookup connection for given unit and connection handle. | |
| 328 | */ | |
| 329 | ||
| 330 | ng_hci_unit_con_p | |
| 331 | ng_hci_con_by_handle(ng_hci_unit_p unit, int con_handle) | |
| 332 | { | |
| 333 | ng_hci_unit_con_p con = NULL; | |
| 334 | ||
| 335 | LIST_FOREACH(con, &unit->con_list, next) | |
| 336 | if (con->con_handle == con_handle) | |
| 337 | break; | |
| 338 | ||
| 339 | return (con); | |
| 340 | } /* ng_hci_con_by_handle */ | |
| 341 | ||
| 342 | /* | |
| 343 | * Lookup connection for given unit, link type and remove unit address | |
| 344 | */ | |
| 345 | ||
| 346 | ng_hci_unit_con_p | |
| 347 | ng_hci_con_by_bdaddr(ng_hci_unit_p unit, bdaddr_p bdaddr, int link_type) | |
| 348 | { | |
| 349 | ng_hci_unit_con_p con = NULL; | |
| 350 | ||
| 351 | LIST_FOREACH(con, &unit->con_list, next) | |
| 352 | if (con->link_type == link_type && | |
| 353 | bcmp(&con->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0) | |
| 354 | break; | |
| 355 | ||
| 356 | return (con); | |
| 357 | } /* ng_hci_con_by_bdaddr */ | |
| 358 | ||
| 359 | /* | |
| 360 | * Set HCI command timeout | |
| 361 | * XXX FIXME: check return code from ng_callout | |
| 362 | */ | |
| 363 | ||
| 364 | int | |
| 365 | ng_hci_command_timeout(ng_hci_unit_p unit) | |
| 366 | { | |
| 367 | if (unit->state & NG_HCI_UNIT_COMMAND_PENDING) | |
| 368 | panic( | |
| 369 | "%s: %s - Duplicated command timeout!\n", __func__, NG_NODE_NAME(unit->node)); | |
| 370 | ||
| 371 | unit->state |= NG_HCI_UNIT_COMMAND_PENDING; | |
| 372 | ng_callout(&unit->cmd_timo, unit->node, NULL, | |
| 373 | bluetooth_hci_command_timeout(), | |
| 374 | ng_hci_process_command_timeout, NULL, 0); | |
| 375 | ||
| 376 | return (0); | |
| 377 | } /* ng_hci_command_timeout */ | |
| 378 | ||
| 379 | /* | |
| 380 | * Unset HCI command timeout | |
| 381 | */ | |
| 382 | ||
| 383 | int | |
| 384 | ng_hci_command_untimeout(ng_hci_unit_p unit) | |
| 385 | { | |
| 386 | if (!(unit->state & NG_HCI_UNIT_COMMAND_PENDING)) | |
| 387 | panic( | |
| 388 | "%s: %s - No command timeout!\n", __func__, NG_NODE_NAME(unit->node)); | |
| 389 | ||
| 390 | if (ng_uncallout(&unit->cmd_timo, unit->node) == 0) | |
| 391 | return (ETIMEDOUT); | |
| 392 | ||
| 393 | unit->state &= ~NG_HCI_UNIT_COMMAND_PENDING; | |
| 394 | ||
| 395 | return (0); | |
| 396 | } /* ng_hci_command_untimeout */ | |
| 397 | ||
| 398 | /* | |
| 399 | * Set HCI connection timeout | |
| 400 | * XXX FIXME: check return code from ng_callout | |
| 401 | */ | |
| 402 | ||
| 403 | int | |
| 404 | ng_hci_con_timeout(ng_hci_unit_con_p con) | |
| 405 | { | |
| 406 | if (con->flags & NG_HCI_CON_TIMEOUT_PENDING) | |
| 407 | panic( | |
| 408 | "%s: %s - Duplicated connection timeout!\n", | |
| 409 | __func__, NG_NODE_NAME(con->unit->node)); | |
| 410 | ||
| 411 | con->flags |= NG_HCI_CON_TIMEOUT_PENDING; | |
| 412 | ng_callout(&con->con_timo, con->unit->node, NULL, | |
| 413 | bluetooth_hci_connect_timeout(), | |
| 414 | ng_hci_process_con_timeout, NULL, | |
| 415 | con->con_handle); | |
| 416 | ||
| 417 | return (0); | |
| 418 | } /* ng_hci_con_timeout */ | |
| 419 | ||
| 420 | /* | |
| 421 | * Unset HCI connection timeout | |
| 422 | */ | |
| 423 | ||
| 424 | int | |
| 425 | ng_hci_con_untimeout(ng_hci_unit_con_p con) | |
| 426 | { | |
| 427 | if (!(con->flags & NG_HCI_CON_TIMEOUT_PENDING)) | |
| 428 | panic( | |
| 429 | "%s: %s - No connection timeout!\n", __func__, NG_NODE_NAME(con->unit->node)); | |
| 430 | ||
| 431 | if (ng_uncallout(&con->con_timo, con->unit->node) == 0) | |
| 432 | return (ETIMEDOUT); | |
| 433 | ||
| 434 | con->flags &= ~NG_HCI_CON_TIMEOUT_PENDING; | |
| 435 | ||
| 436 | return (0); | |
| 437 | } /* ng_hci_con_untimeout */ | |
| 438 | ||
| 439 | #if 0 | |
| 440 | /* | |
| 441 | * Convert numeric error code/reason to a string | |
| 442 | */ | |
| 443 | ||
| 444 | char const * const | |
| 445 | ng_hci_str_error(u_int16_t code) | |
| 446 | { | |
| c157ff7a | 447 | #define LAST_ERROR_CODE (NELEM(s)-1) |
| b06ebda0 MD |
448 | static char const * const s[] = { |
| 449 | /* 0x00 */ "No error", | |
| 450 | /* 0x01 */ "Unknown HCI command", | |
| 451 | /* 0x02 */ "No connection", | |
| 452 | /* 0x03 */ "Hardware failure", | |
| 453 | /* 0x04 */ "Page timeout", | |
| 454 | /* 0x05 */ "Authentication failure", | |
| 455 | /* 0x06 */ "Key missing", | |
| 456 | /* 0x07 */ "Memory full", | |
| 457 | /* 0x08 */ "Connection timeout", | |
| 458 | /* 0x09 */ "Max number of connections", | |
| 459 | /* 0x0a */ "Max number of SCO connections to a unit", | |
| 460 | /* 0x0b */ "ACL connection already exists", | |
| 461 | /* 0x0c */ "Command disallowed", | |
| 462 | /* 0x0d */ "Host rejected due to limited resources", | |
| 463 | /* 0x0e */ "Host rejected due to securiity reasons", | |
| 464 | /* 0x0f */ "Host rejected due to remote unit is a personal unit", | |
| 465 | /* 0x10 */ "Host timeout", | |
| 466 | /* 0x11 */ "Unsupported feature or parameter value", | |
| 467 | /* 0x12 */ "Invalid HCI command parameter", | |
| 468 | /* 0x13 */ "Other end terminated connection: User ended connection", | |
| 469 | /* 0x14 */ "Other end terminated connection: Low resources", | |
| 470 | /* 0x15 */ "Other end terminated connection: About to power off", | |
| 471 | /* 0x16 */ "Connection terminated by local host", | |
| 472 | /* 0x17 */ "Repeated attempts", | |
| 473 | /* 0x18 */ "Pairing not allowed", | |
| 474 | /* 0x19 */ "Unknown LMP PDU", | |
| 475 | /* 0x1a */ "Unsupported remote feature", | |
| 476 | /* 0x1b */ "SCO offset rejected", | |
| 477 | /* 0x1c */ "SCO interval rejected", | |
| 478 | /* 0x1d */ "SCO air mode rejected", | |
| 479 | /* 0x1e */ "Invalid LMP parameters", | |
| 480 | /* 0x1f */ "Unspecified error", | |
| 481 | /* 0x20 */ "Unsupported LMP parameter value", | |
| 482 | /* 0x21 */ "Role change not allowed", | |
| 483 | /* 0x22 */ "LMP response timeout", | |
| 484 | /* 0x23 */ "LMP error transaction collision", | |
| 485 | /* 0x24 */ "LMP PSU not allowed", | |
| 486 | /* 0x25 */ "Encryption mode not acceptable", | |
| 487 | /* 0x26 */ "Unit key used", | |
| 488 | /* 0x27 */ "QoS is not supported", | |
| 489 | /* 0x28 */ "Instant passed", | |
| 490 | /* 0x29 */ "Paring with unit key not supported", | |
| 491 | /* SHOULD ALWAYS BE LAST */ "Unknown error" | |
| 492 | }; | |
| 493 | ||
| 494 | return ((code >= LAST_ERROR_CODE)? s[LAST_ERROR_CODE] : s[code]); | |
| 495 | } /* ng_hci_str_error */ | |
| 496 | #endif | |
| 497 |