Hi Brian, > --- > mesh/display.c | 67 +++++ > mesh/hci.c | 699 +++++++++++++++++++++++++++++++++++++++++++++++++ > mesh/mesh-io-generic.c | 660 ++++++++++++++++++++++++++++++++++++++++++++++ > mesh/mesh-io.c | 187 +++++++++++++ > 4 files changed, 1613 insertions(+) > create mode 100644 mesh/display.c > create mode 100644 mesh/hci.c > create mode 100644 mesh/mesh-io-generic.c > create mode 100644 mesh/mesh-io.c > > diff --git a/mesh/display.c b/mesh/display.c > new file mode 100644 > index 000000000..adf92cae0 > --- /dev/null > +++ b/mesh/display.c > @@ -0,0 +1,67 @@ > +/* > + * > + * BlueZ - Bluetooth protocol stack for Linux > + * > + * Copyright (C) 2018 Intel Corporation. All rights reserved. > + * > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2.1 of the License, or (at your option) any later version. > + * > + * This library is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + */ > + > +#include <stdio.h> > +#include <unistd.h> > +#include <termios.h> > +#include <sys/ioctl.h> > +#include <sys/time.h> > +#include <ell/ell.h> > + > +#include "display.h" > + > +#define likely(x) __builtin_expect(!!(x), 1) > +#define unlikely(x) __builtin_expect(!!(x), 0) I highly doubt we will make good use of likely and unlikely. Just remove that. We can measure that at some point, but right now it seems useless. On a side note, I am not convinced that our usage in ELL is actually worth while doing. > + > +static unsigned int cached_num_columns; > + > +unsigned int num_columns(void) > +{ > + struct winsize ws; > + > + if (likely(cached_num_columns > 0)) > + return cached_num_columns; > + > + if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0 || ws.ws_col == 0) > + cached_num_columns = 80; > + else > + cached_num_columns = ws.ws_col; > + > + return cached_num_columns; > +} > + > +void print_packet(const char *label, const void *data, uint16_t size) > +{ > + struct timeval pkt_time; > + > + gettimeofday(&pkt_time, NULL); > + > + if (size > 0) { > + char *str; > + > + str = l_util_hexstring(data, size); > + l_info("%05d.%03d %s: %s", > + (uint32_t) pkt_time.tv_sec % 100000, > + (uint32_t) pkt_time.tv_usec/1000, label, str); > + l_free(str); > + } else > + l_info("%05d.%03d %s: empty", > + (uint32_t) pkt_time.tv_sec % 100000, > + (uint32_t) pkt_time.tv_usec/1000, label); > +} I have no idea why we are not using util_hexdump() or l_util_hexdump() since they are hooked up properly with debug defines etc. > diff --git a/mesh/hci.c b/mesh/hci.c > new file mode 100644 > index 000000000..da6838a55 > --- /dev/null > +++ b/mesh/hci.c > @@ -0,0 +1,699 @@ > +/* > + * > + * BlueZ - Bluetooth protocol stack for Linux > + * > + * Copyright (C) 2018 Intel Corporation. All rights reserved. > + * > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2.1 of the License, or (at your option) any later version. > + * > + * This library is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + */ > + > +#ifdef HAVE_CONFIG_H > +#include <config.h> > +#endif > + > +#include <unistd.h> > +#include <stdlib.h> > +#include <sys/uio.h> > +#include <sys/socket.h> > +#include <ell/ell.h> > + > +#include "monitor/bt.h" > + > +#include "mesh/hci.h" > + > +#define BTPROTO_HCI 1 > +struct sockaddr_hci { > + sa_family_t hci_family; > + unsigned short hci_dev; > + unsigned short hci_channel; > +}; > +#define HCI_CHANNEL_USER 1 > + > +struct bt_hci { > + int ref_count; > + struct l_io *io; > + bool is_stream; > + bool writer_active; > + uint8_t num_cmds; > + uint8_t num_pkts; > + unsigned int next_cmd_id; > + unsigned int next_evt_id; > + struct l_queue *cmd_queue; > + struct l_queue *rsp_queue; > + struct l_queue *evt_list; > + struct l_queue *pkt_queue; > + bt_hci_receive_func_t receive_callback; > + bt_hci_destroy_func_t receive_destroy; > + void *receive_data; > +}; What is wrong with src/shared/hci.[ch] instead having a copy here? > + > +struct cmd { > + unsigned int id; > + uint16_t opcode; > + void *data; > + uint8_t size; > + bt_hci_callback_func_t callback; > + bt_hci_destroy_func_t destroy; > + void *user_data; > +}; > + > +struct evt { > + unsigned int id; > + uint8_t event; > + bt_hci_callback_func_t callback; > + bt_hci_destroy_func_t destroy; > + void *user_data; > +}; > + > +struct pkt { > + uint16_t handle; > + uint8_t flags; > + void *data; > + uint16_t size; > +}; > + > +static void cmd_free(void *data) > +{ > + struct cmd *cmd = data; > + > + if (cmd->destroy) > + cmd->destroy(cmd->user_data); > + > + l_free(cmd->data); > + l_free(cmd); > +} > + > +static void evt_free(void *data) > +{ > + struct evt *evt = data; > + > + if (evt->destroy) > + evt->destroy(evt->user_data); > + > + l_free(evt); > +} > + > +static void pkt_free(void *data) > +{ > + struct pkt *pkt = data; > + > + l_free(pkt->data); > + l_free(pkt); > +} > + > +static void send_command(struct bt_hci *hci, uint16_t opcode, > + void *data, uint8_t size) > +{ > + uint8_t type = BT_H4_CMD_PKT; > + struct bt_hci_cmd_hdr hdr; > + struct iovec iov[3]; > + int fd, iovcnt; > + > + hdr.opcode = L_CPU_TO_LE16(opcode); > + hdr.plen = size; > + > + iov[0].iov_base = &type; > + iov[0].iov_len = 1; > + iov[1].iov_base = &hdr; > + iov[1].iov_len = sizeof(hdr); > + > + if (size > 0) { > + iov[2].iov_base = data; > + iov[2].iov_len = size; > + iovcnt = 3; > + } else > + iovcnt = 2; > + > + fd = l_io_get_fd(hci->io); > + if (fd < 0) { > + l_error("hci->io Bad"); > + return; > + } > + > + if (writev(fd, iov, iovcnt) < 0) { > + l_error("writev Bad"); > + return; > + } > + > + hci->num_cmds--; > +} > + > +static void send_packet(struct bt_hci *hci, uint16_t handle, uint8_t flags, > + void *data, uint8_t size) > +{ > + uint8_t type = BT_H4_ACL_PKT; > + struct bt_hci_acl_hdr hdr; > + struct iovec iov[3]; > + int fd, iovcnt; > + > + hdr.handle = L_CPU_TO_LE16(flags << 12 | handle); > + hdr.dlen = L_CPU_TO_LE16(size); > + > + iov[0].iov_base = &type; > + iov[0].iov_len = 1; > + iov[1].iov_base = &hdr; > + iov[1].iov_len = sizeof(hdr); > + > + if (size > 0) { > + iov[2].iov_base = data; > + iov[2].iov_len = size; > + iovcnt = 3; > + } else > + iovcnt = 2; > + > + fd = l_io_get_fd(hci->io); > + if (fd < 0) { > + l_error("hci->io Bad"); > + return; > + } > + > + if (writev(fd, iov, iovcnt) < 0) { > + l_error("writev Bad"); > + return; > + } > + > + hci->num_pkts--; > +} > + > +static bool io_write_callback(struct l_io *io, void *user_data) > +{ > + struct bt_hci *hci = user_data; > + struct cmd *cmd; > + > + if (hci->num_cmds > 0) > + cmd = l_queue_pop_head(hci->cmd_queue); > + else > + cmd = NULL; > + > + if (cmd) { > + send_command(hci, cmd->opcode, cmd->data, cmd->size); > + l_queue_push_tail(hci->rsp_queue, cmd); > + } else { > + struct pkt *pkt; > + > + if (hci->num_pkts < 1) > + goto done; > + > + pkt = l_queue_pop_head(hci->pkt_queue); > + if (pkt) { > + send_packet(hci, pkt->handle, pkt->flags, > + pkt->data, pkt->size); > + pkt_free(pkt); > + } > + } > + > + if (!l_queue_isempty(hci->pkt_queue)) > + return true; > + > +done: > + hci->writer_active = false; > + > + return false; > +} > + > +static void wakeup_writer(struct bt_hci *hci) > +{ > + if (hci->writer_active) > + return; > + > + if (l_queue_isempty(hci->cmd_queue) && l_queue_isempty(hci->pkt_queue)) > + return; > + > + if (!l_io_set_write_handler(hci->io, io_write_callback, hci, NULL)) > + return; > + > + hci->writer_active = true; > +} > + > +static bool match_cmd_opcode(const void *a, const void *b) > +{ > + const struct cmd *cmd = a; > + uint16_t opcode = L_PTR_TO_UINT(b); > + > + return cmd->opcode == opcode; > +} > + > +static void process_response(struct bt_hci *hci, uint16_t opcode, > + const void *data, size_t size) > +{ > + struct cmd *cmd; > + > + if (opcode == BT_HCI_CMD_NOP) > + goto done; > + > + cmd = l_queue_remove_if(hci->rsp_queue, match_cmd_opcode, > + L_UINT_TO_PTR(opcode)); > + if (!cmd) > + goto done; > + > + if (cmd->callback) > + cmd->callback(data, size, cmd->user_data); > + > + cmd_free(cmd); > + > +done: > + wakeup_writer(hci); > +} > + > +static void process_notify(void *data, void *user_data) > +{ > + struct bt_hci_evt_hdr *hdr = user_data; > + struct evt *evt = data; > + > + if (evt->event == hdr->evt) > + evt->callback(user_data + sizeof(struct bt_hci_evt_hdr), > + hdr->plen, evt->user_data); > +} > + > +static void process_event(struct bt_hci *hci, const void *data, size_t size) > +{ > + const struct bt_hci_evt_hdr *hdr = data; > + const struct bt_hci_evt_cmd_complete *cc; > + const struct bt_hci_evt_cmd_status *cs; > + const struct bt_hci_evt_num_completed_packets *cp; > + > + if (size < sizeof(struct bt_hci_evt_hdr)) > + return; > + > + data += sizeof(struct bt_hci_evt_hdr); > + size -= sizeof(struct bt_hci_evt_hdr); > + > + if (hdr->plen != size) > + return; > + > + switch (hdr->evt) { > + case BT_HCI_EVT_CMD_COMPLETE: > + if (size < sizeof(*cc)) > + return; > + cc = data; > + hci->num_cmds = cc->ncmd; > + process_response(hci, L_LE16_TO_CPU(cc->opcode), > + data + sizeof(*cc), > + size - sizeof(*cc)); > + break; > + > + case BT_HCI_EVT_CMD_STATUS: > + if (size < sizeof(*cs)) > + return; > + cs = data; > + hci->num_cmds = cs->ncmd; > + process_response(hci, L_LE16_TO_CPU(cs->opcode), > + &cs->status, 1); > + break; > + > + case BT_HCI_EVT_NUM_COMPLETED_PACKETS: > + if (size < sizeof(*cp)) > + return; > + cp = data; > + l_debug("BT_HCI_EVT_NUM_COMPLETED_PACKETS:%d", cp->count); > + /* Ignoring handle information for now */ > + hci->num_pkts = cp->count; > + wakeup_writer(hci); > + break; > + > + default: > + l_queue_foreach(hci->evt_list, process_notify, (void *) hdr); > + break; > + } > +} > + > +static void process_acl(struct bt_hci *hci, const void *data, size_t size) > +{ > + const struct bt_hci_acl_hdr *hdr = data; > + uint16_t handle; > + > + if (size < sizeof(struct bt_hci_acl_hdr)) > + return; > + > + data += sizeof(struct bt_hci_acl_hdr); > + size -= sizeof(struct bt_hci_acl_hdr); > + > + if (L_LE16_TO_CPU(hdr->dlen) != size) > + return; > + > + handle = L_LE16_TO_CPU(hdr->handle); > + > + if (hci->receive_callback) > + hci->receive_callback(handle & 0x0fff, handle >> 12, > + data, size, hci->receive_data); > +} > + > +static bool io_read_callback(struct l_io *io, void *user_data) > +{ > + struct bt_hci *hci = user_data; > + uint8_t buf[512]; > + ssize_t len; > + int fd; > + > + fd = l_io_get_fd(hci->io); > + if (fd < 0) > + return false; > + > + if (hci->is_stream) > + return false; > + > + len = read(fd, buf, sizeof(buf)); > + if (len < 0) > + return false; > + > + if (len < 1) > + return true; > + > + switch (buf[0]) { > + case BT_H4_EVT_PKT: > + process_event(hci, buf + 1, len - 1); > + break; > + case BT_H4_ACL_PKT: > + process_acl(hci, buf + 1, len - 1); > + break; > + default: > + l_info("%2.2x-RXed", buf[0]); > + } > + > + return true; > +} > + > +static struct bt_hci *create_hci(int fd) > +{ > + struct bt_hci *hci; > + > + if (fd < 0) > + return NULL; > + > + hci = l_new(struct bt_hci, 1); > + > + hci->io = l_io_new(fd); > + > + hci->is_stream = true; > + hci->writer_active = false; > + hci->num_cmds = 1; > + hci->num_pkts = 1; > + hci->next_cmd_id = 1; > + hci->next_evt_id = 1; > + > + hci->cmd_queue = l_queue_new(); > + hci->rsp_queue = l_queue_new(); > + hci->evt_list = l_queue_new(); > + hci->pkt_queue = l_queue_new(); > + > + if (!l_io_set_read_handler(hci->io, io_read_callback, hci, NULL)) { > + l_queue_destroy(hci->pkt_queue, NULL); > + l_queue_destroy(hci->evt_list, NULL); > + l_queue_destroy(hci->rsp_queue, NULL); > + l_queue_destroy(hci->cmd_queue, NULL); > + l_io_destroy(hci->io); > + l_free(hci); > + return NULL; > + } > + > + return bt_hci_ref(hci); > +} > + > +struct bt_hci *bt_hci_new(int fd) > +{ > + struct bt_hci *hci; > + > + hci = create_hci(fd); > + if (!hci) > + return NULL; > + > + return hci; > +} > + > +static int create_socket(uint16_t index, uint16_t channel) > +{ > + struct sockaddr_hci addr; > + int fd; > + > + fd = socket(PF_BLUETOOTH, SOCK_RAW | SOCK_CLOEXEC | SOCK_NONBLOCK, > + BTPROTO_HCI); > + if (fd < 0) > + return -1; > + > + memset(&addr, 0, sizeof(addr)); > + addr.hci_family = AF_BLUETOOTH; > + addr.hci_dev = index; > + addr.hci_channel = channel; > + > + if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { > + close(fd); > + return -1; > + } > + > + return fd; > +} > + > +struct bt_hci *bt_hci_new_user_channel(uint16_t index) > +{ > + struct bt_hci *hci; > + int fd; > + > + fd = create_socket(index, HCI_CHANNEL_USER); > + if (fd < 0) > + return NULL; > + > + hci = create_hci(fd); > + if (!hci) { > + close(fd); > + return NULL; > + } > + > + hci->is_stream = false; > + > + bt_hci_set_close_on_unref(hci, true); > + > + return hci; > +} > + > +struct bt_hci *bt_hci_ref(struct bt_hci *hci) > +{ > + if (!hci) > + return NULL; > + > + __sync_fetch_and_add(&hci->ref_count, 1); > + > + return hci; > +} > + > +void bt_hci_unref(struct bt_hci *hci) > +{ > + if (!hci) > + return; > + > + if (__sync_sub_and_fetch(&hci->ref_count, 1)) > + return; > + > + if (hci->receive_destroy) > + hci->receive_destroy(hci->receive_data); > + > + l_queue_destroy(hci->pkt_queue, pkt_free); > + l_queue_destroy(hci->evt_list, evt_free); > + l_queue_destroy(hci->cmd_queue, cmd_free); > + l_queue_destroy(hci->rsp_queue, cmd_free); > + > + l_io_destroy(hci->io); > + > + l_free(hci); > +} > + > +bool bt_hci_set_close_on_unref(struct bt_hci *hci, bool do_close) > +{ > + if (!hci) > + return false; > + > + return l_io_set_close_on_destroy(hci->io, do_close); > +} > + > +unsigned int bt_hci_send(struct bt_hci *hci, uint16_t opcode, > + const void *data, uint8_t size, > + bt_hci_callback_func_t callback, > + void *user_data, bt_hci_destroy_func_t destroy) > +{ > + struct cmd *cmd; > + > + if (!hci) > + return 0; > + > + cmd = l_new(struct cmd, 1); > + > + cmd->opcode = opcode; > + cmd->size = size; > + > + if (cmd->size > 0) > + cmd->data = l_memdup(data, cmd->size); > + > + if (hci->next_cmd_id < 1) > + hci->next_cmd_id = 1; > + > + cmd->id = hci->next_cmd_id++; > + > + cmd->callback = callback; > + cmd->destroy = destroy; > + cmd->user_data = user_data; > + > + if (!l_queue_push_tail(hci->cmd_queue, cmd)) { > + l_free(cmd->data); > + l_free(cmd); > + return 0; > + } > + > + wakeup_writer(hci); > + > + return cmd->id; > +} > + > +static bool match_cmd_id(const void *a, const void *b) > +{ > + const struct cmd *cmd = a; > + unsigned int id = L_PTR_TO_UINT(b); > + > + return cmd->id == id; > +} > + > +bool bt_hci_cancel(struct bt_hci *hci, unsigned int id) > +{ > + struct cmd *cmd; > + > + if (!hci || !id) > + return false; > + > + cmd = l_queue_remove_if(hci->cmd_queue, match_cmd_id, > + L_UINT_TO_PTR(id)); > + if (!cmd) { > + cmd = l_queue_remove_if(hci->rsp_queue, match_cmd_id, > + L_UINT_TO_PTR(id)); > + if (!cmd) > + return false; > + } > + > + cmd_free(cmd); > + > + wakeup_writer(hci); > + > + return true; > +} > + > +bool bt_hci_flush(struct bt_hci *hci) > +{ > + if (!hci) > + return false; > + > + if (hci->writer_active) { > + l_io_set_write_handler(hci->io, NULL, NULL, NULL); > + hci->writer_active = false; > + } > + > + l_queue_clear(hci->cmd_queue, cmd_free); > + l_queue_clear(hci->rsp_queue, cmd_free); > + > + return true; > +} > + > +unsigned int bt_hci_register(struct bt_hci *hci, uint8_t event, > + bt_hci_callback_func_t callback, > + void *user_data, bt_hci_destroy_func_t destroy) > +{ > + struct evt *evt; > + > + if (!hci) > + return 0; > + > + evt = l_new(struct evt, 1); > + > + evt->event = event; > + > + if (hci->next_evt_id < 1) > + hci->next_evt_id = 1; > + > + evt->id = hci->next_evt_id++; > + > + evt->callback = callback; > + evt->destroy = destroy; > + evt->user_data = user_data; > + > + if (!l_queue_push_tail(hci->evt_list, evt)) { > + l_free(evt); > + return 0; > + } > + > + return evt->id; > +} > + > +static bool match_evt_id(const void *a, const void *b) > +{ > + const struct evt *evt = a; > + unsigned int id = L_PTR_TO_UINT(b); > + > + return evt->id == id; > +} > + > +bool bt_hci_unregister(struct bt_hci *hci, unsigned int id) > +{ > + struct evt *evt; > + > + if (!hci || !id) > + return false; > + > + evt = l_queue_remove_if(hci->evt_list, match_evt_id, > + L_UINT_TO_PTR(id)); > + if (!evt) > + return false; > + > + evt_free(evt); > + > + return true; > +} > + > +bool bt_hci_receive(struct bt_hci *hci, bt_hci_receive_func_t callback, > + void *user_data, bt_hci_destroy_func_t destroy) > +{ > + if (!hci) > + return false; > + > + if (hci->receive_destroy) > + hci->receive_destroy(hci->receive_data); > + > + hci->receive_callback = callback; > + hci->receive_destroy = destroy; > + hci->receive_data = user_data; > + > + return true; > +} > + > +bool bt_hci_write(struct bt_hci *hci, uint16_t handle, uint8_t flags, > + const void *data, uint16_t size) > +{ > + struct pkt *pkt; > + > + if (!hci) > + return false; > + > + pkt = l_new(struct pkt, 1); > + > + pkt->handle = handle; > + pkt->flags = flags; > + pkt->size = size; > + > + if (pkt->size > 0) > + pkt->data = l_memdup(data, pkt->size); > + > + if (!l_queue_push_tail(hci->pkt_queue, pkt)) { > + l_free(pkt->data); > + l_free(pkt); > + return false; > + } > + > + wakeup_writer(hci); > + > + return true; > +} > diff --git a/mesh/mesh-io-generic.c b/mesh/mesh-io-generic.c > new file mode 100644 > index 000000000..7a974cff3 > --- /dev/null > +++ b/mesh/mesh-io-generic.c > @@ -0,0 +1,660 @@ > +/* > + * > + * BlueZ - Bluetooth protocol stack for Linux > + * > + * Copyright (C) 2018 Intel Corporation. All rights reserved. > + * > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2.1 of the License, or (at your option) any later version. > + * > + * This library is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + */ > + > +#ifdef HAVE_CONFIG_H > +#include <config.h> > +#endif > + > +#include <sys/time.h> > +#include <ell/ell.h> > + > +#include "lib/bluetooth.h" > +#include "lib/hci.h" > + > +#include "monitor/bt.h" > + > +#include "mesh/hci.h" > +#include "mesh/display.h" > +#include "mesh/mesh-io.h" > +#include "mesh/mesh-io-api.h" > + > +#include "mesh/mesh-io-generic.h" > + > +struct mesh_io_private { > + uint16_t index; > + struct bt_hci *hci; > + struct l_timeout *tx_timeout; > + struct l_queue *rx_regs; > + struct l_queue *tx_pkts; > + uint8_t filters[3]; /* Simple filtering on AD type only */ > + bool sending; > + struct tx_pkt *tx; > + uint16_t interval; > +}; > + > +struct pvt_rx_reg { > + uint8_t filter_id; > + mesh_io_recv_func_t cb; > + void *user_data; > +}; > + > +struct process_data { > + struct mesh_io_private *pvt; > + const uint8_t *data; > + uint8_t len; > + struct mesh_io_recv_info info; > +}; > + > +struct tx_pkt { > + struct mesh_io_send_info info; > + bool delete; > + uint8_t len; > + uint8_t pkt[30]; > +}; > + > +struct tx_pattern { > + const uint8_t *data; > + uint8_t len; > +}; > + > +static uint32_t get_instant(void) > +{ > + struct timeval tm; > + uint32_t instant; > + > + gettimeofday(&tm, NULL); > + instant = tm.tv_sec * 1000; > + instant += tm.tv_usec / 1000; > + > + return instant; > +} > + > +static uint32_t instant_remaining_ms(uint32_t instant) > +{ > + instant -= get_instant(); > + return instant; > +} > + > +static void process_rx_callbacks(void *v_rx, void *v_reg) > +{ > + struct pvt_rx_reg *rx_reg = v_rx; > + struct process_data *rx = v_reg; > + uint8_t ad_type; > + > + ad_type = rx->pvt->filters[rx_reg->filter_id - 1]; > + > + if (rx->data[0] == ad_type && rx_reg->cb) > + rx_reg->cb(rx_reg->user_data, &rx->info, rx->data, rx->len); > +} > + > +static void process_rx(struct mesh_io_private *pvt, int8_t rssi, > + uint32_t instant, > + const uint8_t *data, uint8_t len) > +{ > + struct process_data rx = { > + .pvt = pvt, > + .data = data, > + .len = len, > + .info.instant = instant, > + .info.chan = 7, > + .info.rssi = rssi, > + }; > + > + l_queue_foreach(pvt->rx_regs, process_rx_callbacks, &rx); > +} > + > +static void event_adv_report(struct mesh_io *io, const void *buf, uint8_t size) > +{ > + const struct bt_hci_evt_le_adv_report *evt = buf; > + const uint8_t *adv; > + uint32_t instant; > + uint8_t adv_len; > + uint16_t len = 0; > + int8_t rssi; > + > + if (evt->event_type != 0x03) > + return; > + > + if (evt->addr_type != BDADDR_LE_PUBLIC && > + evt->addr_type != BDADDR_LE_RANDOM) > + return; > + > + instant = get_instant(); > + adv = evt->data; > + adv_len = evt->data_len; > + > + /* rssi is just beyond last byte of data */ > + rssi = (int8_t) adv[adv_len]; > + > + while (len < adv_len - 1) { > + uint8_t field_len = adv[0]; > + > + /* Check for the end of advertising data */ > + if (field_len == 0) > + break; > + > + len += field_len + 1; > + > + /* Do not continue data parsing if got incorrect length */ > + if (len > adv_len) > + break; > + > + /* TODO: Create an Instant to use */ > + process_rx(io->pvt, rssi, instant, adv + 1, adv[0]); > + > + adv += field_len + 1; > + } > +} > + > +static void event_callback(const void *buf, uint8_t size, void *user_data) > +{ > + uint8_t event = l_get_u8(buf); > + struct mesh_io *io = user_data; > + > + switch (event) { > + case BT_HCI_EVT_LE_ADV_REPORT: > + event_adv_report(io, buf + 1, size - 1); > + break; > + > + default: > + l_info("Other Meta Evt - %d", event); > + } > +} > + > +static bool dev_init(uint16_t index, struct mesh_io *io) > +{ > + struct mesh_io_private *tmp; > + > + if (!io || io->pvt) > + return false; > + > + tmp = l_new(struct mesh_io_private, 1); > + > + if (tmp == NULL) > + return false; > + > + tmp->rx_regs = l_queue_new(); > + tmp->tx_pkts = l_queue_new(); > + if (!tmp->rx_regs || !tmp->tx_pkts) > + goto fail; > + > + tmp->hci = bt_hci_new_user_channel(index); > + if (!tmp->hci) > + goto fail; > + > + bt_hci_register(tmp->hci, BT_HCI_EVT_LE_META_EVENT, > + event_callback, io, NULL); > + > + io->pvt = tmp; > + return true; > + > +fail: > + l_queue_destroy(tmp->rx_regs, l_free); > + l_queue_destroy(tmp->tx_pkts, l_free); > + l_free(tmp); > + return false; > +} > + > +static bool dev_destroy(struct mesh_io *io) > +{ > + struct mesh_io_private *pvt = io->pvt; > + > + if (!pvt) > + return true; > + > + bt_hci_unref(pvt->hci); > + l_timeout_remove(pvt->tx_timeout); > + l_queue_destroy(pvt->rx_regs, l_free); > + l_queue_destroy(pvt->tx_pkts, l_free); > + l_free(pvt); > + io->pvt = NULL; > + > + return true; > +} > + > +static bool dev_caps(struct mesh_io *io, struct mesh_io_caps *caps) > +{ > + struct mesh_io_private *pvt = io->pvt; > + > + if (!pvt || !caps) > + return false; > + > + caps->max_num_filters = sizeof(pvt->filters); > + caps->window_accuracy = 50; > + > + return true; > +} > + > +static void send_cancel_done(const void *buf, uint8_t size, > + void *user_data) > +{ > + struct mesh_io_private *pvt = user_data; > + struct bt_hci_cmd_le_set_random_address cmd; > + > + if (!pvt) > + return; > + > + pvt->sending = false; > + > + /* At end of any burst of ADVs, change random address */ > + l_getrandom(cmd.addr, 6); > + cmd.addr[5] |= 0xc0; > + bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_RANDOM_ADDRESS, > + &cmd, sizeof(cmd), NULL, NULL, NULL); > +} > + > +static void send_cancel(struct mesh_io_private *pvt) > +{ > + struct bt_hci_cmd_le_set_adv_enable cmd; > + > + if (!pvt) > + return; > + > + if (!pvt->sending) { > + send_cancel_done(NULL, 0, pvt); > + return; > + } > + > + cmd.enable = 0x00; /* Disable advertising */ > + bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_ADV_ENABLE, > + &cmd, sizeof(cmd), > + send_cancel_done, pvt, NULL); > +} > + > +static void set_send_adv_enable(const void *buf, uint8_t size, > + void *user_data) > +{ > + struct mesh_io_private *pvt = user_data; > + struct bt_hci_cmd_le_set_adv_enable cmd; > + > + if (!pvt) > + return; > + > + pvt->sending = true; > + cmd.enable = 0x01; /* Enable advertising */ > + bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_ADV_ENABLE, > + &cmd, sizeof(cmd), NULL, NULL, NULL); > +} > + > +static void set_send_adv_data(const void *buf, uint8_t size, > + void *user_data) > +{ > + struct mesh_io_private *pvt = user_data; > + struct tx_pkt *tx; > + struct bt_hci_cmd_le_set_adv_data cmd; > + > + if (!pvt || !pvt->tx) > + return; > + > + tx = pvt->tx; > + if (tx->len >= sizeof(cmd.data)) > + goto done; > + > + memset(&cmd, 0, sizeof(cmd)); > + > + cmd.len = tx->len + 1; > + cmd.data[0] = tx->len; > + memcpy(cmd.data + 1, tx->pkt, tx->len); > + > + bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_ADV_DATA, > + &cmd, sizeof(cmd), > + set_send_adv_enable, pvt, NULL); > +done: > + if (tx->delete) > + l_free(tx); > + > + pvt->tx = NULL; > +} > + > +static void set_send_adv_params(const void *buf, uint8_t size, > + void *user_data) > +{ > + struct mesh_io_private *pvt = user_data; > + struct bt_hci_cmd_le_set_adv_parameters cmd; > + uint16_t hci_interval; > + > + if (!pvt) > + return; > + > + hci_interval = (pvt->interval * 16) / 10; > + cmd.min_interval = L_CPU_TO_LE16(hci_interval); > + cmd.max_interval = L_CPU_TO_LE16(hci_interval); > + cmd.type = 0x03; /* ADV_NONCONN_IND */ > + cmd.own_addr_type = 0x01; /* ADDR_TYPE_RANDOM */ > + cmd.direct_addr_type = 0x00; > + memset(cmd.direct_addr, 0, 6); > + cmd.channel_map = 0x07; > + cmd.filter_policy = 0x03; > + > + bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_ADV_PARAMETERS, > + &cmd, sizeof(cmd), > + set_send_adv_data, pvt, NULL); > +} > + > +static void send_pkt(struct mesh_io_private *pvt, struct tx_pkt *tx, > + uint16_t interval) > +{ > + struct bt_hci_cmd_le_set_adv_enable cmd; > + > + pvt->tx = tx; > + pvt->interval = interval; > + > + if (!pvt->sending) { > + set_send_adv_params(NULL, 0, pvt); > + return; > + } > + > + cmd.enable = 0x00; /* Disable advertising */ > + bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_ADV_ENABLE, > + &cmd, sizeof(cmd), > + set_send_adv_params, pvt, NULL); > +} > + > +static void tx_timeout(struct l_timeout *timeout, void *user_data) > +{ > + struct mesh_io_private *pvt = user_data; > + struct tx_pkt *tx; > + uint16_t ms; > + uint8_t count; > + > + if (!pvt) > + return; > + > + tx = l_queue_pop_head(pvt->tx_pkts); > + if (!tx) { > + l_timeout_remove(timeout); > + pvt->tx_timeout = NULL; > + send_cancel(pvt); > + return; > + } > + > + if (tx->info.type == MESH_IO_TIMING_TYPE_GENERAL) { > + ms = tx->info.u.gen.interval; > + count = tx->info.u.gen.cnt; > + if (count != MESH_IO_TX_COUNT_UNLIMITED) > + tx->info.u.gen.cnt--; > + } else { > + ms = 25; > + count = 1; > + } > + > + tx->delete = !!(count == 1); > + > + send_pkt(pvt, tx, ms); > + > + if (count == 1) { > + /* send_pkt will delete when done */ > + tx = l_queue_peek_head(pvt->tx_pkts); > + if (tx && tx->info.type == MESH_IO_TIMING_TYPE_POLL_RSP) { > + ms = instant_remaining_ms(tx->info.u.poll_rsp.instant + > + tx->info.u.poll_rsp.delay); > + } > + } else > + l_queue_push_tail(pvt->tx_pkts, tx); > + > + if (timeout) { > + pvt->tx_timeout = timeout; > + l_timeout_modify_ms(timeout, ms); > + } else > + pvt->tx_timeout = l_timeout_create_ms(ms, tx_timeout, > + pvt, NULL); > +} > + > +static void tx_worker(void *user_data) > +{ > + struct mesh_io_private *pvt = user_data; > + struct tx_pkt *tx; > + uint32_t delay; > + > + tx = l_queue_peek_head(pvt->tx_pkts); > + if (!tx) > + return; > + > + switch (tx->info.type) { > + case MESH_IO_TIMING_TYPE_GENERAL: > + if (tx->info.u.gen.min_delay == tx->info.u.gen.max_delay) > + delay = tx->info.u.gen.min_delay; > + else { > + l_getrandom(&delay, sizeof(delay)); > + delay %= tx->info.u.gen.max_delay - > + tx->info.u.gen.min_delay; > + delay += tx->info.u.gen.min_delay; > + } > + break; > + > + case MESH_IO_TIMING_TYPE_POLL: > + if (tx->info.u.poll.min_delay == tx->info.u.poll.max_delay) > + delay = tx->info.u.poll.min_delay; > + else { > + l_getrandom(&delay, sizeof(delay)); > + delay %= tx->info.u.poll.max_delay - > + tx->info.u.poll.min_delay; > + delay += tx->info.u.poll.min_delay; > + } > + break; > + > + case MESH_IO_TIMING_TYPE_POLL_RSP: > + /* Delay until Instant + Delay */ > + delay = instant_remaining_ms(tx->info.u.poll_rsp.instant + > + tx->info.u.poll_rsp.delay); > + if (delay > 255) > + delay = 0; > + break; > + > + default: > + return; > + } > + > + if (!delay) > + tx_timeout(pvt->tx_timeout, pvt); > + else if (pvt->tx_timeout) > + l_timeout_modify_ms(pvt->tx_timeout, delay); > + else > + pvt->tx_timeout = l_timeout_create_ms(delay, tx_timeout, > + pvt, NULL); > +} > + > +static bool send_tx(struct mesh_io *io, struct mesh_io_send_info *info, > + const uint8_t *data, uint16_t len) > +{ > + struct mesh_io_private *pvt = io->pvt; > + struct tx_pkt *tx; > + bool sending = false; > + > + if (!info || !data || !len || len > sizeof(tx->pkt)) > + return false; > + > + > + tx = l_new(struct tx_pkt, 1); > + if (!tx) > + return false; > + > + memcpy(&tx->info, info, sizeof(tx->info)); > + memcpy(&tx->pkt, data, len); > + tx->len = len; > + > + if (info->type == MESH_IO_TIMING_TYPE_POLL_RSP) > + l_queue_push_head(pvt->tx_pkts, tx); > + else { > + sending = !l_queue_isempty(pvt->tx_pkts); > + l_queue_push_tail(pvt->tx_pkts, tx); > + } > + > + if (!sending) { > + l_timeout_remove(pvt->tx_timeout); > + pvt->tx_timeout = NULL; > + l_idle_oneshot(tx_worker, pvt, NULL); > + } > + > + return true; > +} > + > +static bool find_by_ad_type(const void *a, const void *b) > +{ > + const struct tx_pkt *tx = a; > + uint8_t ad_type = L_PTR_TO_UINT(b); > + > + return !ad_type || ad_type == tx->pkt[0]; > +} > + > +static bool find_by_pattern(const void *a, const void *b) > +{ > + const struct tx_pkt *tx = a; > + const struct tx_pattern *pattern = b; > + > + if (tx->len < pattern->len) > + return false; > + > + return (!memcmp(tx->pkt, pattern->data, pattern->len)); > +} > + > +static bool tx_cancel(struct mesh_io *io, uint8_t *data, uint8_t len) > +{ > + struct mesh_io_private *pvt = io->pvt; > + struct tx_pkt *tx; > + > + if (!data) > + return false; > + > + if (len == 1) { > + do { > + tx = l_queue_remove_if(pvt->tx_pkts, find_by_ad_type, > + L_UINT_TO_PTR(data[0])); > + l_free(tx); > + } while (tx); > + } else { > + struct tx_pattern pattern = { > + .data = data, > + .len = len > + }; > + > + do { > + tx = l_queue_remove_if(pvt->tx_pkts, find_by_pattern, > + &pattern); > + l_free(tx); > + } while (tx); > + } > + > + if (l_queue_isempty(pvt->tx_pkts)) { > + send_cancel(pvt); > + l_timeout_remove(pvt->tx_timeout); > + pvt->tx_timeout = NULL; > + } > + > + return true; > +} > + > +static bool find_by_filter_id(const void *a, const void *b) > +{ > + const struct pvt_rx_reg *rx_reg = a; > + uint8_t filter_id = L_PTR_TO_UINT(b); > + > + return rx_reg->filter_id == filter_id; > +} > + > +static bool recv_register(struct mesh_io *io, uint8_t filter_id, > + mesh_io_recv_func_t cb, void *user_data) > +{ > + struct bt_hci_cmd_le_set_scan_enable cmd; > + struct mesh_io_private *pvt = io->pvt; > + struct pvt_rx_reg *rx_reg; > + bool scanning; > + > + l_info("%s %d", __func__, filter_id); > + if (!cb || !filter_id || filter_id > sizeof(pvt->filters)) > + return false; > + > + rx_reg = l_queue_remove_if(pvt->rx_regs, find_by_filter_id, > + L_UINT_TO_PTR(filter_id)); > + > + if (!rx_reg) { > + rx_reg = l_new(struct pvt_rx_reg, 1); > + if (!rx_reg) > + return false; > + } > + > + rx_reg->filter_id = filter_id; > + rx_reg->cb = cb; > + rx_reg->user_data = user_data; > + > + scanning = !l_queue_isempty(pvt->rx_regs); > + > + l_queue_push_head(pvt->rx_regs, rx_reg); > + > + if (!scanning) { > + cmd.enable = 0x01; /* Enable scanning */ > + cmd.filter_dup = 0x00; /* Report duplicates */ > + bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_SCAN_ENABLE, > + &cmd, sizeof(cmd), NULL, NULL, NULL); > + } > + > + return true; > +} > + > +static bool recv_deregister(struct mesh_io *io, uint8_t filter_id) > +{ > + struct bt_hci_cmd_le_set_scan_enable cmd; > + struct mesh_io_private *pvt = io->pvt; > + > + struct pvt_rx_reg *rx_reg; > + > + rx_reg = l_queue_remove_if(pvt->rx_regs, find_by_filter_id, > + L_UINT_TO_PTR(filter_id)); > + > + if (rx_reg) > + l_free(rx_reg); > + > + if (l_queue_isempty(pvt->rx_regs)) { > + cmd.enable = 0x00; /* Disable scanning */ > + cmd.filter_dup = 0x00; /* Report duplicates */ > + bt_hci_send(pvt->hci, BT_HCI_CMD_LE_SET_SCAN_ENABLE, > + &cmd, sizeof(cmd), NULL, NULL, NULL); > + > + } > + > + return true; > +} > + > +static bool filter_set(struct mesh_io *io, > + uint8_t filter_id, const uint8_t *data, uint8_t len, > + mesh_io_status_func_t callback, void *user_data) > +{ > + struct mesh_io_private *pvt = io->pvt; > + > + l_info("%s id: %d, --> %2.2x", __func__, filter_id, data[0]); > + if (!data || !len || !filter_id || filter_id > sizeof(pvt->filters)) > + return false; > + > + pvt->filters[filter_id - 1] = data[0]; > + > + /* TODO: Delayed Call to successful status */ > + > + return true; > +} > + > +const struct mesh_io_api mesh_io_generic = { > + .init = dev_init, > + .destroy = dev_destroy, > + .caps = dev_caps, > + .send = send_tx, > + .reg = recv_register, > + .dereg = recv_deregister, > + .set = filter_set, > + .cancel = tx_cancel, > +}; > diff --git a/mesh/mesh-io.c b/mesh/mesh-io.c > new file mode 100644 > index 000000000..762eb2c6f > --- /dev/null > +++ b/mesh/mesh-io.c > @@ -0,0 +1,187 @@ > +/* > + * > + * BlueZ - Bluetooth protocol stack for Linux > + * > + * Copyright (C) 2018 Intel Corporation. All rights reserved. > + * > + * > + * This library is free software; you can redistribute it and/or > + * modify it under the terms of the GNU Lesser General Public > + * License as published by the Free Software Foundation; either > + * version 2.1 of the License, or (at your option) any later version. > + * > + * This library is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU > + * Lesser General Public License for more details. > + * > + */ > + > +#ifdef HAVE_CONFIG_H > +#include <config.h> > +#endif > + > +#include <ell/ell.h> > + > +#include "lib/bluetooth.h" > +#include "lib/hci.h" > + > +#include "mesh/mesh-defs.h" > + > +#include "mesh/mesh-io.h" > +#include "mesh/mesh-io-api.h" > + > +/* List of Mesh-IO Type headers */ > +#include "mesh/mesh-io-generic.h" > + > +/* List of Supported Mesh-IO Types */ > +static const struct mesh_io_table table[] = { > + {MESH_IO_TYPE_GENERIC, &mesh_io_generic}, > +}; { MESH.., &mesh.. } > + > +static struct l_queue *io_list; > + > +static bool match_by_io(const void *a, const void *b) > +{ > + return a == b; > +} > + > +static bool match_by_index(const void *a, const void *b) > +{ > + const struct mesh_io *io = a; > + > + return io->index == L_PTR_TO_UINT(b); > +} > + > +struct mesh_io *mesh_io_new(uint16_t index, enum mesh_io_type type) > +{ > + const struct mesh_io_api *api = NULL; > + struct mesh_io *io; > + uint16_t i; No alignment of variable names please. > + > + l_info("%s %d\n", __func__, type); Empty line here. > + for (i = 0; i < L_ARRAY_SIZE(table); i++) { > + if (table[i].type == type) { > + api = table[i].api; > + break; > + } > + } > + > + io = l_queue_find(io_list, match_by_index, L_UINT_TO_PTR(index)); > + > + if (api == NULL || api->init == NULL || io != NULL) > + return NULL; > + > + io = l_new(struct mesh_io, 1); > + > + if (io == NULL) > + return NULL; Use if (!io). > + > + io->type = type; > + io->index = index; > + io->api = api; > + if (!api->init(index, io)) > + goto fail; > + > + if (io_list == NULL) > + io_list = l_queue_new(); > + > + if (api->set) { > + uint8_t pkt = MESH_AD_TYPE_NETWORK; > + uint8_t bec = MESH_AD_TYPE_BEACON; > + uint8_t prv = MESH_AD_TYPE_PROVISION; > + > + api->set(io, 1, &bec, 1, NULL, NULL); > + api->set(io, 2, &prv, 1, NULL, NULL); > + api->set(io, 3, &pkt, 1, NULL, NULL); > + } > + > + if (l_queue_push_head(io_list, io)) > + return io; > + > +fail: > + if (api->destroy) > + api->destroy(io); > + > + l_free(io); > + return NULL; > +} > + > +void mesh_io_destroy(struct mesh_io *io) > +{ > + io = l_queue_remove_if(io_list, match_by_io, io); > + > + if (io && io->api && io->api->destroy) > + io->api->destroy(io); > + > + l_free(io); > + > + if (l_queue_isempty(io_list)) { > + l_queue_destroy(io_list, NULL); > + io_list = NULL; > + } > +} > + > +bool mesh_io_get_caps(struct mesh_io *io, struct mesh_io_caps *caps) > +{ > + io = l_queue_find(io_list, match_by_io, io); > + > + if (io && io->api && io->api->caps) > + return io->api->caps(io, caps); > + > + return false; > +} > + > +bool mesh_io_register_recv_cb(struct mesh_io *io, uint8_t filter_id, > + mesh_io_recv_func_t cb, void *user_data) > +{ > + io = l_queue_find(io_list, match_by_io, io); > + > + if (io && io->api && io->api->reg) > + return io->api->reg(io, filter_id, cb, user_data); > + > + return false; > +} > + > +bool mesh_io_deregister_recv_cb(struct mesh_io *io, uint8_t filter_id) > +{ > + io = l_queue_find(io_list, match_by_io, io); > + > + if (io && io->api && io->api->dereg) > + return io->api->dereg(io, filter_id); > + > + return false; > +} > + > +bool mesh_set_filter(struct mesh_io *io, uint8_t filter_id, > + const uint8_t *data, uint8_t len, > + mesh_io_status_func_t cb, void *user_data) > +{ > + io = l_queue_find(io_list, match_by_io, io); > + > + if (io && io->api && io->api->set) > + return io->api->set(io, filter_id, data, len, cb, user_data); > + > + return false; > +} > + > +bool mesh_io_send(struct mesh_io *io, struct mesh_io_send_info *info, > + const uint8_t *data, uint16_t len) > +{ > + io = l_queue_find(io_list, match_by_io, io); > + > + if (io && io->api && io->api->send) > + return io->api->send(io, info, data, len); > + > + return false; > +} > + > +bool mesh_io_send_cancel(struct mesh_io *io, uint8_t *pattern, uint8_t len) > +{ > + io = l_queue_find(io_list, match_by_io, io); > + > + if (io && io->api && io->api->cancel) > + return io->api->cancel(io, pattern, len); > + > + return false; > +} Regards Marcel -- To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html