linux-bluetooth.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
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

  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).