* [PATCH BlueZ 1/4] shared/mgmt: Add mgmt_parse_io_capability
2026-02-27 14:24 [PATCH Bluez v2 0/4] client: align 'io-cap' and 'pair' cmds with the docs Christian Eggers
@ 2026-02-27 14:24 ` Christian Eggers
2026-02-27 15:39 ` client: align 'io-cap' and 'pair' cmds with the docs bluez.test.bot
2026-02-27 14:24 ` [PATCH BlueZ 2/4] adapter/agent/device: migrate to shared enum/parser for IO capabilities Christian Eggers
` (3 subsequent siblings)
4 siblings, 1 reply; 7+ messages in thread
From: Christian Eggers @ 2026-02-27 14:24 UTC (permalink / raw)
To: linux-bluetooth; +Cc: ceggers
This can be shared between bluetoothd (adapter/agent/device) and
bluetootctl (mgmt).
---
src/shared/mgmt.c | 32 ++++++++++++++++++++++++++++++++
src/shared/mgmt.h | 11 +++++++++++
2 files changed, 43 insertions(+)
diff --git a/src/shared/mgmt.c b/src/shared/mgmt.c
index 6a7eb5798cb8..930e70a094d0 100644
--- a/src/shared/mgmt.c
+++ b/src/shared/mgmt.c
@@ -81,6 +81,20 @@ struct mgmt_tlv_list {
uint16_t size;
};
+struct arg_table {
+ const char *name;
+ enum mgmt_io_capability value;
+};
+
+static const struct arg_table iocap_arguments[] = {
+ { "DisplayOnly", MGMT_IO_CAPABILITY_DISPLAYONLY },
+ { "DisplayYesNo", MGMT_IO_CAPABILITY_DISPLAYYESNO },
+ { "KeyboardOnly", MGMT_IO_CAPABILITY_KEYBOARDONLY },
+ { "NoInputNoOutput", MGMT_IO_CAPABILITY_NOINPUTNOOUTPUT },
+ { "KeyboardDisplay", MGMT_IO_CAPABILITY_KEYBOARDDISPLAY },
+ { NULL, 0}
+};
+
static void destroy_request(void *data)
{
struct mgmt_request *request = data;
@@ -1039,3 +1053,21 @@ uint16_t mgmt_get_mtu(struct mgmt *mgmt)
return mgmt->mtu;
}
+
+enum mgmt_io_capability mgmt_parse_io_capability(const char *capability)
+{
+ const char *arg;
+ int index = 0;
+
+ if (!strcmp(capability, ""))
+ return MGMT_IO_CAPABILITY_KEYBOARDDISPLAY;
+
+ while ((arg = iocap_arguments[index].name)) {
+ if (!strncmp(arg, capability, strlen(capability)))
+ return iocap_arguments[index].value;
+
+ index++;
+ }
+
+ return MGMT_IO_CAPABILITY_INVALID;
+}
diff --git a/src/shared/mgmt.h b/src/shared/mgmt.h
index 2629fbd59cf6..c99f2ff0620e 100644
--- a/src/shared/mgmt.h
+++ b/src/shared/mgmt.h
@@ -89,3 +89,14 @@ bool mgmt_unregister_index(struct mgmt *mgmt, uint16_t index);
bool mgmt_unregister_all(struct mgmt *mgmt);
uint16_t mgmt_get_mtu(struct mgmt *mgmt);
+
+enum mgmt_io_capability {
+ MGMT_IO_CAPABILITY_DISPLAYONLY = 0x00,
+ MGMT_IO_CAPABILITY_DISPLAYYESNO = 0x01,
+ MGMT_IO_CAPABILITY_KEYBOARDONLY = 0x02,
+ MGMT_IO_CAPABILITY_NOINPUTNOOUTPUT = 0x03,
+ MGMT_IO_CAPABILITY_KEYBOARDDISPLAY = 0x04,
+ MGMT_IO_CAPABILITY_INVALID = 0xFF,
+};
+
+enum mgmt_io_capability mgmt_parse_io_capability(const char *capability);
--
2.51.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH BlueZ 2/4] adapter/agent/device: migrate to shared enum/parser for IO capabilities
2026-02-27 14:24 [PATCH Bluez v2 0/4] client: align 'io-cap' and 'pair' cmds with the docs Christian Eggers
2026-02-27 14:24 ` [PATCH BlueZ 1/4] shared/mgmt: Add mgmt_parse_io_capability Christian Eggers
@ 2026-02-27 14:24 ` Christian Eggers
2026-02-27 14:24 ` [PATCH BlueZ 3/4] client/mgmt: align implementation cmd_io_cap with its documentation Christian Eggers
` (2 subsequent siblings)
4 siblings, 0 replies; 7+ messages in thread
From: Christian Eggers @ 2026-02-27 14:24 UTC (permalink / raw)
To: linux-bluetooth; +Cc: ceggers
---
src/adapter.c | 18 ++++++++++--------
src/adapter.h | 11 ++++++++---
src/agent.c | 33 ++++++++-------------------------
src/agent.h | 9 ++-------
src/device.c | 12 ++++++------
src/shared/mgmt.h | 2 ++
6 files changed, 36 insertions(+), 49 deletions(-)
diff --git a/src/adapter.c b/src/adapter.c
index 9bb1950a9f7d..4e5ff219fe8b 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -42,7 +42,6 @@
#include "log.h"
#include "textfile.h"
-#include "src/shared/mgmt.h"
#include "src/shared/util.h"
#include "src/shared/queue.h"
#include "src/shared/att.h"
@@ -8548,7 +8547,8 @@ static void pair_device_complete(uint8_t status, uint16_t length,
}
int adapter_create_bonding(struct btd_adapter *adapter, const bdaddr_t *bdaddr,
- uint8_t addr_type, uint8_t io_cap)
+ uint8_t addr_type,
+ enum mgmt_io_capability io_cap)
{
suspend_discovery(adapter);
@@ -8557,7 +8557,8 @@ int adapter_create_bonding(struct btd_adapter *adapter, const bdaddr_t *bdaddr,
/* Starts a new bonding attempt in a fresh new bonding_req or a retried one. */
int adapter_bonding_attempt(struct btd_adapter *adapter, const bdaddr_t *bdaddr,
- uint8_t addr_type, uint8_t io_cap)
+ uint8_t addr_type,
+ enum mgmt_io_capability io_cap)
{
struct mgmt_cp_pair_device cp;
char addr[18];
@@ -9151,12 +9152,13 @@ static void new_conn_param(uint16_t index, uint16_t length,
ev->latency, ev->timeout);
}
-int adapter_set_io_capability(struct btd_adapter *adapter, uint8_t io_cap)
+int adapter_set_io_capability(struct btd_adapter *adapter,
+ enum mgmt_io_capability io_cap)
{
struct mgmt_cp_set_io_capability cp;
if (!btd_opts.pairable) {
- if (io_cap == IO_CAPABILITY_INVALID) {
+ if (io_cap == MGMT_IO_CAPABILITY_INVALID) {
if (adapter->current_settings & MGMT_SETTING_BONDABLE)
set_mode(adapter, MGMT_OP_SET_BONDABLE, 0x00);
@@ -9165,8 +9167,8 @@ int adapter_set_io_capability(struct btd_adapter *adapter, uint8_t io_cap)
if (!(adapter->current_settings & MGMT_SETTING_BONDABLE))
set_mode(adapter, MGMT_OP_SET_BONDABLE, 0x01);
- } else if (io_cap == IO_CAPABILITY_INVALID)
- io_cap = IO_CAPABILITY_NOINPUTNOOUTPUT;
+ } else if (io_cap == MGMT_IO_CAPABILITY_INVALID)
+ io_cap = MGMT_IO_CAPABILITY_NOINPUTNOOUTPUT;
memset(&cp, 0, sizeof(cp));
cp.io_capability = io_cap;
@@ -9399,7 +9401,7 @@ static int adapter_register(struct btd_adapter *adapter)
agent = agent_get(NULL);
if (agent) {
- uint8_t io_cap = agent_get_io_capability(agent);
+ enum mgmt_io_capability io_cap = agent_get_io_capability(agent);
adapter_set_io_capability(adapter, io_cap);
agent_unref(agent);
}
diff --git a/src/adapter.h b/src/adapter.h
index 7a7e5c8f9dfd..4e07f71ad130 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -16,6 +16,8 @@
#include <bluetooth/bluetooth.h>
#include <bluetooth/sdp.h>
+#include "shared/mgmt.h"
+
#define ADAPTER_INTERFACE "org.bluez.Adapter1"
#define MAX_NAME_LENGTH 248
@@ -207,15 +209,18 @@ int btd_adapter_passkey_reply(struct btd_adapter *adapter,
uint32_t passkey);
int adapter_create_bonding(struct btd_adapter *adapter, const bdaddr_t *bdaddr,
- uint8_t addr_type, uint8_t io_cap);
+ uint8_t addr_type,
+ enum mgmt_io_capability io_cap);
int adapter_bonding_attempt(struct btd_adapter *adapter, const bdaddr_t *bdaddr,
- uint8_t addr_type, uint8_t io_cap);
+ uint8_t addr_type,
+ enum mgmt_io_capability io_cap);
int adapter_cancel_bonding(struct btd_adapter *adapter, const bdaddr_t *bdaddr,
uint8_t addr_type);
-int adapter_set_io_capability(struct btd_adapter *adapter, uint8_t io_cap);
+int adapter_set_io_capability(struct btd_adapter *adapter,
+ enum mgmt_io_capability io_cap);
int btd_adapter_read_local_oob_data(struct btd_adapter *adapter);
diff --git a/src/agent.c b/src/agent.c
index 3696575b83e6..caa719d90e7f 100644
--- a/src/agent.c
+++ b/src/agent.c
@@ -56,7 +56,7 @@ struct agent {
int ref;
char *owner;
char *path;
- uint8_t capability;
+ enum mgmt_io_capability capability;
struct agent_request *request;
guint watch;
};
@@ -126,12 +126,12 @@ static void agent_request_free(struct agent_request *req, gboolean destroy)
static void set_io_cap(struct btd_adapter *adapter, gpointer user_data)
{
struct agent *agent = user_data;
- uint8_t io_cap;
+ enum mgmt_io_capability io_cap;
if (agent)
io_cap = agent->capability;
else
- io_cap = IO_CAPABILITY_INVALID;
+ io_cap = MGMT_IO_CAPABILITY_INVALID;
adapter_set_io_capability(adapter, io_cap);
}
@@ -261,7 +261,7 @@ struct agent *agent_get(const char *owner)
}
static struct agent *agent_create( const char *name, const char *path,
- uint8_t capability)
+ enum mgmt_io_capability capability)
{
struct agent *agent;
@@ -922,7 +922,7 @@ failed:
return err;
}
-uint8_t agent_get_io_capability(struct agent *agent)
+enum mgmt_io_capability agent_get_io_capability(struct agent *agent)
{
return agent->capability;
}
@@ -944,29 +944,12 @@ static void agent_destroy(gpointer data)
agent_unref(agent);
}
-static uint8_t parse_io_capability(const char *capability)
-{
- if (g_str_equal(capability, ""))
- return IO_CAPABILITY_KEYBOARDDISPLAY;
- if (g_str_equal(capability, "DisplayOnly"))
- return IO_CAPABILITY_DISPLAYONLY;
- if (g_str_equal(capability, "DisplayYesNo"))
- return IO_CAPABILITY_DISPLAYYESNO;
- if (g_str_equal(capability, "KeyboardOnly"))
- return IO_CAPABILITY_KEYBOARDONLY;
- if (g_str_equal(capability, "NoInputNoOutput"))
- return IO_CAPABILITY_NOINPUTNOOUTPUT;
- if (g_str_equal(capability, "KeyboardDisplay"))
- return IO_CAPABILITY_KEYBOARDDISPLAY;
- return IO_CAPABILITY_INVALID;
-}
-
static DBusMessage *register_agent(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
struct agent *agent;
const char *sender, *path, *capability;
- uint8_t cap;
+ enum mgmt_io_capability cap;
sender = dbus_message_get_sender(msg);
@@ -979,8 +962,8 @@ static DBusMessage *register_agent(DBusConnection *conn,
DBUS_TYPE_INVALID) == FALSE)
return btd_error_invalid_args(msg);
- cap = parse_io_capability(capability);
- if (cap == IO_CAPABILITY_INVALID)
+ cap = mgmt_parse_io_capability(capability);
+ if (cap == MGMT_IO_CAPABILITY_INVALID)
return btd_error_invalid_args(msg);
agent = agent_create(sender, path, cap);
diff --git a/src/agent.h b/src/agent.h
index bd0502030fa0..63cd7e5ccc4d 100644
--- a/src/agent.h
+++ b/src/agent.h
@@ -9,12 +9,7 @@
*
*/
-#define IO_CAPABILITY_DISPLAYONLY 0x00
-#define IO_CAPABILITY_DISPLAYYESNO 0x01
-#define IO_CAPABILITY_KEYBOARDONLY 0x02
-#define IO_CAPABILITY_NOINPUTNOOUTPUT 0x03
-#define IO_CAPABILITY_KEYBOARDDISPLAY 0x04
-#define IO_CAPABILITY_INVALID 0xFF
+#include "shared/mgmt.h"
struct agent;
@@ -61,7 +56,7 @@ int agent_display_pincode(struct agent *agent, struct btd_device *device,
int agent_cancel(struct agent *agent);
-uint8_t agent_get_io_capability(struct agent *agent);
+enum mgmt_io_capability agent_get_io_capability(struct agent *agent);
void btd_agent_init(void);
void btd_agent_cleanup(void);
diff --git a/src/device.c b/src/device.c
index fd7dc4eb7b7c..3ea683667bd8 100644
--- a/src/device.c
+++ b/src/device.c
@@ -3333,7 +3333,7 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg,
const char *sender;
struct agent *agent;
struct bonding_req *bonding;
- uint8_t io_cap;
+ enum mgmt_io_capability io_cap;
int err;
btd_device_set_temporary(device, false);
@@ -3375,7 +3375,7 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg,
if (agent)
io_cap = agent_get_io_capability(agent);
else
- io_cap = IO_CAPABILITY_NOINPUTNOOUTPUT;
+ io_cap = MGMT_IO_CAPABILITY_NOINPUTNOOUTPUT;
bonding = bonding_request_new(msg, device, bdaddr_type, agent);
@@ -6504,7 +6504,7 @@ static void att_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
{
struct btd_device *device = user_data;
DBusMessage *reply;
- uint8_t io_cap;
+ enum mgmt_io_capability io_cap;
int err = 0;
g_io_channel_unref(device->att_io);
@@ -6543,7 +6543,7 @@ static void att_connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
if (device->bonding->agent)
io_cap = agent_get_io_capability(device->bonding->agent);
else
- io_cap = IO_CAPABILITY_NOINPUTNOOUTPUT;
+ io_cap = MGMT_IO_CAPABILITY_NOINPUTNOOUTPUT;
err = adapter_create_bonding(device->adapter, &device->bdaddr,
device->bdaddr_type, io_cap);
@@ -7433,7 +7433,7 @@ static gboolean device_bonding_retry(gpointer data)
struct btd_device *device = data;
struct btd_adapter *adapter = device_get_adapter(device);
struct bonding_req *bonding = device->bonding;
- uint8_t io_cap;
+ enum mgmt_io_capability io_cap;
int err;
if (!bonding)
@@ -7451,7 +7451,7 @@ static gboolean device_bonding_retry(gpointer data)
if (bonding->agent)
io_cap = agent_get_io_capability(bonding->agent);
else
- io_cap = IO_CAPABILITY_NOINPUTNOOUTPUT;
+ io_cap = MGMT_IO_CAPABILITY_NOINPUTNOOUTPUT;
err = adapter_bonding_attempt(adapter, &device->bdaddr,
device->bdaddr_type, io_cap);
diff --git a/src/shared/mgmt.h b/src/shared/mgmt.h
index c99f2ff0620e..c1f62ebb9c9d 100644
--- a/src/shared/mgmt.h
+++ b/src/shared/mgmt.h
@@ -8,6 +8,8 @@
*
*/
+#pragma once
+
#include <stdbool.h>
#include <stdint.h>
--
2.51.0
^ permalink raw reply related [flat|nested] 7+ messages in thread* [PATCH BlueZ 3/4] client/mgmt: align implementation cmd_io_cap with its documentation
2026-02-27 14:24 [PATCH Bluez v2 0/4] client: align 'io-cap' and 'pair' cmds with the docs Christian Eggers
2026-02-27 14:24 ` [PATCH BlueZ 1/4] shared/mgmt: Add mgmt_parse_io_capability Christian Eggers
2026-02-27 14:24 ` [PATCH BlueZ 2/4] adapter/agent/device: migrate to shared enum/parser for IO capabilities Christian Eggers
@ 2026-02-27 14:24 ` Christian Eggers
2026-02-27 14:24 ` [PATCH BlueZ 4/4] client/mgmt: align implementation cmd_pair " Christian Eggers
2026-02-27 15:20 ` [PATCH Bluez v2 0/4] client: align 'io-cap' and 'pair' cmds with the docs patchwork-bot+bluetooth
4 siblings, 0 replies; 7+ messages in thread
From: Christian Eggers @ 2026-02-27 14:24 UTC (permalink / raw)
To: linux-bluetooth; +Cc: ceggers
Documentation of io-cap in doc/bluetootctl-mgmt.rst states that the IO
capability has to passed as string instead of an integer. Additionally
improve error checking after calling strtol().
---
client/mgmt.c | 18 +++++++++++++++---
src/shared/mgmt.c | 20 ++++++++++++++++++++
src/shared/mgmt.h | 1 +
3 files changed, 36 insertions(+), 3 deletions(-)
diff --git a/client/mgmt.c b/client/mgmt.c
index 1d3a842afd55..430a96a9e4b7 100644
--- a/client/mgmt.c
+++ b/client/mgmt.c
@@ -4226,14 +4226,25 @@ static void io_cap_rsp(uint8_t status, uint16_t len, const void *param,
static void cmd_io_cap(int argc, char **argv)
{
struct mgmt_cp_set_io_capability cp;
- uint8_t cap;
+ long cap;
uint16_t index;
index = mgmt_index;
if (index == MGMT_INDEX_NONE)
index = 0;
- cap = strtol(argv[1], NULL, 0);
+ cap = mgmt_parse_io_capability(argv[1]);
+ if (cap == MGMT_IO_CAPABILITY_INVALID) {
+ char *endptr;
+
+ errno = 0;
+ cap = strtol(argv[1], &endptr, 0);
+ if (errno || (cap < 0) || (cap > UINT8_MAX) || *endptr) {
+ bt_shell_printf("Invalid argument %s\n", argv[1]);
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ }
+ }
+
memset(&cp, 0, sizeof(cp));
cp.io_capability = cap;
@@ -6149,7 +6160,8 @@ static const struct bt_shell_menu mgmt_menu = {
{ "conn-info", "[-t type] <remote address>",
cmd_conn_info, "Get connection information" },
{ "io-cap", "<cap>",
- cmd_io_cap, "Set IO Capability" },
+ cmd_io_cap, "Set IO Capability",
+ mgmt_iocap_generator },
{ "scan-params", "<interval> <window>",
cmd_scan_params, "Set Scan Parameters" },
{ "get-clock", "[address]",
diff --git a/src/shared/mgmt.c b/src/shared/mgmt.c
index 930e70a094d0..f56bcee14059 100644
--- a/src/shared/mgmt.c
+++ b/src/shared/mgmt.c
@@ -1054,6 +1054,26 @@ uint16_t mgmt_get_mtu(struct mgmt *mgmt)
return mgmt->mtu;
}
+char *mgmt_iocap_generator(const char *text, int state)
+{
+ static int index, len;
+ const char *arg;
+
+ if (!state) {
+ index = 0;
+ len = strlen(text);
+ }
+
+ while ((arg = iocap_arguments[index].name)) {
+ index++;
+
+ if (!strncmp(arg, text, len))
+ return strdup(arg);
+ }
+
+ return NULL;
+}
+
enum mgmt_io_capability mgmt_parse_io_capability(const char *capability)
{
const char *arg;
diff --git a/src/shared/mgmt.h b/src/shared/mgmt.h
index c1f62ebb9c9d..417798c1718c 100644
--- a/src/shared/mgmt.h
+++ b/src/shared/mgmt.h
@@ -101,4 +101,5 @@ enum mgmt_io_capability {
MGMT_IO_CAPABILITY_INVALID = 0xFF,
};
+char *mgmt_iocap_generator(const char *text, int state);
enum mgmt_io_capability mgmt_parse_io_capability(const char *capability);
--
2.51.0
^ permalink raw reply related [flat|nested] 7+ messages in thread