From: Luiz Augusto von Dentz <luiz.dentz@gmail.com>
To: Pauli Virtanen <pav@iki.fi>
Cc: linux-bluetooth@vger.kernel.org
Subject: Re: [PATCH BlueZ v3 4/7] avctp: move uinput utilities to uinput-util.c
Date: Wed, 10 Dec 2025 11:25:07 -0500 [thread overview]
Message-ID: <CABBYNZJ8Oj1c47kqwws8gttMBF0BNMdf72UTOjtySmKpWSA-8g@mail.gmail.com> (raw)
In-Reply-To: <0ca3e2b822e0111525e7b88edc2db54c35fcee0b.1765383173.git.pav@iki.fi>
Hi Pauli,
On Wed, Dec 10, 2025 at 11:13 AM Pauli Virtanen <pav@iki.fi> wrote:
>
> Move basic uinput utilities to a separate file, so they can be reused
> for MCS.
> ---
> Makefile.plugins | 4 +-
> profiles/audio/avctp.c | 119 +++-------------------------
> profiles/audio/uinput-util.c | 146 +++++++++++++++++++++++++++++++++++
> profiles/audio/uinput-util.h | 23 ++++++
> 4 files changed, 181 insertions(+), 111 deletions(-)
> create mode 100644 profiles/audio/uinput-util.c
> create mode 100644 profiles/audio/uinput-util.h
>
> diff --git a/Makefile.plugins b/Makefile.plugins
> index 3572ee845..54647e2ab 100644
> --- a/Makefile.plugins
> +++ b/Makefile.plugins
> @@ -44,7 +44,9 @@ if AVRCP
> builtin_modules += avrcp
> builtin_sources += profiles/audio/control.h profiles/audio/control.c \
> profiles/audio/avctp.h profiles/audio/avctp.c \
> - profiles/audio/avrcp.h profiles/audio/avrcp.c
> + profiles/audio/avrcp.h profiles/audio/avrcp.c \
> + profiles/audio/uinput-util.h \
> + profiles/audio/uinput-util.c
> endif
>
> if NETWORK
> diff --git a/profiles/audio/avctp.c b/profiles/audio/avctp.c
> index 65eec6f6c..346a97dd1 100644
> --- a/profiles/audio/avctp.c
> +++ b/profiles/audio/avctp.c
> @@ -44,6 +44,7 @@
>
> #include "avctp.h"
> #include "avrcp.h"
> +#include "uinput-util.h"
>
> /* AV/C Panel 1.23, page 76:
> * command with the pressed value is valid for two seconds
> @@ -228,11 +229,7 @@ struct avctp_browsing_pdu_handler {
> GDestroyNotify destroy;
> };
>
> -static const struct {
> - const char *name;
> - uint8_t avc;
> - uint16_t uinput;
> -} key_map[] = {
> +static const struct uinput_key_map key_map[] = {
> { "SELECT", AVC_SELECT, KEY_SELECT },
> { "UP", AVC_UP, KEY_UP },
> { "DOWN", AVC_DOWN, KEY_DOWN },
> @@ -301,25 +298,9 @@ static gboolean avctp_passthrough_rsp(struct avctp *session, uint8_t code,
> uint8_t *operands, size_t operand_count,
> void *user_data);
>
> -static int send_event(int fd, uint16_t type, uint16_t code, int32_t value)
> -{
> - struct input_event event;
> -
> - memset(&event, 0, sizeof(event));
> - event.type = type;
> - event.code = code;
> - event.value = value;
> -
> - return write(fd, &event, sizeof(event));
> -}
> -
> static void send_key(int fd, uint16_t key, int pressed)
> {
> - if (fd < 0)
> - return;
> -
> - send_event(fd, EV_KEY, key, pressed);
> - send_event(fd, EV_SYN, SYN_REPORT, 0);
> + uinput_send_key(fd, key, pressed);
> }
>
> static bool auto_release(gpointer user_data)
> @@ -401,12 +382,12 @@ static size_t handle_panel_passthrough(struct avctp *session,
> for (i = 0; key_map[i].name != NULL; i++) {
> uint8_t key_quirks;
>
> - if ((operands[0] & 0x7F) != key_map[i].avc)
> + if ((operands[0] & 0x7F) != key_map[i].code)
> continue;
>
> DBG("AV/C: %s %s", key_map[i].name, status);
>
> - key_quirks = session->key_quirks[key_map[i].avc];
> + key_quirks = session->key_quirks[key_map[i].code];
>
> if (key_quirks & QUIRK_NO_RELEASE) {
> if (!pressed) {
> @@ -1154,89 +1135,6 @@ failed:
> return FALSE;
> }
>
> -static int uinput_create(struct btd_device *device, const char *name,
> - const char *suffix)
> -{
> - struct uinput_user_dev dev;
> - int fd, err, i;
> - char src[18];
> -
> - fd = open("/dev/uinput", O_RDWR);
> - if (fd < 0) {
> - fd = open("/dev/input/uinput", O_RDWR);
> - if (fd < 0) {
> - fd = open("/dev/misc/uinput", O_RDWR);
> - if (fd < 0) {
> - err = -errno;
> - error("Can't open input device: %s (%d)",
> - strerror(-err), -err);
> - return err;
> - }
> - }
> - }
> -
> - memset(&dev, 0, sizeof(dev));
> -
> - if (name) {
> - strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE - 1);
> - dev.name[UINPUT_MAX_NAME_SIZE - 1] = '\0';
> - }
> -
> - if (suffix) {
> - int len, slen;
> -
> - len = strlen(dev.name);
> - slen = strlen(suffix);
> -
> - /* If name + suffix don't fit, truncate the name, then add the
> - * suffix.
> - */
> - if (len + slen < UINPUT_MAX_NAME_SIZE - 1) {
> - strcpy(dev.name + len, suffix);
> - } else {
> - len = UINPUT_MAX_NAME_SIZE - slen - 1;
> - strncpy(dev.name + len, suffix, slen);
> - dev.name[UINPUT_MAX_NAME_SIZE - 1] = '\0';
> - }
> - }
> -
> - dev.id.bustype = BUS_BLUETOOTH;
> - dev.id.vendor = btd_device_get_vendor(device);
> - dev.id.product = btd_device_get_product(device);
> - dev.id.version = btd_device_get_version(device);
> -
> - if (write(fd, &dev, sizeof(dev)) < 0) {
> - err = -errno;
> - error("Can't write device information: %s (%d)",
> - strerror(-err), -err);
> - close(fd);
> - return err;
> - }
> -
> - ioctl(fd, UI_SET_EVBIT, EV_KEY);
> - ioctl(fd, UI_SET_EVBIT, EV_REL);
> - ioctl(fd, UI_SET_EVBIT, EV_REP);
> - ioctl(fd, UI_SET_EVBIT, EV_SYN);
> -
> - ba2strlc(btd_adapter_get_address(device_get_adapter(device)), src);
> - ioctl(fd, UI_SET_PHYS, src);
> -
> - for (i = 0; key_map[i].name != NULL; i++)
> - ioctl(fd, UI_SET_KEYBIT, key_map[i].uinput);
> -
> - if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) {
> - err = -errno;
> - error("Can't create uinput device: %s (%d)",
> - strerror(-err), -err);
> - close(fd);
> - return err;
> - }
> -
> - send_event(fd, EV_REP, REP_DELAY, 300);
> -
> - return fd;
> -}
> -
> static void init_uinput(struct avctp *session)
> {
> char name[UINPUT_MAX_NAME_SIZE];
> @@ -1249,7 +1147,8 @@ static void init_uinput(struct avctp *session)
> session->key_quirks[AVC_PAUSE] |= QUIRK_NO_RELEASE;
> }
>
> - session->uinput = uinput_create(session->device, name, " (AVRCP)");
> + session->uinput = uinput_create(device_get_adapter(session->device),
> + session->device, name, " (AVRCP)", key_map);
> if (session->uinput < 0)
> error("AVRCP: failed to init uinput for %s", name);
> else
> @@ -1793,7 +1692,7 @@ static const char *op2str(uint8_t op)
> int i;
>
> for (i = 0; key_map[i].name != NULL; i++) {
> - if ((op & 0x7F) == key_map[i].avc)
> + if ((op & 0x7F) == key_map[i].code)
> return key_map[i].name;
> }
>
> @@ -2232,7 +2131,7 @@ bool avctp_supports_avc(uint8_t avc)
> int i;
>
> for (i = 0; key_map[i].name != NULL; i++) {
> - if (key_map[i].avc == avc)
> + if (key_map[i].code == avc)
> return true;
> }
> return false;
> diff --git a/profiles/audio/uinput-util.c b/profiles/audio/uinput-util.c
> new file mode 100644
> index 000000000..75f2a500c
> --- /dev/null
> +++ b/profiles/audio/uinput-util.c
> @@ -0,0 +1,146 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + *
> + * BlueZ - Bluetooth protocol stack for Linux
> + *
> + * Copyright (C) 2006-2010 Nokia Corporation
> + * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + *
> + *
> + */
> +
> +#ifdef HAVE_CONFIG_H
> +#include <config.h>
> +#endif
> +
> +#include <unistd.h>
> +#include <fcntl.h>
> +#include <sys/ioctl.h>
> +#include <errno.h>
> +#include <string.h>
> +#include <linux/uinput.h>
> +
> +#include <glib.h>
> +
> +#include "bluetooth/bluetooth.h"
> +#include "bluetooth/uuid.h"
> +
> +#include "src/adapter.h"
> +#include "src/device.h"
> +#include "src/log.h"
> +
> +#include "uinput-util.h"
> +
> +
> +static int send_event(int fd, uint16_t type, uint16_t code, int32_t value)
> +{
> + struct input_event event;
> +
> + memset(&event, 0, sizeof(event));
> + event.type = type;
> + event.code = code;
> + event.value = value;
> +
> + return write(fd, &event, sizeof(event));
> +}
> +
> +void uinput_send_key(int fd, uint16_t key, int pressed)
> +{
> + if (fd < 0)
> + return;
> +
> + send_event(fd, EV_KEY, key, pressed);
> + send_event(fd, EV_SYN, SYN_REPORT, 0);
> +}
> +
> +int uinput_create(struct btd_adapter *adapter, struct btd_device *device,
> + const char *name, const char *suffix,
> + const struct uinput_key_map *key_map)
> +{
> + struct uinput_user_dev dev;
> + int fd, err, i;
> + char src[18];
> +
> + fd = open("/dev/uinput", O_RDWR);
> + if (fd < 0) {
> + fd = open("/dev/input/uinput", O_RDWR);
> + if (fd < 0) {
> + fd = open("/dev/misc/uinput", O_RDWR);
> + if (fd < 0) {
> + err = -errno;
> + error("Can't open input device: %s (%d)",
> + strerror(-err), -err);
> + return err;
> + }
> + }
> + }
> +
> + memset(&dev, 0, sizeof(dev));
> +
> + if (name) {
> + strncpy(dev.name, name, UINPUT_MAX_NAME_SIZE - 1);
> + dev.name[UINPUT_MAX_NAME_SIZE - 1] = '\0';
> + }
> +
> + if (suffix) {
> + int len, slen;
> +
> + len = strlen(dev.name);
> + slen = strlen(suffix);
> +
> + /* If name + suffix don't fit, truncate the name, then add the
> + * suffix.
> + */
> + if (len + slen < UINPUT_MAX_NAME_SIZE - 1) {
> + strcpy(dev.name + len, suffix);
> + } else {
> + len = UINPUT_MAX_NAME_SIZE - slen - 1;
> + strncpy(dev.name + len, suffix, slen);
> + dev.name[UINPUT_MAX_NAME_SIZE - 1] = '\0';
> + }
> + }
> +
> + if (device) {
> + dev.id.bustype = BUS_BLUETOOTH;
> + dev.id.vendor = btd_device_get_vendor(device);
> + dev.id.product = btd_device_get_product(device);
> + dev.id.version = btd_device_get_version(device);
> + } else {
> + dev.id.bustype = BUS_VIRTUAL;
> + dev.id.vendor = 0;
> + dev.id.product = 0;
> + dev.id.version = 0;
> + }
> +
> + if (write(fd, &dev, sizeof(dev)) < 0) {
> + err = -errno;
> + error("Can't write device information: %s (%d)",
> + strerror(-err), -err);
> + close(fd);
> + return err;
> + }
> +
> + ioctl(fd, UI_SET_EVBIT, EV_KEY);
> + ioctl(fd, UI_SET_EVBIT, EV_REL);
> + ioctl(fd, UI_SET_EVBIT, EV_REP);
> + ioctl(fd, UI_SET_EVBIT, EV_SYN);
> +
> + ba2strlc(btd_adapter_get_address(adapter), src);
> + ioctl(fd, UI_SET_PHYS, src);
> +
> + for (i = 0; key_map[i].name != NULL; i++)
> + ioctl(fd, UI_SET_KEYBIT, key_map[i].uinput);
> +
> + if (ioctl(fd, UI_DEV_CREATE, NULL) < 0) {
> + err = -errno;
> + error("Can't create uinput device: %s (%d)",
> + strerror(-err), -err);
> + close(fd);
> + return err;
> + }
> +
> + send_event(fd, EV_REP, REP_DELAY, 300);
> +
> + return fd;
> +}
> diff --git a/profiles/audio/uinput-util.h b/profiles/audio/uinput-util.h
> new file mode 100644
> index 000000000..75de8aedd
> --- /dev/null
> +++ b/profiles/audio/uinput-util.h
> @@ -0,0 +1,23 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + *
> + * BlueZ - Bluetooth protocol stack for Linux
> + *
> + * Copyright (C) 2006-2010 Nokia Corporation
> + * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + *
> + *
> + */
> +
> +struct uinput_key_map {
> + const char *name;
> + unsigned int code;
> + uint16_t uinput;
> +};
> +
> +int uinput_create(struct btd_adapter *adapter, struct btd_device *device,
> + const char *name, const char *suffix,
> + const struct uinput_key_map *key_map);
> +
> +void uinput_send_key(int fd, uint16_t key, int pressed);
> --
> 2.51.1
>
I would consider moving this to src/shared though, so we can later
create unit tests if we need to, we will need to decouple it from
btd_adapter and btd_device and create a bt_uinput instance that wraps
the access to uinput fd.
--
Luiz Augusto von Dentz
next prev parent reply other threads:[~2025-12-10 16:25 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-12-10 16:13 [PATCH BlueZ v3 0/7] mcp: support multiple MCP and implement local GMCS Pauli Virtanen
2025-12-10 16:13 ` [PATCH BlueZ v3 1/7] shared/mcp: support multiple MCP, and add non-stub MCS server Pauli Virtanen
2025-12-10 17:21 ` mcp: support multiple MCP and implement local GMCS bluez.test.bot
2025-12-10 16:13 ` [PATCH BlueZ v3 2/7] test-mcp: add tests for MCP / MCS Pauli Virtanen
2025-12-10 16:13 ` [PATCH BlueZ v3 3/7] mcp: adapt to new MCP API to support multiple remote MCS services Pauli Virtanen
2025-12-10 16:13 ` [PATCH BlueZ v3 4/7] avctp: move uinput utilities to uinput-util.c Pauli Virtanen
2025-12-10 16:25 ` Luiz Augusto von Dentz [this message]
2025-12-10 16:13 ` [PATCH BlueZ v3 5/7] uinput-util: fix compiler complaint about strncpy usage Pauli Virtanen
2025-12-10 16:13 ` [PATCH BlueZ v3 6/7] mcp: add local GMCS service that emits uinput media keys Pauli Virtanen
2025-12-10 16:13 ` [PATCH BlueZ v3 7/7] shared/gatt-client: fix notify_data leak in notify_data_write_ccc Pauli Virtanen
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=CABBYNZJ8Oj1c47kqwws8gttMBF0BNMdf72UTOjtySmKpWSA-8g@mail.gmail.com \
--to=luiz.dentz@gmail.com \
--cc=linux-bluetooth@vger.kernel.org \
--cc=pav@iki.fi \
/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).