diff --git a/src/ctl/ctl.h b/src/ctl/ctl.h
index 15ed24c..668c451 100644
--- a/src/ctl/ctl.h
+++ b/src/ctl/ctl.h
@@ -137,10 +137,31 @@ bool ctl_sink_is_connected(struct ctl_sink *s);
bool ctl_sink_is_closed(struct ctl_sink *s);
/* wfd session */
+#define wfd_session(s) (assert(s), (struct wfd_session *) (s))
+#define wfd_is_session(s) ( \
+ (s) && \
+ (WFD_SESSION_DIR_OUT == wfd_session(s)->dir || \
+ WFD_SESSION_DIR_IN == wfd_session(s)->dir) \
+)
+#define wfd_is_out_session(s) (WFD_SESSION_DIR_OUT == wfd_session(s)->dir)
+#define wfd_is_in_session(s) (WFD_SESSION_DIR_IN == wfd_session(s)->dir)
#define wfd_session_to_htable(s) (&(s)->id)
#define wfd_session_from_htable(s) (shl_htable_entry(s, struct wfd_session, id))
+#define _wfd_session_free_ _shl_cleanup_(wfd_session_freep)
struct wfd_sink;
+struct wfd_out_session;
+
+enum wfd_session_state
+{
+ WFD_SESSION_STATE_NULL,
+ WFD_SESSION_STATE_CONNECTING,
+ WFD_SESSION_STATE_CAPS_EXCHAING,
+ WFD_SESSION_STATE_SETING_UP,
+ WFD_SESSION_STATE_PLAYING,
+ WFD_SESSION_STATE_PAUSED,
+ WFD_SESSION_STATE_TEARING_DOWN,
+};
enum wfd_session_dir
{
@@ -151,22 +172,29 @@ enum wfd_session_dir
struct wfd_session
{
enum wfd_session_dir dir;
+ enum wfd_session_state state;
uint64_t id;
char *url;
- int fd;
struct rtsp *rtsp;
- bool connected : 1;
- bool hup : 1;
+ bool hup: 1;
};
int wfd_out_session_new(struct wfd_session **out, struct wfd_sink *sink);
-int wfd_session_start(struct wfd_session *s);
+int wfd_session_start(struct wfd_session *s, uint64_t id);
+int wfd_session_is_started(struct wfd_session *s);
+void wfd_session_end(struct wfd_session *s);
void wfd_session_free(struct wfd_session *s);
uint64_t wfd_session_get_id(struct wfd_session *s);
-void wfd_session_set_id(struct wfd_session *s, uint64_t id);
+struct wfd_sink * wfd_out_session_get_sink(struct wfd_session *s);
+static inline void wfd_session_freep(struct wfd_session **s)
+{
+ wfd_session_free(*s);
+ *s = NULL;
+}
/* wfd sink */
+#define _wfd_sink_free_ _shl_cleanup_(wfd_sink_freep)
#define wfd_sink_to_htable(s) (&(s)->label)
#define wfd_sink_from_htable(s) shl_htable_entry(s, struct wfd_sink, label)
@@ -188,11 +216,23 @@ const char * wfd_sink_get_label(struct wfd_sink *sink);
const union wfd_sube * wfd_sink_get_dev_info(struct wfd_sink *sink);
int wfd_sink_start_session(struct wfd_sink *sink,
struct wfd_session **session);
+void wfd_sink_handle_session_ended(struct wfd_sink *sink);
bool wfd_sink_is_session_started(struct wfd_sink *sink);
+static inline void wfd_sink_freep(struct wfd_sink **s)
+{
+ wfd_sink_free(*s);
+ *s = NULL;
+}
/* wfd handling */
#define ctl_wfd_foreach_sink(_i, _w) \
- SHL_HTABLE_FOREACH_MACRO(_i, &(_w)->sinks, wfd_sink_from_htable)
+ SHL_HTABLE_FOREACH_MACRO(_i, \
+ &(_w)->sinks, \
+ wfd_sink_from_htable)
+#define ctl_wfd_foreach_session(_i, _w) \
+ SHL_HTABLE_FOREACH_MACRO(_i, \
+ &(_w)->sessions, \
+ wfd_session_from_htable)
struct ctl_wfd
{
@@ -206,18 +246,21 @@ struct ctl_wfd
};
struct ctl_wfd * ctl_wfd_get();
-int ctl_wfd_new(struct ctl_wfd **out, sd_event *loop, sd_bus *bus);
-void ctl_wfd_free(struct ctl_wfd *wfd);
int ctl_wfd_find_sink_by_label(struct ctl_wfd *wfd,
const char *label,
struct wfd_sink **out);
int ctl_wfd_add_session(struct ctl_wfd *wfd, struct wfd_session *s);
int ctl_wfd_find_session_by_id(struct ctl_wfd *wfd,
- unsigned int id,
+ uint64_t id,
struct wfd_session **out);
int ctl_wfd_remove_session_by_id(struct ctl_wfd *wfd,
uint64_t id,
struct wfd_session **out);
+uint64_t ctl_wfd_alloc_session_id(struct ctl_wfd *wfd);
+static inline struct sd_event * ctl_wfd_get_loop()
+{
+ return ctl_wfd_get()->loop;
+}
/* CLI handling */
@@ -351,5 +394,12 @@ void ctl_fn_sink_resolution_set(struct ctl_sink *s);
void cli_fn_help(void);
+int wfd_fn_sink_new(struct wfd_sink *s);
+int wfd_fn_sink_free(struct wfd_sink *s);
+
+int wfd_fn_session_new(struct wfd_session *s);
+int wfd_fn_session_free(struct wfd_session *s);
+int wfd_fn_out_session_ended(struct wfd_session *s);
+
#endif /* CTL_CTL_H */
diff --git a/src/ctl/wfd-dbus.c b/src/ctl/wfd-dbus.c
index 1c46d2e..e49a876 100644
--- a/src/ctl/wfd-dbus.c
+++ b/src/ctl/wfd-dbus.c
@@ -24,6 +24,21 @@
#include "util.h"
#include "wfd-dbus.h"
+#define wfd_dbus_object_added(o, argv...) ({ \
+ const char *ifaces[] = { argv }; \
+ _wfd_dbus_object_added(wfd_dbus_get(), \
+ (o), \
+ ifaces, \
+ SHL_ARRAY_LENGTH(ifaces)); \
+})
+#define wfd_dbus_object_removed(o, argv...) ({ \
+ const char *ifaces[] = { argv }; \
+ _wfd_dbus_object_removed(wfd_dbus_get(), \
+ (o), \
+ ifaces, \
+ SHL_ARRAY_LENGTH(ifaces)); \
+})
+
struct wfd_dbus
{
sd_bus *bus;
@@ -43,6 +58,7 @@ int wfd_dbus_new(struct wfd_dbus **out, sd_event *loop, sd_bus *bus)
wfd_dbus->loop = sd_event_ref(loop);
*out = wfd_dbus;
+
return 0;
}
@@ -95,9 +111,9 @@ static int wfd_dbus_enum(sd_bus *bus,
{
int r = 0, i = 0;
char **nodes, *node;
- struct wfd_dbus *wfd_dbus = userdata;
- struct ctl_wfd *wfd = ctl_wfd_get();
struct wfd_sink *sink;
+ struct wfd_session *session;
+ struct ctl_wfd *wfd = ctl_wfd_get();
if(strcmp("/org/freedesktop/miracle/wfd", path)) {
return 0;
@@ -107,7 +123,7 @@ static int wfd_dbus_enum(sd_bus *bus,
return 0;
}
- nodes = malloc((wfd->n_sinks + 1) * sizeof(char *));
+ nodes = malloc((wfd->n_sinks + wfd->n_sessions + 1) * sizeof(char *));
if(!nodes) {
return -ENOMEM;
}
@@ -120,6 +136,14 @@ static int wfd_dbus_enum(sd_bus *bus,
nodes[i ++] = node;
}
+ ctl_wfd_foreach_session(session, wfd) {
+ r = wfd_dbus_get_session_path(session, &node);
+ if(0 > r) {
+ goto free_nodes;
+ }
+ nodes[i ++] = node;
+ }
+
nodes[i ++] = NULL;
*out = nodes;
@@ -133,20 +157,69 @@ free_nodes:
return r;
}
-int wfd_dbus_notify_new_sink(struct wfd_dbus *wfd_dbus, const char *p2p_mac)
+int _wfd_dbus_object_removed(struct wfd_dbus *wfd_dbus,
+ const char *path,
+ const char **ifaces,
+ size_t n_ifaces)
{
- _shl_free_ char *path;
+ int i, r;
_sd_bus_message_unref_ sd_bus_message *m;
- int r = sd_bus_message_new_signal(wfd_dbus->bus,
+
+ if(!wfd_dbus) {
+ return -ECANCELED;
+ }
+
+ r = sd_bus_message_new_signal(wfd_dbus->bus,
&m,
"/org/freedesktop/miracle/wfd",
"org.freedesktop.DBus.ObjectManager",
- "InterfaceAdded");
+ "InterfaceRemoved");
if(0 > r) {
return r;
}
- r = sd_bus_path_encode("/org/freedesktop/miracle/wfd/sink", p2p_mac, &path);
+ r = sd_bus_message_append(m, "o", path);
+ if(0 > r) {
+ return r;
+ }
+
+ r = sd_bus_message_open_container(m, 'a', "s");
+ if(0 > r) {
+ return r;
+ }
+
+ for(i = 0; i < n_ifaces; i ++) {
+ r = sd_bus_message_append(m, "s", ifaces[i]);
+ if(0 > r) {
+ return r;
+ }
+ }
+
+ r = sd_bus_message_close_container(m);
+ if(0 > r) {
+ return r;
+ }
+
+ return sd_bus_send(wfd_dbus->bus, m, NULL);
+}
+
+int _wfd_dbus_object_added(struct wfd_dbus *wfd_dbus,
+ const char *path,
+ const char **ifaces,
+ size_t n_ifaces)
+{
+ int i, r;
+ _sd_bus_message_unref_ sd_bus_message *m;
+
+ if(!wfd_dbus) {
+ return -ECANCELED;
+ }
+
+ r = sd_bus_message_new_signal(wfd_dbus->bus,
+ &m,
+ "/org/freedesktop/miracle/wfd",
+ "org.freedesktop.DBus.ObjectManager",
+ "InterfaceAdded");
if(0 > r) {
return r;
}
@@ -161,30 +234,66 @@ int wfd_dbus_notify_new_sink(struct wfd_dbus *wfd_dbus, const char *p2p_mac)
return r;
}
- r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.miracle.wfd.Sink", 0);
+ for(i = 0; i < n_ifaces; i ++) {
+ r = sd_bus_message_append(m, "{sa{sv}}", ifaces[i], 0);
+ if(0 > r) {
+ return r;
+ }
+ }
+
+ r = sd_bus_message_close_container(m);
if(0 > r) {
return r;
}
- r = sd_bus_message_open_container(m, 'e', "sa{sv}");
+ return sd_bus_send(wfd_dbus->bus, m, NULL);
+}
+
+int wfd_fn_sink_new(struct wfd_sink *s)
+{
+ _shl_free_ char *path;
+ int r = sd_bus_path_encode("/org/freedesktop/miracle/wfd/sink",
+ wfd_sink_get_label(s),
+ &path);
if(0 > r) {
return r;
}
- /*r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Peer", 0);*/
- /*if (r < 0)*/
- /*return r;*/
- /*r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Introspectable", 0);*/
- /*if (r < 0)*/
- /*return r;*/
- /*r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.Properties", 0);*/
- /*if (r < 0)*/
- /*return r;*/
- /*r = sd_bus_message_append(m, "{sa{sv}}", "org.freedesktop.DBus.ObjectManager", 0);*/
- /*if (r < 0)*/
- /*return r;*/
+ return wfd_dbus_object_added(path, "org.freedesktop.miracle.wfd.Sink");
+}
- return 0;
+int wfd_fn_sink_free(struct wfd_sink *s)
+{
+ _shl_free_ char *path;
+ int r = sd_bus_path_encode("/org/freedesktop/miracle/wfd/sink",
+ wfd_sink_get_label(s),
+ &path);
+ if(0 > r) {
+ return r;
+ }
+
+ return wfd_dbus_object_added(path, "org.freedesktop.miracle.wfd.Sink");
+}
+
+int _wfd_fn_sink_properties_changed(struct wfd_sink *s, char **names)
+{
+ _shl_free_ char *path;
+ int r;
+ struct wfd_dbus *wfd_dbus = wfd_dbus_get();
+
+ if(!wfd_dbus) {
+ return -ECANCELED;
+ }
+
+ r = wfd_dbus_get_sink_path(s, &path);
+ if(0 > r) {
+ return r;
+ }
+
+ return sd_bus_emit_properties_changed_strv(wfd_dbus->bus,
+ path,
+ "org.freedesktop.miracle.wfd.Sink",
+ names);
}
static int wfd_dbus_find_sink(sd_bus *bus,
@@ -195,7 +304,6 @@ static int wfd_dbus_find_sink(sd_bus *bus,
sd_bus_error *ret_error)
{
_shl_free_ char *node = NULL;
- struct wfd_dbus *wfd_dbus = userdata;
struct wfd_sink *sink;
int r = sd_bus_path_decode(path,
"/org/freedesktop/miracle/wfd/sink",
@@ -212,6 +320,29 @@ static int wfd_dbus_find_sink(sd_bus *bus,
return r;
}
+int wfd_fn_session_new(struct wfd_session *s)
+{
+ _shl_free_ char *path;
+ int r = wfd_dbus_get_session_path(s, &path);
+ if(0 > r) {
+ return r;
+ }
+
+ return wfd_dbus_object_added(path, "org.freedesktop.miracle.wfd.Session");
+}
+
+int wfd_fn_session_free(struct wfd_session *s)
+{
+ _shl_free_ char *path;
+ int r = wfd_dbus_get_session_path(s, &path);
+ if(0 > r) {
+ return r;
+ }
+
+ return wfd_dbus_object_removed(path,
+ "org.freedesktop.miracle.wfd.Session");
+}
+
static int wfd_dbus_find_session(sd_bus *bus,
const char *path,
const char *interface,
@@ -219,7 +350,23 @@ static int wfd_dbus_find_session(sd_bus *bus,
void **ret_found,
sd_bus_error *ret_error)
{
- return 0;
+ struct wfd_session *s;
+ _shl_free_ char *node = NULL;
+ int r = sd_bus_path_decode(path,
+ "/org/freedesktop/miracle/wfd/session",
+ &node);
+ if(0 > r) {
+ return r;
+ }
+
+ r = ctl_wfd_find_session_by_id(ctl_wfd_get(),
+ strtoull(node, NULL, 10),
+ &s);
+ if(r) {
+ *ret_found = s;
+ }
+
+ return r;
}
static int wfd_dbus_sink_start_session(sd_bus_message *m,
@@ -227,7 +374,7 @@ static int wfd_dbus_sink_start_session(sd_bus_message *m,
sd_bus_error *ret_error)
{
struct wfd_sink *sink = userdata;
- struct wfd_session *session;
+ _wfd_session_free_ struct wfd_session *session = NULL;
_shl_free_ char *path = NULL;
int r = wfd_sink_start_session(sink, &session);
if(0 > r) {
@@ -236,52 +383,46 @@ static int wfd_dbus_sink_start_session(sd_bus_message *m,
r = wfd_dbus_get_session_path(session, &path);
if(0 > r) {
- goto free_session;
- }
-
- log_debug("path=%s", path);
- r = sd_bus_reply_method_return(m, "o", path);
- if(0 <= r) {
return r;
}
-free_session:
- wfd_session_free(session);
- return r;
+ session = NULL;
+
+ return sd_bus_reply_method_return(m, "o", path);
}
-static int wfd_dbus_sink_get_audio_formats(sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *ret_error)
-{
- return 0;
-}
-
-static int wfd_dbus_sink_get_video_formats(sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *ret_error)
-{
- return 0;
-}
-
-static int wfd_dbus_sink_has_video(sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *ret_error)
-{
- return 0;
-}
+//static int wfd_dbus_sink_get_audio_formats(sd_bus *bus,
+// const char *path,
+// const char *interface,
+// const char *property,
+// sd_bus_message *reply,
+// void *userdata,
+// sd_bus_error *ret_error)
+//{
+// return 0;
+//}
+//
+//static int wfd_dbus_sink_get_video_formats(sd_bus *bus,
+// const char *path,
+// const char *interface,
+// const char *property,
+// sd_bus_message *reply,
+// void *userdata,
+// sd_bus_error *ret_error)
+//{
+// return 0;
+//}
+//
+//static int wfd_dbus_sink_has_video(sd_bus *bus,
+// const char *path,
+// const char *interface,
+// const char *property,
+// sd_bus_message *reply,
+// void *userdata,
+// sd_bus_error *ret_error)
+//{
+// return 0;
+//}
static int wfd_dbus_sink_get_peer(sd_bus *bus,
const char *path,
@@ -291,25 +432,36 @@ static int wfd_dbus_sink_get_peer(sd_bus *bus,
void *userdata,
sd_bus_error *ret_error)
{
- return 0;
+ struct wfd_sink *s = userdata;
+ _shl_free_ char *peer_path;
+ int r = sd_bus_path_encode("/org/freedesktop/miracle/wifi/peer",
+ s->label,
+ &peer_path);
+ if(0 > r) {
+ return r;
+ }
+
+ return sd_bus_message_append(reply, "o", peer_path);
}
-static int wfd_dbus_sink_has_audio(sd_bus *bus,
- const char *path,
- const char *interface,
- const char *property,
- sd_bus_message *reply,
- void *userdata,
- sd_bus_error *ret_error)
-{
- return 0;
-}
+//static int wfd_dbus_sink_has_audio(sd_bus *bus,
+// const char *path,
+// const char *interface,
+// const char *property,
+// sd_bus_message *reply,
+// void *userdata,
+// sd_bus_error *ret_error)
+//{
+// return 0;
+//}
static int wfd_dbus_session_end(sd_bus_message *m,
void *userdata,
sd_bus_error *ret_error)
{
- return 0;
+ wfd_session_end(userdata);
+
+ return sd_bus_reply_method_return(m, NULL);
}
static int wfd_dbus_session_get_sink(sd_bus *bus,
@@ -320,7 +472,33 @@ static int wfd_dbus_session_get_sink(sd_bus *bus,
void *userdata,
sd_bus_error *ret_error)
{
- return 0;
+ struct wfd_session *s = userdata;
+ _shl_free_ char *sink_path;
+ int r;
+
+ if(s->dir != WFD_SESSION_DIR_OUT) {
+ return 0;
+ }
+
+ r = wfd_dbus_get_sink_path(wfd_out_session_get_sink(s),
+ &sink_path);
+ if(0 > r) {
+ return r;
+ }
+
+ return sd_bus_message_append(reply, "o", sink_path);
+}
+
+static int wfd_dbus_session_get_url(sd_bus *bus,
+ const char *path,
+ const char *interface,
+ const char *property,
+ sd_bus_message *reply,
+ void *userdata,
+ sd_bus_error *ret_error)
+{
+ struct wfd_session *s = userdata;
+ return sd_bus_message_append(reply, "s", s->url);
}
static int wfd_dbus_session_get_state(sd_bus *bus,
@@ -331,7 +509,29 @@ static int wfd_dbus_session_get_state(sd_bus *bus,
void *userdata,
sd_bus_error *ret_error)
{
- return 0;
+ struct wfd_session *s = userdata;
+ return sd_bus_message_append(reply, "i", s->state);
+}
+
+int _wfd_fn_session_properties_changed(struct wfd_session *s, char **names)
+{
+ _shl_free_ char *path;
+ int r;
+ struct wfd_dbus *wfd_dbus = wfd_dbus_get();
+
+ if(!wfd_dbus) {
+ return -ECANCELED;
+ }
+
+ r = wfd_dbus_get_session_path(s, &path);
+ if(0 > r) {
+ return r;
+ }
+
+ return sd_bus_emit_properties_changed_strv(wfd_dbus_get()->bus,
+ path,
+ "org.freedesktop.miracle.wfd.Session",
+ names);
}
static const sd_bus_vtable wfd_dbus_vtable[] = {
@@ -346,7 +546,7 @@ static const sd_bus_vtable wfd_dbus_sink_vtable[] = {
/*SD_BUS_PROPERTY("VideoFormats", "a{sv}", wfd_dbus_sink_get_video_formats, 0, SD_BUS_VTABLE_PROPERTY_CONST),*/
/*SD_BUS_PROPERTY("HasAudio", "b", wfd_dbus_sink_has_audio, 0, SD_BUS_VTABLE_PROPERTY_CONST),*/
/*SD_BUS_PROPERTY("HasVideo", "b", wfd_dbus_sink_has_video, 0, SD_BUS_VTABLE_PROPERTY_CONST),*/
- /*SD_BUS_PROPERTY("Peer", "o", wfd_dbus_sink_get_peer, 0, SD_BUS_VTABLE_PROPERTY_CONST),*/
+ SD_BUS_PROPERTY("Peer", "o", wfd_dbus_sink_get_peer, 0, SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_VTABLE_END,
};
@@ -354,6 +554,7 @@ static const sd_bus_vtable wfd_dbus_session_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_METHOD("End", NULL, NULL, wfd_dbus_session_end, 0),
SD_BUS_PROPERTY("Sink", "o", wfd_dbus_session_get_sink, 0, SD_BUS_VTABLE_PROPERTY_CONST),
+ SD_BUS_PROPERTY("Url", "o", wfd_dbus_session_get_url, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_PROPERTY("State", "i", wfd_dbus_session_get_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
SD_BUS_VTABLE_END,
};
@@ -367,7 +568,7 @@ int wfd_dbus_expose(struct wfd_dbus *wfd_dbus)
wfd_dbus_vtable,
wfd_dbus);
if(0 > r) {
- goto end;
+ return r;
}
r = sd_bus_add_fallback_vtable(wfd_dbus->bus,
@@ -378,19 +579,19 @@ int wfd_dbus_expose(struct wfd_dbus *wfd_dbus)
wfd_dbus_find_sink,
wfd_dbus);
if(0 > r) {
- goto end;
+ return r;
}
- /*r = sd_bus_add_fallback_vtable(wfd_dbus->bus,*/
- /*NULL,*/
- /*"/org/freedesktop/miracle/wfd/session",*/
- /*"org.freedesktop.miracle.wfd.Session",*/
- /*wfd_dbus_session_vtable,*/
- /*wfd_dbus_find_session,*/
- /*wfd_dbus);*/
- /*if(0 > r) {*/
- /*goto end;*/
- /*}*/
+ r = sd_bus_add_fallback_vtable(wfd_dbus->bus,
+ NULL,
+ "/org/freedesktop/miracle/wfd/session",
+ "org.freedesktop.miracle.wfd.Session",
+ wfd_dbus_session_vtable,
+ wfd_dbus_find_session,
+ wfd_dbus);
+ if(0 > r) {
+ return r;
+ }
r = sd_bus_add_node_enumerator(wfd_dbus->bus,
NULL,
@@ -398,12 +599,12 @@ int wfd_dbus_expose(struct wfd_dbus *wfd_dbus)
wfd_dbus_enum,
wfd_dbus);
if(0 > r) {
- goto end;
+ return r;
}
r = sd_bus_add_object_manager(wfd_dbus->bus, NULL, "/org/freedesktop/miracle/wfd");
if(0 > r) {
- goto end;
+ return r;
}
r = sd_bus_request_name(wfd_dbus->bus, "org.freedesktop.miracle.wfd", 0);
@@ -411,7 +612,6 @@ int wfd_dbus_expose(struct wfd_dbus *wfd_dbus)
wfd_dbus->exposed = true;
}
-end:
return r;
}
diff --git a/src/ctl/wfd-dbus.h b/src/ctl/wfd-dbus.h
index 8013283..c15a6c3 100644
--- a/src/ctl/wfd-dbus.h
+++ b/src/ctl/wfd-dbus.h
@@ -22,12 +22,25 @@
#ifndef CTL_WFD_DBUS_H
#define CTL_WFD_DBUS_H
-struct wfd_dbus;
+#define wfd_fn_sink_properties_changed(s, namev...) ({ \
+ char *names[] = { namev, NULL }; \
+ _wfd_fn_sink_properties_changed((s), names); \
+})
+#define wfd_fn_session_properties_changed(s, namev...) ({ \
+ char *names[] = { namev, NULL }; \
+ _wfd_fn_session_properties_changed((s), names); \
+})
+
+struct wfd_dbus;
+struct wfd_session;
+
+struct wfd_dbus * wfd_dbus_get();
int wfd_dbus_new(struct wfd_dbus **out, sd_event *loop, sd_bus *bus);
void wfd_dbus_free(struct wfd_dbus *wfd_dbus);
int wfd_dbus_expose(struct wfd_dbus *wfd_dbus);
-int wfd_dbus_notify_new_sink(struct wfd_dbus *wfd_dbus, const char *p2p_mac);
+int _wfd_fn_sink_properties_changed(struct wfd_sink *s, char **names);
+int _wfd_fn_session_properties_changed(struct wfd_session *s, char **names);
#endif
diff --git a/src/ctl/wfd-session.c b/src/ctl/wfd-session.c
index 71395aa..6885397 100644
--- a/src/ctl/wfd-session.c
+++ b/src/ctl/wfd-session.c
@@ -16,25 +16,20 @@
* You should have received a copy of the GNU Lesser General Public License
* along with MiracleCast; If not, see .
*/
+#define LOG_SUBSYSTEM "wfd-session"
+
#include
#include "ctl.h"
#include "rtsp.h"
+#include "wfd-dbus.h"
-#define wfd_session(s) ((struct wfd_session *) (s))
-#define wfd_is_session(s) (\
- (s) && \
- (WFD_SESSION_DIR_OUT == wfd_session(s)->dir || \
- WFD_SESSION_DIR_IN == wfd_session(s)->dir) \
-)
-#define wfd_out_session(s) ((struct wfd_out_session *) (s))
-#define wfd_is_out_session(s) (WFD_SESSION_DIR_OUT == wfd_session(s)->dir)
-#define wfd_in_session(s) ((struct wfd_in_session *) (s))
-#define wfd_is_in_session(s) (WFD_SESSION_DIR_IN == wfd_session(s)->dir)
+#define wfd_out_session(s) (assert(wfd_is_out_session(s)), (struct wfd_out_session *) (s))
+#define wfd_in_session(s) (assert(wfd_is_in_session(s)), (struct wfd_in_session *) (s))
struct wfd_session_vtable
{
int (*start)(struct wfd_session *s);
- int (*end)(struct wfd_session *s);
+ void (*end)(struct wfd_session *s);
void (*distruct)(struct wfd_session *s);
};
@@ -42,10 +37,18 @@ struct wfd_out_session
{
struct wfd_session parent;
struct wfd_sink *sink;
+ int fd;
+
+ bool sink_has_video: 1;
+ bool sink_has_audio: 1;
+ bool sink_has_pref_disp_mode: 1;
+ bool sink_has_3d: 1;
+ bool sink_has_uibc: 1;
};
+static void wfd_session_set_state(struct wfd_session *s, enum wfd_session_state state);
static int wfd_out_session_start(struct wfd_session *s);
-static int wfd_out_session_end(struct wfd_session *s);
+static void wfd_out_session_end(struct wfd_session *s);
static void wfd_out_session_distruct(struct wfd_session *s);
static const struct wfd_session_vtable session_vtables[] = {
@@ -58,26 +61,25 @@ static const struct wfd_session_vtable session_vtables[] = {
int wfd_out_session_new(struct wfd_session **out, struct wfd_sink *sink)
{
- int r;
struct wfd_out_session *s = calloc(1, sizeof(struct wfd_out_session));
if(!s) {
return -ENOMEM;
}
wfd_session(s)->dir = WFD_SESSION_DIR_OUT;
- wfd_session(s)->fd = -1;
- wfd_session(s)->hup = true;
+ s->fd = -1;
s->sink = sink;
- r = ctl_wfd_add_session(ctl_wfd_get(), wfd_session(s));
- if(0 > r) {
- wfd_session_free(wfd_session(s));
- }
- else {
- *out = wfd_session(s);
- }
+ *out = wfd_session(s);
- return r;
+ return 0;
+}
+
+struct wfd_sink * wfd_out_session_get_sink(struct wfd_session *s)
+{
+ assert(wfd_is_out_session(s));
+
+ return wfd_out_session(s)->sink;
}
uint64_t wfd_session_get_id(struct wfd_session *s)
@@ -85,35 +87,94 @@ uint64_t wfd_session_get_id(struct wfd_session *s)
return s->id;
}
-void wfd_session_set_id(struct wfd_session *s, uint64_t id)
+static void wfd_session_set_state(struct wfd_session *s,
+ enum wfd_session_state state)
{
- assert(id);
+ if(state == s->state) {
+ return;
+ }
- s->id = id;
+ s->state = state;
+
+ wfd_fn_session_properties_changed(s, "State");
}
-int wfd_session_start(struct wfd_session *s)
+int wfd_session_start(struct wfd_session *s, uint64_t id)
+{
+ int r;
+
+ assert(wfd_is_session(s));
+ assert(id);
+
+ if(wfd_session_is_started(s)) {
+ return -EINPROGRESS;
+ }
+
+ r = (*session_vtables[s->dir].start)(s);
+ if(0 <= r) {
+ s->id = id;
+ wfd_session_set_state(s, WFD_SESSION_STATE_CONNECTING);
+ }
+
+ return r;
+}
+
+int wfd_session_is_started(struct wfd_session *s)
{
assert(wfd_is_session(s));
- return (*session_vtables[s->dir].start)(s);
+ return 0 != s->id;
+}
+
+void wfd_session_end(struct wfd_session *s)
+{
+ assert(wfd_is_session(s));
+
+ if(!wfd_session_is_started(s)) {
+ return;
+ }
+
+ log_info("session %lu ended", s->id);
+
+ (*session_vtables[s->dir].end)(s);
+
+ if(s->rtsp) {
+ rtsp_unref(s->rtsp);
+ s->rtsp = NULL;
+ }
+
+ if(s->url) {
+ free(s->url);
+ s->url = NULL;
+ }
+
+ s->hup = false;
+
+ wfd_session_set_state(s, WFD_SESSION_STATE_NULL);
+
+ if(wfd_is_out_session(s)) {
+ wfd_fn_out_session_ended(s);
+ }
}
void wfd_session_free(struct wfd_session *s)
{
+ enum wfd_session_dir dir;
+
if(!wfd_is_session(s)) {
return;
}
- if(s->id) {
- ctl_wfd_remove_session_by_id(ctl_wfd_get(), s->id, NULL);
+ if(wfd_is_out_session(s)) {
+ wfd_out_session(s)->sink = NULL;
}
- if(session_vtables[s->dir].distruct) {
- (*session_vtables[s->dir].distruct)(s);
+ wfd_session_end(s);
+
+ if(session_vtables[dir].distruct) {
+ (*session_vtables[dir].distruct)(s);
}
- free(s->url);
- rtsp_unref(s->rtsp);
+
free(s);
}
@@ -122,6 +183,11 @@ enum wfd_session_dir wfd_session_get_dir(struct wfd_session *s)
return s->dir;
}
+const char * wfd_session_get_url(struct wfd_session *s)
+{
+ return s->url;
+}
+
static int wfd_session_gen_url(struct wfd_session *s, const char *addr)
{
char *url;
@@ -134,172 +200,334 @@ static int wfd_session_gen_url(struct wfd_session *s, const char *addr)
return r;
}
-int wfd_out_session_handle_message(struct rtsp *rtsp,
+static int wfd_out_session_handle_get_parameter_reply(struct rtsp *bus,
struct rtsp_message *m,
void *userdata)
{
+ log_debug("Received GET_PARAMETER reply (M4): %s\n",
+ (char *) rtsp_message_get_raw(m));
+ if(RTSP_CODE_OK == rtsp_message_get_code(m)) {
+ return 0;
+ }
+
+ log_warning("Sink reply GET_PARAMETER (M4) with code: %d",
+ rtsp_message_get_code(m));
+
+ wfd_session_end(wfd_session(userdata));
+
+ return -EPROTO;
+}
+
+static int wfd_out_session_send_get_parameter(sd_event_source *source,
+ void *userdata)
+{
+ struct wfd_session *s = userdata;
+ _rtsp_message_unref_ struct rtsp_message *m = NULL;
+ int r = rtsp_message_new_request(s->rtsp,
+ &m,
+ "GET_PARAMETER",
+ s->url);
+ if (0 > r) {
+ goto error;
+ }
+
+ r = rtsp_message_append(m, "{&}",
+ "wfd_video_formats\n"
+ "wfd_audio_codecs\n"
+ "wfd_client_rtp_ports"
+ //"wfd_uibc_capability"
+ );
+ if (0 > r) {
+ goto error;
+ }
+
+ rtsp_message_seal(m);
+
+ r = rtsp_call_async(s->rtsp,
+ m,
+ wfd_out_session_handle_get_parameter_reply,
+ s,
+ 0,
+ NULL);
+ if (r < 0) {
+ goto error;
+ }
+
+ log_debug("Sending GET_PARAMETER (M3): %s\n",
+ (char *) rtsp_message_get_raw(m));
+
return 0;
+
+error:
+ wfd_session_end(s);
+
+ return r;
}
static int wfd_out_session_handle_options_reply(struct rtsp *bus,
struct rtsp_message *m,
void *userdata)
{
- int r = 0;
- /*struct wfd_src *s = data;*/
- /*_rtsp_message_unref_ struct rtsp_message *req = NULL;*/
+ int r = 0, i;
+ struct wfd_session *s = userdata;
+ char *public, *methods[3] = { NULL };
- /*cli_debug("INCOMING (M1): %s\n", rtsp_message_get_raw(m));*/
+ log_trace("received OPTIONS reply (M1): %s",
+ (char *) rtsp_message_get_raw(m));
- /*if(!rtsp_message_is_reply(m, RTSP_CODE_OK, NULL)) {*/
- /*cli_printf("[" CLI_RED "ERROR" CLI_DEFAULT "] Failed to get OPTIONS from sink\n");*/
- /*goto error;*/
- /*}*/
+ if(!rtsp_message_is_reply(m, RTSP_CODE_OK, NULL)) {
+ r = -EPROTO;
+ goto error;
+ }
- /*r = rtsp_message_new_request(s->rtsp,*/
- /*&req,*/
- /*"GET_PARAMETER",*/
- /*s->url);*/
- /*if (r < 0) {*/
- /*cli_vERR(r);*/
- /*goto error;*/
- /*}*/
+ r = rtsp_message_read(m, "<&>", "Public", &public);
+ if(0 > r) {
+ goto error;
+ }
- /*r = rtsp_message_append(req, "{&}",*/
- /*"wfd_video_formats\n"*/
- /*//"wfd_audio_codecs\n"*/
- /*"wfd_client_rtp_ports\n"*/
- /*//"wfd_uibc_capability"*/
- /*);*/
- /*if (r < 0) {*/
- /*cli_vERR(r);*/
- /*goto error;*/
- /*}*/
+ r = sscanf(public, "%m[^,], %m[^,], %ms", &methods[0], &methods[1], &methods[2]);
+ if(3 != r) {
+ r = -EINVAL;
+ goto error;
+ }
- /*rtsp_message_seal(req);*/
- /*cli_debug("OUTGOING (M3): %s\n", rtsp_message_get_raw(req));*/
+ for(i = 0; i < SHL_ARRAY_LENGTH(methods); i ++) {
+ if(strcmp("org.wfa.wfd1.0", methods[i]) &&
+ strcmp("SET_PARAMETER", methods[i]) &&
+ strcmp("GET_PARAMETER", methods[i]))
+ {
+ r = -EINVAL;
+ log_info("Got invalid method from sink: %s", methods[i]);
+ }
- /*r = rtsp_call_async(s->rtsp, req, src_get_parameter_rep_fn, s, 0, NULL);*/
- /*if (r < 0) {*/
- /*cli_vERR(r);*/
- /*goto error;*/
- /*}*/
+ free(methods[i]);
+ }
- /*return 0;*/
+ if(0 > r) {
+ goto error;
+ }
-/*error:*/
- /*wfd_src_close(s);*/
- /*wfd_fn_src_disconnected(s);*/
+ r = sd_event_add_defer(ctl_wfd_get_loop(),
+ NULL,
+ wfd_out_session_send_get_parameter,
+ s);
+ if(0 > r) {
+ goto error;
+ }
+
+ return 0;
+
+error:
+ wfd_session_end(s);
+
+ log_info("error occured while handling reply of OPTIONS");
return r;
}
-static int wfd_out_session_send_options(struct wfd_session *s)
+static int wfd_out_session_send_options(sd_event_source *source,
+ void *userdata)
{
- _rtsp_message_unref_ struct rtsp_message *req = NULL;
+ struct wfd_session *s = userdata;
+ _rtsp_message_unref_ struct rtsp_message *m = NULL;
int r = rtsp_message_new_request(s->rtsp,
- &req,
- "OPTIONS",
- "*");
+ &m,
+ "OPTIONS", "*");
if (0 > r) {
- return r;
+ goto error;
}
- r = rtsp_message_append(req, "",
- "Require",
- "org.wfa.wfd1.0");
+ r = rtsp_message_append(m,
+ "",
+ "Require", "org.wfa.wfd1.0");
if (0 > r) {
- return r;
+ goto error;
}
-
- r = rtsp_message_seal(req);
+
+ r = rtsp_message_seal(m);
if(0 > r) {
- return r;
+ goto error;
}
r = rtsp_call_async(s->rtsp,
- req,
+ m,
wfd_out_session_handle_options_reply,
s,
0,
NULL);
if (0 > r) {
- return r;
+ goto error;
}
- log_debug("Sending RTSP M1 message: %s",
- (char *) rtsp_message_get_raw(req));
+ log_debug("sending OPTIONS (M1): %s", (char *) rtsp_message_get_raw(m));
return 0;
+
+error:
+ log_info("failed to send OPTIONS request: %s", strerror(errno));
+ wfd_session_end(s);
+
+ return r;
}
-static int wfd_out_session_handle_incoming_conn(struct wfd_out_session *os)
+static int wfd_out_session_handle_options_request(struct rtsp *bus,
+ struct rtsp_message *request,
+ struct wfd_session *s)
{
- int r, val;
- struct sockaddr_storage addr;
- socklen_t len;
- _shl_close_ int fd = -1;
- sd_event *loop;
- struct wfd_session *s = wfd_session(os);
+ const char *require;
+ _rtsp_message_unref_ struct rtsp_message *m = NULL;
+ int r = rtsp_message_read(request, "", "Require", &require);
+ if(0 > r) {
+ goto error;
+ }
- log_debug("got connection request\n");
+ if(strcmp("org.wfa.wfd1.0", require)) {
+ r = rtsp_message_new_reply_for(request,
+ &m,
+ RTSP_CODE_OPTION_NOT_SUPPORTED,
+ "Invalid specification");
+ }
+ else {
+ r = rtsp_message_new_reply_for(request,
+ &m,
+ RTSP_CODE_OK,
+ NULL);
+ if(0 > r) {
+ goto error;
+ }
+
+ r = rtsp_message_append(m,
+ "",
+ "Public", "org.wfa.wfd1.0, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER");
+ }
+
+ if(0 > r) {
+ goto error;
+ }
+
+ r = rtsp_message_seal(m);
+ if(0 > r) {
+ goto error;
+ }
+
+ r = rtsp_send(bus, m);
+ if(0 > r) {
+ goto error;
+ }
+
+ log_debug("sending OPTIONS reply (M2): %s",
+ (char *) rtsp_message_get_raw(m));
+
+ return 0;
+
+error:
+ s->hup = true;
+ return r;
+}
+
+static int wfd_out_session_dispatch_request(struct rtsp *bus,
+ struct rtsp_message *m,
+ void *userdata)
+{
+ const char *method;
+ struct wfd_session *s = userdata;
+ int r = 0;
+
+ if (!m) {
+ s->hup = true;
+ goto end;
+ }
+
+ method = rtsp_message_get_method(m);
+ if(!method) {
+ log_info("unexpected message: %s", (char *) rtsp_message_get_raw(m));
+ r = -EINVAL;
+ goto end;
+ }
+
+ if (!strcmp(method, "OPTIONS")) {
+ r = wfd_out_session_handle_options_request(bus, m, s);
+ }
+ /*else if (!strcmp(method, "GET_PARAMETER")) {*/
+ /*wfd_out_session_handle_get_parameter_reply(s, m);*/
+ /*}*/
+ /*[>else if (!strcmp(method, "SETUP")) {<]*/
+ /*[>wfd_out_session_handle_setup_reply(s, m);<]*/
+ /*[>}<]*/
+ /*[>else if (!strcmp(method, "PLAY")) {<]*/
+ /*[>wfd_out_session_handle_play_reply(s, m);<]*/
+ /*[>}<]*/
+ /*[>else if (!strcmp(method, "PAUSE")) {<]*/
+ /*[>wfd_out_session_handle_pause_reply(s, m);<]*/
+ /*[>}<]*/
+ /*[>else if (!strcmp(method, "TEARDOWN")) {<]*/
+ /*[>wfd_out_session_handle_teardown_reply(s, m);<]*/
+ /*[>}<]*/
+
+end:
+ if (s->hup) {
+ wfd_session_end(s);
+ }
+
+ return r;
+
+}
+
+static int wfd_out_session_accept_connection(struct wfd_out_session *os)
+{
+ int r;
+ socklen_t len;
+ struct sockaddr_storage addr;
+ _shl_close_ int fd = -1;
+ sd_event *loop = ctl_wfd_get_loop();
+ struct wfd_session *s = wfd_session(os);
+ _rtsp_unref_ struct rtsp *rtsp = NULL;
+
+ log_debug("accepting incoming RTSP connection\n");
len = sizeof(addr);
- fd = accept4(s->fd, (struct sockaddr *) &addr, &len, SOCK_CLOEXEC);
- close(s->fd);
- s->fd = -1;
+ fd = accept4(os->fd,
+ (struct sockaddr *) &addr,
+ &len,
+ SOCK_NONBLOCK | SOCK_CLOEXEC);
if(0 > fd) {
return -ENOTCONN;
}
- r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &len);
- if (r < 0) {
- s->hup = true;
- return r;
- }
- else if (val) {
- s->hup = true;
- errno = val;
- return r;
- }
+ log_info("RTSP connection established");
- cli_debug("connection established");
-
- r = rtsp_open(&s->rtsp, s->fd);
+ r = rtsp_open(&rtsp, fd);
if (0 > r) {
goto error;
}
- r = sd_event_default(&loop);
- if(0 > r) {
- goto unref_rtsp;
- }
-
- r = rtsp_attach_event(s->rtsp, loop, 0);
- sd_event_unref(loop);
- if (0 > r) {
- goto unref_rtsp;
- }
-
- r = rtsp_add_match(s->rtsp, wfd_out_session_handle_message, s);
- if (0 > r) {
- goto unref_rtsp;
- }
-
- r = wfd_out_session_send_options(s);
- if(0 > r) {
- goto unref_rtsp;
- }
-
- s->fd = fd;
fd = -1;
- s->connected = true;
- //wfd_fn_src_connected(s);
+
+ r = rtsp_attach_event(rtsp, loop, 0);
+ if (0 > r) {
+ goto error;
+ }
+
+ r = rtsp_add_match(rtsp, wfd_out_session_dispatch_request, s);
+ if (0 > r) {
+ goto error;
+ }
+
+ r = sd_event_add_defer(loop, NULL, wfd_out_session_send_options, s);
+ if(0 > r) {
+ goto error;
+ }
+
+ wfd_session_set_state(s, WFD_SESSION_STATE_CAPS_EXCHAING);
+
+ s->rtsp = rtsp;
+ rtsp = NULL;
+
+ close(os->fd);
+ os->fd = -1;
return 0;
-unref_rtsp:
- rtsp_unref(s->rtsp);
error:
s->hup = true;
return r;
@@ -310,24 +538,33 @@ static int wfd_out_session_handle_io(sd_event_source *source,
uint32_t mask,
void *userdata)
{
- sd_event_source_set_enabled(source, SD_EVENT_OFF);
- sd_event_source_unref(source);
+ int r, val;
+ socklen_t len;
+ struct wfd_session *s = userdata;
- if (mask & EPOLLIN) {
- return wfd_out_session_handle_incoming_conn(userdata);
+ sd_event_source_set_enabled(source, SD_EVENT_OFF);
+
+ if (mask & EPOLLERR) {
+ r = getsockopt(fd, SOL_SOCKET, SO_ERROR, &val, &len);
+ s->hup = true;
+ if(0 <= r) {
+ r = -val;
+ errno = val;
+ }
+ goto end;
}
- /*if (mask & EPOLLERR) {*/
- /*cli_notice("ERR on socket");*/
- /*s->hup = true;*/
- /*}*/
+ if (mask & EPOLLIN) {
+ r = wfd_out_session_accept_connection(userdata);
+ }
- /*if (s->hup) {*/
- /*wfd_src_close(s);*/
- /*wfd_fn_src_disconnected(s);*/
- /*}*/
+end:
+ if (s->hup) {
+ log_info("failed to accept remote connection: %s", strerror(errno));
+ wfd_session_end(s);
+ }
- return 0;
+ return r;
}
static int wfd_out_session_start(struct wfd_session *s)
@@ -336,9 +573,8 @@ static int wfd_out_session_start(struct wfd_session *s)
union wfd_sube sube;
struct sockaddr_in addr = {};
struct ctl_peer *p = os->sink->peer;
- int enable;
_shl_close_ int fd = -1;
- sd_event *loop;
+ int enable;
int r;
if(!os->sink->peer->connected) {
@@ -346,7 +582,9 @@ static int wfd_out_session_start(struct wfd_session *s)
return -ENOTCONN;
}
- r = wfd_sube_parse(p->l->wfd_subelements, &sube);
+ r = wfd_sube_parse_with_id(WFD_SUBE_ID_DEVICE_INFO,
+ p->l->wfd_subelements,
+ &sube);
if(0 > r) {
log_warning("WfdSubelements property of link must be set before P2P scan");
return -EINVAL;
@@ -355,6 +593,10 @@ static int wfd_out_session_start(struct wfd_session *s)
return -EAFNOSUPPORT;
}
+ if(-1 != os->fd) {
+ return EINPROGRESS;
+ }
+
r = inet_pton(AF_INET, p->local_address, &addr.sin_addr);
if (!r) {
return -EAFNOSUPPORT;
@@ -380,7 +622,7 @@ static int wfd_out_session_start(struct wfd_session *s)
return r;
}
- r = listen(fd, 1);
+ r = listen(fd, 10);
if (0 > r) {
return r;
}
@@ -389,15 +631,10 @@ static int wfd_out_session_start(struct wfd_session *s)
p->local_address,
wfd_sube_device_get_rtsp_port(&sube));
- r = sd_event_default(&loop);
- if(0 > r) {
- return r;
- }
-
- r = sd_event_add_io(loop,
+ r = sd_event_add_io(ctl_wfd_get_loop(),
NULL,
fd,
- EPOLLERR | EPOLLIN | EPOLLET,
+ EPOLLIN,
wfd_out_session_handle_io,
s);
if (r < 0) {
@@ -406,20 +643,27 @@ static int wfd_out_session_start(struct wfd_session *s)
r = wfd_session_gen_url(s, p->local_address);
if(0 <= r) {
- s->fd = fd;
+ os->fd = fd;
fd = -1;
}
return r;
}
-static int wfd_out_session_end(struct wfd_session *s)
+static void wfd_out_session_end(struct wfd_session *s)
{
- return 0;
+ struct wfd_out_session *os = wfd_out_session(s);
+ if(os->fd) {
+ close(os->fd);
+ os->fd = -1;
+ }
}
static void wfd_out_session_distruct(struct wfd_session *s)
{
-
+ struct wfd_out_session *os = wfd_out_session(s);
+ if(0 <= os->fd) {
+ close(os->fd);
+ }
}
diff --git a/src/ctl/wfd-sink.c b/src/ctl/wfd-sink.c
index 7aa384c..6e4d162 100644
--- a/src/ctl/wfd-sink.c
+++ b/src/ctl/wfd-sink.c
@@ -16,15 +16,17 @@
* You should have received a copy of the GNU Lesser General Public License
* along with MiracleCast; If not, see .
*/
+#define LOG_SUBSYSTEM "wfd-session"
+
#include
#include "ctl.h"
+#include "wfd-dbus.h"
int wfd_sink_new(struct wfd_sink **out,
struct ctl_peer *peer,
union wfd_sube *sube)
{
struct wfd_sink *sink;
- int r;
assert(out);
assert(peer);
@@ -84,7 +86,7 @@ struct ctl_peer * wfd_sink_get_peer(struct wfd_sink *sink)
int wfd_sink_start_session(struct wfd_sink *sink, struct wfd_session **out)
{
int r;
- struct wfd_session *session = NULL;
+ _wfd_session_free_ struct wfd_session *s = NULL;
assert(sink);
assert(out);
@@ -93,25 +95,43 @@ int wfd_sink_start_session(struct wfd_sink *sink, struct wfd_session **out)
return -EALREADY;
}
- r = wfd_out_session_new(&session, sink);
+ r = wfd_out_session_new(&s, sink);
if(0 > r) {
return r;
}
- r = wfd_session_start(session);
+ r = wfd_session_start(s, ctl_wfd_alloc_session_id(ctl_wfd_get()));
if(0 > r) {
- goto free_session;
+ return r;
}
- sink->session = session;
- *out = session;
+ r = ctl_wfd_add_session(ctl_wfd_get(), s);
+ if(0 > r) {
+ return r;
+ }
- goto end;
+ wfd_fn_sink_properties_changed(sink, "Session");
-free_session:
- wfd_session_free(session);
-end:
- return r;
+ sink->session = s;
+ *out = s;
+ s = NULL;
+
+ return 0;
+}
+
+int wfd_fn_out_session_ended(struct wfd_session *s)
+{
+ assert(wfd_is_out_session(s));
+
+ struct wfd_sink *sink = wfd_out_session_get_sink(s);
+ if(sink) {
+ wfd_fn_sink_properties_changed(sink, "Session");
+ ctl_wfd_remove_session_by_id(ctl_wfd_get(), s->id, NULL);
+ sink->session = NULL;
+ wfd_session_free(s);
+ }
+
+ return 0;
}
bool wfd_sink_is_session_started(struct wfd_sink *sink)
diff --git a/src/ctl/wfd.c b/src/ctl/wfd.c
index e6bfef2..8965d5a 100644
--- a/src/ctl/wfd.c
+++ b/src/ctl/wfd.c
@@ -305,16 +305,28 @@ static int wfd_sube_parse_ext_caps(const char *in, union wfd_sube *out)
int wfd_sube_parse(const char *in, union wfd_sube *out)
{
uint8_t id;
- uint16_t len;
const char *eoi = in + strlen(in);
int r;
- if((in + 6) >= eoi) {
+ if((in + 2) >= eoi) {
return -EINVAL;
}
- r = sscanf(in, "%2hhx%4hx", &id, &len);
- if(2 > r) {
+ r = sscanf(in, "%2hhx", &id);
+ if(1 > r) {
+ return -EINVAL;
+ }
+
+ return wfd_sube_parse_with_id(id, in + 2, out);
+}
+
+int wfd_sube_parse_with_id(enum wfd_sube_id id,
+ const char *in,
+ union wfd_sube *out)
+{
+ uint16_t len;
+ int r = sscanf(in, "%4hx", &len);
+ if(1 > r) {
return -EINVAL;
}
@@ -326,7 +338,7 @@ int wfd_sube_parse(const char *in, union wfd_sube *out)
return 0;
}
- r = (*parse_func_tbl[id])(in + 6, out);
+ r = (*parse_func_tbl[id])(in + 4, out);
if(0 > r) {
return r;
}
diff --git a/src/ctl/wfd.h b/src/ctl/wfd.h
index 9f77c7d..1fcc31e 100644
--- a/src/ctl/wfd.h
+++ b/src/ctl/wfd.h
@@ -114,6 +114,9 @@ int vfd_get_cea_resolution(uint32_t mask, int *hres, int *vres);
int vfd_get_vesa_resolution(uint32_t mask, int *hres, int *vres);
int vfd_get_hh_resolution(uint32_t mask, int *hres, int *vres);
int wfd_sube_parse(const char *in, union wfd_sube *out);
+int wfd_sube_parse_with_id(enum wfd_sube_id id,
+ const char *in,
+ union wfd_sube *out);
static inline int wfd_sube_device_get_type(const union wfd_sube *sube)
{
diff --git a/src/ctl/wfdctl.c b/src/ctl/wfdctl.c
index d4cd978..3ecffb7 100644
--- a/src/ctl/wfdctl.c
+++ b/src/ctl/wfdctl.c
@@ -41,7 +41,8 @@
#include "shl_log.h"
#include "config.h"
-void ctl_wfd_free(struct ctl_wfd *wfd);
+static int ctl_wfd_init(struct ctl_wfd *wfd, sd_bus *bus);
+static void ctl_wfd_free(struct ctl_wfd *wfd);
static struct ctl_wfd *wfd = NULL;
static struct wfd_dbus *wfd_dbus = NULL;
@@ -61,42 +62,51 @@ int ctl_wfd_new(struct ctl_wfd **out, sd_event *loop, sd_bus *bus)
int r;
struct ctl_wfd *wfd = calloc(1, sizeof(struct ctl_wfd));
if(!wfd) {
- return -ENOMEM;
- }
-
- r = ctl_wifi_new(&wfd->wifi, bus);
- if(0 > r) {
- ctl_wfd_free(wfd);
- return -ENOMEM;
+ r = -ENOMEM;
+ goto error;
}
shl_htable_init_str(&wfd->sinks);
shl_htable_init_u64(&wfd->sessions);
wfd->loop = sd_event_ref(loop);
+ r = ctl_wfd_init(wfd, bus);
+ if(0 > r) {
+ goto error;
+ }
+
*out = wfd;
return 0;
+
+error:
+ ctl_wfd_free(wfd);
+ return r;
}
static void ctl_wfd_clear_sink(char **elem, void *ctx)
{
+ if(*elem == ctx) {
+ return;
+ }
wfd_sink_free(wfd_sink_from_htable(elem));
}
-static void ctl_wfd_clear_session(uint64_t *elem, void *ctx)
+static void ctl_wfd_destroy(struct ctl_wfd *wfd)
{
- wfd_session_free(wfd_session_from_htable(elem));
+ ctl_wifi_free(wfd->wifi);
+ wfd->wifi = NULL;
+ shl_htable_clear_str(&wfd->sinks, NULL, NULL);
+ shl_htable_clear_u64(&wfd->sessions, NULL, NULL);
}
-void ctl_wfd_free(struct ctl_wfd *wfd)
+static void ctl_wfd_free(struct ctl_wfd *wfd)
{
if(!wfd) {
return;
}
- shl_htable_clear_str(&wfd->sinks, ctl_wfd_clear_sink, NULL);
- shl_htable_clear_u64(&wfd->sessions, ctl_wfd_clear_session, NULL);
+ ctl_wfd_destroy(wfd);
if(wfd->loop) {
sd_event_unref(wfd->loop);
@@ -105,24 +115,37 @@ void ctl_wfd_free(struct ctl_wfd *wfd)
free(wfd);
}
-static int ctl_wfd_add_sink(struct ctl_wfd *wfd, struct wfd_sink *sink)
+int ctl_wfd_add_sink(struct ctl_wfd *wfd,
+ struct ctl_peer *p,
+ union wfd_sube *sube,
+ struct wfd_sink **out)
{
+ _wfd_sink_free_ struct wfd_sink *s;
int r = shl_htable_lookup_str(&wfd->sinks,
- wfd_sink_get_label(sink),
+ p->label,
NULL,
NULL);
if(r) {
return -EEXIST;
}
- r = shl_htable_insert_str(&wfd->sinks,
- wfd_sink_to_htable(sink),
- NULL);
- if(0 <= r) {
- ++wfd->n_sinks;
+ r = wfd_sink_new(&s, p, sube);
+ if(0 > r) {
+ return r;
}
- return r;
+ r = shl_htable_insert_str(&wfd->sinks,
+ wfd_sink_to_htable(s),
+ NULL);
+ if(0 > r) {
+ return r;
+ }
+
+ ++wfd->n_sinks;
+ *out = s;
+ s = NULL;
+
+ return 0;
}
int ctl_wfd_find_sink_by_label(struct ctl_wfd *wfd,
@@ -138,16 +161,29 @@ int ctl_wfd_find_sink_by_label(struct ctl_wfd *wfd,
return r;
}
-static int ctl_wfd_remove_sink_by_label(struct ctl_wfd *wfd, const char *label)
+static int ctl_wfd_remove_sink_by_label(struct ctl_wfd *wfd,
+ const char *label,
+ struct wfd_sink **out)
{
char **entry;
- if(!shl_htable_remove_str(&wfd->sinks, label, NULL, &entry)) {
- return 0;
+ int r = shl_htable_remove_str(&wfd->sinks, label, NULL, &entry);
+ if(!r) {
+ goto end;
}
- wfd_sink_free(wfd_sink_from_htable(entry));
+ --wfd->n_sinks;
- return 1;
+ if(out) {
+ *out = wfd_sink_from_htable(entry);
+ }
+
+end:
+ return r;
+}
+
+uint64_t ctl_wfd_alloc_session_id(struct ctl_wfd *wfd)
+{
+ return ++wfd->id_pool;
}
int ctl_wfd_add_session(struct ctl_wfd *wfd, struct wfd_session *s)
@@ -155,9 +191,8 @@ int ctl_wfd_add_session(struct ctl_wfd *wfd, struct wfd_session *s)
int r;
assert(wfd);
- assert(s && !s->id);
-
- wfd_session_set_id(s, ++wfd->id_pool);
+ assert(s && s->id);
+ assert(!ctl_wfd_find_session_by_id(wfd, s->id, NULL));
r = shl_htable_insert_u64(&wfd->sessions, wfd_session_to_htable(s));
if(0 > r) {
@@ -166,11 +201,13 @@ int ctl_wfd_add_session(struct ctl_wfd *wfd, struct wfd_session *s)
++wfd->n_sessions;
+ wfd_fn_session_new(s);
+
return r;
}
int ctl_wfd_find_session_by_id(struct ctl_wfd *wfd,
- unsigned int id,
+ uint64_t id,
struct wfd_session **out)
{
uint64_t *entry;
@@ -187,9 +224,31 @@ int ctl_wfd_remove_session_by_id(struct ctl_wfd *wfd,
struct wfd_session **out)
{
uint64_t *entry;
+ struct wfd_session *s;
int r = shl_htable_remove_u64(&wfd->sessions, id, &entry);
- if(r && out) {
- *out = wfd_session_from_htable(entry);
+ if(!r) {
+ return 0;
+ }
+
+ --wfd->n_sessions;
+
+ s = wfd_session_from_htable(entry);
+ wfd_fn_session_free(s);
+ if(out) {
+ *out = s;
+ }
+
+ return 1;
+}
+
+static int ctl_wfd_fetch_info(sd_event_source *s, void *userdata)
+{
+ struct ctl_wfd *wfd = userdata;
+ int r = ctl_wifi_fetch(wfd->wifi);
+ if(0 > r) {
+ log_warning("failed to fetch information about links and peers: %s",
+ strerror(errno));
+ sd_event_exit(wfd->loop, r);
}
return r;
@@ -199,20 +258,23 @@ static int ctl_wfd_handle_signal(sd_event_source *s,
const struct signalfd_siginfo *si,
void *userdata)
{
+ int r;
+ sd_event_source *exit_source;
struct ctl_wfd *wfd = userdata;
+ ctl_wfd_destroy(wfd);
+
sd_event_exit(wfd->loop, 0);
+ sd_event_source_unref(s);
+
return 0;
}
-int ctl_wfd_init(struct ctl_wfd *wfd)
+static int ctl_wfd_init(struct ctl_wfd *wfd, sd_bus *bus)
{
- int i;
+ int i, r;
const int signals[] = { SIGINT, SIGHUP, SIGQUIT, SIGTERM };
- int r = ctl_wifi_fetch(wfd->wifi);
- if(0 > r) {
- goto end;
- }
+ struct ctl_wifi *wifi;
for(i = 0; i < SHL_ARRAY_LENGTH(signals); i ++) {
sigset_t mask;
@@ -233,6 +295,20 @@ int ctl_wfd_init(struct ctl_wfd *wfd)
}
}
+ r = ctl_wifi_new(&wifi, bus);
+ if(0 > r) {
+ r = -ENOMEM;
+ goto end;
+ }
+
+ r = sd_event_add_defer(wfd->loop, NULL, ctl_wfd_fetch_info, wfd);
+ if(0 > r) {
+ ctl_wifi_free(wifi);
+ goto end;
+ }
+
+ wfd->wifi = wifi;
+
end:
return r;
}
@@ -261,52 +337,59 @@ void wfd_fn_src_playing(struct wfd_src *s)
void ctl_fn_peer_new(struct ctl_peer *p)
{
+ struct wfd_sink *s;
union wfd_sube sube;
- int r = wfd_sube_parse(p->wfd_subelements, &sube);
+ int r = wfd_sube_parse_with_id(WFD_SUBE_ID_DEVICE_INFO,
+ p->wfd_subelements,
+ &sube);
if(0 > r) {
log_debug("invalid subelement: '%s'", p->wfd_subelements);
return;
}
if(wfd_sube_device_is_sink(&sube)) {
- struct wfd_sink *sink;
- if(0 > wfd_sink_new(&sink, p, &sube)) {
- log_warning("failed to create sink (%s): %s",
+ r = ctl_wfd_add_sink(ctl_wfd_get(), p, &sube, &s);
+ if(0 > r) {
+ log_warning("failed to add sink (%s, '%s'): %s",
+ p->friendly_name,
p->p2p_mac,
strerror(errno));
return;
}
- if(0 > ctl_wfd_add_sink(wfd, sink)) {
- wfd_sink_free(sink);
- log_warning("failed to add sink (%s): %s",
- p->p2p_mac,
+
+ r = wfd_fn_sink_new(s);
+ if(0 > r) {
+ log_warning("failed to publish newly added sink (%s): %s",
+ wfd_sink_get_label(s),
strerror(errno));
return;
}
- /*if(0 > wfd_dbus_notify_new_sink(wfd_dbus, p->p2p_mac)) {*/
- /*log_warning("failed to notify about newly added sink (%s): %s",*/
- /*p->p2p_mac,*/
- /*strerror(errno));*/
- /*return;*/
- /*}*/
- log_debug("sink added: %s (%s)",
- wfd_sink_get_label(sink),
- p->friendly_name);
+
+ log_info("sink %s added", s->label);
}
}
void ctl_fn_peer_free(struct ctl_peer *p)
{
- union wfd_sube sube;
- int r = wfd_sube_parse(p->wfd_subelements, &sube);
- if(0 > r) {
- log_debug("invalid subelement: %s", p->wfd_subelements);
+ struct wfd_sink *s;
+ _shl_free_ char *label;
+ int r = ctl_wfd_remove_sink_by_label(wfd, p->label, &s);
+ if(!r) {
return;
}
- if(wfd_sube_device_is_sink(&sube)) {
- ctl_wfd_remove_sink_by_label(wfd, p->label);
+ label = strdup(s->label);
+
+ r = wfd_fn_sink_free(s);
+ if(0 > r) {
+ log_warning("failed to unpublish removed sink (%s): %s",
+ wfd_sink_get_label(s),
+ strerror(errno));
}
+
+ wfd_sink_free(s);
+
+ log_info("sink %s removed", label);
}
void ctl_fn_peer_provision_discovery(struct ctl_peer *p,
@@ -364,11 +447,14 @@ int main(int argc, char **argv)
r = sd_bus_default_system(&bus);
if(0 > r) {
+ log_warning("unabled to connect to system DBus: %s", strerror(errno));
goto unref_loop;
}
r = sd_bus_attach_event(bus, loop, 0);
if(0 > r) {
+ log_warning("unabled to attache DBus event source to loop: %s",
+ strerror(errno));
goto unref_bus;
}
@@ -377,11 +463,6 @@ int main(int argc, char **argv)
goto bus_detach_event;;
}
- r = ctl_wfd_init(wfd);
- if(0 > r) {
- goto free_ctl_wfd;
- }
-
r = wfd_dbus_new(&wfd_dbus, loop, bus);
if(0 > r) {
goto free_ctl_wfd;
@@ -389,18 +470,21 @@ int main(int argc, char **argv)
r = wfd_dbus_expose(wfd_dbus);
if(0 > r) {
+ log_warning("unabled to publish WFD service: %s", strerror(errno));
goto free_ctl_wfd;
}
r = ctl_wfd_run(wfd);
if(0 > r) {
- log_warning("%s\n", strerror(errno));
+ log_warning("unabled to keep WFD service running: %s", strerror(errno));
}
wfd_dbus_free(wfd_dbus);
+ wfd_dbus = NULL;
free_ctl_wfd:
ctl_wfd_free(wfd);
+ wfd = NULL;
bus_detach_event:
sd_bus_detach_event(bus);
unref_bus: