mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-27 06:04:32 +08:00
93d0d8b2fc
This aligns the behavior of command line option --io to add new "auto" value and modify the behavior of "generic" value: *auto* - Use first available controller: via MGMT interface if kernel supports it, otherwise, via raw HCI socket (i.e., default to *generic*). *generic:[hci]<index>* - Use generic HCI io on interface hci<index> The default value is now *auto*, whereas *generic* is used only if the specific HCI controller is explicitly specified.
324 lines
6.9 KiB
C
324 lines
6.9 KiB
C
// SPDX-License-Identifier: LGPL-2.1-or-later
|
|
/*
|
|
*
|
|
* BlueZ - Bluetooth protocol stack for Linux
|
|
*
|
|
* Copyright (C) 2018 Intel Corporation. All rights reserved.
|
|
*
|
|
*
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include <ell/ell.h>
|
|
|
|
#include "lib/bluetooth.h"
|
|
#include "lib/mgmt.h"
|
|
#include "src/shared/mgmt.h"
|
|
|
|
#include "mesh/mesh-defs.h"
|
|
#include "mesh/mesh-mgmt.h"
|
|
#include "mesh/mesh-io.h"
|
|
#include "mesh/mesh-io-api.h"
|
|
|
|
/* List of Mesh-IO Type headers */
|
|
#include "mesh/mesh-io-mgmt.h"
|
|
#include "mesh/mesh-io-generic.h"
|
|
#include "mesh/mesh-io-unit.h"
|
|
|
|
struct loop_data {
|
|
uint16_t len;
|
|
uint8_t data[];
|
|
};
|
|
|
|
/* List of Supported Mesh-IO Types */
|
|
static const struct mesh_io_table table[] = {
|
|
{MESH_IO_TYPE_MGMT, &mesh_io_mgmt},
|
|
{MESH_IO_TYPE_GENERIC, &mesh_io_generic},
|
|
{MESH_IO_TYPE_UNIT_TEST, &mesh_io_unit},
|
|
};
|
|
|
|
static const uint8_t unprv_filter[] = { MESH_AD_TYPE_BEACON, 0 };
|
|
|
|
static struct mesh_io *default_io;
|
|
static struct l_timeout *loop_adv_to;
|
|
|
|
static const struct mesh_io_api *io_api(enum mesh_io_type type)
|
|
{
|
|
uint16_t i;
|
|
|
|
for (i = 0; i < L_ARRAY_SIZE(table); i++) {
|
|
if (table[i].type == type)
|
|
return table[i].api;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void refresh_rx(void *a, void *b)
|
|
{
|
|
struct mesh_io_reg *rx_reg = a;
|
|
struct mesh_io *io = b;
|
|
|
|
if (io->api && io->api->reg)
|
|
io->api->reg(io, rx_reg->filter, rx_reg->len, rx_reg->cb,
|
|
rx_reg->user_data);
|
|
}
|
|
|
|
static void ctl_alert(int index, bool up, bool pwr, bool mesh, void *user_data)
|
|
{
|
|
enum mesh_io_type type = L_PTR_TO_UINT(user_data);
|
|
const struct mesh_io_api *api = NULL;
|
|
|
|
l_warn("index %u up:%d pwr: %d mesh: %d", index, up, pwr, mesh);
|
|
|
|
/* If specific IO controller requested, honor it */
|
|
if (default_io->favored_index != MGMT_INDEX_NONE) {
|
|
if (default_io->favored_index != index)
|
|
return;
|
|
|
|
if (!up | pwr) {
|
|
l_warn("HCI%u failed to start generic IO %s",
|
|
index, pwr ? ": already powered on" : "");
|
|
if (default_io->ready)
|
|
default_io->ready(default_io->user_data, false);
|
|
}
|
|
}
|
|
|
|
if (!up && default_io->index == index) {
|
|
/* Our controller has disappeared */
|
|
if (default_io->api && default_io->api->destroy) {
|
|
default_io->api->destroy(default_io);
|
|
default_io->api = NULL;
|
|
}
|
|
|
|
/* Re-enumerate controllers */
|
|
mesh_mgmt_list(ctl_alert, user_data);
|
|
return;
|
|
}
|
|
|
|
/* If we already have an API, keep using it */
|
|
if (!up || default_io->api)
|
|
return;
|
|
|
|
if (mesh && type != MESH_IO_TYPE_GENERIC)
|
|
api = io_api(MESH_IO_TYPE_MGMT);
|
|
else if (!pwr)
|
|
api = io_api(MESH_IO_TYPE_GENERIC);
|
|
|
|
if (api) {
|
|
default_io->index = index;
|
|
default_io->api = api;
|
|
api->init(default_io, &index, default_io->user_data);
|
|
l_queue_foreach(default_io->rx_regs, refresh_rx, default_io);
|
|
}
|
|
}
|
|
|
|
static void free_io(struct mesh_io *io)
|
|
{
|
|
if (io) {
|
|
if (io->api && io->api->destroy)
|
|
io->api->destroy(io);
|
|
|
|
l_queue_destroy(io->rx_regs, l_free);
|
|
io->rx_regs = NULL;
|
|
l_free(io);
|
|
l_warn("Destroy %p", io);
|
|
}
|
|
}
|
|
|
|
static struct mesh_io_reg *find_by_filter(struct l_queue *rx_regs,
|
|
const uint8_t *filter, uint8_t len)
|
|
{
|
|
const struct l_queue_entry *entry;
|
|
|
|
entry = l_queue_get_entries(rx_regs);
|
|
|
|
for (; entry; entry = entry->next) {
|
|
struct mesh_io_reg *rx_reg = entry->data;
|
|
|
|
if (rx_reg->len == len && !memcmp(rx_reg->filter, filter, len))
|
|
return rx_reg;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
struct mesh_io *mesh_io_new(enum mesh_io_type type, void *opts,
|
|
mesh_io_ready_func_t cb, void *user_data)
|
|
{
|
|
const struct mesh_io_api *api = NULL;
|
|
|
|
/* Only allow one IO */
|
|
if (default_io)
|
|
return NULL;
|
|
|
|
default_io = l_new(struct mesh_io, 1);
|
|
default_io->ready = cb;
|
|
default_io->user_data = user_data;
|
|
default_io->favored_index = *(int *) opts;
|
|
default_io->rx_regs = l_queue_new();
|
|
|
|
if (type >= MESH_IO_TYPE_AUTO) {
|
|
if (!mesh_mgmt_list(ctl_alert, L_UINT_TO_PTR(type)))
|
|
goto fail;
|
|
|
|
return default_io;
|
|
}
|
|
|
|
api = io_api(type);
|
|
|
|
if (!api || !api->init)
|
|
goto fail;
|
|
|
|
default_io->api = api;
|
|
|
|
if (!api->init(default_io, opts, user_data))
|
|
goto fail;
|
|
|
|
return default_io;
|
|
|
|
fail:
|
|
free_io(default_io);
|
|
default_io = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
void mesh_io_destroy(struct mesh_io *io)
|
|
{
|
|
}
|
|
|
|
bool mesh_io_get_caps(struct mesh_io *io, struct mesh_io_caps *caps)
|
|
{
|
|
if (io != default_io)
|
|
return false;
|
|
|
|
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, const uint8_t *filter,
|
|
uint8_t len, mesh_io_recv_func_t cb,
|
|
void *user_data)
|
|
{
|
|
struct mesh_io_reg *rx_reg;
|
|
|
|
if (io == NULL)
|
|
io = default_io;
|
|
|
|
if (io != default_io || !cb || !filter || !len)
|
|
return false;
|
|
|
|
rx_reg = find_by_filter(io->rx_regs, filter, len);
|
|
|
|
l_free(rx_reg);
|
|
l_queue_remove(io->rx_regs, rx_reg);
|
|
|
|
rx_reg = l_malloc(sizeof(struct mesh_io_reg) + len);
|
|
rx_reg->cb = cb;
|
|
rx_reg->len = len;
|
|
rx_reg->user_data = user_data;
|
|
memcpy(rx_reg->filter, filter, len);
|
|
|
|
l_queue_push_head(io->rx_regs, rx_reg);
|
|
|
|
if (io && io->api && io->api->reg)
|
|
return io->api->reg(io, filter, len, cb, user_data);
|
|
|
|
return false;
|
|
}
|
|
|
|
bool mesh_io_deregister_recv_cb(struct mesh_io *io, const uint8_t *filter,
|
|
uint8_t len)
|
|
{
|
|
struct mesh_io_reg *rx_reg;
|
|
|
|
if (io != default_io)
|
|
return false;
|
|
|
|
rx_reg = find_by_filter(io->rx_regs, filter, len);
|
|
|
|
l_queue_remove(io->rx_regs, rx_reg);
|
|
l_free(rx_reg);
|
|
|
|
if (io && io->api && io->api->dereg)
|
|
return io->api->dereg(io, filter, len);
|
|
|
|
return false;
|
|
}
|
|
|
|
static void loop_foreach(void *data, void *user_data)
|
|
{
|
|
struct mesh_io_reg *rx_reg = data;
|
|
struct loop_data *rx = user_data;
|
|
|
|
if (!memcmp(rx_reg->filter, unprv_filter, sizeof(unprv_filter)))
|
|
rx_reg->cb(rx_reg->user_data, NULL, rx->data, rx->len);
|
|
}
|
|
|
|
static void loop_rx(struct l_timeout *timeout, void *user_data)
|
|
{
|
|
struct loop_data *rx = user_data;
|
|
|
|
l_queue_foreach(default_io->rx_regs, loop_foreach, rx);
|
|
l_timeout_modify_ms(loop_adv_to, 500);
|
|
}
|
|
|
|
static void loop_destroy(void *user_data)
|
|
{
|
|
l_free(user_data);
|
|
}
|
|
|
|
static void loop_unprv_beacon(const uint8_t *data, uint16_t len)
|
|
{
|
|
struct loop_data *pkt = l_malloc(len + sizeof(struct loop_data));
|
|
|
|
memcpy(pkt->data, data, len);
|
|
pkt->len = len;
|
|
l_timeout_remove(loop_adv_to);
|
|
loop_adv_to = l_timeout_create_ms(500, loop_rx, pkt, loop_destroy);
|
|
}
|
|
|
|
bool mesh_io_send(struct mesh_io *io, struct mesh_io_send_info *info,
|
|
const uint8_t *data, uint16_t len)
|
|
{
|
|
if (io && io != default_io)
|
|
return false;
|
|
|
|
if (!io)
|
|
io = default_io;
|
|
|
|
/* Loop unprovisioned beacons for local clients */
|
|
if (!memcmp(data, unprv_filter, sizeof(unprv_filter)))
|
|
loop_unprv_beacon(data, len);
|
|
|
|
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, const uint8_t *pattern,
|
|
uint8_t len)
|
|
{
|
|
if (io && io != default_io)
|
|
return false;
|
|
|
|
if (!io)
|
|
io = default_io;
|
|
|
|
if (loop_adv_to && len >= 2 && !memcmp(pattern, unprv_filter, 2)) {
|
|
l_timeout_remove(loop_adv_to);
|
|
loop_adv_to = NULL;
|
|
}
|
|
|
|
if (io && io->api && io->api->cancel)
|
|
return io->api->cancel(io, pattern, len);
|
|
|
|
return false;
|
|
}
|