/* Data are taken from RFC 4493 Test Vectors */ static void test_le_encrypt(const void *test_data) { struct user_data *user = tester_get_data(); struct bt_hci_cmd_le_encrypt cmd; uint8_t key[16] = { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c }; uint8_t plaintext[16] = { 0 }; /* Swap bytes since our interface has LE interface, opposed to * common crypto interface */ swap_buf(key, cmd.key, 16); swap_buf(plaintext, cmd.plaintext, 16); util_hexdump('<', cmd.key, 16, test_debug, NULL); util_hexdump('<', cmd.plaintext, 16, test_debug, NULL); if (!bt_hci_send(user->hci_ut, BT_HCI_CMD_LE_ENCRYPT, &cmd, sizeof(cmd), test_le_encrypt_complete, NULL, NULL)) { tester_warn("Failed to send HCI LE Encrypt command"); tester_test_failed(); return; } }
static gboolean test_handler(GIOChannel *channel, GIOCondition cond, gpointer user_data) { struct context *context = user_data; unsigned char buf[512]; const struct test_pdu *pdu; ssize_t len; int fd; pdu = &context->data->pdu_list[context->pdu_offset++]; if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) { context->source = 0; g_print("%s: cond %x\n", __func__, cond); return FALSE; } fd = g_io_channel_unix_get_fd(channel); len = read(fd, buf, sizeof(buf)); g_assert(len > 0); util_hexdump('>', buf, len, test_debug, "hog: "); g_assert_cmpint(len, ==, pdu->size); g_assert(memcmp(buf, pdu->data, pdu->size) == 0); context->process = g_idle_add(send_pdu, context); return TRUE; }
static void setup_le_read_local_pk_complete(const void *data, uint8_t size, void *user_data) { const uint8_t *event = data; const struct bt_hci_evt_le_read_local_pk256_complete *evt; struct le_keys *keys = user_data; if (*event != BT_HCI_EVT_LE_READ_LOCAL_PK256_COMPLETE) { tester_warn("Failed Read Local PK256 command"); tester_setup_failed(); return; } evt = (void *)(event + 1); if (evt->status) { tester_warn("HCI Read Local PK complete failed (0x%02x)", evt->status); tester_setup_failed(); return; } memcpy(keys->local_pk, evt->local_pk256, 64); util_hexdump('>', evt->local_pk256, 64, test_debug, NULL); tester_setup_complete(); }
static void hf_write_tracing(const void *buf, size_t count, void *user_data) { struct hfp_hf *hfp = user_data; util_hexdump('<', buf, count, hfp->debug_callback, hfp->debug_data); }
static void avrcp_cid_hook_cb(const void *data, uint16_t len, void *user_data) { struct step *step; uint8_t pdu, event; util_hexdump('>', data, len, print_avrcp, NULL); pdu = ((uint8_t *) data)[9]; switch (pdu) { case AVRCP_GET_PLAY_STATUS: step = g_new0(struct step, 1); step->callback = CB_AVRCP_PLAY_STATUS_RSP; step->callback_result.song_length = get_be32(data + 13); step->callback_result.song_position = get_be32(data + 17); step->callback_result.play_status = ((uint8_t *) data)[21]; schedule_callback_verification(step); break; case AVRCP_REGISTER_NOTIFICATION: event = ((uint8_t *) data)[13]; switch (event) { case 0x01: step = g_new0(struct step, 1); step->callback = CB_AVRCP_REG_NOTIF_RSP; step->callback_result.play_status = ((uint8_t *) data)[14]; schedule_callback_verification(step); break; case 0x02: step = g_new0(struct step, 1); step->callback = CB_AVRCP_REG_NOTIF_RSP; step->callback_result.rc_index = get_be64(data + 14); schedule_callback_verification(step); break; case 0x05: step = g_new0(struct step, 1); step->callback = CB_AVRCP_REG_NOTIF_RSP; step->callback_result.song_position = get_be32(data + 14); schedule_callback_verification(step); break; } break; case AVRCP_GET_ELEMENT_ATTRIBUTES: step = g_new0(struct step, 1); step->callback = CB_AVRCP_GET_ATTR_RSP; step->callback_result.num_of_attrs = ((uint8_t *) data)[13]; memset(exp_attrs, 0, 2 * sizeof(btrc_element_attr_val_t)); exp_attrs[0].attr_id = get_be16(data + 16); memcpy(exp_attrs[0].text, data + 22, 19); exp_attrs[1].attr_id = get_be16(data + 43); memcpy(exp_attrs[1].text, data + 49, 6); step->callback_result.attrs = exp_attrs; schedule_callback_verification(step); break; } }
static bool can_write_data(struct io *io, void *user_data) { struct bt_att *att = user_data; struct att_send_op *op; struct timeout_data *timeout; ssize_t bytes_written; op = pick_next_send_op(att); if (!op) return false; bytes_written = write(att->fd, op->pdu, op->len); if (bytes_written < 0) { util_debug(att->debug_callback, att->debug_data, "write failed: %s", strerror(errno)); if (op->callback) op->callback(BT_ATT_OP_ERROR_RSP, NULL, 0, op->user_data); destroy_att_send_op(op); return true; } util_debug(att->debug_callback, att->debug_data, "ATT op 0x%02x", op->opcode); util_hexdump('<', op->pdu, bytes_written, att->debug_callback, att->debug_data); /* Based on the operation type, set either the pending request or the * pending indication. If it came from the write queue, then there is * no need to keep it around. */ switch (op->type) { case ATT_OP_TYPE_REQ: att->pending_req = op; break; case ATT_OP_TYPE_IND: att->pending_ind = op; break; default: destroy_att_send_op(op); return true; } timeout = new0(struct timeout_data, 1); if (!timeout) return true; timeout->att = att; timeout->id = op->id; op->timeout_id = timeout_add(ATT_TIMEOUT_INTERVAL, timeout_cb, timeout, free); /* Return true as there may be more operations ready to write. */ return true; }
static void test_le_generate_dhkey_complete(const void *data, uint8_t size, void *user_data) { const uint8_t *event = data; const struct bt_hci_evt_le_generate_dhkey_complete *evt; struct le_keys *keys = user_data; uint8_t dhkey[32]; if (*event != BT_HCI_EVT_LE_GENERATE_DHKEY_COMPLETE) { tester_warn("Failed DHKey generation command"); tester_test_failed(); return; } evt = (void *)(event + 1); if (evt->status) { tester_warn("HCI Generate DHKey complete failed (0x%02x)", evt->status); tester_test_failed(); return; } util_hexdump('>', evt->dhkey, 32, test_debug, NULL); util_hexdump('S', keys->remote_sk, 32, test_debug, NULL); util_hexdump('P', keys->local_pk, 64, test_debug, NULL); /* Generate DHKey ourself with local public key and remote * private key we got when generated public / private key * pair for BT_HCI_CMD_LE_GENERATE_DHKEY argument. */ ecdh_shared_secret(keys->local_pk, keys->remote_sk, dhkey); util_hexdump('D', dhkey, 32, test_debug, NULL); if (!memcmp(dhkey, evt->dhkey, 32)) tester_test_passed(); else tester_test_failed(); }
static gboolean can_write_data(GIOChannel *channel, GIOCondition cond, gpointer user_data) { struct mgmt *mgmt = user_data; struct mgmt_request *request; ssize_t bytes_written; if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) return FALSE; request = g_queue_pop_head(mgmt->reply_queue); if (!request) { /* only reply commands can jump the queue */ if (mgmt->pending_list) return FALSE; request = g_queue_pop_head(mgmt->request_queue); if (!request) return FALSE; } bytes_written = write(mgmt->fd, request->buf, request->len); if (bytes_written < 0) { util_debug(mgmt->debug_callback, mgmt->debug_data, "write failed: %s", strerror(errno)); if (request->callback) request->callback(MGMT_STATUS_FAILED, 0, NULL, request->user_data); destroy_request(request, NULL); return TRUE; } util_debug(mgmt->debug_callback, mgmt->debug_data, "[0x%04x] command 0x%04x", request->index, request->opcode); util_hexdump('<', request->buf, bytes_written, mgmt->debug_callback, mgmt->debug_data); mgmt->pending_list = g_list_append(mgmt->pending_list, request); return FALSE; }
static bool can_read_data(struct io *io, void *user_data) { struct bt_att *att = user_data; uint8_t opcode; uint8_t *pdu; ssize_t bytes_read; bytes_read = read(att->fd, att->buf, att->mtu); if (bytes_read < 0) return false; util_hexdump('>', att->buf, bytes_read, att->debug_callback, att->debug_data); if (bytes_read < ATT_MIN_PDU_LEN) return true; pdu = att->buf; opcode = pdu[0]; /* Act on the received PDU based on the opcode type */ switch (get_op_type(opcode)) { case ATT_OP_TYPE_RSP: util_debug(att->debug_callback, att->debug_data, "ATT response received: 0x%02x", opcode); handle_rsp(att, opcode, pdu + 1, bytes_read - 1); break; case ATT_OP_TYPE_CONF: util_debug(att->debug_callback, att->debug_data, "ATT opcode cannot be handled: 0x%02x", opcode); break; default: /* For all other opcodes notify the upper layer of the PDU and * let them act on it. */ util_debug(att->debug_callback, att->debug_data, "ATT PDU received: 0x%02x", opcode); handle_notify(att, opcode, pdu + 1, bytes_read - 1); break; } return true; }
static gboolean send_pdu(gpointer user_data) { struct context *context = user_data; const struct test_pdu *pdu; ssize_t len; pdu = &context->data->pdu_list[context->pdu_offset++]; len = write(context->fd, pdu->data, pdu->size); util_hexdump('<', pdu->data, len, test_debug, "hog: "); g_assert_cmpint(len, ==, pdu->size); context->process = 0; if (!context->data->pdu_list[context->pdu_offset].valid) context_quit(context); return FALSE; }
static bool can_write_data(struct io *io, void *user_data) { struct mgmt *mgmt = user_data; struct mgmt_request *request; ssize_t bytes_written; request = queue_pop_head(mgmt->reply_queue); if (!request) { /* only reply commands can jump the queue */ if (!queue_isempty(mgmt->pending_list)) return false; request = queue_pop_head(mgmt->request_queue); if (!request) return false; } bytes_written = write(mgmt->fd, request->buf, request->len); if (bytes_written < 0) { util_debug(mgmt->debug_callback, mgmt->debug_data, "write failed: %s", strerror(errno)); if (request->callback) request->callback(MGMT_STATUS_FAILED, 0, NULL, request->user_data); destroy_request(request); return true; } util_debug(mgmt->debug_callback, mgmt->debug_data, "[0x%04x] command 0x%04x", request->index, request->opcode); util_hexdump('<', request->buf, bytes_written, mgmt->debug_callback, mgmt->debug_data); queue_push_tail(mgmt->pending_list, request); return false; }
static gboolean can_write_data(GIOChannel *chan, GIOCondition cond, gpointer user_data) { struct netlink_info *netlink = user_data; struct command *command; struct sockaddr_nl addr; const void *data; ssize_t written; int sk; command = g_queue_pop_head(netlink->command_queue); if (!command) return FALSE; sk = g_io_channel_unix_get_fd(chan); memset(&addr, 0, sizeof(addr)); addr.nl_family = AF_NETLINK; addr.nl_pid = 0; data = ((void *) command) + NLMSG_ALIGN(sizeof(struct command)); written = sendto(sk, data, command->len, 0, (struct sockaddr *) &addr, sizeof(addr)); if (written < 0 || (uint32_t) written != command->len) { g_hash_table_remove(netlink->command_lookup, GUINT_TO_POINTER(command->id)); destroy_command(command); return FALSE; } util_hexdump('<', data, command->len, netlink->debug_handler, netlink->debug_data); g_hash_table_replace(netlink->command_pending, GUINT_TO_POINTER(command->seq), command); return g_queue_get_length(netlink->command_queue) > 0; }
static bool write_packet(int fd, const void *data, size_t size, void *user_data) { while (size > 0) { ssize_t written; written = write(fd, data, size); if (written < 0) { if (errno == EAGAIN || errno == EINTR) continue; return false; } if (debug_enabled) util_hexdump('<', data, written, hexdump_print, user_data); data += written; size -= written; } return true; }
static void test_le_encrypt_complete(const void *data, uint8_t size, void *user_data) { const struct bt_hci_rsp_le_encrypt *rsp = data; uint8_t sample[16] = { 0x7d, 0xf7, 0x6b, 0x0c, 0x1a, 0xb8, 0x99, 0xb3, 0x3e, 0x42, 0xf0, 0x47, 0xb9, 0x1b, 0x54, 0x6f }; uint8_t enc_data[16]; if (rsp->status) { tester_warn("Failed HCI LE Encrypt (0x%02x)", rsp->status); tester_test_failed(); return; } swap_buf(rsp->data, enc_data, 16); util_hexdump('>', enc_data, 16, test_debug, NULL); if (!memcmp(sample, enc_data, 16)) tester_test_passed(); else tester_test_failed(); }
static void dev_read_callback(int fd, uint32_t events, void *user_data) { struct proxy *proxy = user_data; struct bt_hci_evt_hdr *evt_hdr; struct bt_hci_acl_hdr *acl_hdr; struct bt_hci_sco_hdr *sco_hdr; ssize_t len; uint16_t pktlen; if (events & (EPOLLERR | EPOLLHUP)) { fprintf(stderr, "Error from device descriptor\n"); mainloop_remove_fd(proxy->dev_fd); return; } if (events & EPOLLRDHUP) { fprintf(stderr, "Remote hangup of device descriptor\n"); mainloop_remove_fd(proxy->host_fd); return; } len = read(proxy->dev_fd, proxy->dev_buf + proxy->dev_len, sizeof(proxy->dev_buf) - proxy->dev_len); if (len < 0) { if (errno == EAGAIN || errno == EINTR) return; fprintf(stderr, "Read from device descriptor failed\n"); mainloop_remove_fd(proxy->dev_fd); return; } if (debug_enabled) util_hexdump('>', proxy->dev_buf + proxy->dev_len, len, hexdump_print, "D: "); proxy->dev_len += len; process_packet: if (proxy->dev_len < 1) return; switch (proxy->dev_buf[0]) { case BT_H4_EVT_PKT: if (proxy->dev_len < 1 + sizeof(*evt_hdr)) return; evt_hdr = (void *) (proxy->dev_buf + 1); pktlen = 1 + sizeof(*evt_hdr) + evt_hdr->plen; break; case BT_H4_ACL_PKT: if (proxy->dev_len < 1 + sizeof(*acl_hdr)) return; acl_hdr = (void *) (proxy->dev_buf + 1); pktlen = 1 + sizeof(*acl_hdr) + cpu_to_le16(acl_hdr->dlen); break; case BT_H4_SCO_PKT: if (proxy->dev_len < 1 + sizeof(*sco_hdr)) return; sco_hdr = (void *) (proxy->dev_buf + 1); pktlen = 1 + sizeof(*sco_hdr) + sco_hdr->dlen; break; default: fprintf(stderr, "Received unknown device packet type 0x%02x\n", proxy->dev_buf[0]); mainloop_remove_fd(proxy->dev_fd); return; } if (proxy->dev_len < pktlen) return; if (!write_packet(proxy->host_fd, proxy->dev_buf, pktlen, "H: ")) { fprintf(stderr, "Write to host descriptor failed\n"); mainloop_remove_fd(proxy->host_fd); return; } if (proxy->dev_len > pktlen) { memmove(proxy->dev_buf, proxy->dev_buf + pktlen, proxy->dev_len - pktlen); proxy->dev_len -= pktlen; goto process_packet; } proxy->dev_len = 0; }
static gboolean can_read_data(GIOChannel *chan, GIOCondition cond, gpointer data) { struct netlink_info *netlink = data; struct cmsghdr *cmsg; struct msghdr msg; struct iovec iov; struct nlmsghdr *nlmsg; unsigned char buffer[4096]; unsigned char control[32]; uint32_t group = 0; ssize_t len; int sk; sk = g_io_channel_unix_get_fd(chan); iov.iov_base = buffer; iov.iov_len = sizeof(buffer); memset(&msg, 0, sizeof(msg)); msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = control; msg.msg_controllen = sizeof(control); len = recvmsg(sk, &msg, 0); if (len < 0) return FALSE; util_hexdump('>', buffer, len, netlink->debug_handler, netlink->debug_data); for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg)) { struct nl_pktinfo *pktinfo; if (cmsg->cmsg_level != SOL_NETLINK) continue; if (cmsg->cmsg_type != NETLINK_PKTINFO) continue; pktinfo = (void *) CMSG_DATA(cmsg); group = pktinfo->group; } for (nlmsg = iov.iov_base; NLMSG_OK(nlmsg, (uint32_t) len); nlmsg = NLMSG_NEXT(nlmsg, len)) { if (group > 0 && nlmsg->nlmsg_seq == 0) { process_broadcast(netlink, group, nlmsg); continue; } if (nlmsg->nlmsg_pid != netlink->pid) continue; if (nlmsg->nlmsg_flags & NLM_F_MULTI) process_multi(netlink, nlmsg); else process_message(netlink, nlmsg); } return TRUE; }
static gboolean received_data(GIOChannel *channel, GIOCondition cond, gpointer user_data) { struct mgmt *mgmt = user_data; struct mgmt_hdr *hdr; struct mgmt_ev_cmd_complete *cc; struct mgmt_ev_cmd_status *cs; ssize_t bytes_read; uint16_t opcode, event, index, length; if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) return FALSE; bytes_read = read(mgmt->fd, mgmt->buf, mgmt->len); if (bytes_read < 0) return TRUE; util_hexdump('>', mgmt->buf, bytes_read, mgmt->debug_callback, mgmt->debug_data); if (bytes_read < MGMT_HDR_SIZE) return TRUE; hdr = mgmt->buf; event = btohs(hdr->opcode); index = btohs(hdr->index); length = btohs(hdr->len); if (bytes_read < length + MGMT_HDR_SIZE) return TRUE; switch (event) { case MGMT_EV_CMD_COMPLETE: cc = mgmt->buf + MGMT_HDR_SIZE; opcode = btohs(cc->opcode); util_debug(mgmt->debug_callback, mgmt->debug_data, "[0x%04x] command 0x%04x complete: 0x%02x", index, opcode, cc->status); request_complete(mgmt, cc->status, opcode, index, length - 3, mgmt->buf + MGMT_HDR_SIZE + 3); break; case MGMT_EV_CMD_STATUS: cs = mgmt->buf + MGMT_HDR_SIZE; opcode = btohs(cs->opcode); util_debug(mgmt->debug_callback, mgmt->debug_data, "[0x%04x] command 0x%02x status: 0x%02x", index, opcode, cs->status); request_complete(mgmt, cs->status, opcode, index, 0, NULL); break; default: util_debug(mgmt->debug_callback, mgmt->debug_data, "[0x%04x] event 0x%04x", index, event); process_notify(mgmt, event, index, length, mgmt->buf + MGMT_HDR_SIZE); break; } if (mgmt->destroyed) return FALSE; return TRUE; }
static void read_tracing(const void *buf, size_t count, void *user_data) { struct hfp_gw *hfp = user_data; util_hexdump('>', buf, count, hfp->debug_callback, hfp->debug_data); }
static bool can_read_data(struct io *io, void *user_data) { struct bt_att *att = user_data; uint8_t opcode; uint8_t *pdu; ssize_t bytes_read; bytes_read = read(att->fd, att->buf, att->mtu); if (bytes_read < 0) return false; util_hexdump('>', att->buf, bytes_read, att->debug_callback, att->debug_data); if (bytes_read < ATT_MIN_PDU_LEN) return true; pdu = att->buf; opcode = pdu[0]; bt_att_ref(att); /* Act on the received PDU based on the opcode type */ switch (get_op_type(opcode)) { case ATT_OP_TYPE_RSP: util_debug(att->debug_callback, att->debug_data, "ATT response received: 0x%02x", opcode); handle_rsp(att, opcode, pdu + 1, bytes_read - 1); break; case ATT_OP_TYPE_CONF: util_debug(att->debug_callback, att->debug_data, "ATT confirmation received: 0x%02x", opcode); handle_conf(att, pdu + 1, bytes_read - 1); break; case ATT_OP_TYPE_REQ: /* * If a request is currently pending, then the sequential * protocol was violated. Disconnect the bearer, which will * promptly notify the upper layer via disconnect handlers. */ if (att->in_req) { util_debug(att->debug_callback, att->debug_data, "Received request while another is " "pending: 0x%02x", opcode); io_shutdown(att->io); bt_att_unref(att); return false; } att->in_req = true; /* Fall through to the next case */ case ATT_OP_TYPE_CMD: case ATT_OP_TYPE_NOT: case ATT_OP_TYPE_UNKNOWN: case ATT_OP_TYPE_IND: default: /* For all other opcodes notify the upper layer of the PDU and * let them act on it. */ util_debug(att->debug_callback, att->debug_data, "ATT PDU received: 0x%02x", opcode); handle_notify(att, opcode, pdu + 1, bytes_read - 1); break; } bt_att_unref(att); return true; }
static bool can_write_data(struct io *io, void *user_data) { struct bt_att *att = user_data; struct att_send_op *op; struct timeout_data *timeout; ssize_t ret; struct iovec iov; op = pick_next_send_op(att); if (!op) return false; iov.iov_base = op->pdu; iov.iov_len = op->len; ret = io_send(io, &iov, 1); if (ret < 0) { util_debug(att->debug_callback, att->debug_data, "write failed: %s", strerror(-ret)); if (op->callback) op->callback(BT_ATT_OP_ERROR_RSP, NULL, 0, op->user_data); destroy_att_send_op(op); return true; } util_debug(att->debug_callback, att->debug_data, "ATT op 0x%02x", op->opcode); util_hexdump('<', op->pdu, ret, att->debug_callback, att->debug_data); /* Based on the operation type, set either the pending request or the * pending indication. If it came from the write queue, then there is * no need to keep it around. */ switch (op->type) { case ATT_OP_TYPE_REQ: att->pending_req = op; break; case ATT_OP_TYPE_IND: att->pending_ind = op; break; case ATT_OP_TYPE_RSP: /* Set in_req to false to indicate that no request is pending */ att->in_req = false; /* Fall through to the next case */ case ATT_OP_TYPE_CMD: case ATT_OP_TYPE_NOT: case ATT_OP_TYPE_CONF: case ATT_OP_TYPE_UNKNOWN: default: destroy_att_send_op(op); return true; } timeout = new0(struct timeout_data, 1); if (!timeout) return true; timeout->att = att; timeout->id = op->id; op->timeout_id = timeout_add(ATT_TIMEOUT_INTERVAL, timeout_cb, timeout, free); /* Return true as there may be more operations ready to write. */ return true; }
static bool can_read_data(struct io *io, void *user_data) { struct mgmt *mgmt = user_data; struct mgmt_hdr *hdr; struct mgmt_ev_cmd_complete *cc; struct mgmt_ev_cmd_status *cs; ssize_t bytes_read; uint16_t opcode, event, index, length; bytes_read = read(mgmt->fd, mgmt->buf, mgmt->len); if (bytes_read < 0) return false; util_hexdump('>', mgmt->buf, bytes_read, mgmt->debug_callback, mgmt->debug_data); if (bytes_read < MGMT_HDR_SIZE) return true; hdr = mgmt->buf; event = btohs(hdr->opcode); index = btohs(hdr->index); length = btohs(hdr->len); if (bytes_read < length + MGMT_HDR_SIZE) return true; switch (event) { case MGMT_EV_CMD_COMPLETE: cc = mgmt->buf + MGMT_HDR_SIZE; opcode = btohs(cc->opcode); util_debug(mgmt->debug_callback, mgmt->debug_data, "[0x%04x] command 0x%04x complete: 0x%02x", index, opcode, cc->status); request_complete(mgmt, cc->status, opcode, index, length - 3, mgmt->buf + MGMT_HDR_SIZE + 3); break; case MGMT_EV_CMD_STATUS: cs = mgmt->buf + MGMT_HDR_SIZE; opcode = btohs(cs->opcode); util_debug(mgmt->debug_callback, mgmt->debug_data, "[0x%04x] command 0x%02x status: 0x%02x", index, opcode, cs->status); request_complete(mgmt, cs->status, opcode, index, 0, NULL); break; default: util_debug(mgmt->debug_callback, mgmt->debug_data, "[0x%04x] event 0x%04x", index, event); process_notify(mgmt, event, index, length, mgmt->buf + MGMT_HDR_SIZE); break; } if (mgmt->destroyed) return false; return true; }