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: