mirror of
https://git.kernel.org/pub/scm/bluetooth/bluez.git
synced 2024-11-15 08:14:28 +08:00
client/player: Add transport menu
This adds transport menu: [bluetooth]# menu transport Menu transport: Available commands: ------------------- list List available transports show <transport> Transport information acquire <transport> Acquire Transport release <transport> Release Transport send <filename> Send contents of a file
This commit is contained in:
parent
46f171a86c
commit
777bc7c3f5
385
client/player.c
385
client/player.c
@ -50,6 +50,7 @@
|
||||
#define BLUEZ_MEDIA_FOLDER_INTERFACE "org.bluez.MediaFolder1"
|
||||
#define BLUEZ_MEDIA_ITEM_INTERFACE "org.bluez.MediaItem1"
|
||||
#define BLUEZ_MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint1"
|
||||
#define BLUEZ_MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport1"
|
||||
|
||||
#define BLUEZ_MEDIA_ENDPOINT_PATH "/local/endpoint"
|
||||
|
||||
@ -75,6 +76,16 @@ static GList *folders = NULL;
|
||||
static GList *items = NULL;
|
||||
static GList *endpoints = NULL;
|
||||
static GList *local_endpoints = NULL;
|
||||
static GList *transports = NULL;
|
||||
|
||||
struct transport {
|
||||
int sk;
|
||||
int mtu[2];
|
||||
struct io *io;
|
||||
uint32_t seq;
|
||||
} transport = {
|
||||
.sk = -1,
|
||||
};
|
||||
|
||||
static void endpoint_unregister(void *data)
|
||||
{
|
||||
@ -1639,8 +1650,9 @@ static char *uuid_generator(const char *text, int state)
|
||||
static int index = 0;
|
||||
size_t i;
|
||||
|
||||
if (!state)
|
||||
if (!state) {
|
||||
index = 0;
|
||||
}
|
||||
|
||||
for (i = index; i < ARRAY_SIZE(caps); i++) {
|
||||
const struct capabilities *cap = &caps[i];
|
||||
@ -2144,6 +2156,26 @@ static void endpoint_added(GDBusProxy *proxy)
|
||||
print_endpoint(proxy, COLORED_NEW);
|
||||
}
|
||||
|
||||
static void print_transport(void *data, void *user_data)
|
||||
{
|
||||
GDBusProxy *proxy = data;
|
||||
const char *description = user_data;
|
||||
char *str;
|
||||
|
||||
str = proxy_description(proxy, "Transport", description);
|
||||
|
||||
bt_shell_printf("%s\n", str);
|
||||
|
||||
g_free(str);
|
||||
}
|
||||
|
||||
static void transport_added(GDBusProxy *proxy)
|
||||
{
|
||||
transports = g_list_append(transports, proxy);
|
||||
|
||||
print_transport(proxy, COLORED_NEW);
|
||||
}
|
||||
|
||||
static void proxy_added(GDBusProxy *proxy, void *user_data)
|
||||
{
|
||||
const char *interface;
|
||||
@ -2160,6 +2192,8 @@ static void proxy_added(GDBusProxy *proxy, void *user_data)
|
||||
item_added(proxy);
|
||||
else if (!strcmp(interface, BLUEZ_MEDIA_ENDPOINT_INTERFACE))
|
||||
endpoint_added(proxy);
|
||||
else if (!strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE))
|
||||
transport_added(proxy);
|
||||
}
|
||||
|
||||
static void media_removed(GDBusProxy *proxy)
|
||||
@ -2200,6 +2234,13 @@ static void endpoint_removed(GDBusProxy *proxy)
|
||||
print_endpoint(proxy, COLORED_DEL);
|
||||
}
|
||||
|
||||
static void transport_removed(GDBusProxy *proxy)
|
||||
{
|
||||
transports = g_list_remove(transports, proxy);
|
||||
|
||||
print_transport(proxy, COLORED_DEL);
|
||||
}
|
||||
|
||||
static void proxy_removed(GDBusProxy *proxy, void *user_data)
|
||||
{
|
||||
const char *interface;
|
||||
@ -2216,6 +2257,8 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
|
||||
item_removed(proxy);
|
||||
if (!strcmp(interface, BLUEZ_MEDIA_ENDPOINT_INTERFACE))
|
||||
endpoint_removed(proxy);
|
||||
if (!strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE))
|
||||
transport_removed(proxy);
|
||||
}
|
||||
|
||||
static void player_property_changed(GDBusProxy *proxy, const char *name,
|
||||
@ -2258,6 +2301,134 @@ static void endpoint_property_changed(GDBusProxy *proxy, const char *name,
|
||||
g_free(str);
|
||||
}
|
||||
|
||||
static struct endpoint *find_ep_by_transport(const char *path)
|
||||
{
|
||||
GList *l;
|
||||
|
||||
for (l = local_endpoints; l; l = g_list_next(l)) {
|
||||
struct endpoint *ep = l->data;
|
||||
|
||||
if (ep->transport && !strcmp(ep->transport, path))
|
||||
return ep;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool transport_disconnected(struct io *io, void *user_data)
|
||||
{
|
||||
bt_shell_printf("Transport fd disconnected\n");
|
||||
|
||||
io_destroy(transport.io);
|
||||
transport.io = NULL;
|
||||
transport.sk = -1;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool transport_recv(struct io *io, void *user_data)
|
||||
{
|
||||
uint8_t buf[1024];
|
||||
int ret;
|
||||
|
||||
ret = read(io_get_fd(io), buf, sizeof(buf));
|
||||
if (ret < 0) {
|
||||
bt_shell_printf("Failed to read: %s (%d)\n", strerror(errno),
|
||||
-errno);
|
||||
return true;
|
||||
}
|
||||
|
||||
bt_shell_printf("[seq %d] recv: %u bytes\n", transport.seq, ret);
|
||||
|
||||
transport.seq++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void acquire_reply(DBusMessage *message, void *user_data)
|
||||
{
|
||||
DBusError error;
|
||||
|
||||
dbus_error_init(&error);
|
||||
|
||||
if (dbus_set_error_from_message(&error, message) == TRUE) {
|
||||
bt_shell_printf("Failed to acquire: %s\n", error.name);
|
||||
dbus_error_free(&error);
|
||||
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!dbus_message_get_args(message, &error,
|
||||
DBUS_TYPE_UNIX_FD, &transport.sk,
|
||||
DBUS_TYPE_UINT16, &transport.mtu[0],
|
||||
DBUS_TYPE_UINT16, &transport.mtu[1],
|
||||
DBUS_TYPE_INVALID)) {
|
||||
bt_shell_printf("Failed to parse Acquire() reply: %s",
|
||||
error.name);
|
||||
dbus_error_free(&error);
|
||||
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
bt_shell_printf("Acquire successful: fd %d MTU %d:%d\n", transport.sk,
|
||||
transport.mtu[0], transport.mtu[1]);
|
||||
|
||||
io_destroy(transport.io);
|
||||
transport.io = io_new(transport.sk);
|
||||
|
||||
io_set_disconnect_handler(transport.io, transport_disconnected, NULL,
|
||||
NULL);
|
||||
io_set_read_handler(transport.io, transport_recv, NULL, NULL);
|
||||
|
||||
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void transport_acquire(const char *input, void *user_data)
|
||||
{
|
||||
GDBusProxy *proxy = user_data;
|
||||
|
||||
if (!strcasecmp(input, "y") || !strcasecmp(input, "yes")) {
|
||||
if (!g_dbus_proxy_method_call(proxy, "Acquire", NULL,
|
||||
acquire_reply, NULL, NULL))
|
||||
bt_shell_printf("Failed acquire transport\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void transport_property_changed(GDBusProxy *proxy, const char *name,
|
||||
DBusMessageIter *iter)
|
||||
{
|
||||
char *str;
|
||||
struct endpoint *ep;
|
||||
|
||||
str = proxy_description(proxy, "Transport", COLORED_CHG);
|
||||
print_iter(str, name, iter);
|
||||
g_free(str);
|
||||
|
||||
if (strcmp(name, "State"))
|
||||
return;
|
||||
|
||||
dbus_message_iter_get_basic(iter, &str);
|
||||
|
||||
if (strcmp(str, "pending"))
|
||||
return;
|
||||
|
||||
/* Only attempt to acquire if transport is configured with a local
|
||||
* endpoint.
|
||||
*/
|
||||
ep = find_ep_by_transport(g_dbus_proxy_get_path(proxy));
|
||||
if (!ep)
|
||||
return;
|
||||
|
||||
if (ep->auto_accept) {
|
||||
bt_shell_printf("Auto Accepting...\n");
|
||||
if (!g_dbus_proxy_method_call(proxy, "Acquire", NULL,
|
||||
acquire_reply, NULL, NULL))
|
||||
bt_shell_printf("Failed acquire transport\n");
|
||||
return;
|
||||
}
|
||||
|
||||
bt_shell_prompt_input(g_dbus_proxy_get_path(proxy), "Acquire (yes/no):",
|
||||
transport_acquire, proxy);
|
||||
}
|
||||
|
||||
static void property_changed(GDBusProxy *proxy, const char *name,
|
||||
DBusMessageIter *iter, void *user_data)
|
||||
{
|
||||
@ -2273,14 +2444,226 @@ static void property_changed(GDBusProxy *proxy, const char *name,
|
||||
item_property_changed(proxy, name, iter);
|
||||
else if (!strcmp(interface, BLUEZ_MEDIA_ENDPOINT_INTERFACE))
|
||||
endpoint_property_changed(proxy, name, iter);
|
||||
else if (!strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE))
|
||||
transport_property_changed(proxy, name, iter);
|
||||
}
|
||||
|
||||
static char *transport_generator(const char *text, int state)
|
||||
{
|
||||
return generic_generator(text, state, transports);
|
||||
}
|
||||
|
||||
static void cmd_list_transport(int argc, char *argv[])
|
||||
{
|
||||
GList *l;
|
||||
|
||||
for (l = transports; l; l = g_list_next(l)) {
|
||||
GDBusProxy *proxy = l->data;
|
||||
print_transport(proxy, NULL);
|
||||
}
|
||||
|
||||
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static void cmd_show_transport(int argc, char *argv[])
|
||||
{
|
||||
GDBusProxy *proxy;
|
||||
|
||||
proxy = g_dbus_proxy_lookup(transports, NULL, argv[1],
|
||||
BLUEZ_MEDIA_TRANSPORT_INTERFACE);
|
||||
if (!proxy) {
|
||||
bt_shell_printf("Transport %s not found\n", argv[1]);
|
||||
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
bt_shell_printf("Transport %s\n", g_dbus_proxy_get_path(proxy));
|
||||
|
||||
print_property(proxy, "UUID");
|
||||
print_property(proxy, "Codec");
|
||||
print_property(proxy, "Configuration");
|
||||
print_property(proxy, "Device");
|
||||
print_property(proxy, "State");
|
||||
print_property(proxy, "Delay");
|
||||
print_property(proxy, "Volume");
|
||||
print_property(proxy, "Endpoint");
|
||||
|
||||
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static void cmd_acquire_transport(int argc, char *argv[])
|
||||
{
|
||||
GDBusProxy *proxy;
|
||||
|
||||
if (transport.sk >= 0) {
|
||||
bt_shell_printf("Transport socked %d already acquired\n",
|
||||
transport.sk);
|
||||
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
proxy = g_dbus_proxy_lookup(transports, NULL, argv[1],
|
||||
BLUEZ_MEDIA_TRANSPORT_INTERFACE);
|
||||
if (!proxy) {
|
||||
bt_shell_printf("Transport %s not found\n", argv[1]);
|
||||
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!g_dbus_proxy_method_call(proxy, "Acquire", NULL,
|
||||
acquire_reply, NULL, NULL)) {
|
||||
bt_shell_printf("Failed acquire transport\n");
|
||||
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static void release_reply(DBusMessage *message, void *user_data)
|
||||
{
|
||||
DBusError error;
|
||||
|
||||
dbus_error_init(&error);
|
||||
|
||||
if (dbus_set_error_from_message(&error, message) == TRUE) {
|
||||
bt_shell_printf("Failed to release: %s\n", error.name);
|
||||
dbus_error_free(&error);
|
||||
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
close(transport.sk);
|
||||
transport.sk = -1;
|
||||
|
||||
bt_shell_printf("Release successful\n");
|
||||
|
||||
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void cmd_release_transport(int argc, char *argv[])
|
||||
{
|
||||
GDBusProxy *proxy;
|
||||
|
||||
if (transport.sk < 0) {
|
||||
bt_shell_printf("No Transport Socked found\n");
|
||||
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
proxy = g_dbus_proxy_lookup(transports, NULL, argv[1],
|
||||
BLUEZ_MEDIA_TRANSPORT_INTERFACE);
|
||||
if (!proxy) {
|
||||
bt_shell_printf("Transport %s not found\n", argv[1]);
|
||||
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!g_dbus_proxy_method_call(proxy, "Release", NULL,
|
||||
release_reply, NULL, NULL)) {
|
||||
bt_shell_printf("Failed release transport\n");
|
||||
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static int open_file(const char *filename)
|
||||
{
|
||||
int fd = -1;
|
||||
|
||||
bt_shell_printf("Opening %s ...\n", filename);
|
||||
|
||||
fd = open(filename, O_RDONLY);
|
||||
if (fd <= 0)
|
||||
bt_shell_printf("Can't open file %s: %s\n", filename,
|
||||
strerror(errno));
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int transport_send(int fd)
|
||||
{
|
||||
uint8_t *buf;
|
||||
|
||||
buf = malloc(transport.mtu[1]);
|
||||
if (!buf) {
|
||||
bt_shell_printf("malloc: %s (%d)", strerror(errno), errno);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (transport.seq = 0; ; transport.seq++) {
|
||||
ssize_t ret;
|
||||
int queued;
|
||||
|
||||
ret = read(fd, buf, transport.mtu[1]);
|
||||
if (ret <= 0) {
|
||||
if (ret < 0)
|
||||
bt_shell_printf("read failed: %s (%d)",
|
||||
strerror(errno), errno);
|
||||
close(fd);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = send(transport.sk, buf, ret, 0);
|
||||
if (ret <= 0) {
|
||||
bt_shell_printf("Send failed: %s (%d)",
|
||||
strerror(errno), errno);
|
||||
return -errno;
|
||||
}
|
||||
|
||||
ioctl(transport.sk, TIOCOUTQ, &queued);
|
||||
|
||||
bt_shell_printf("[seq %d] send: %zd bytes "
|
||||
"(TIOCOUTQ %d bytes)\n",
|
||||
transport.seq, ret, queued);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static void cmd_send_transport(int argc, char *argv[])
|
||||
{
|
||||
int fd, err;
|
||||
|
||||
if (transport.sk < 0) {
|
||||
bt_shell_printf("No Transport Socked found\n");
|
||||
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
fd = open_file(argv[1]);
|
||||
|
||||
bt_shell_printf("Sending ...\n");
|
||||
err = transport_send(fd);
|
||||
|
||||
close(fd);
|
||||
|
||||
if (err < 0)
|
||||
return bt_shell_noninteractive_quit(EXIT_FAILURE);
|
||||
|
||||
return bt_shell_noninteractive_quit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
static const struct bt_shell_menu transport_menu = {
|
||||
.name = "transport",
|
||||
.desc = "Media Transport Submenu",
|
||||
.entries = {
|
||||
{ "list", NULL, cmd_list_transport,
|
||||
"List available transports" },
|
||||
{ "show", "<transport>", cmd_show_transport,
|
||||
"Transport information",
|
||||
transport_generator },
|
||||
{ "acquire", "<transport>", cmd_acquire_transport,
|
||||
"Acquire Transport",
|
||||
transport_generator },
|
||||
{ "release", "<transport>", cmd_release_transport,
|
||||
"Release Transport",
|
||||
transport_generator },
|
||||
{ "send", "<filename>", cmd_send_transport,
|
||||
"Send contents of a file" },
|
||||
{} },
|
||||
};
|
||||
|
||||
static GDBusClient *client;
|
||||
|
||||
void player_add_submenu(void)
|
||||
{
|
||||
bt_shell_add_submenu(&player_menu);
|
||||
bt_shell_add_submenu(&endpoint_menu);
|
||||
bt_shell_add_submenu(&transport_menu);
|
||||
|
||||
dbus_conn = bt_shell_get_env("DBUS_CONNECTION");
|
||||
if (!dbus_conn || client)
|
||||
|
Loading…
Reference in New Issue
Block a user