2022-12-15 21:16:38

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ v2 1/5] client/player: Make transport.send non-blocking

From: Luiz Augusto von Dentz <[email protected]>

This makes transport.send command non-blocking by using timerfd
callback to initiate the transfers.
---
client/player.c | 205 ++++++++++++++++++++++++++++++++----------------
1 file changed, 138 insertions(+), 67 deletions(-)

diff --git a/client/player.c b/client/player.c
index 8b3785d6b632..1f10387f89ad 100644
--- a/client/player.c
+++ b/client/player.c
@@ -24,6 +24,7 @@
#include <sys/ioctl.h>
#include <sys/uio.h>
#include <wordexp.h>
+#include <sys/timerfd.h>

#include <glib.h>

@@ -92,6 +93,7 @@ struct transport {
int fd;
struct io *io;
uint32_t seq;
+ struct io *timer_io;
};

static void endpoint_unregister(void *data)
@@ -2959,6 +2961,8 @@ static void transport_close(struct transport *transport)
return;

close(transport->fd);
+ transport->fd = -1;
+
free(transport->filename);
}

@@ -2966,6 +2970,7 @@ static void transport_free(void *data)
{
struct transport *transport = data;

+ io_destroy(transport->timer_io);
io_destroy(transport->io);
free(transport);
}
@@ -3330,104 +3335,166 @@ static int open_file(const char *filename, int flags)
return fd;
}

-#define NSEC_USEC(_t) (_t / 1000L)
-#define SEC_USEC(_t) (_t * 1000000L)
-#define TS_USEC(_ts) (SEC_USEC((_ts)->tv_sec) + NSEC_USEC((_ts)->tv_nsec))
-
-static void send_wait(struct timespec *t_start, uint32_t us)
+static int elapsed_time(bool reset, int *secs, int *nsecs)
{
- struct timespec t_now;
- struct timespec t_diff;
- int64_t delta_us;
+ static struct timespec start;
+ struct timespec curr;

- /* Skip sleep at start */
- if (!us)
- return;
-
- if (clock_gettime(CLOCK_MONOTONIC, &t_now) < 0) {
- bt_shell_printf("clock_gettime: %s (%d)", strerror(errno),
- errno);
- return;
+ if (reset) {
+ if (clock_gettime(CLOCK_MONOTONIC, &start) < 0) {
+ bt_shell_printf("clock_gettime: %s (%d)",
+ strerror(errno), errno);
+ return -errno;
+ }
}

- t_diff.tv_sec = t_now.tv_sec - t_start->tv_sec;
- if (t_start->tv_nsec > t_now.tv_nsec) {
- t_diff.tv_sec--;
- t_now.tv_nsec += 1000000000L;
- }
- t_diff.tv_nsec = t_now.tv_nsec - t_start->tv_nsec;
-
- delta_us = us - TS_USEC(&t_diff);
-
- if (delta_us < 0) {
- bt_shell_printf("Send is behind: %" PRId64 " us - skip sleep",
- delta_us);
- delta_us = 1000;
- }
-
- usleep(delta_us);
-
- if (clock_gettime(CLOCK_MONOTONIC, t_start) < 0)
+ if (clock_gettime(CLOCK_MONOTONIC, &curr) < 0) {
bt_shell_printf("clock_gettime: %s (%d)", strerror(errno),
- errno);
-}
-
-static int transport_send(struct transport *transport, int fd,
- struct bt_iso_qos *qos)
-{
- struct timespec t_start;
- uint8_t *buf;
- uint32_t num = 0;
-
- if (qos && clock_gettime(CLOCK_MONOTONIC, &t_start) < 0) {
- bt_shell_printf("clock_gettime: %s (%d)", strerror(errno),
- errno);
+ errno);
return -errno;
}

- buf = malloc(transport->mtu[1]);
- if (!buf) {
- bt_shell_printf("malloc: %s (%d)", strerror(errno), errno);
- return -ENOMEM;
+ *secs = curr.tv_sec - start.tv_sec;
+ *nsecs = curr.tv_nsec - start.tv_nsec;
+ if (*nsecs < 0) {
+ (*secs)--;
+ *nsecs += 1000000000;
}

- /* num of packets = latency (ms) / interval (us) */
- if (qos)
- num = (qos->out.latency * 1000 / qos->out.interval);
+ return 0;
+}

- for (transport->seq = 0; ; transport->seq++) {
+static int transport_send_seq(struct transport *transport, int fd, uint32_t num)
+{
+ uint8_t *buf;
+ uint32_t i;
+
+ if (!num)
+ return 0;
+
+ buf = malloc(transport->mtu[1]);
+ if (!buf)
+ return -ENOMEM;
+
+ for (i = 0; i < num; i++, transport->seq++) {
ssize_t ret;
int queued;
+ int secs = 0, nsecs = 0;

ret = read(fd, buf, transport->mtu[1]);
if (ret <= 0) {
if (ret < 0)
bt_shell_printf("read failed: %s (%d)",
strerror(errno), errno);
- close(fd);
+ free(buf);
return ret;
}

ret = send(transport->sk, buf, ret, 0);
if (ret <= 0) {
- bt_shell_printf("Send failed: %s (%d)",
+ bt_shell_printf("send failed: %s (%d)",
strerror(errno), errno);
+ free(buf);
return -errno;
}

+ elapsed_time(!transport->seq, &secs, &nsecs);
+
ioctl(transport->sk, TIOCOUTQ, &queued);

- bt_shell_printf("[seq %d] send: %zd bytes "
+ bt_shell_printf("[seq %d %d.%03ds] send: %zd bytes "
"(TIOCOUTQ %d bytes)\n",
- transport->seq, ret, queued);
-
- if (qos) {
- if (transport->seq && !((transport->seq + 1) % num))
- send_wait(&t_start, num * qos->out.interval);
- }
+ transport->seq, secs,
+ (nsecs + 500000) / 1000000,
+ ret, queued);
}

free(buf);
+
+ return i;
+}
+
+static bool transport_timer_read(struct io *io, void *user_data)
+{
+ struct transport *transport = user_data;
+ struct bt_iso_qos qos;
+ socklen_t len;
+ int ret, fd;
+ uint32_t num;
+ uint64_t exp;
+
+ if (transport->fd < 0)
+ return false;
+
+ fd = io_get_fd(io);
+ ret = read(fd, &exp, sizeof(exp));
+ if (ret < 0) {
+ bt_shell_printf("Failed to read: %s (%d)\n", strerror(errno),
+ -errno);
+ return false;
+ }
+
+ /* Read QoS if available */
+ memset(&qos, 0, sizeof(qos));
+ len = sizeof(qos);
+ if (getsockopt(transport->sk, SOL_BLUETOOTH, BT_ISO_QOS, &qos,
+ &len) < 0) {
+ bt_shell_printf("Failed to getsockopt(BT_ISO_QOS): %s (%d)\n",
+ strerror(errno), -errno);
+ return false;
+ }
+
+ /* num of packets = latency (ms) / interval (us) */
+ num = (qos.out.latency * 1000 / qos.out.interval);
+
+ ret = transport_send_seq(transport, transport->fd, num);
+ if (ret < 0) {
+ bt_shell_printf("Unable to send: %s (%d)\n",
+ strerror(-ret), ret);
+ return false;
+ }
+
+ if (!ret) {
+ transport_close(transport);
+ return false;
+ }
+
+ return true;
+}
+
+static int transport_send(struct transport *transport, int fd,
+ struct bt_iso_qos *qos)
+{
+ struct itimerspec ts;
+ int timer_fd;
+
+ transport->seq = 0;
+
+ if (!qos)
+ return transport_send_seq(transport, fd, UINT32_MAX);
+
+ if (transport->fd >= 0)
+ return -EALREADY;
+
+ timer_fd = timerfd_create(CLOCK_MONOTONIC, 0);
+ if (timer_fd < 0)
+ return -errno;
+
+ memset(&ts, 0, sizeof(ts));
+ ts.it_value.tv_nsec = qos->out.latency * 1000000;
+ ts.it_interval.tv_nsec = qos->out.latency * 1000000;
+
+ if (timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &ts, NULL) < 0)
+ return -errno;
+
+ transport->fd = fd;
+
+ transport->timer_io = io_new(timer_fd);
+
+ io_set_read_handler(transport->timer_io, transport_timer_read,
+ transport, NULL);
+
+ return transport_send_seq(transport, fd, 1);
}

static void cmd_send_transport(int argc, char *argv[])
@@ -3457,6 +3524,8 @@ static void cmd_send_transport(int argc, char *argv[])
}

fd = open_file(argv[2], O_RDONLY);
+ if (fd < 0)
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);

bt_shell_printf("Sending ...\n");

@@ -3469,10 +3538,12 @@ static void cmd_send_transport(int argc, char *argv[])
else
err = transport_send(transport, fd, &qos);

- close(fd);
-
- if (err < 0)
+ if (err < 0) {
+ bt_shell_printf("Unable to send: %s (%d)", strerror(-err),
+ -err);
+ close(fd);
return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }

return bt_shell_noninteractive_quit(EXIT_SUCCESS);
}
--
2.37.3


2022-12-15 21:16:52

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ v2 3/5] client/player: Use bt_shell_echo to print transfer progress

From: Luiz Augusto von Dentz <[email protected]>

This uses bt_shell_echo to print transfer progress.
---
client/player.c | 12 ++++--------
src/shared/shell.c | 10 +++++-----
2 files changed, 9 insertions(+), 13 deletions(-)

diff --git a/client/player.c b/client/player.c
index 1f10387f89ad..e73ed6ac925a 100644
--- a/client/player.c
+++ b/client/player.c
@@ -3000,11 +3000,11 @@ static bool transport_recv(struct io *io, void *user_data)
return true;
}

- bt_shell_printf("[seq %d] recv: %u bytes\n", transport->seq, ret);
+ bt_shell_echo("[seq %d] recv: %u bytes", transport->seq, ret);

transport->seq++;

- if (transport->fd) {
+ if (transport->fd >= 0) {
len = write(transport->fd, buf, ret);
if (len < 0)
bt_shell_printf("Unable to write: %s (%d)",
@@ -3378,7 +3378,6 @@ static int transport_send_seq(struct transport *transport, int fd, uint32_t num)

for (i = 0; i < num; i++, transport->seq++) {
ssize_t ret;
- int queued;
int secs = 0, nsecs = 0;

ret = read(fd, buf, transport->mtu[1]);
@@ -3400,13 +3399,10 @@ static int transport_send_seq(struct transport *transport, int fd, uint32_t num)

elapsed_time(!transport->seq, &secs, &nsecs);

- ioctl(transport->sk, TIOCOUTQ, &queued);
-
- bt_shell_printf("[seq %d %d.%03ds] send: %zd bytes "
- "(TIOCOUTQ %d bytes)\n",
+ bt_shell_echo("[seq %d %d.%03ds] send: %zd bytes ",
transport->seq, secs,
(nsecs + 500000) / 1000000,
- ret, queued);
+ ret);
}

free(buf);
diff --git a/src/shared/shell.c b/src/shared/shell.c
index 3c0e61dbc414..3358b383e9e4 100644
--- a/src/shared/shell.c
+++ b/src/shared/shell.c
@@ -580,15 +580,15 @@ void bt_shell_echo(const char *fmt, ...)
{
va_list args;
char *str;
- int err;
+ int ret;

va_start(args, fmt);
- err = vasprintf(&str, fmt, args);
- if (!err)
- err = asprintf(&str, COLOR_HIGHLIGHT "%s#" COLOR_OFF, str);
+ ret = vasprintf(&str, fmt, args);
+ if (ret >= 0)
+ ret = asprintf(&str, COLOR_HIGHLIGHT "%s " COLOR_OFF "#", str);
va_end(args);

- if (err)
+ if (ret < 0)
return;

rl_save_prompt();
--
2.37.3

2022-12-15 21:17:09

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ v2 4/5] client/player: Print transport progress

From: Luiz Augusto von Dentz <[email protected]>

This uses bt_shell_echo to print out the transfer progress on the echo
area.
---
client/player.c | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/client/player.c b/client/player.c
index e73ed6ac925a..6e33274d320d 100644
--- a/client/player.c
+++ b/client/player.c
@@ -25,6 +25,7 @@
#include <sys/uio.h>
#include <wordexp.h>
#include <sys/timerfd.h>
+#include <sys/stat.h>

#include <glib.h>

@@ -91,6 +92,7 @@ struct transport {
uint16_t mtu[2];
char *filename;
int fd;
+ struct stat stat;
struct io *io;
uint32_t seq;
struct io *timer_io;
@@ -3379,6 +3381,7 @@ static int transport_send_seq(struct transport *transport, int fd, uint32_t num)
for (i = 0; i < num; i++, transport->seq++) {
ssize_t ret;
int secs = 0, nsecs = 0;
+ off_t offset;

ret = read(fd, buf, transport->mtu[1]);
if (ret <= 0) {
@@ -3399,10 +3402,19 @@ static int transport_send_seq(struct transport *transport, int fd, uint32_t num)

elapsed_time(!transport->seq, &secs, &nsecs);

- bt_shell_echo("[seq %d %d.%03ds] send: %zd bytes ",
+ if (!transport->seq && fstat(fd, &transport->stat) < 0) {
+ bt_shell_printf("fstat failed: %s (%d)",
+ strerror(errno), errno);
+ free(buf);
+ return -errno;
+ }
+
+ offset = lseek(fd, 0, SEEK_CUR);
+
+ bt_shell_echo("[seq %d %d.%03ds] send: %zd/%zd bytes",
transport->seq, secs,
(nsecs + 500000) / 1000000,
- ret);
+ offset, transport->stat.st_size);
}

free(buf);
--
2.37.3

2022-12-15 21:17:29

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: [PATCH BlueZ v2 5/5] client/player: Fix transport.send/receice tab completion

From: Luiz Augusto von Dentz <[email protected]>

Commands transport.send/receive were not settings any completion
callback so this makes sure it uses transport_generator to generate the
list of transport that could be used to send.
---
client/player.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/client/player.c b/client/player.c
index 6e33274d320d..eba233329276 100644
--- a/client/player.c
+++ b/client/player.c
@@ -3654,9 +3654,11 @@ static const struct bt_shell_menu transport_menu = {
"Release Transport",
transport_generator },
{ "send", "<transport> <filename>", cmd_send_transport,
- "Send contents of a file" },
+ "Send contents of a file",
+ transport_generator },
{ "receive", "<transport> [filename]", cmd_receive_transport,
- "Get/Set file to receive" },
+ "Get/Set file to receive",
+ transport_generator },
{ "volume", "<transport> [value]", cmd_volume_transport,
"Get/Set transport volume",
transport_generator },
--
2.37.3

2022-12-15 23:11:23

by bluez.test.bot

[permalink] [raw]
Subject: RE: [BlueZ,v2,1/5] client/player: Make transport.send non-blocking

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=704921

---Test result---

Test Summary:
CheckPatch FAIL 2.79 seconds
GitLint PASS 1.42 seconds
BuildEll PASS 27.44 seconds
BluezMake PASS 779.86 seconds
MakeCheck PASS 11.19 seconds
MakeDistcheck PASS 149.27 seconds
CheckValgrind PASS 241.68 seconds
bluezmakeextell PASS 95.57 seconds
IncrementalBuild PASS 3135.98 seconds
ScanBuild WARNING 978.11 seconds

Details
##############################
Test: CheckPatch - FAIL
Desc: Run checkpatch.pl script
Output:
[BlueZ,v2,2/5] shared/shell: Add bt_shell_echo
WARNING:PREFER_DEFINED_ATTRIBUTE_MACRO: Prefer __printf(1, 2) over __attribute__((format(printf, 1, 2)))
#131: FILE: src/shared/shell.h:74:
+ ...) __attribute__((format(printf, 1, 2)));

/github/workspace/src/src/13074521.patch total: 0 errors, 1 warnings, 34 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
mechanically convert to the typical style using --fix or --fix-inplace.

/github/workspace/src/src/13074521.patch has style problems, please review.

NOTE: Ignored message types: COMMIT_MESSAGE COMPLEX_MACRO CONST_STRUCT FILE_PATH_CHANGES MISSING_SIGN_OFF PREFER_PACKED SPDX_LICENSE_TAG SPLIT_STRING SSCANF_TO_KSTRTO

NOTE: If any of the errors are false positives, please report
them to the maintainer, see CHECKPATCH in MAINTAINERS.


##############################
Test: ScanBuild - WARNING
Desc: Run Scan Build
Output:
src/shared/shell.c:1180:13: warning: Access to field 'options' results in a dereference of a null pointer (loaded from variable 'opt')
if (c != opt->options[index - offset].val) {
^~~~~~~~~~~~
1 warning generated.



---
Regards,
Linux Bluetooth

2022-12-16 01:04:42

by patchwork-bot+bluetooth

[permalink] [raw]
Subject: Re: [PATCH BlueZ v2 1/5] client/player: Make transport.send non-blocking

Hello:

This series was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <[email protected]>:

On Thu, 15 Dec 2022 13:10:33 -0800 you wrote:
> From: Luiz Augusto von Dentz <[email protected]>
>
> This makes transport.send command non-blocking by using timerfd
> callback to initiate the transfers.
> ---
> client/player.c | 205 ++++++++++++++++++++++++++++++++----------------
> 1 file changed, 138 insertions(+), 67 deletions(-)

Here is the summary with links:
- [BlueZ,v2,1/5] client/player: Make transport.send non-blocking
(no matching commit)
- [BlueZ,v2,2/5] shared/shell: Add bt_shell_echo
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=7b8c87ccc248
- [BlueZ,v2,3/5] client/player: Use bt_shell_echo to print transfer progress
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=e51d6c5f2e51
- [BlueZ,v2,4/5] client/player: Print transport progress
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=318b1a19cb74
- [BlueZ,v2,5/5] client/player: Fix transport.send/receice tab completion
https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=1270afa5aa1c

You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html