linux-bluetooth.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Stotland, Inga" <inga.stotland@intel.com>
To: "michal.lowas-rzechonek@silvair.com" 
	<michal.lowas-rzechonek@silvair.com>
Cc: "luiz.dentz@gmail.com" <luiz.dentz@gmail.com>,
	"Gix, Brian" <brian.gix@intel.com>,
	"linux-bluetooth@vger.kernel.org"
	<linux-bluetooth@vger.kernel.org>
Subject: Re: [PATCH BlueZ v3 2/3] mesh: Add unit test IO
Date: Wed, 10 Feb 2021 07:08:35 +0000	[thread overview]
Message-ID: <b419211dd47a92cae5ba627a47b0d2dc80831745.camel@intel.com> (raw)
In-Reply-To: <ffb0ac27b73cd291373f56912816a769a959c0eb.camel@intel.com>

On Tue, 2021-02-09 at 22:48 -0800, Stotland, Inga wrote:
> Hi Michal,
> 
> On Tue, 2021-02-09 at 14:49 +0100, Michał Lowas-Rzechonek wrote:
> > Hi Inga, Brian,
> > 
> > We ended up implementing a TCP server inside bluetooth-meshd that
> > can be
> > used by external radio adapters.
> > 
> > This is useful not only in tests, but also for remote radios in
> > situations where the box runing bluetooth-meshd is in e.g. a
> > basement,
> > so you might want to have a small embedded device that does (non-
> > HCI)
> > bluetooth and TCP/IP.
> > 
> > For various reasons we're authenticating and encrypting the link
> > using
> > TLS-PSK, so our setup is a bit complex, but I guess it could be
> > trimmed
> > down.
> > 
> > The underlying protocol is also "ours", but again I'm open to
> > adjusting
> > it if this means we could include it in the official release.
> > 
> > Would you be interested in something like this? We use it for great
> > effect to test the daemon using 
> > https://docs.pytest.org/en/stable/
> > 
> > 
> > https://github.com/homersoft/bluez/blob/master/mesh/silvair-io.c
> > 
> > https://github.com/homersoft/bluez/blob/master/mesh/mesh-io-uart.c
> > 
> > https://github.com/homersoft/bluez/blob/master/mesh/mesh-io-tcpserver.c
> > 
> 
> Thank you. I will take a look. If we could have a version of io that
> is adaptable for various testing scenarious that would be great.
> One thing to keep in mind is that for BlueZ framework, the test
> should be able to run in non-sudo mode.

oh yes, one more thing: the test should be runnable even if no
controllers are available. The "unit" IO is essentially a loopback in
our case.

> 
> Best regards, 
> Inga
> 
> > On 02/05, Inga Stotland wrote:
> > > From: Brian Gix <
> > > brian.gix@intel.com
> > > >
> > > 
> > > This adds a new type of mesh IO that is used for non-interactive
> > > testing.
> > > The new io option can be specified on command line as:
> > > --io unit:<socket_name>
> > > 
> > > When the bluetooth-meshd daemon starts with the "unit" IO type,
> > > the daemon opens a socket (fd to open is provided after "unit:"
> > > in <socket_name>). The communication with the daemon is done
> > > either
> > > through the loop-back using mesh DBus-based APIs or the specified
> > > named socket.
> > > ---
> > >  Makefile.mesh       |   2 +
> > >  mesh/main.c         |  41 +++-
> > >  mesh/mesh-io-unit.c | 533
> > > ++++++++++++++++++++++++++++++++++++++++++++
> > >  mesh/mesh-io-unit.h |  11 +
> > >  mesh/mesh-io.c      |   9 +-
> > >  mesh/mesh-io.h      |   3 +-
> > >  6 files changed, 582 insertions(+), 17 deletions(-)
> > >  create mode 100644 mesh/mesh-io-unit.c
> > >  create mode 100644 mesh/mesh-io-unit.h
> > > 
> > > diff --git a/Makefile.mesh b/Makefile.mesh
> > > index 228dd1b5f..73eaded4a 100644
> > > --- a/Makefile.mesh
> > > +++ b/Makefile.mesh
> > > @@ -17,6 +17,8 @@ mesh_sources = mesh/mesh.h mesh/mesh.c \
> > >  				mesh/error.h mesh/mesh-io-api.h \
> > >  				mesh/mesh-io-generic.h \
> > >  				mesh/mesh-io-generic.c \
> > > +				mesh/mesh-io-unit.h \
> > > +				mesh/mesh-io-unit.c \
> > >  				mesh/net.h mesh/net.c \
> > >  				mesh/crypto.h mesh/crypto.c \
> > >  				mesh/friend.h mesh/friend.c \
> > > diff --git a/mesh/main.c b/mesh/main.c
> > > index 4356e3f65..1b466598b 100644
> > > --- a/mesh/main.c
> > > +++ b/mesh/main.c
> > > @@ -61,7 +61,7 @@ static void usage(void)
> > >  	       "\t--help            Show %s information\n", __func__);
> > >  	fprintf(stderr,
> > >  	       "io:\n"
> > > -	       "\t([hci]<index> | generic[:[hci]<index>])\n"
> > > +	       "\t([hci]<index> | generic[:[hci]<index>] |
> > > unit:<fd_path>)\n"
> > >  	       "\t\tUse generic HCI io on interface hci<index>, or the
> > > first\n"
> > >  	       "\t\tavailable one\n");
> > >  }
> > > @@ -77,6 +77,7 @@ static void mesh_ready_callback(void
> > > *user_data, bool success)
> > >  {
> > >  	struct l_dbus *dbus = user_data;
> > >  
> > > +	l_info("mesh_ready_callback");
> > >  	if (!success) {
> > >  		l_error("Failed to start mesh");
> > >  		l_main_quit();
> > > @@ -92,10 +93,8 @@ static void mesh_ready_callback(void
> > > *user_data, bool success)
> > >  static void request_name_callback(struct l_dbus *dbus, bool
> > > success,
> > >  					bool queued, void *user_data)
> > >  {
> > > -	l_info("Request name %s",
> > > -		success ? "success": "failed");
> > > -
> > > -	if (!success) {
> > > +	if (!success && io_type != MESH_IO_TYPE_UNIT_TEST) {
> > > +		l_info("Request name failed");
> > >  		l_main_quit();
> > >  		return;
> > >  	}
> > > @@ -159,6 +158,21 @@ static bool parse_io(const char *optarg,
> > > enum mesh_io_type *type, void **opts)
> > >  			return true;
> > >  
> > >  		return false;
> > > +
> > > +	} else if (strstr(optarg, "unit") == optarg) {
> > > +		char *test_path;
> > > +
> > > +		*type = MESH_IO_TYPE_UNIT_TEST;
> > > +
> > > +		optarg += strlen("unit");
> > > +		if (*optarg != ':')
> > > +			return false;
> > > +
> > > +		optarg++;
> > > +		test_path = strdup(optarg);
> > > +
> > > +		*opts = test_path;
> > > +		return true;
> > >  	}
> > >  
> > >  	return false;
> > > @@ -187,11 +201,19 @@ int main(int argc, char *argv[])
> > >  	for (;;) {
> > >  		int opt;
> > >  
> > > -		opt = getopt_long(argc, argv, "i:s:c:ndbh",
> > > main_options, NULL);
> > > +		opt = getopt_long(argc, argv, "u:i:s:c:ndbh",
> > > main_options,
> > > +									
> > > NULL);
> > >  		if (opt < 0)
> > >  			break;
> > >  
> > >  		switch (opt) {
> > > +		case 'u':
> > > +			if (sscanf(optarg, "%d", &hci_index) == 1 ||
> > > +					sscanf(optarg, "%d",
> > > &hci_index) == 1)
> > > +				io = l_strdup_printf("unit:%d",
> > > hci_index);
> > > +			else
> > > +				io = l_strdup(optarg);
> > > +			break;
> > >  		case 'i':
> > >  			if (sscanf(optarg, "hci%d", &hci_index) == 1 ||
> > >  					sscanf(optarg, "%d",
> > > &hci_index) == 1)
> > > @@ -261,11 +283,8 @@ int main(int argc, char *argv[])
> > >  	status = l_main_run_with_signal(signal_handler, NULL);
> > >  
> > >  done:
> > > -	if (io)
> > > -		l_free(io);
> > > -
> > > -	if (io_opts)
> > > -		l_free(io_opts);
> > > +	l_free(io);
> > > +	l_free(io_opts);
> > >  
> > >  	mesh_cleanup();
> > >  	l_dbus_destroy(dbus);
> > > diff --git a/mesh/mesh-io-unit.c b/mesh/mesh-io-unit.c
> > > new file mode 100644
> > > index 000000000..c5aae6741
> > > --- /dev/null
> > > +++ b/mesh/mesh-io-unit.c
> > > @@ -0,0 +1,533 @@
> > > +// SPDX-License-Identifier: LGPL-2.1-or-later
> > > +/*
> > > + *
> > > + *  BlueZ - Bluetooth protocol stack for Linux
> > > + *
> > > + *  Copyright (C) 2021  Intel Corporation. All rights reserved.
> > > + *
> > > + *
> > > + */
> > > +
> > > +#ifdef HAVE_CONFIG_H
> > > +#include <config.h>
> > > +#endif
> > > +
> > > +#include <errno.h>
> > > +#include <string.h>
> > > +#include <sys/time.h>
> > > +#include <sys/socket.h>
> > > +#include <sys/un.h>
> > > +#include <unistd.h>
> > > +#include <stdio.h>
> > > +#include <ell/ell.h>
> > > +
> > > +#include "mesh/mesh-defs.h"
> > > +#include "mesh/dbus.h"
> > > +#include "mesh/mesh-io.h"
> > > +#include "mesh/mesh-io-api.h"
> > > +#include "mesh/mesh-io-generic.h"
> > > +
> > > +struct mesh_io_private {
> > > +	struct l_io *sio;
> > > +	void *user_data;
> > > +	char *unique_name;
> > > +	mesh_io_ready_func_t ready_callback;
> > > +	struct l_timeout *tx_timeout;
> > > +	struct l_queue *rx_regs;
> > > +	struct l_queue *tx_pkts;
> > > +	struct sockaddr_un addr;
> > > +	int fd;
> > > +	uint16_t interval;
> > > +};
> > > +
> > > +struct pvt_rx_reg {
> > > +	mesh_io_recv_func_t cb;
> > > +	void *user_data;
> > > +	uint8_t len;
> > > +	uint8_t filter[0];
> > > +};
> > > +
> > > +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_reg, void *v_rx)
> > > +{
> > > +	struct pvt_rx_reg *rx_reg = v_reg;
> > > +	struct process_data *rx = v_rx;
> > > +
> > > +	if (!memcmp(rx->data, rx_reg->filter, rx_reg->len))
> > > +		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
> > > *addr,
> > > +					const uint8_t *data, uint8_t
> > > len)
> > > +{
> > > +	struct process_data rx = {
> > > +		.pvt = pvt,
> > > +		.data = data,
> > > +		.len = len,
> > > +		.info.instant = instant,
> > > +		.info.addr = addr,
> > > +		.info.chan = 7,
> > > +		.info.rssi = rssi,
> > > +	};
> > > +
> > > +	l_queue_foreach(pvt->rx_regs, process_rx_callbacks, &rx);
> > > +}
> > > +
> > > +static bool incoming(struct l_io *sio, void *user_data)
> > > +{
> > > +	struct mesh_io_private *pvt = user_data;
> > > +	uint32_t instant;
> > > +	uint8_t buf[31];
> > > +	size_t size;
> > > +
> > > +	instant = get_instant();
> > > +
> > > +	size = recv(pvt->fd, buf, sizeof(buf), MSG_DONTWAIT);
> > > +
> > > +	if (size > 9 && buf[0]) {
> > > +		process_rx(pvt, -20, instant, NULL, buf + 1,
> > > (uint8_t)size);
> > > +	} else if (size == 1 && !buf[0] && pvt->unique_name) {
> > > +
> > > +		/* Return DBUS unique name */
> > > +		size = strlen(pvt->unique_name);
> > > +
> > > +		if (size > sizeof(buf) - 2)
> > > +			return true;
> > > +
> > > +		buf[0] = 0;
> > > +		memcpy(buf + 1, pvt->unique_name, size + 1);
> > > +		send(pvt->fd, buf, size + 2, MSG_DONTWAIT);
> > > +	}
> > > +
> > > +	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 void free_socket(struct mesh_io_private *pvt)
> > > +{
> > > +	l_io_destroy(pvt->sio);
> > > +	close(pvt->fd);
> > > +	unlink(pvt->addr.sun_path);
> > > +}
> > > +
> > > +static void hello_callback(struct l_dbus_message *msg, void
> > > *user_data)
> > > +{
> > > +	struct mesh_io_private *pvt = user_data;
> > > +
> > > +	pvt->unique_name =
> > > l_strdup(l_dbus_message_get_destination(msg));
> > > +	l_debug("User-Daemon unique name: %s", pvt->unique_name);
> > > +}
> > > +
> > > +static void get_name(struct l_timeout *timeout, void *user_data)
> > > +{
> > > +	struct mesh_io_private *pvt = user_data;
> > > +	struct l_dbus *dbus = dbus_get_bus();
> > > +	struct l_dbus_message *msg;
> > > +
> > > +	l_timeout_remove(timeout);
> > > +	if (!dbus) {
> > > +		l_timeout_create_ms(20, get_name, pvt, NULL);
> > > +		return;
> > > +	}
> > > +
> > > +	/* Retrieve unique name */
> > > +	msg = l_dbus_message_new_method_call(dbus,
> > > "org.freedesktop.DBus",
> > > +							"/org/freedeskt
> > > op/DBus",
> > > +							"org.freedeskto
> > > p.DBus",
> > > +							"GetId");
> > > +
> > > +	l_dbus_message_set_arguments(msg, "");
> > > +
> > > +	l_dbus_send_with_reply(dbus, msg, hello_callback, pvt, NULL);
> > > +}
> > > +
> > > +static void unit_up(void *user_data)
> > > +{
> > > +	struct mesh_io_private *pvt = user_data;
> > > +
> > > +	l_debug("Started io-unit");
> > > +
> > > +	if (pvt->ready_callback)
> > > +		pvt->ready_callback(pvt->user_data, true);
> > > +
> > > +	l_timeout_create_ms(1, get_name, pvt, NULL);
> > > +}
> > > +
> > > +static bool unit_init(struct mesh_io *io, void *opt,
> > > +				mesh_io_ready_func_t cb, void
> > > *user_data)
> > > +{
> > > +	struct mesh_io_private *pvt;
> > > +	char *sk_path;
> > > +	size_t size;
> > > +
> > > +	l_debug("Starting Unit test IO");
> > > +	if (!io || io->pvt)
> > > +		return false;
> > > +
> > > +	sk_path = (char *) opt;
> > > +
> > > +	pvt = l_new(struct mesh_io_private, 1);
> > > +
> > > +	pvt->addr.sun_family = AF_LOCAL;
> > > +	snprintf(pvt->addr.sun_path, sizeof(pvt->addr.sun_path), "%s",
> > > +								sk_path
> > > );
> > > +
> > > +	pvt->fd = socket(PF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0);
> > > +	if (pvt->fd < 0)
> > > +		goto fail;
> > > +
> > > +	unlink(pvt->addr.sun_path);
> > > +	size = offsetof(struct sockaddr_un, sun_path) +
> > > +						strlen(pvt-
> > > >addr.sun_path);
> > > +
> > > +	if (bind(pvt->fd, (struct sockaddr *) &pvt->addr, size) < 0)
> > > +		goto fail;
> > > +
> > > +	/* Setup socket handlers */
> > > +	pvt->sio = l_io_new(pvt->fd);
> > > +	if (!l_io_set_read_handler(pvt->sio, incoming, pvt, NULL))
> > > +		goto fail;
> > > +
> > > +	pvt->rx_regs = l_queue_new();
> > > +	pvt->tx_pkts = l_queue_new();
> > > +
> > > +	pvt->ready_callback = cb;
> > > +	pvt->user_data = user_data;
> > > +
> > > +	io->pvt = pvt;
> > > +
> > > +	l_idle_oneshot(unit_up, pvt, NULL);
> > > +
> > > +	return true;
> > > +
> > > +fail:
> > > +	l_error("Failed to bind Unit Test socket");
> > > +	free_socket(pvt);
> > > +	l_free(pvt);
> > > +
> > > +	return false;
> > > +}
> > > +
> > > +static bool unit_destroy(struct mesh_io *io)
> > > +{
> > > +	struct mesh_io_private *pvt = io->pvt;
> > > +
> > > +	if (!pvt)
> > > +		return true;
> > > +
> > > +	l_free(pvt->unique_name);
> > > +	l_timeout_remove(pvt->tx_timeout);
> > > +	l_queue_destroy(pvt->rx_regs, l_free);
> > > +	l_queue_destroy(pvt->tx_pkts, l_free);
> > > +
> > > +	free_socket(pvt);
> > > +
> > > +	l_free(pvt);
> > > +	io->pvt = NULL;
> > > +
> > > +	return true;
> > > +}
> > > +
> > > +static bool unit_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 = 255;
> > > +	caps->window_accuracy = 50;
> > > +
> > > +	return true;
> > > +}
> > > +
> > > +static bool simple_match(const void *a, const void *b)
> > > +{
> > > +	return a == b;
> > > +}
> > > +
> > > +static void send_pkt(struct mesh_io_private *pvt, struct tx_pkt
> > > *tx,
> > > +							uint16_t
> > > interval)
> > > +{
> > > +	send(pvt->fd, tx->pkt, tx->len, MSG_DONTWAIT);
> > > +
> > > +	if (tx->delete) {
> > > +		l_queue_remove_if(pvt->tx_pkts, simple_match, tx);
> > > +		l_free(tx);
> > > +	}
> > > +}
> > > +
> > > +static void tx_to(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;
> > > +		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) {
> > > +		/* Recalculate wakeup if we are responding to POLL */
> > > +		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_to, 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_to(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_to,
> > > 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);
> > > +
> > > +	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 tx_cancel(struct mesh_io *io, const 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(d
> > > ata[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,
> > > +								&patter
> > > n);
> > > +			l_free(tx);
> > > +
> > > +		} while (tx);
> > > +	}
> > > +
> > > +	if (l_queue_isempty(pvt->tx_pkts)) {
> > > +		l_timeout_remove(pvt->tx_timeout);
> > > +		pvt->tx_timeout = NULL;
> > > +	}
> > > +
> > > +	return true;
> > > +}
> > > +
> > > +static bool find_by_filter(const void *a, const void *b)
> > > +{
> > > +	const struct pvt_rx_reg *rx_reg = a;
> > > +	const uint8_t *filter = b;
> > > +
> > > +	return !memcmp(rx_reg->filter, filter, rx_reg->len);
> > > +}
> > > +
> > > +static bool recv_register(struct mesh_io *io, const uint8_t
> > > *filter,
> > > +			uint8_t len, mesh_io_recv_func_t cb, void
> > > *user_data)
> > > +{
> > > +	struct mesh_io_private *pvt = io->pvt;
> > > +	struct pvt_rx_reg *rx_reg;
> > > +
> > > +	if (!cb || !filter || !len)
> > > +		return false;
> > > +
> > > +	rx_reg = l_queue_remove_if(pvt->rx_regs, find_by_filter,
> > > filter);
> > > +
> > > +	l_free(rx_reg);
> > > +	rx_reg = l_malloc(sizeof(*rx_reg) + len);
> > > +
> > > +	memcpy(rx_reg->filter, filter, len);
> > > +	rx_reg->len = len;
> > > +	rx_reg->cb = cb;
> > > +	rx_reg->user_data = user_data;
> > > +
> > > +	l_queue_push_head(pvt->rx_regs, rx_reg);
> > > +
> > > +	return true;
> > > +}
> > > +
> > > +static bool recv_deregister(struct mesh_io *io, const uint8_t
> > > *filter,
> > > +								uint8_t
> > > len)
> > > +{
> > > +	return true;
> > > +}
> > > +
> > > +const struct mesh_io_api mesh_io_unit = {
> > > +	.init = unit_init,
> > > +	.destroy = unit_destroy,
> > > +	.caps = unit_caps,
> > > +	.send = send_tx,
> > > +	.reg = recv_register,
> > > +	.dereg = recv_deregister,
> > > +	.cancel = tx_cancel,
> > > +};
> > > diff --git a/mesh/mesh-io-unit.h b/mesh/mesh-io-unit.h
> > > new file mode 100644
> > > index 000000000..846eea7bc
> > > --- /dev/null
> > > +++ b/mesh/mesh-io-unit.h
> > > @@ -0,0 +1,11 @@
> > > +// SPDX-License-Identifier: LGPL-2.1-or-later
> > > +/*
> > > + *
> > > + *  BlueZ - Bluetooth protocol stack for Linux
> > > + *
> > > + *  Copyright (C) 2021  Intel Corporation. All rights reserved.
> > > + *
> > > + *
> > > + */
> > > +
> > > +extern const struct mesh_io_api mesh_io_unit;
> > > diff --git a/mesh/mesh-io.c b/mesh/mesh-io.c
> > > index 62fc5d12e..96891313a 100644
> > > --- a/mesh/mesh-io.c
> > > +++ b/mesh/mesh-io.c
> > > @@ -22,10 +22,12 @@
> > >  
> > >  /* List of Mesh-IO Type headers */
> > >  #include "mesh/mesh-io-generic.h"
> > > +#include "mesh/mesh-io-unit.h"
> > >  
> > >  /* List of Supported Mesh-IO Types */
> > >  static const struct mesh_io_table table[] = {
> > > -	{MESH_IO_TYPE_GENERIC,	&mesh_io_generic}
> > > +	{MESH_IO_TYPE_GENERIC, &mesh_io_generic},
> > > +	{MESH_IO_TYPE_UNIT_TEST, &mesh_io_unit},
> > >  };
> > >  
> > >  static struct l_queue *io_list;
> > > @@ -64,12 +66,9 @@ struct mesh_io *mesh_io_new(enum mesh_io_type
> > > type, void *opts,
> > >  
> > >  	io = l_new(struct mesh_io, 1);
> > >  
> > > -	if (!io)
> > > -		return NULL;
> > > -
> > >  	io->type = type;
> > > -
> > >  	io->api = api;
> > > +
> > >  	if (!api->init(io, opts, cb, user_data))
> > >  		goto fail;
> > >  
> > > diff --git a/mesh/mesh-io.h b/mesh/mesh-io.h
> > > index b11c6c6e1..80ef3fa3e 100644
> > > --- a/mesh/mesh-io.h
> > > +++ b/mesh/mesh-io.h
> > > @@ -14,7 +14,8 @@ struct mesh_io;
> > >  
> > >  enum mesh_io_type {
> > >  	MESH_IO_TYPE_NONE = 0,
> > > -	MESH_IO_TYPE_GENERIC
> > > +	MESH_IO_TYPE_GENERIC,
> > > +	MESH_IO_TYPE_UNIT_TEST
> > >  };
> > >  
> > >  enum mesh_io_timing_type {
> > > -- 
> > > 2.26.2
> > > 

  parent reply	other threads:[~2021-02-10  7:09 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-02-06  5:50 [PATCH BlueZ v3 0/3] Framework for non-interactive mesh test Inga Stotland
2021-02-06  5:50 ` [PATCH BlueZ v3 1/3] shared/tester: Create ell-based version of tester code Inga Stotland
2021-02-06  6:36   ` Framework for non-interactive mesh test bluez.test.bot
2021-02-06  5:50 ` [PATCH BlueZ v3 2/3] mesh: Add unit test IO Inga Stotland
2021-02-09 13:49   ` Michał Lowas-Rzechonek
     [not found]     ` <ffb0ac27b73cd291373f56912816a769a959c0eb.camel@intel.com>
2021-02-10  7:08       ` Stotland, Inga [this message]
2021-02-06  5:50 ` [PATCH BlueZ v3 3/3] tools/mesh-cfgtest: Non-iteractive test for mesh daemon Inga Stotland

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=b419211dd47a92cae5ba627a47b0d2dc80831745.camel@intel.com \
    --to=inga.stotland@intel.com \
    --cc=brian.gix@intel.com \
    --cc=linux-bluetooth@vger.kernel.org \
    --cc=luiz.dentz@gmail.com \
    --cc=michal.lowas-rzechonek@silvair.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).