Re: [PATCH BlueZ v2] client: Support single profile connection/disconnection

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Arkadiusz,

On Mon, Mar 3, 2025 at 2:51 PM Arkadiusz Bokowy
<arkadiusz.bokowy@xxxxxxxxx> wrote:
>
> ---
>  client/bluetoothctl.rst | 20 +++++++--

Documentation shall be split into its own patch.

>  client/main.c           | 93 +++++++++++++++++++++++++++++++++++------

Same for uuid-helper.

>  src/uuid-helper.c       | 32 +++++++-------
>  3 files changed, 114 insertions(+), 31 deletions(-)
>
> diff --git a/client/bluetoothctl.rst b/client/bluetoothctl.rst
> index b6c2efa35..c60bf719f 100644
> --- a/client/bluetoothctl.rst
> +++ b/client/bluetoothctl.rst
> @@ -9,7 +9,7 @@ Bluetooth Control Command Line Tool
>  :Version: BlueZ
>  :Copyright: Free use of this software is granted under the terms of the GNU
>              Lesser General Public Licenses (LGPL).
> -:Date: November 2022
> +:Date: March 2024
>  :Manual section: 1
>  :Manual group: Linux System Administration
>
> @@ -262,6 +262,13 @@ Connect device.
>
>  This will initiate a connection to a device.
>
> +By default this commands tries to connect all the profiles the remote device
> +supports and have been flagged as auto-connectable. In case when the UUID of
> +the remote service is given only that service will be connected. The UUID can
> +be either a short form (16-bit UUID) or a long form (128-bit UUID). There are
> +also some special values for well-known profiles like "a2dp-sink",
> +"a2dp-source", "hfp-hf", "hfp-ag", "ftp" or "spp".
> +
>  To connect with an LE device the controller must have an active scan report of
>  the device it wants to connect to.
>
> @@ -269,17 +276,24 @@ If no advertising report is received before the timeout a
>  le-connection-abort-by-local error will be issued. In that case either try
>  again to connect assuming the device is advertising.
>
> -:Usage: **> connect <dev>**
> +:Usage: **> connect <dev> [uuid]**
> +:Example: **> connect 1C:48:F9:9D:81:5C hsp-hs**
> +:Example: **> connect 1C:48:F9:9D:81:5C 00001108-0000-1000-8000-00805f9b34fb**
> +:Example: **> connect 1C:48:F9:9D:81:5C 0x1108**
>
>  disconnect
>  ----------
>
>  Disconnect device.
>
> +By default this commands disconnects all profiles and then terminates the
> +connection. In case when the UUID of the remote service is given only that
> +service will be disconnected.
> +
>  For LE when disconnecting from an active connection the device address is not
>  needed.
>
> -:Usage: **> disconnect <dev>**
> +:Usage: **> disconnect <dev> [uuid]**
>
>  info
>  ----
> diff --git a/client/main.c b/client/main.c
> index 6b938da3f..3f2bfcf6b 100644
> --- a/client/main.c
> +++ b/client/main.c
> @@ -1981,13 +1981,44 @@ static void cmd_remove(int argc, char *argv[])
>         remove_device(proxy);
>  }
>
> +struct connection_data {
> +       GDBusProxy *proxy;
> +       char *uuid;
> +};
> +
> +static void connection_setup(DBusMessageIter *iter, void *user_data)
> +{
> +       struct connection_data *data = user_data;
> +
> +       if (!data->uuid)
> +               return;
> +
> +       dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &data->uuid);
> +}
> +
> +static void format_connection_profile(char *output, size_t size,
> +                                                       const char *uuid)
> +{
> +       const char *text;
> +
> +       text = bt_uuidstr_to_str(uuid);
> +       if (!text)
> +               text = uuid;
> +
> +       snprintf(output, size, " profile \"%s\"", text);
> +}
> +
>  static void connect_reply(DBusMessage *message, void *user_data)
>  {
> -       GDBusProxy *proxy = user_data;
> +       struct connection_data *data = user_data;
> +       GDBusProxy *proxy = data->proxy;
>         DBusError error;
>
>         dbus_error_init(&error);
>
> +       g_free(data->uuid);
> +       g_free(data);
> +
>         if (dbus_set_error_from_message(&error, message) == TRUE) {
>                 bt_shell_printf("Failed to connect: %s %s\n", error.name,
>                                 error.message);
> @@ -2003,6 +2034,9 @@ static void connect_reply(DBusMessage *message, void *user_data)
>
>  static void cmd_connect(int argc, char *argv[])
>  {
> +       struct connection_data *data;
> +       const char *method = "Connect";
> +       char profile[128] = "";
>         GDBusProxy *proxy;
>
>         if (check_default_ctrl() == FALSE)
> @@ -2014,31 +2048,49 @@ static void cmd_connect(int argc, char *argv[])
>                 return bt_shell_noninteractive_quit(EXIT_FAILURE);
>         }
>
> -       if (g_dbus_proxy_method_call(proxy, "Connect", NULL, connect_reply,
> -                                                       proxy, NULL) == FALSE) {
> +       data = new0(struct connection_data, 1);
> +       data->proxy = proxy;
> +
> +       if (argc == 3) {
> +               method = "ConnectProfile";
> +               data->uuid = g_strdup(argv[2]);
> +               format_connection_profile(profile, sizeof(profile), argv[2]);
> +       }
> +
> +       if (g_dbus_proxy_method_call(proxy, method, connection_setup,
> +                                       connect_reply, data, NULL) == FALSE) {
>                 bt_shell_printf("Failed to connect\n");
>                 return bt_shell_noninteractive_quit(EXIT_FAILURE);
>         }
>
> -       bt_shell_printf("Attempting to connect to %s\n", argv[1]);
> +       bt_shell_printf("Attempting to connect%s to %s\n", profile, argv[1]);
>  }
>
>  static void disconn_reply(DBusMessage *message, void *user_data)
>  {
> -       GDBusProxy *proxy = user_data;
> +       struct connection_data *data = user_data;
> +       const bool profile_disconnected = data->uuid != NULL;
> +       GDBusProxy *proxy = data->proxy;
>         DBusError error;
>
>         dbus_error_init(&error);
>
> +       g_free(data->uuid);
> +       g_free(data);
> +
>         if (dbus_set_error_from_message(&error, message) == TRUE) {
>                 bt_shell_printf("Failed to disconnect: %s\n", error.name);
>                 dbus_error_free(&error);
>                 return bt_shell_noninteractive_quit(EXIT_FAILURE);
>         }
>
> -       bt_shell_printf("Successful disconnected\n");
> +       bt_shell_printf("Disconnection successful\n");
>
> -       if (proxy == default_dev)
> +       /* If only a single profile was disconnected, the device itself might
> +        * still be connected. In that case, let the property change handler
> +        * take care of setting the default device to NULL.
> +        */
> +       if (proxy == default_dev && !profile_disconnected)
>                 set_default_device(NULL, NULL);
>
>         return bt_shell_noninteractive_quit(EXIT_SUCCESS);
> @@ -2046,19 +2098,31 @@ static void disconn_reply(DBusMessage *message, void *user_data)
>
>  static void cmd_disconn(int argc, char *argv[])
>  {
> +       struct connection_data *data;
> +       const char *method = "Disconnect";
> +       char profile[128] = "";
>         GDBusProxy *proxy;
>
>         proxy = find_device(argc, argv);
>         if (!proxy)
>                 return bt_shell_noninteractive_quit(EXIT_FAILURE);
>
> -       if (g_dbus_proxy_method_call(proxy, "Disconnect", NULL, disconn_reply,
> -                                                       proxy, NULL) == FALSE) {
> +       data = new0(struct connection_data, 1);
> +       data->proxy = proxy;
> +
> +       if (argc == 3) {
> +               method = "DisconnectProfile";
> +               data->uuid = g_strdup(argv[2]);
> +               format_connection_profile(profile, sizeof(profile), argv[2]);
> +       }
> +
> +       if (g_dbus_proxy_method_call(proxy, method, connection_setup,
> +                                       disconn_reply, data, NULL) == FALSE) {
>                 bt_shell_printf("Failed to disconnect\n");
>                 return bt_shell_noninteractive_quit(EXIT_FAILURE);
>         }
>
> -       bt_shell_printf("Attempting to disconnect from %s\n",
> +       bt_shell_printf("Attempting to disconnect%s from %s\n", profile,
>                                                 proxy_address(proxy));
>  }
>
> @@ -3253,10 +3317,13 @@ static const struct bt_shell_menu main_menu = {
>                                                                 dev_generator },
>         { "remove",       "<dev>",    cmd_remove, "Remove device",
>                                                         dev_generator },
> -       { "connect",      "<dev>",    cmd_connect, "Connect device",
> -                                                       dev_generator },
> -       { "disconnect",   "[dev]",    cmd_disconn, "Disconnect device",
> +       { "connect",      "<dev> [uuid]", cmd_connect,
> +                               "Connect a device and all its profiles or "
> +                               "optionally connect a single profile only",
>                                                         dev_generator },
> +       { "disconnect",   "[dev] [uuid]", cmd_disconn,
> +                               "Disconnect a device or optionally disconnect "
> +                               "a single profile only", dev_generator },
>         { "wake",         "[dev] [on/off]",    cmd_wake, "Get/Set wake support",
>                                                         dev_generator },
>         { } },
> diff --git a/src/uuid-helper.c b/src/uuid-helper.c
> index f32ee0a85..640592fd2 100644
> --- a/src/uuid-helper.c
> +++ b/src/uuid-helper.c
> @@ -101,29 +101,31 @@ static struct {
>         const char      *name;
>         uint16_t        class;
>  } bt_services[] = {
> -       { "pbap",       PBAP_SVCLASS_ID                 },
> -       { "sap",        SAP_SVCLASS_ID                  },
> -       { "ftp",        OBEX_FILETRANS_SVCLASS_ID       },
> -       { "bpp",        BASIC_PRINTING_SVCLASS_ID       },
> +       { "a2dp-sink",  AUDIO_SINK_SVCLASS_ID           },
> +       { "a2dp-source",AUDIO_SOURCE_SVCLASS_ID         },
>         { "bip",        IMAGING_SVCLASS_ID              },
> -       { "synch",      IRMC_SYNC_SVCLASS_ID            },
> +       { "bpp",        BASIC_PRINTING_SVCLASS_ID       },
>         { "dun",        DIALUP_NET_SVCLASS_ID           },
> -       { "opp",        OBEX_OBJPUSH_SVCLASS_ID         },
>         { "fax",        FAX_SVCLASS_ID                  },
> -       { "spp",        SERIAL_PORT_SVCLASS_ID          },
> -       { "hsp",        HEADSET_SVCLASS_ID              },
> -       { "hsp-hs",     HEADSET_SVCLASS_ID              },
> -       { "hsp-ag",     HEADSET_AGW_SVCLASS_ID          },
> +       { "ftp",        OBEX_FILETRANS_SVCLASS_ID       },
> +       { "gnss",       GNSS_SERVER_SVCLASS_ID          },
>         { "hfp",        HANDSFREE_SVCLASS_ID            },
> -       { "hfp-hf",     HANDSFREE_SVCLASS_ID            },
>         { "hfp-ag",     HANDSFREE_AGW_SVCLASS_ID        },
> -       { "pbap-pce",   PBAP_PCE_SVCLASS_ID             },
> -       { "pbap-pse",   PBAP_PSE_SVCLASS_ID             },
> -       { "map-mse",    MAP_MSE_SVCLASS_ID              },
> +       { "hfp-hf",     HANDSFREE_SVCLASS_ID            },
> +       { "hsp",        HEADSET_SVCLASS_ID              },
> +       { "hsp-ag",     HEADSET_AGW_SVCLASS_ID          },
> +       { "hsp-hs",     HEADSET_SVCLASS_ID              },
>         { "map-mas",    MAP_MSE_SVCLASS_ID              },
>         { "map-mce",    MAP_MCE_SVCLASS_ID              },
>         { "map-mns",    MAP_MCE_SVCLASS_ID              },
> -       { "gnss",       GNSS_SERVER_SVCLASS_ID          },
> +       { "map-mse",    MAP_MSE_SVCLASS_ID              },
> +       { "opp",        OBEX_OBJPUSH_SVCLASS_ID         },
> +       { "pbap",       PBAP_SVCLASS_ID                 },
> +       { "pbap-pce",   PBAP_PCE_SVCLASS_ID             },
> +       { "pbap-pse",   PBAP_PSE_SVCLASS_ID             },
> +       { "sap",        SAP_SVCLASS_ID                  },
> +       { "spp",        SERIAL_PORT_SVCLASS_ID          },
> +       { "synch",      IRMC_SYNC_SVCLASS_ID            },
>         { }

These may need to go in a separate patch.

>  };
>
> --
> 2.39.5
>


-- 
Luiz Augusto von Dentz





[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux