From: Pauli Virtanen <pav@iki.fi>
To: linux-bluetooth@vger.kernel.org
Cc: Pauli Virtanen <pav@iki.fi>
Subject: [PATCH BlueZ v2.. 3/7] mcp: adapt to new MCP API to support multiple remote MCS services
Date: Wed, 10 Dec 2025 17:44:08 +0200 [thread overview]
Message-ID: <d1193c2026933eb7d131a43a1f92d10f87b7f23f.1765381438.git.pav@iki.fi> (raw)
In-Reply-To: <cover.1765381438.git.pav@iki.fi>
Rewrite to use the new shared/mcp API, adding support for multiple MCS
services on the remote side.
---
profiles/audio/mcp.c | 647 +++++++++++++++++++++++++++----------------
1 file changed, 407 insertions(+), 240 deletions(-)
diff --git a/profiles/audio/mcp.c b/profiles/audio/mcp.c
index 8d4eed643..e64cbd01d 100644
--- a/profiles/audio/mcp.c
+++ b/profiles/audio/mcp.c
@@ -22,6 +22,7 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
+#include <math.h>
#include <glib.h>
@@ -51,50 +52,54 @@
#include "src/service.h"
#include "src/log.h"
#include "src/error.h"
+
#include "player.h"
-#define GMCS_UUID_STR "00001849-0000-1000-8000-00805f9b34fb"
+#define MCS_UUID_STR "00001848-0000-1000-8000-00805f9b34fb"
+#define GMCS_UUID_STR "00001849-0000-1000-8000-00805f9b34fb"
-struct mcp_data {
- struct btd_device *device;
- struct btd_service *service;
+
+/*
+ * Remote player
+ */
+
+struct remote_player {
struct bt_mcp *mcp;
- unsigned int state_id;
-
+ uint8_t ccid;
struct media_player *mp;
+ uint8_t playing_order;
};
-static void mcp_debug(const char *str, void *user_data)
-{
- DBG_IDX(0xffff, "%s", str);
-}
-
static char *name2utf8(const uint8_t *name, uint16_t len)
{
- char utf8_name[HCI_MAX_NAME_LENGTH + 2];
+ char *utf8_name;
- len = MIN(len, sizeof(utf8_name) - 1);
+ utf8_name = malloc(len + 1);
+ if (!utf8_name)
+ return NULL;
- memset(utf8_name, 0, sizeof(utf8_name));
- strncpy(utf8_name, (char *) name, len);
+ if (len)
+ memcpy(utf8_name, name, len);
+
+ utf8_name[len] = 0;
strtoutf8(utf8_name, len);
/* Remove leading and trailing whitespace characters */
g_strstrip(utf8_name);
- return g_strdup(utf8_name);
+ return utf8_name;
}
static const char *mcp_status_val_to_string(uint8_t status)
{
switch (status) {
- case BT_MCS_STATUS_PLAYING:
+ case BT_MCS_STATE_PLAYING:
return "playing";
- case BT_MCS_STATUS_PAUSED:
+ case BT_MCS_STATE_PAUSED:
return "paused";
- case BT_MCS_STATUS_INACTIVE:
+ case BT_MCS_STATE_INACTIVE:
return "stopped";
- case BT_MCS_STATUS_SEEKING:
+ case BT_MCS_STATE_SEEKING:
/* TODO: find a way for fwd/rvs seeking, probably by storing
* control point operation sent before
*/
@@ -104,307 +109,469 @@ static const char *mcp_status_val_to_string(uint8_t status)
}
}
-static struct mcp_data *mcp_data_new(struct btd_device *device)
-{
- struct mcp_data *data;
-
- data = new0(struct mcp_data, 1);
- data->device = device;
-
- return data;
-}
-
-static void cb_player_name(struct bt_mcp *mcp, const uint8_t *value,
- uint16_t length)
+static void remote_media_player_name(void *data, const uint8_t *value,
+ uint16_t length)
{
+ struct remote_player *remote = data;
char *name;
- struct media_player *mp = bt_mcp_get_user_data(mcp);
name = name2utf8(value, length);
+ if (!name)
+ return;
+
DBG("Media Player Name %s", (const char *)name);
- media_player_set_name(mp, name);
+ media_player_set_name(remote->mp, name);
- g_free(name);
+ free(name);
}
-static void cb_track_changed(struct bt_mcp *mcp)
+static void remote_track_changed(void *data)
{
+ struct remote_player *remote = data;
+
DBG("Track Changed");
- /* Since track changed has happened
- * track title notification is expected
- */
+
+ media_player_metadata_changed(remote->mp);
}
-static void cb_track_title(struct bt_mcp *mcp, const uint8_t *value,
- uint16_t length)
+static void remote_track_title(void *data, const uint8_t *value,
+ uint16_t length)
{
+ struct remote_player *remote = data;
char *name;
uint16_t len;
- struct media_player *mp = bt_mcp_get_user_data(mcp);
name = name2utf8(value, length);
+ if (!name)
+ return;
+
len = strlen(name);
DBG("Track Title %s", (const char *)name);
- media_player_set_metadata(mp, NULL, "Title", name, len);
- media_player_metadata_changed(mp);
+ media_player_set_metadata(remote->mp, NULL, "Title", name, len);
+ media_player_metadata_changed(remote->mp);
g_free(name);
}
-static void cb_track_duration(struct bt_mcp *mcp, int32_t duration)
+static void remote_track_duration(void *data, int32_t duration_centisec)
{
- struct media_player *mp = bt_mcp_get_user_data(mcp);
- unsigned char buf[10];
+ struct remote_player *remote = data;
- /* MCP defines duration is int32 but api takes it as uint32 */
- snprintf((char *)buf, 10, "%d", duration);
- media_player_set_metadata(mp, NULL, "Duration", buf, sizeof(buf));
- media_player_metadata_changed(mp);
-}
-
-static void cb_track_position(struct bt_mcp *mcp, int32_t duration)
-{
- struct media_player *mp = bt_mcp_get_user_data(mcp);
-
- /* MCP defines duration is int32 but api takes it as uint32 */
- media_player_set_position(mp, duration);
-}
-
-static void cb_media_state(struct bt_mcp *mcp, uint8_t status)
-{
- struct media_player *mp = bt_mcp_get_user_data(mcp);
-
- media_player_set_status(mp, mcp_status_val_to_string(status));
-}
-
-static const struct bt_mcp_event_callback cbs = {
- .player_name = cb_player_name,
- .track_changed = cb_track_changed,
- .track_title = cb_track_title,
- .track_duration = cb_track_duration,
- .track_position = cb_track_position,
- .media_state = cb_media_state,
-};
-
-static int ct_play(struct media_player *mp, void *user_data)
-{
- struct bt_mcp *mcp = user_data;
-
- return bt_mcp_play(mcp);
-}
-
-static int ct_pause(struct media_player *mp, void *user_data)
-{
- struct bt_mcp *mcp = user_data;
-
- return bt_mcp_pause(mcp);
-}
-
-static int ct_stop(struct media_player *mp, void *user_data)
-{
- struct bt_mcp *mcp = user_data;
-
- return bt_mcp_stop(mcp);
-}
-
-static int ct_next(struct media_player *mp, void *user_data)
-{
- struct bt_mcp *mcp = user_data;
-
- return bt_mcp_next_track(mcp);
-}
-
-static int ct_previous(struct media_player *mp, void *user_data)
-{
- struct bt_mcp *mcp = user_data;
-
- return bt_mcp_previous_track(mcp);
-}
-
-static const struct media_player_callback ct_cbs = {
- .play = ct_play,
- .pause = ct_pause,
- .stop = ct_stop,
- .next = ct_next,
- .previous = ct_previous,
-};
-
-static int mcp_probe(struct btd_service *service)
-{
- struct btd_device *device = btd_service_get_device(service);
- struct btd_adapter *adapter = device_get_adapter(device);
- struct btd_gatt_database *database = btd_adapter_get_database(adapter);
- struct mcp_data *data = btd_service_get_user_data(service);
- char addr[18];
-
- ba2str(device_get_address(device), addr);
- DBG("%s", addr);
-
- /* Ignore, if we were probed for this device already */
- if (data) {
- error("Profile probed twice for the same device!");
- return -EINVAL;
- }
-
- data = mcp_data_new(device);
- data->service = service;
-
- data->mcp = bt_mcp_new(btd_gatt_database_get_db(database),
- btd_device_get_gatt_db(device));
-
- bt_mcp_set_debug(data->mcp, mcp_debug, NULL, NULL);
- btd_service_set_user_data(service, data);
-
- return 0;
-}
-
-static void mcp_data_free(struct mcp_data *data)
-{
- DBG("");
-
- if (data->service) {
- btd_service_set_user_data(data->service, NULL);
- bt_mcp_set_user_data(data->mcp, NULL);
- }
-
- if (data->mp) {
- media_player_destroy(data->mp);
- data->mp = NULL;
- }
-
- bt_mcp_unref(data->mcp);
- free(data);
-}
-
-static void mcp_data_remove(struct mcp_data *data)
-{
- DBG("data %p", data);
-
- mcp_data_free(data);
-}
-
-static void mcp_remove(struct btd_service *service)
-{
- struct btd_device *device = btd_service_get_device(service);
- struct mcp_data *data;
- char addr[18];
-
- ba2str(device_get_address(device), addr);
- DBG("%s", addr);
-
- data = btd_service_get_user_data(service);
- if (!data) {
- error("MCP service not handled by profile");
+ if (duration_centisec == BT_MCS_POSITION_UNAVAILABLE) {
+ media_player_set_duration(remote->mp, 0);
return;
}
- mcp_data_remove(data);
+ if (duration_centisec < 0)
+ duration_centisec = 0;
+
+ media_player_set_duration(remote->mp, duration_centisec * 10);
+}
+
+static void remote_track_position(void *data, int32_t position_centisec)
+{
+ struct remote_player *remote = data;
+
+ if (position_centisec == BT_MCS_POSITION_UNAVAILABLE) {
+ media_player_set_position(remote->mp, 0);
+ return;
+ }
+
+ if (position_centisec < 0)
+ position_centisec = 0;
+
+ media_player_set_position(remote->mp, position_centisec * 10);
+}
+
+static void remote_playback_speed(void *data, int8_t value)
+{
+ /* TODO */
+}
+
+static void remote_seeking_speed(void *data, int8_t speed)
+{
+ /* TODO */
+}
+
+static const struct {
+ uint16_t basic;
+ uint16_t repeat;
+ bool shuffle;
+ bool single;
+} playing_orders[] = {
+ { BT_MCS_ORDER_SINGLE_ONCE, BT_MCS_ORDER_SINGLE_REPEAT,
+ .single = true },
+ { BT_MCS_ORDER_IN_ORDER_ONCE, BT_MCS_ORDER_IN_ORDER_REPEAT },
+ { BT_MCS_ORDER_OLDEST_ONCE, BT_MCS_ORDER_OLDEST_REPEAT },
+ { BT_MCS_ORDER_NEWEST_ONCE, BT_MCS_ORDER_NEWEST_REPEAT },
+ { BT_MCS_ORDER_SHUFFLE_ONCE, BT_MCS_ORDER_SHUFFLE_REPEAT,
+ .shuffle = true },
+};
+
+static void remote_playing_order(void *data, uint8_t order)
+{
+ struct remote_player *remote = data;
+ const char *repeat = "off";
+ unsigned int i;
+ bool shuffle;
+
+ remote->playing_order = order;
+
+ for (i = 0; i < ARRAY_SIZE(playing_orders); ++i) {
+ shuffle = playing_orders[i].shuffle;
+ if (order == playing_orders[i].basic) {
+ break;
+ } else if (order == playing_orders[i].repeat) {
+ repeat = playing_orders[i].single ? "singletrack" :
+ "alltracks";
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(playing_orders))
+ return;
+
+ media_player_set_setting(remote->mp, "Repeat", repeat);
+ media_player_set_setting(remote->mp, "Shuffle", shuffle ? "on" : "off");
+}
+
+static void remote_media_state(void *data, uint8_t status)
+{
+ struct remote_player *remote = data;
+
+ media_player_set_status(remote->mp, mcp_status_val_to_string(status));
+}
+
+static void remote_destroy(void *data)
+{
+ struct remote_player *remote = data;
+
+ media_player_destroy(remote->mp);
+ free(data);
+}
+
+static const struct bt_mcp_listener_callback remote_cb = {
+ .media_player_name = remote_media_player_name,
+ .track_changed = remote_track_changed,
+ .track_title = remote_track_title,
+ .track_duration = remote_track_duration,
+ .track_position = remote_track_position,
+ .playback_speed = remote_playback_speed,
+ .seeking_speed = remote_seeking_speed,
+ .playing_order = remote_playing_order,
+ .media_state = remote_media_state,
+ .destroy = remote_destroy,
+};
+
+static int remote_mp_play(struct media_player *mp, void *user_data)
+{
+ struct remote_player *remote = user_data;
+
+ return bt_mcp_play(remote->mcp, remote->ccid);
+}
+
+static int remote_mp_pause(struct media_player *mp, void *user_data)
+{
+ struct remote_player *remote = user_data;
+
+ return bt_mcp_pause(remote->mcp, remote->ccid);
+}
+
+static int remote_mp_stop(struct media_player *mp, void *user_data)
+{
+ struct remote_player *remote = user_data;
+
+ return bt_mcp_stop(remote->mcp, remote->ccid);
+}
+
+static int remote_mp_next(struct media_player *mp, void *user_data)
+{
+ struct remote_player *remote = user_data;
+
+ return bt_mcp_next_track(remote->mcp, remote->ccid);
+}
+
+static int remote_mp_previous(struct media_player *mp, void *user_data)
+{
+ struct remote_player *remote = user_data;
+
+ return bt_mcp_previous_track(remote->mcp, remote->ccid);
+}
+
+static bool remote_mp_set_setting(struct media_player *mp, const char *key,
+ const char *value, void *user_data)
+{
+ struct remote_player *remote = user_data;
+ unsigned int i;
+
+ if (strcmp(key, "Repeat") == 0) {
+ bool repeat = (strcmp(value, "alltracks") == 0);
+ uint8_t order = remote->playing_order;
+
+ /* Some sensible mapping, 1-to-1 not possible */
+ for (i = 0; i < ARRAY_SIZE(playing_orders); ++i) {
+ if (order == playing_orders[i].basic) {
+ if (repeat)
+ order = playing_orders[i].repeat;
+ break;
+ } else if (order == playing_orders[i].repeat) {
+ if (!repeat)
+ order = playing_orders[i].basic;
+ break;
+ }
+ }
+
+ if (strcmp(value, "singletrack") == 0)
+ order = BT_MCS_ORDER_SINGLE_REPEAT;
+
+ DBG("Set Repeat %s -> 0x%02x", value, order);
+
+ if (order == remote->playing_order)
+ return true;
+ return bt_mcp_set_playing_order(remote->mcp, remote->ccid,
+ order);
+ }
+
+ if (strcmp(key, "Shuffle") == 0) {
+ bool shuffle = (strcmp(value, "off") != 0);
+ uint8_t order = remote->playing_order;
+
+ /* Some sensible mapping, 1-to-1 not possible */
+ switch (order) {
+ case BT_MCS_ORDER_SHUFFLE_ONCE:
+ if (!shuffle)
+ order = BT_MCS_ORDER_IN_ORDER_ONCE;
+ break;
+ case BT_MCS_ORDER_SHUFFLE_REPEAT:
+ if (!shuffle)
+ order = BT_MCS_ORDER_IN_ORDER_REPEAT;
+ break;
+ case BT_MCS_ORDER_SINGLE_ONCE:
+ case BT_MCS_ORDER_IN_ORDER_ONCE:
+ case BT_MCS_ORDER_OLDEST_ONCE:
+ case BT_MCS_ORDER_NEWEST_ONCE:
+ if (shuffle)
+ order = BT_MCS_ORDER_SHUFFLE_ONCE;
+ break;
+ case BT_MCS_ORDER_SINGLE_REPEAT:
+ case BT_MCS_ORDER_IN_ORDER_REPEAT:
+ case BT_MCS_ORDER_OLDEST_REPEAT:
+ case BT_MCS_ORDER_NEWEST_REPEAT:
+ if (shuffle)
+ order = BT_MCS_ORDER_SHUFFLE_REPEAT;
+ break;
+ }
+
+ DBG("Set Shuffle %s -> 0x%02x", value, order);
+
+ if (order == remote->playing_order)
+ return true;
+ return bt_mcp_set_playing_order(remote->mcp, remote->ccid,
+ order);
+ }
+
+ return false;
+}
+
+static const struct media_player_callback remote_mp_cb = {
+ .play = remote_mp_play,
+ .pause = remote_mp_pause,
+ .stop = remote_mp_stop,
+ .next = remote_mp_next,
+ .previous = remote_mp_previous,
+ .set_setting = remote_mp_set_setting,
+};
+
+static void mcp_ccid(void *data, uint8_t ccid, bool gmcs)
+{
+ struct btd_service *service = data;
+ struct btd_device *device = btd_service_get_device(service);
+ struct bt_mcp *mcp = btd_service_get_user_data(service);
+ struct remote_player *remote;
+ struct media_player *mp;
+
+ mp = media_player_controller_create(device_get_path(device),
+ gmcs ? "mcp_gmcs" : "mcp_mcs", ccid);
+ if (!mp) {
+ DBG("Unable to create Media Player");
+ return;
+ }
+
+ remote = new0(struct remote_player, 1);
+ remote->mcp = mcp;
+ remote->ccid = ccid;
+ remote->mp = mp;
+
+ media_player_set_callbacks(remote->mp, &remote_mp_cb, remote);
+
+ if (!bt_mcp_add_listener(mcp, ccid, &remote_cb, remote)) {
+ DBG("Unable to register Media Player with MCP");
+ media_player_destroy(mp);
+ free(remote);
+ return;
+ }
+}
+
+static void mcp_debug(void *data, const char *str)
+{
+ DBG_IDX(0xffff, "%s", str);
+}
+
+static void mcp_ready(void *data)
+{
+ struct btd_service *service = data;
+
+ btd_service_connecting_complete(service, 0);
+}
+
+static const struct bt_mcp_callback mcp_cb = {
+ .ccid = mcp_ccid,
+ .debug = mcp_debug,
+ .ready = mcp_ready,
+};
+
+
+/*
+ * Profile
+ */
+
+static struct btd_profile mcp_gmcs_profile;
+
+static int add_service(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct bt_gatt_client *client = btd_device_get_gatt_client(device);
+ struct bt_mcp *mcp = btd_service_get_user_data(service);
+ bool gmcs;
+
+ if (mcp)
+ return -EALREADY;
+
+ gmcs = btd_service_get_profile(service) == &mcp_gmcs_profile;
+
+ mcp = bt_mcp_attach(client, gmcs, &mcp_cb, service);
+ if (!mcp) {
+ DBG("Unable to attach MCP");
+ return -EINVAL;
+ }
+
+ btd_service_set_user_data(service, mcp);
+ return 0;
+}
+
+static void remove_service(struct btd_service *service)
+{
+ struct bt_mcp *mcp = btd_service_get_user_data(service);
+
+ btd_service_set_user_data(service, NULL);
+ bt_mcp_detach(mcp);
}
static int mcp_accept(struct btd_service *service)
{
struct btd_device *device = btd_service_get_device(service);
- struct bt_gatt_client *client = btd_device_get_gatt_client(device);
- struct mcp_data *data = btd_service_get_user_data(service);
char addr[18];
ba2str(device_get_address(device), addr);
DBG("%s", addr);
- bt_mcp_attach(data->mcp, client);
-
- data->mp = media_player_controller_create(device_get_path(device),
- "mcp", 0);
- if (data->mp == NULL) {
- DBG("Unable to create Media Player");
- return -EINVAL;
- }
-
- media_player_set_callbacks(data->mp, &ct_cbs, data->mcp);
-
- bt_mcp_set_user_data(data->mcp, data->mp);
- bt_mcp_set_event_callbacks(data->mcp, &cbs, data->mp);
- btd_service_connecting_complete(service, 0);
-
- return 0;
+ return add_service(service);
}
static int mcp_connect(struct btd_service *service)
{
- struct btd_device *device = btd_service_get_device(service);
- char addr[18];
-
- ba2str(device_get_address(device), addr);
- DBG("%s", addr);
-
return 0;
}
static int mcp_disconnect(struct btd_service *service)
{
struct btd_device *device = btd_service_get_device(service);
- struct mcp_data *data = btd_service_get_user_data(service);
char addr[18];
ba2str(device_get_address(device), addr);
DBG("%s", addr);
- if (data->mp) {
- media_player_destroy(data->mp);
- data->mp = NULL;
- }
-
- bt_mcp_detach(data->mcp);
-
+ remove_service(service);
btd_service_disconnecting_complete(service, 0);
-
return 0;
}
-static int media_control_server_probe(struct btd_profile *p,
- struct btd_adapter *adapter)
+static int mcp_probe(struct btd_service *service)
{
- struct btd_gatt_database *database = btd_adapter_get_database(adapter);
-
- bt_mcp_register(btd_gatt_database_get_db(database));
-
return 0;
}
-static void media_control_server_remove(struct btd_profile *p,
- struct btd_adapter *adapter)
+static void mcp_remove(struct btd_service *service)
{
-
+ remove_service(service);
}
-static struct btd_profile mcp_profile = {
- .name = "mcp",
+static int gmcs_probe(struct btd_profile *p, struct btd_adapter *adapter)
+{
+ return 0;
+}
+
+static void gmcs_remove(struct btd_profile *p, struct btd_adapter *adapter)
+{
+}
+
+static struct btd_profile mcp_gmcs_profile = {
+ .name = "mcp-gmcs",
.priority = BTD_PROFILE_PRIORITY_MEDIUM,
- .bearer = BTD_PROFILE_BEARER_LE,
- .remote_uuid = GMCS_UUID_STR,
- .device_probe = mcp_probe,
- .device_remove = mcp_remove,
+ .bearer = BTD_PROFILE_BEARER_LE,
+ .remote_uuid = GMCS_UUID_STR,
+ .device_probe = mcp_probe,
+ .device_remove = mcp_remove,
.accept = mcp_accept,
.connect = mcp_connect,
.disconnect = mcp_disconnect,
- .adapter_probe = media_control_server_probe,
- .adapter_remove = media_control_server_remove,
+ .adapter_probe = gmcs_probe,
+ .adapter_remove = gmcs_remove,
- .experimental = true,
+ .experimental = true,
+};
+
+static struct btd_profile mcp_mcs_profile = {
+ .name = "mcp-mcs",
+ .priority = BTD_PROFILE_PRIORITY_MEDIUM,
+ .bearer = BTD_PROFILE_BEARER_LE,
+ .remote_uuid = MCS_UUID_STR,
+ .device_probe = mcp_probe,
+ .device_remove = mcp_remove,
+ .accept = mcp_accept,
+ .connect = mcp_connect,
+ .disconnect = mcp_disconnect,
+
+ .adapter_probe = NULL,
+ .adapter_remove = NULL,
+
+ .experimental = true,
};
static int mcp_init(void)
{
- return btd_profile_register(&mcp_profile);
+ int err;
+
+ err = btd_profile_register(&mcp_gmcs_profile);
+ if (err)
+ return err;
+
+ err = btd_profile_register(&mcp_mcs_profile);
+ if (err) {
+ btd_profile_unregister(&mcp_gmcs_profile);
+ return err;
+ }
+
+ return err;
}
static void mcp_exit(void)
{
- btd_profile_unregister(&mcp_profile);
+ btd_profile_unregister(&mcp_gmcs_profile);
+ btd_profile_unregister(&mcp_mcs_profile);
}
BLUETOOTH_PLUGIN_DEFINE(mcp, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
--
2.51.1
next prev parent reply other threads:[~2025-12-10 15:44 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-12-10 15:44 [PATCH BlueZ v2.. 0/7] mcp: support multiple MCP and implement local GMCS Pauli Virtanen
2025-12-10 15:44 ` [PATCH BlueZ v2.. 1/7] shared/mcp: support multiple MCP, and add non-stub MCS server Pauli Virtanen
2025-12-10 16:20 ` mcp: support multiple MCP and implement local GMCS bluez.test.bot
2025-12-10 15:44 ` [PATCH BlueZ v2.. 2/7] test-mcp: add tests for MCP / MCS Pauli Virtanen
2025-12-10 15:44 ` Pauli Virtanen [this message]
2025-12-10 15:44 ` [PATCH BlueZ v2.. 4/7] avctp: move uinput utilities to uinput-util.c Pauli Virtanen
2025-12-10 15:44 ` [PATCH BlueZ v2.. 5/7] uinput-util: fix compiler complaint about strncpy usage Pauli Virtanen
2025-12-10 15:44 ` [PATCH BlueZ v2.. 6/7] mcp: add local GMCS service that emits uinput media keys Pauli Virtanen
2025-12-10 15:44 ` [PATCH BlueZ v2.. 7/7] shared/gatt-client: fix notify_data leak in notify_data_write_ccc Pauli Virtanen
2025-12-10 16:11 ` [PATCH BlueZ v2.. 0/7] mcp: support multiple MCP and implement local GMCS Luiz Augusto von Dentz
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=d1193c2026933eb7d131a43a1f92d10f87b7f23f.1765381438.git.pav@iki.fi \
--to=pav@iki.fi \
--cc=linux-bluetooth@vger.kernel.org \
/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).