mirror of
https://github.com/albfan/miraclecast.git
synced 2025-03-09 23:38:56 +00:00
run as source
This commit is contained in:
parent
439dac09c5
commit
fba5eb4a11
72 changed files with 9577 additions and 399 deletions
|
|
@ -5,6 +5,7 @@ add_subdirectory(wifi)
|
|||
add_subdirectory(dhcp)
|
||||
add_subdirectory(ctl)
|
||||
add_subdirectory(uibc)
|
||||
add_subdirectory(disp)
|
||||
|
||||
set(miracled_SRCS miracled.h miracled.c)
|
||||
add_executable(miracled ${miracled_SRCS})
|
||||
|
|
|
|||
|
|
@ -50,4 +50,5 @@ endif(READLINE_FOUND)
|
|||
|
||||
target_link_libraries(miracle-sinkctl miracle-shared)
|
||||
|
||||
INCLUDE_DIRECTORIES(${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/src/shared)
|
||||
include_directories(${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_SOURCE_DIR}/src/shared)
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ static void sink_handle_options(struct ctl_sink *s,
|
|||
if (r < 0)
|
||||
return cli_vERR(r);
|
||||
|
||||
r = rtsp_message_append(rep, "<s>",
|
||||
r = rtsp_message_append(rep, "<&>",
|
||||
"Public",
|
||||
"org.wfa.wfd1.0, GET_PARAMETER, SET_PARAMETER");
|
||||
if (r < 0)
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ bool check_rtsp_option(struct rtsp_message *m, char *option);
|
|||
|
||||
#define check_and_response_option(option, response) \
|
||||
if (check_rtsp_option(m, option)) { \
|
||||
char option_response[512]; \
|
||||
gchar option_response[512]; \
|
||||
sprintf(option_response, "%s: %s", option, response); \
|
||||
r = rtsp_message_append(rep, "{&}", option_response); \
|
||||
if (r < 0) {\
|
||||
|
|
|
|||
|
|
@ -637,6 +637,7 @@ int ctl_link_set_managed(struct ctl_link *l, bool val)
|
|||
_sd_bus_message_unref_ sd_bus_message *m = NULL;
|
||||
_sd_bus_error_free_ sd_bus_error err = SD_BUS_ERROR_NULL;
|
||||
_shl_free_ char *node = NULL;
|
||||
const char *method = val ? "Manage" : "Unmanage";
|
||||
int r;
|
||||
|
||||
if (!l)
|
||||
|
|
@ -654,26 +655,8 @@ int ctl_link_set_managed(struct ctl_link *l, bool val)
|
|||
&m,
|
||||
"org.freedesktop.miracle.wifi",
|
||||
node,
|
||||
"org.freedesktop.DBus.Properties",
|
||||
"Set");
|
||||
if (r < 0)
|
||||
return cli_log_create(r);
|
||||
|
||||
r = sd_bus_message_append(m, "ss",
|
||||
"org.freedesktop.miracle.wifi.Link",
|
||||
"Managed");
|
||||
if (r < 0)
|
||||
return cli_log_create(r);
|
||||
|
||||
r = sd_bus_message_open_container(m, 'v', "b");
|
||||
if (r < 0)
|
||||
return cli_log_create(r);
|
||||
|
||||
r = sd_bus_message_append(m, "b", val);
|
||||
if (r < 0)
|
||||
return cli_log_create(r);
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
"org.freedesktop.miracle.wifi.Link",
|
||||
method);
|
||||
if (r < 0)
|
||||
return cli_log_create(r);
|
||||
|
||||
|
|
@ -823,7 +806,7 @@ static int ctl_wifi_parse_peer(struct ctl_wifi *w,
|
|||
|
||||
l = ctl_wifi_find_link_by_peer(w, label);
|
||||
if (!l)
|
||||
return 0;
|
||||
return -EINVAL;
|
||||
|
||||
r = ctl_peer_new(&p, l, label);
|
||||
if (r < 0)
|
||||
|
|
@ -901,6 +884,9 @@ static int ctl_wifi_parse_object(struct ctl_wifi *w,
|
|||
}
|
||||
}
|
||||
|
||||
free(label);
|
||||
label = NULL;
|
||||
|
||||
r = sd_bus_path_decode(t,
|
||||
"/org/freedesktop/miracle/wifi/peer",
|
||||
&label);
|
||||
|
|
@ -1068,6 +1054,38 @@ static int ctl_wifi_peer_fn(sd_bus_message *m,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void ctl_wifi_unlink_all_links(struct ctl_wifi *w)
|
||||
{
|
||||
struct ctl_link *l;
|
||||
while (!shl_dlist_empty(&w->links)) {
|
||||
l = shl_dlist_last_entry(&w->links, struct ctl_link, list);
|
||||
ctl_link_free(l);
|
||||
}
|
||||
}
|
||||
|
||||
static int ctl_wifi_wifid_up_or_down_fn(sd_bus_message *m,
|
||||
void *data,
|
||||
sd_bus_error *err)
|
||||
{
|
||||
struct ctl_wifi *w = data;
|
||||
char *from = NULL, *to = NULL;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_read(m, "sss", NULL, &from, &to);
|
||||
if(r < 0) {
|
||||
return r;
|
||||
}
|
||||
|
||||
if(*from && !*to) {
|
||||
ctl_wifi_unlink_all_links(w);
|
||||
}
|
||||
else if(!*from && *to) {
|
||||
r = ctl_wifi_fetch(w);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int ctl_wifi_init(struct ctl_wifi *w)
|
||||
{
|
||||
int r;
|
||||
|
|
@ -1099,6 +1117,18 @@ static int ctl_wifi_init(struct ctl_wifi *w)
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_add_match(w->bus, NULL,
|
||||
"type='signal',"
|
||||
"sender='org.freedesktop.DBus',"
|
||||
"path='/org/freedesktop/DBus',"
|
||||
"interface='org.freedesktop.DBus',"
|
||||
"member='NameOwnerChanged',"
|
||||
"arg0namespace='org.freedesktop.miracle.wifi'",
|
||||
ctl_wifi_wifid_up_or_down_fn,
|
||||
w);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1135,21 +1165,52 @@ int ctl_wifi_new(struct ctl_wifi **out, sd_bus *bus)
|
|||
|
||||
void ctl_wifi_free(struct ctl_wifi *w)
|
||||
{
|
||||
struct ctl_link *l;
|
||||
|
||||
if (!w)
|
||||
return;
|
||||
|
||||
while (!shl_dlist_empty(&w->links)) {
|
||||
l = shl_dlist_last_entry(&w->links, struct ctl_link, list);
|
||||
ctl_link_free(l);
|
||||
}
|
||||
ctl_wifi_unlink_all_links(w);
|
||||
|
||||
ctl_wifi_destroy(w);
|
||||
sd_bus_unref(w->bus);
|
||||
free(w);
|
||||
}
|
||||
|
||||
static int ctl_wifi_parse_objects(struct ctl_wifi *w,
|
||||
sd_bus_message *m,
|
||||
bool ignore_link_not_found)
|
||||
{
|
||||
bool again = false;
|
||||
int r = sd_bus_message_enter_container(m, 'a', "{oa{sa{sv}}}");
|
||||
if (r < 0)
|
||||
return cli_log_parser(r);
|
||||
|
||||
while ((r = sd_bus_message_enter_container(m,
|
||||
'e',
|
||||
"oa{sa{sv}}")) > 0) {
|
||||
r = ctl_wifi_parse_object(w, m, true);
|
||||
if(ignore_link_not_found && -EINVAL == r) {
|
||||
r = sd_bus_message_skip(m, "a{sa{sv}}");
|
||||
if(0 > r)
|
||||
return cli_log_parser(r);
|
||||
again = true;
|
||||
}
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_exit_container(m);
|
||||
if (r < 0)
|
||||
return cli_log_parser(r);
|
||||
}
|
||||
if (r < 0)
|
||||
return cli_log_parser(r);
|
||||
|
||||
r = sd_bus_message_exit_container(m);
|
||||
if (r < 0)
|
||||
return cli_log_parser(r);
|
||||
|
||||
return again ? -EAGAIN : 0;
|
||||
}
|
||||
|
||||
int ctl_wifi_fetch(struct ctl_wifi *w)
|
||||
{
|
||||
_sd_bus_message_unref_ sd_bus_message *m = NULL;
|
||||
|
|
@ -1173,29 +1234,20 @@ int ctl_wifi_fetch(struct ctl_wifi *w)
|
|||
return r;
|
||||
}
|
||||
|
||||
r = sd_bus_message_enter_container(m, 'a', "{oa{sa{sv}}}");
|
||||
if (r < 0)
|
||||
return cli_log_parser(r);
|
||||
|
||||
while ((r = sd_bus_message_enter_container(m,
|
||||
'e',
|
||||
"oa{sa{sv}}")) > 0) {
|
||||
r = ctl_wifi_parse_object(w, m, true);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = sd_bus_message_exit_container(m);
|
||||
if (r < 0)
|
||||
return cli_log_parser(r);
|
||||
r = ctl_wifi_parse_objects(w, m, true);
|
||||
if(0 <= r || -EAGAIN != r) {
|
||||
goto end;
|
||||
}
|
||||
if (r < 0)
|
||||
return cli_log_parser(r);
|
||||
|
||||
r = sd_bus_message_exit_container(m);
|
||||
if (r < 0)
|
||||
return cli_log_parser(r);
|
||||
r = sd_bus_message_rewind(m, true);
|
||||
if(0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
return 0;
|
||||
r = ctl_wifi_parse_objects(w, m, false);
|
||||
|
||||
end:
|
||||
return r;
|
||||
}
|
||||
|
||||
struct ctl_link *ctl_wifi_find_link(struct ctl_wifi *w,
|
||||
|
|
|
|||
|
|
@ -30,7 +30,9 @@
|
|||
#include <sys/types.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
#include "shl_dlist.h"
|
||||
#include "shl_htable.h"
|
||||
#include "shl_log.h"
|
||||
#include "wfd.h"
|
||||
|
||||
/* *sigh* readline doesn't include all their deps, so put them last */
|
||||
#include <readline/history.h>
|
||||
|
|
@ -262,3 +264,4 @@ void ctl_fn_sink_resolution_set(struct ctl_sink *s);
|
|||
void cli_fn_help(void);
|
||||
|
||||
#endif /* CTL_CTL_H */
|
||||
|
||||
|
|
|
|||
|
|
@ -279,10 +279,8 @@ static int cmd_run(char **args, unsigned int n)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (!l->managed) {
|
||||
cli_printf("link %s not managed\n", l->label);
|
||||
return 0;
|
||||
}
|
||||
if (!l->managed)
|
||||
return log_EUNMANAGED();
|
||||
|
||||
run_on(l);
|
||||
|
||||
|
|
@ -314,10 +312,8 @@ static int cmd_bind(char **args, unsigned int n)
|
|||
if (!l)
|
||||
return 0;
|
||||
|
||||
if (!l->managed) {
|
||||
cli_printf("link %s not managed\n", l->label);
|
||||
return 0;
|
||||
}
|
||||
if (!l->managed)
|
||||
return log_EUNMANAGED();
|
||||
|
||||
run_on(l);
|
||||
|
||||
|
|
@ -859,9 +855,8 @@ static int ctl_main(int argc, char *argv[])
|
|||
if (r < 0)
|
||||
return r;
|
||||
|
||||
left = argc - optind;
|
||||
left = left <= 0 ? 0 : left;
|
||||
r = ctl_interactive(argv + optind, left);
|
||||
left = argc - optind;
|
||||
r = ctl_interactive(argv + optind, left <= 0 ? 0 : left);
|
||||
|
||||
/* stop all scans */
|
||||
shl_dlist_for_each(i, &wifi->links) {
|
||||
|
|
@ -912,19 +907,19 @@ static int parse_argv(int argc, char *argv[])
|
|||
|
||||
uibc_option = false;
|
||||
uibc_enabled = false;
|
||||
external_player = false;
|
||||
external_player = false;
|
||||
rstp_port = DEFAULT_RSTP_PORT;
|
||||
|
||||
while ((c = getopt_long(argc, argv, "he:p:", options, NULL)) >= 0) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
cli_fn_help();
|
||||
return 0;
|
||||
cli_fn_help();
|
||||
return 0;
|
||||
case ARG_HELP_COMMANDS:
|
||||
return cli_help(cli_cmds, 20);
|
||||
case ARG_HELP_RES:
|
||||
wfd_print_resolutions("");
|
||||
return 0;
|
||||
return 0;
|
||||
case ARG_VERSION:
|
||||
puts(PACKAGE_STRING);
|
||||
return 0;
|
||||
|
|
@ -976,74 +971,74 @@ static int parse_argv(int argc, char *argv[])
|
|||
int main(int argc, char **argv)
|
||||
{
|
||||
int r;
|
||||
bool free_argv = false;
|
||||
bool free_argv = false;
|
||||
|
||||
setlocale(LC_ALL, "");
|
||||
|
||||
GKeyFile* gkf = load_ini_file();
|
||||
GKeyFile* gkf = load_ini_file();
|
||||
|
||||
gchar** autocmds_free = NULL;
|
||||
if (gkf) {
|
||||
player = g_key_file_get_string (gkf, "sinkctl", "external-player", NULL);
|
||||
if (player) {
|
||||
gchar** autocmds_free = NULL;
|
||||
if (gkf) {
|
||||
player = g_key_file_get_string (gkf, "sinkctl", "external-player", NULL);
|
||||
if (player) {
|
||||
external_player = true;
|
||||
}
|
||||
gchar* log_level;
|
||||
log_level = g_key_file_get_string (gkf, "sinkctl", "log-journal-level", NULL);
|
||||
if (log_level) {
|
||||
log_max_sev = log_parse_arg(log_level);
|
||||
g_free(log_level);
|
||||
}
|
||||
log_level = g_key_file_get_string (gkf, "sinkctl", "log-level", NULL);
|
||||
if (log_level) {
|
||||
cli_max_sev = log_parse_arg(log_level);
|
||||
g_free(log_level);
|
||||
}
|
||||
gchar* rstp_port_str = g_key_file_get_string (gkf, "sinkctl", "rstp-port", NULL);
|
||||
if (rstp_port_str) {
|
||||
rstp_port = atoi(rstp_port_str);
|
||||
g_free(rstp_port_str);
|
||||
}
|
||||
gchar* autocmd;
|
||||
autocmd = g_key_file_get_string (gkf, "sinkctl", "autocmd", NULL);
|
||||
if (autocmd) {
|
||||
gchar** autocmds = g_strsplit(autocmd, " ", -1);
|
||||
autocmds_free = autocmds;
|
||||
while (*autocmds) {
|
||||
if (strcmp(*autocmds, "") != 0) {
|
||||
gchar **newv = malloc((argc + 2) * sizeof(gchar*));
|
||||
memmove(newv, argv, sizeof(gchar*) * argc);
|
||||
newv[argc] = *autocmds;
|
||||
newv[argc+1] = NULL;
|
||||
argc++;
|
||||
if (free_argv) {
|
||||
free(argv);
|
||||
}
|
||||
argv = newv;
|
||||
free_argv = true;
|
||||
}
|
||||
autocmds++;
|
||||
}
|
||||
g_free(autocmd);
|
||||
}
|
||||
gchar** sinkctl_keys;
|
||||
gsize len = 0;
|
||||
protocol_extensions = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
}
|
||||
gchar* log_level;
|
||||
log_level = g_key_file_get_string (gkf, "sinkctl", "log-journal-level", NULL);
|
||||
if (log_level) {
|
||||
log_max_sev = log_parse_arg(log_level);
|
||||
g_free(log_level);
|
||||
}
|
||||
log_level = g_key_file_get_string (gkf, "sinkctl", "log-level", NULL);
|
||||
if (log_level) {
|
||||
cli_max_sev = log_parse_arg(log_level);
|
||||
g_free(log_level);
|
||||
}
|
||||
gchar* rstp_port_str = g_key_file_get_string (gkf, "sinkctl", "rstp-port", NULL);
|
||||
if (rstp_port_str) {
|
||||
rstp_port = atoi(rstp_port_str);
|
||||
g_free(rstp_port_str);
|
||||
}
|
||||
gchar* autocmd;
|
||||
autocmd = g_key_file_get_string (gkf, "sinkctl", "autocmd", NULL);
|
||||
if (autocmd) {
|
||||
gchar** autocmds = g_strsplit(autocmd, " ", -1);
|
||||
autocmds_free = autocmds;
|
||||
while (*autocmds) {
|
||||
if (strcmp(*autocmds, "") != 0) {
|
||||
gchar **newv = malloc((argc + 2) * sizeof(gchar*));
|
||||
memmove(newv, argv, sizeof(gchar*) * argc);
|
||||
newv[argc] = *autocmds;
|
||||
newv[argc+1] = NULL;
|
||||
argc++;
|
||||
if (free_argv) {
|
||||
free(argv);
|
||||
}
|
||||
argv = newv;
|
||||
free_argv = true;
|
||||
}
|
||||
autocmds++;
|
||||
}
|
||||
g_free(autocmd);
|
||||
}
|
||||
gchar** sinkctl_keys;
|
||||
gsize len = 0;
|
||||
protocol_extensions = g_hash_table_new(g_str_hash, g_str_equal);
|
||||
|
||||
sinkctl_keys = g_key_file_get_keys (gkf,
|
||||
"sinkctl",
|
||||
&len,
|
||||
NULL);
|
||||
for (int i = 0; i < (int)len; i++) {
|
||||
if (g_str_has_prefix(sinkctl_keys[i], "extends.")) {
|
||||
gchar* orig_key = sinkctl_keys[i];
|
||||
gchar* key = orig_key+8;
|
||||
gchar* value = g_key_file_get_string (gkf, "sinkctl", orig_key, NULL);
|
||||
g_hash_table_insert(protocol_extensions, key, value);
|
||||
}
|
||||
}
|
||||
g_key_file_free(gkf);
|
||||
}
|
||||
sinkctl_keys = g_key_file_get_keys (gkf,
|
||||
"sinkctl",
|
||||
&len,
|
||||
NULL);
|
||||
for (int i = 0; i < (int)len; i++) {
|
||||
if (g_str_has_prefix(sinkctl_keys[i], "extends.")) {
|
||||
gchar* orig_key = sinkctl_keys[i];
|
||||
gchar* key = orig_key+8;
|
||||
gchar* value = g_key_file_get_string (gkf, "sinkctl", orig_key, NULL);
|
||||
g_hash_table_insert(protocol_extensions, key, value);
|
||||
}
|
||||
}
|
||||
g_key_file_free(gkf);
|
||||
}
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r < 0)
|
||||
|
|
@ -1058,10 +1053,10 @@ int main(int argc, char **argv)
|
|||
}
|
||||
|
||||
r = ctl_main(argc, argv);
|
||||
g_strfreev(autocmds_free);
|
||||
if (free_argv) {
|
||||
free(argv);
|
||||
}
|
||||
g_strfreev(autocmds_free);
|
||||
if (free_argv) {
|
||||
free(argv);
|
||||
}
|
||||
sd_bus_unref(bus);
|
||||
|
||||
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
|
|
|
|||
632
src/ctl/wfd.c
632
src/ctl/wfd.c
|
|
@ -20,109 +20,152 @@
|
|||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include "ctl.h"
|
||||
#include "wfd.h"
|
||||
#include "util.h"
|
||||
|
||||
struct resolution_bitmap {
|
||||
int index;
|
||||
int hres;
|
||||
int vres;
|
||||
int fps;
|
||||
typedef int (*wfd_sube_parse_func)(const char *in, union wfd_sube *out);
|
||||
|
||||
struct wfd_sube_info
|
||||
{
|
||||
wfd_sube_parse_func parser;
|
||||
uint8_t len;
|
||||
};
|
||||
|
||||
static int wfd_sube_parse_device_info(const char *in, union wfd_sube *out);
|
||||
static int wfd_sube_parse_audio_formats(const char *in, union wfd_sube *out);
|
||||
static int wfd_sube_parse_video_formats(const char *in, union wfd_sube *out);
|
||||
static int wfd_sube_parse_ext_caps(const char *in, union wfd_sube *out);
|
||||
|
||||
/*
|
||||
* CEA resolutions and refrash rate bitmap/index table
|
||||
* also used in native resolution field
|
||||
*/
|
||||
struct resolution_bitmap resolutions_cea[] = {
|
||||
{0, 640, 480, 60}, /* p60 */
|
||||
{1, 720, 480, 60}, /* p60 */
|
||||
{2, 720, 480, 60}, /* i60 */
|
||||
{3, 720, 576, 50}, /* p50 */
|
||||
{4, 720, 576, 50}, /* i50 */
|
||||
{5, 1280, 720, 30}, /* p30 */
|
||||
{6, 1280, 720, 60}, /* p60 */
|
||||
{7, 1920, 1080, 30}, /* p30 */
|
||||
{8, 1920, 1080, 60}, /* p60 */
|
||||
{9, 1920, 1080, 60}, /* i60 */
|
||||
{10, 1280, 720, 25}, /* p25 */
|
||||
{11, 1280, 720, 50}, /* p50 */
|
||||
{12, 1920, 1080, 25}, /* p25 */
|
||||
{13, 1920, 1080, 50}, /* p50 */
|
||||
{14, 1920, 1080, 50}, /* i50 */
|
||||
{15, 1280, 720, 24}, /* p24 */
|
||||
{16, 1920, 1080, 24}, /* p24 */
|
||||
{0, 0, 0, 0},
|
||||
static const struct wfd_resolution resolutions_cea[] = {
|
||||
{0, 640, 480, 60, 1}, /* p60 */
|
||||
{1, 720, 480, 60, 1}, /* p60 */
|
||||
{2, 720, 480, 60, 0}, /* i60 */
|
||||
{3, 720, 576, 50, 1}, /* p50 */
|
||||
{4, 720, 576, 50, 0}, /* i50 */
|
||||
{5, 1280, 720, 30, 1}, /* p30 */
|
||||
{6, 1280, 720, 60, 1}, /* p60 */
|
||||
{7, 1920, 1080, 30, 1}, /* p30 */
|
||||
{8, 1920, 1080, 60, 1}, /* p60 */
|
||||
{9, 1920, 1080, 60, 0}, /* i60 */
|
||||
{10, 1280, 720, 25, 1}, /* p25 */
|
||||
{11, 1280, 720, 50, 1}, /* p50 */
|
||||
{12, 1920, 1080, 25, 1}, /* p25 */
|
||||
{13, 1920, 1080, 50, 1}, /* p50 */
|
||||
{14, 1920, 1080, 50, 0}, /* i50 */
|
||||
{15, 1280, 720, 24, 1}, /* p24 */
|
||||
{16, 1920, 1080, 24, 1}, /* p24 */
|
||||
};
|
||||
|
||||
struct resolution_bitmap resolutions_vesa[] = {
|
||||
{0, 800, 600, 30}, /* p30 */
|
||||
{1, 800, 600, 60}, /* p60 */
|
||||
{2, 1024, 768, 30}, /* p30 */
|
||||
{3, 1024, 768, 60}, /* p60 */
|
||||
{4, 1152, 854, 30}, /* p30 */
|
||||
{5, 1152, 854, 60}, /* p60 */
|
||||
{6, 1280, 768, 30}, /* p30 */
|
||||
{7, 1280, 768, 60}, /* p60 */
|
||||
{8, 1280, 800, 30}, /* p30 */
|
||||
{9, 1280, 800, 60}, /* p60 */
|
||||
{10, 1360, 768, 30}, /* p30 */
|
||||
{11, 1360, 768, 60}, /* p60 */
|
||||
{12, 1366, 768, 30}, /* p30 */
|
||||
{13, 1366, 768, 60}, /* p60 */
|
||||
{14, 1280, 1024, 30}, /* p30 */
|
||||
{15, 1280, 1024, 60}, /* p60 */
|
||||
{16, 1440, 1050, 30}, /* p30 */
|
||||
{17, 1440, 1050, 60}, /* p60 */
|
||||
{18, 1440, 900, 30}, /* p30 */
|
||||
{19, 1440, 900, 60}, /* p60 */
|
||||
{20, 1600, 900, 30}, /* p30 */
|
||||
{21, 1600, 900, 60}, /* p60 */
|
||||
{22, 1600, 1200, 30}, /* p30 */
|
||||
{23, 1600, 1200, 60}, /* p60 */
|
||||
{24, 1680, 1024, 30}, /* p30 */
|
||||
{25, 1680, 1024, 60}, /* p60 */
|
||||
{26, 1680, 1050, 30}, /* p30 */
|
||||
{27, 1680, 1050, 60}, /* p60 */
|
||||
{28, 1920, 1200, 30}, /* p30 */
|
||||
{0, 0, 0, 0},
|
||||
static const struct wfd_resolution resolutions_vesa[] = {
|
||||
{0, 800, 600, 30, 1}, /* p30 */
|
||||
{1, 800, 600, 60, 1}, /* p60 */
|
||||
{2, 1024, 768, 30, 1}, /* p30 */
|
||||
{3, 1024, 768, 60, 1}, /* p60 */
|
||||
{4, 1152, 854, 30, 1}, /* p30 */
|
||||
{5, 1152, 854, 60, 1}, /* p60 */
|
||||
{6, 1280, 768, 30, 1}, /* p30 */
|
||||
{7, 1280, 768, 60, 1}, /* p60 */
|
||||
{8, 1280, 800, 30, 1}, /* p30 */
|
||||
{9, 1280, 800, 60, 1}, /* p60 */
|
||||
{10, 1360, 768, 30, 1}, /* p30 */
|
||||
{11, 1360, 768, 60, 1}, /* p60 */
|
||||
{12, 1366, 768, 30, 1}, /* p30 */
|
||||
{13, 1366, 768, 60, 1}, /* p60 */
|
||||
{14, 1280, 1024, 30, 1}, /* p30 */
|
||||
{15, 1280, 1024, 60, 1}, /* p60 */
|
||||
{16, 1440, 1050, 30, 1}, /* p30 */
|
||||
{17, 1440, 1050, 60, 1}, /* p60 */
|
||||
{18, 1440, 900, 30, 1}, /* p30 */
|
||||
{19, 1440, 900, 60, 1}, /* p60 */
|
||||
{20, 1600, 900, 30, 1}, /* p30 */
|
||||
{21, 1600, 900, 60, 1}, /* p60 */
|
||||
{22, 1600, 1200, 30, 1}, /* p30 */
|
||||
{23, 1600, 1200, 60, 1}, /* p60 */
|
||||
{24, 1680, 1024, 30, 1}, /* p30 */
|
||||
{25, 1680, 1024, 60, 1}, /* p60 */
|
||||
{26, 1680, 1050, 30, 1}, /* p30 */
|
||||
{27, 1680, 1050, 60, 1}, /* p60 */
|
||||
{28, 1920, 1200, 30, 1}, /* p30 */
|
||||
};
|
||||
|
||||
struct resolution_bitmap resolutions_hh[] = {
|
||||
{0, 800, 480, 30}, /* p30 */
|
||||
{1, 800, 480, 60}, /* p60 */
|
||||
{2, 854, 480, 30}, /* p30 */
|
||||
{3, 854, 480, 60}, /* p60 */
|
||||
{4, 864, 480, 30}, /* p30 */
|
||||
{5, 864, 480, 60}, /* p60 */
|
||||
{6, 640, 360, 30}, /* p30 */
|
||||
{7, 640, 360, 60}, /* p60 */
|
||||
{8, 960, 540, 30}, /* p30 */
|
||||
{9, 960, 540, 60}, /* p60 */
|
||||
{10, 848, 480, 30}, /* p30 */
|
||||
{11, 848, 480, 60}, /* p60 */
|
||||
{0, 0, 0, 0},
|
||||
static const struct wfd_resolution resolutions_hh[] = {
|
||||
{0, 800, 480, 30, 1}, /* p30 */
|
||||
{1, 800, 480, 60, 1}, /* p60 */
|
||||
{2, 854, 480, 30, 1}, /* p30 */
|
||||
{3, 854, 480, 60, 1}, /* p60 */
|
||||
{4, 864, 480, 30, 1}, /* p30 */
|
||||
{5, 864, 480, 60, 1}, /* p60 */
|
||||
{6, 640, 360, 30, 1}, /* p30 */
|
||||
{7, 640, 360, 60, 1}, /* p60 */
|
||||
{8, 960, 540, 30, 1}, /* p30 */
|
||||
{9, 960, 540, 60, 1}, /* p60 */
|
||||
{10, 848, 480, 30, 1}, /* p30 */
|
||||
{11, 848, 480, 60, 1}, /* p60 */
|
||||
};
|
||||
|
||||
static const struct wfd_sube_info parser_tbl[WFD_SUBE_ID_RESERVED] = {
|
||||
[WFD_SUBE_ID_DEVICE_INFO] = { .parser = wfd_sube_parse_device_info, .len = 6 },
|
||||
[WFD_SUBE_ID_AUDIO_FORMATS] = { .parser = wfd_sube_parse_audio_formats, .len = 15 },
|
||||
[WFD_SUBE_ID_VIDEO_FORMATS] = { .parser = wfd_sube_parse_video_formats, .len = 21 },
|
||||
[WFD_SUBE_ID_WFD_EXT_CAPS] = { .parser = wfd_sube_parse_ext_caps, .len = 2 },
|
||||
};
|
||||
|
||||
int wfd_get_resolutions(enum wfd_resolution_standard std,
|
||||
int index,
|
||||
struct wfd_resolution *out)
|
||||
{
|
||||
switch(std) {
|
||||
case WFD_RESOLUTION_STANDARD_CEA:
|
||||
if(0 >= index || index >= SHL_ARRAY_LENGTH(resolutions_cea)) {
|
||||
break;
|
||||
}
|
||||
*out = resolutions_cea[index];
|
||||
return 0;
|
||||
case WFD_RESOLUTION_STANDARD_VESA:
|
||||
if(0 >= index || index >= SHL_ARRAY_LENGTH(resolutions_vesa)) {
|
||||
break;
|
||||
}
|
||||
*out = resolutions_vesa[index];
|
||||
return 0;
|
||||
case WFD_RESOLUTION_STANDARD_HH:
|
||||
if(0 >= index || index >= SHL_ARRAY_LENGTH(resolutions_hh)) {
|
||||
break;
|
||||
}
|
||||
*out = resolutions_hh[index];
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
void wfd_print_resolutions(char * prefix)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf("%sCEA resolutions:\n", prefix);
|
||||
for (i = 0; resolutions_cea[i].hres != 0; i++) {
|
||||
for (i = 0; i < SHL_ARRAY_LENGTH(resolutions_cea); i++) {
|
||||
printf("%s\t%2d %08x %4dx%4d@%d\n", prefix,
|
||||
resolutions_cea[i].index, 1 << resolutions_cea[i].index,
|
||||
resolutions_cea[i].hres, resolutions_cea[i].vres,
|
||||
resolutions_cea[i].fps);
|
||||
}
|
||||
printf("%sVESA resolutions:\n", prefix);
|
||||
for (i = 0; resolutions_vesa[i].hres != 0; i++) {
|
||||
for (i = 0; i < SHL_ARRAY_LENGTH(resolutions_vesa); i++) {
|
||||
printf("%s\t%2d %08x %4dx%4d@%d\n", prefix,
|
||||
resolutions_vesa[i].index, 1 << resolutions_vesa[i].index,
|
||||
resolutions_vesa[i].hres, resolutions_vesa[i].vres,
|
||||
resolutions_vesa[i].fps);
|
||||
}
|
||||
printf("%sHH resolutions:\n", prefix);
|
||||
for (i = 0; resolutions_hh[i].hres != 0; i++) {
|
||||
for (i = 0; i < SHL_ARRAY_LENGTH(resolutions_hh); i++) {
|
||||
printf("%s\t%2d %08x %4dx%4d@%d\n", prefix,
|
||||
resolutions_hh[i].index, 1 << resolutions_hh[i].index,
|
||||
resolutions_hh[i].hres, resolutions_hh[i].vres,
|
||||
|
|
@ -141,7 +184,7 @@ void vfd_dump_resolutions(uint32_t cea_mask, uint32_t vesa_mask, uint32_t hh_mas
|
|||
|
||||
if (cea_mask) {
|
||||
cli_debug("CEA resolutions:");
|
||||
for (i = 0; resolutions_cea[i].hres != 0; i++)
|
||||
for (i = 0; i < SHL_ARRAY_LENGTH(resolutions_cea); i++)
|
||||
if ((1 << resolutions_cea[i].index) & cea_mask)
|
||||
cli_debug("\t%2d %08x %4dx%4d@%d\n",
|
||||
resolutions_cea[i].index, 1 << resolutions_cea[i].index,
|
||||
|
|
@ -150,7 +193,7 @@ void vfd_dump_resolutions(uint32_t cea_mask, uint32_t vesa_mask, uint32_t hh_mas
|
|||
}
|
||||
if (vesa_mask) {
|
||||
cli_debug("VESA resolutions:");
|
||||
for (i = 0; resolutions_vesa[i].hres != 0; i++)
|
||||
for (i = 0; i < SHL_ARRAY_LENGTH(resolutions_vesa); i++)
|
||||
if ((1 << resolutions_vesa[i].index) & vesa_mask)
|
||||
cli_debug("\t%2d %08x %4dx%4d@%d\n",
|
||||
resolutions_vesa[i].index, 1 << resolutions_vesa[i].index,
|
||||
|
|
@ -159,7 +202,7 @@ void vfd_dump_resolutions(uint32_t cea_mask, uint32_t vesa_mask, uint32_t hh_mas
|
|||
}
|
||||
if (hh_mask) {
|
||||
cli_debug("HH resolutions:");
|
||||
for (i = 0; resolutions_hh[i].hres != 0; i++)
|
||||
for (i = 0; i < SHL_ARRAY_LENGTH(resolutions_hh); i++)
|
||||
if ((1 << resolutions_hh[i].index) & hh_mask)
|
||||
cli_debug("\t%2d %08x %4dx%4d@%d\n",
|
||||
resolutions_hh[i].index, 1 << resolutions_hh[i].index,
|
||||
|
|
@ -175,7 +218,7 @@ int vfd_get_cea_resolution(uint32_t mask, int *hres, int *vres)
|
|||
if (!mask)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; resolutions_cea[i].hres != 0; i++) {
|
||||
for (i = SHL_ARRAY_LENGTH(resolutions_cea) - 1; i >= 0; --i) {
|
||||
if ((1 << resolutions_cea[i].index) & mask) {
|
||||
*vres = resolutions_cea[i].vres;
|
||||
*hres = resolutions_cea[i].hres;
|
||||
|
|
@ -192,7 +235,7 @@ int vfd_get_vesa_resolution(uint32_t mask, int *hres, int *vres)
|
|||
if (!mask)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; resolutions_vesa[i].hres != 0; i++) {
|
||||
for (i = SHL_ARRAY_LENGTH(resolutions_vesa) - 1; i >= 0; --i) {
|
||||
if ((1 << resolutions_vesa[i].index) & mask) {
|
||||
*vres = resolutions_vesa[i].vres;
|
||||
*hres = resolutions_vesa[i].hres;
|
||||
|
|
@ -209,7 +252,7 @@ int vfd_get_hh_resolution(uint32_t mask, int *hres, int *vres)
|
|||
if (!mask)
|
||||
return -EINVAL;
|
||||
|
||||
for (i = 0; resolutions_hh[i].hres != 0; i++) {
|
||||
for (i = SHL_ARRAY_LENGTH(resolutions_hh); i >= 0; --i) {
|
||||
if ((1 << resolutions_hh[i].index) & mask) {
|
||||
*vres = resolutions_hh[i].vres;
|
||||
*hres = resolutions_hh[i].hres;
|
||||
|
|
@ -218,3 +261,436 @@ int vfd_get_hh_resolution(uint32_t mask, int *hres, int *vres)
|
|||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int vfd_get_mask_from_resolution(int hres,
|
||||
int vres,
|
||||
enum wfd_resolution_standard *out_std,
|
||||
uint32_t *out_mask)
|
||||
{
|
||||
struct {
|
||||
const struct wfd_resolution *entries;
|
||||
size_t n_entries;
|
||||
} tbls[] = {
|
||||
[WFD_RESOLUTION_STANDARD_CEA] = {
|
||||
resolutions_cea,
|
||||
SHL_ARRAY_LENGTH(resolutions_cea)
|
||||
},
|
||||
[WFD_RESOLUTION_STANDARD_VESA] = {
|
||||
resolutions_vesa,
|
||||
SHL_ARRAY_LENGTH(resolutions_vesa)
|
||||
},
|
||||
[WFD_RESOLUTION_STANDARD_HH] = {
|
||||
resolutions_hh,
|
||||
SHL_ARRAY_LENGTH(resolutions_hh)
|
||||
},
|
||||
};
|
||||
enum wfd_resolution_standard std = WFD_RESOLUTION_STANDARD_CEA;
|
||||
uint32_t mask = 0;
|
||||
size_t n;
|
||||
|
||||
for(; std <= WFD_RESOLUTION_STANDARD_HH; ++ std) {
|
||||
for(n = 0; n < tbls[std].n_entries; ++ n) {
|
||||
if(hres == tbls[std].entries[n].hres &&
|
||||
vres == tbls[std].entries[n].vres) {
|
||||
mask |= 1 << n;
|
||||
}
|
||||
}
|
||||
|
||||
if(mask) {
|
||||
if(out_mask) {
|
||||
*out_mask = mask;
|
||||
}
|
||||
if(out_std) {
|
||||
*out_std = std;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
static int wfd_sube_parse_device_info(const char *in, union wfd_sube *out)
|
||||
{
|
||||
int r = sscanf(in, "%4hx%4hx%4hx",
|
||||
&out->dev_info.dev_info,
|
||||
&out->dev_info.rtsp_port,
|
||||
&out->dev_info.max_throughput);
|
||||
|
||||
return 3 == r ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
static int wfd_sube_parse_audio_formats(const char *in, union wfd_sube *out)
|
||||
{
|
||||
int r = sscanf(in, "%4x%1hhx%4x%1hhx%4x%1hhx",
|
||||
&out->audio_formats.lpcm_modes,
|
||||
&out->audio_formats.lpcm_dec_latency,
|
||||
&out->audio_formats.aac_modes,
|
||||
&out->audio_formats.aac_dec_latency,
|
||||
&out->audio_formats.ac3_modes,
|
||||
&out->audio_formats.ac3_dec_latency);
|
||||
|
||||
return 6 == r ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
static int wfd_sube_parse_video_formats(const char *in, union wfd_sube *out)
|
||||
{
|
||||
int r = sscanf(in, "%4x%4x%4x%1hhx%1hhx%1hhx%1hhx%2hx%2hx%1hhx",
|
||||
&out->video_formats.cea,
|
||||
&out->video_formats.vesa,
|
||||
&out->video_formats.hh,
|
||||
&out->video_formats.native,
|
||||
&out->video_formats.profiles,
|
||||
&out->video_formats.levels,
|
||||
&out->video_formats.latency,
|
||||
&out->video_formats.min_slice_size,
|
||||
&out->video_formats.slice_enc_params,
|
||||
&out->video_formats.video_frame_rate_ctl);
|
||||
|
||||
return 12 == r ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
static int wfd_sube_parse_ext_caps(const char *in, union wfd_sube *out)
|
||||
{
|
||||
int r = sscanf(in, "%2hx", &out->extended_caps.caps);
|
||||
|
||||
return 1 == r ? 0 : -EINVAL;
|
||||
}
|
||||
|
||||
int wfd_sube_parse(const char *in, union wfd_sube *out)
|
||||
{
|
||||
uint8_t id;
|
||||
int 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;
|
||||
union wfd_sube sube;
|
||||
int r;
|
||||
|
||||
if(SHL_ARRAY_LENGTH(parser_tbl) <= id) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = sscanf(in, "%4hx", &len);
|
||||
if(1 > r) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if(parser_tbl[id].len != len) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if(!parser_tbl[id].parser) {
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
r = (*parser_tbl[id].parser)(in + 4, &sube);
|
||||
if(0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
sube.id = id;
|
||||
|
||||
if(out) {
|
||||
*out = sube;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int wfd_video_formats_from_string(const char *l,
|
||||
struct wfd_video_formats **out)
|
||||
{
|
||||
_shl_free_ struct wfd_video_formats *f = NULL;
|
||||
uint8_t native, pref_disp_mode_sup;
|
||||
int r, i, n_codecs;
|
||||
const char *p;
|
||||
char max_hres[5], max_vres[5];
|
||||
|
||||
assert(l);
|
||||
|
||||
if(!strncmp("none", l, 4)) {
|
||||
if(out) {
|
||||
*out = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = sscanf(l, "%2hhx %2hhx", &native, &pref_disp_mode_sup);
|
||||
if(2 != r) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
l += 6;
|
||||
|
||||
for(p = l, n_codecs = 1; (p = strchrnul(p, ','), *p); ++ n_codecs, ++ p);
|
||||
|
||||
f = malloc(sizeof(*f) + (sizeof(f->h264_codecs[0]) * n_codecs));
|
||||
if(!f) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for(i = 0; i < n_codecs; i ++) {
|
||||
r = sscanf(l,
|
||||
"%2hhx %2hhx %8x %8x %8x %2hhx %4hx %4hx %2hhx %4s %4s",
|
||||
&f->h264_codecs[i].profile,
|
||||
&f->h264_codecs[i].level,
|
||||
&f->h264_codecs[i].cea_sup,
|
||||
&f->h264_codecs[i].vesa_sup,
|
||||
&f->h264_codecs[i].hh_sup,
|
||||
&f->h264_codecs[i].latency,
|
||||
&f->h264_codecs[i].min_slice_size,
|
||||
&f->h264_codecs[i].slice_enc_params,
|
||||
&f->h264_codecs[i].frame_rate_ctrl_sup,
|
||||
max_hres,
|
||||
max_vres);
|
||||
if(11 != r) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
|
||||
f->h264_codecs[i].max_hres = !strncmp("none", max_hres, 4)
|
||||
? 0
|
||||
: strtoul(max_hres, NULL, 16);
|
||||
if(errno) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
f->h264_codecs[i].max_vres = !strncmp("none", max_vres, 4)
|
||||
? 0
|
||||
: strtoul(max_vres, NULL, 16);
|
||||
if(errno) {
|
||||
return -errno;
|
||||
}
|
||||
|
||||
l += 60;
|
||||
}
|
||||
|
||||
f->native = native;
|
||||
f->pref_disp_mode_sup = pref_disp_mode_sup;
|
||||
f->n_h264_codecs = n_codecs;
|
||||
|
||||
if(out) {
|
||||
*out = f;
|
||||
f = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline const char * int16_to_res(int16_t v, char *b)
|
||||
{
|
||||
if(!v) {
|
||||
return "none";
|
||||
}
|
||||
|
||||
sprintf(b, "%04hX", v);
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
int wfd_video_formats_to_string(struct wfd_video_formats *f, char **out)
|
||||
{
|
||||
_shl_free_ char *s = NULL;
|
||||
char *p, b1[5], b2[5];
|
||||
size_t len = 6;
|
||||
int r, i;
|
||||
|
||||
assert(f);
|
||||
assert(out);
|
||||
|
||||
len += (f->n_h264_codecs ? f->n_h264_codecs * 60 : 6);
|
||||
p = s = malloc(len);
|
||||
if(!s) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r = snprintf(p, len, "%02hhX %02hhX ", f->native, f->pref_disp_mode_sup);
|
||||
if(0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
p += r;
|
||||
len -= r;
|
||||
|
||||
if(!f->n_h264_codecs) {
|
||||
strcat(p, " none");
|
||||
goto end;
|
||||
}
|
||||
|
||||
for(i = 0; i < f->n_h264_codecs; ++ i) {
|
||||
r = snprintf(p, len,
|
||||
"%02hhX %02hhX %08X %08X %08X %02hhX %04hX %04hX %02hhX %s %s, ",
|
||||
f->h264_codecs[i].profile,
|
||||
f->h264_codecs[i].level,
|
||||
f->h264_codecs[i].cea_sup,
|
||||
f->h264_codecs[i].vesa_sup,
|
||||
f->h264_codecs[i].hh_sup,
|
||||
f->h264_codecs[i].latency,
|
||||
f->h264_codecs[i].min_slice_size,
|
||||
f->h264_codecs[i].slice_enc_params,
|
||||
f->h264_codecs[i].frame_rate_ctrl_sup,
|
||||
int16_to_res(f->h264_codecs[i].max_hres, b1),
|
||||
int16_to_res(f->h264_codecs[i].max_vres, b2));
|
||||
if(0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
p += r;
|
||||
len -= r;
|
||||
}
|
||||
|
||||
p[-2] = '\0';
|
||||
|
||||
end:
|
||||
*out = s;
|
||||
s = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wfd_audio_format_from_string(const char *s, enum wfd_audio_format *f)
|
||||
{
|
||||
enum wfd_audio_format t = WFD_AUDIO_FORMAT_UNKNOWN;
|
||||
if(s) {
|
||||
if(!strncmp("LPCM", s, 4)) {
|
||||
t = WFD_AUDIO_FORMAT_LPCM;
|
||||
}
|
||||
else if(!strncmp("AAC", s, 3)) {
|
||||
t = WFD_AUDIO_FORMAT_AAC;
|
||||
}
|
||||
else if(!strncmp("AC3", s, 3)) {
|
||||
t = WFD_AUDIO_FORMAT_AC3;
|
||||
}
|
||||
|
||||
if(WFD_AUDIO_FORMAT_UNKNOWN != t) {
|
||||
if(f) {
|
||||
*f = t;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
const char * wfd_audio_format_to_string(enum wfd_audio_format f)
|
||||
{
|
||||
switch(f) {
|
||||
case WFD_AUDIO_FORMAT_LPCM:
|
||||
return "LPCM";
|
||||
case WFD_AUDIO_FORMAT_AAC:
|
||||
return "AAC";
|
||||
case WFD_AUDIO_FORMAT_AC3:
|
||||
return "AC3";
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int wfd_audio_codecs_from_string(const char *l,
|
||||
struct wfd_audio_codecs **out)
|
||||
{
|
||||
_shl_free_ struct wfd_audio_codecs *c = NULL;
|
||||
_shl_free_ char *f = NULL;
|
||||
int r, i, n_caps;
|
||||
const char *p;
|
||||
|
||||
assert(l);
|
||||
|
||||
if(!strncmp("none", l, 4)) {
|
||||
if(out) {
|
||||
*out = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
for(p = l, n_caps = 1; (p = strchrnul(p, ','), *p); ++ n_caps, ++ p);
|
||||
|
||||
c = malloc(sizeof(struct wfd_audio_codecs)
|
||||
+ (sizeof(c->caps[0]) * n_caps));
|
||||
|
||||
for(i = 0; i < n_caps; i ++) {
|
||||
r = sscanf(l, "%ms %8x %2hhx",
|
||||
&f,
|
||||
&c->caps[i].modes,
|
||||
&c->caps[i].latency);
|
||||
if(r != 3) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = wfd_audio_format_from_string(f, &c->caps[i].format);
|
||||
if(0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
l += 16;
|
||||
if(WFD_AUDIO_FORMAT_LPCM == c->caps[i].format) {
|
||||
++ l;
|
||||
}
|
||||
|
||||
free(f);
|
||||
f = NULL;
|
||||
}
|
||||
|
||||
c->n_caps = n_caps;
|
||||
|
||||
if(out) {
|
||||
*out = c;
|
||||
c = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wfd_audio_codecs_to_string(struct wfd_audio_codecs *c, char **out)
|
||||
{
|
||||
_shl_free_ char *s = NULL;
|
||||
char *p;
|
||||
int r, i;
|
||||
size_t len;
|
||||
|
||||
assert(c);
|
||||
assert(out);
|
||||
|
||||
len = c->n_caps * 18;
|
||||
p = s = malloc(len);
|
||||
if(!s) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for(i = 0; i < c->n_caps; i ++) {
|
||||
r = snprintf(p, len, "%s %08X %02hhX, ",
|
||||
wfd_audio_format_to_string(c->caps[i].format),
|
||||
c->caps[i].modes,
|
||||
c->caps[i].latency);
|
||||
if(0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
p += r;
|
||||
len -= r;
|
||||
}
|
||||
|
||||
p[-2] = '\n';
|
||||
*out = s;
|
||||
s = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
190
src/ctl/wfd.h
190
src/ctl/wfd.h
|
|
@ -21,9 +21,199 @@
|
|||
#ifndef WFD_H
|
||||
#define WFD_H
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define wfd_sube_is_device_info(w) (WFD_SUBE_ID_DEVICE_INFO == (w)->id)
|
||||
|
||||
#define WFD_DEVINFO_DEV_TYPE_MASK (0x3 << 0)
|
||||
#define WFD_DEVINFO_SRC_COUPLED_SINK_MASK (0x1 << 2)
|
||||
#define WFD_DEVINFO_SINK_COUPLED_SINK_MASK (0x1 << 3)
|
||||
#define WFD_DEVINFO_SESSION_MASK (0x3 << 4)
|
||||
#define WFD_DEVINFO_WSD_MASK (0x1 << 6)
|
||||
#define WFD_DEVINFO_PC_MASK (0x1 << 7)
|
||||
#define WFD_DEVINFO_CP_MASK (0x1 << 8)
|
||||
#define WFD_DEVINFO_TIME_SYNC_MASK (0x1 << 9)
|
||||
#define WFD_DEVINFO_PRI_SINK_AUDIO_MASK (0x1 << 10)
|
||||
#define WFD_DEVINFO_SRC_AUDIO_ONLY_MASK (0x1 << 11)
|
||||
|
||||
enum wfd_sube_id {
|
||||
WFD_SUBE_ID_DEVICE_INFO,
|
||||
WFD_SUBE_ID_ASSOCIATED_BSSID,
|
||||
WFD_SUBE_ID_AUDIO_FORMATS,
|
||||
WFD_SUBE_ID_VIDEO_FORMATS,
|
||||
WFD_SUBE_ID_3D_VIDEO_FORMATS,
|
||||
WFD_SUBE_ID_CONTENT_PROTECTION,
|
||||
WFD_SUBE_ID_COUPLED_SINK_INFO,
|
||||
WFD_SUBE_ID_WFD_EXT_CAPS,
|
||||
WFD_SUBE_ID_LOCAL_IP_ADDR,
|
||||
WFD_SUBE_ID_RESERVED,
|
||||
};
|
||||
|
||||
enum wfd_resolution_standard
|
||||
{
|
||||
WFD_RESOLUTION_STANDARD_CEA,
|
||||
WFD_RESOLUTION_STANDARD_VESA,
|
||||
WFD_RESOLUTION_STANDARD_HH,
|
||||
};
|
||||
|
||||
enum wfd_audio_format
|
||||
{
|
||||
WFD_AUDIO_FORMAT_UNKNOWN,
|
||||
WFD_AUDIO_FORMAT_LPCM,
|
||||
WFD_AUDIO_FORMAT_AAC,
|
||||
WFD_AUDIO_FORMAT_AC3
|
||||
};
|
||||
|
||||
union wfd_sube
|
||||
{
|
||||
enum wfd_sube_id id;
|
||||
|
||||
struct {
|
||||
enum wfd_sube_id id;
|
||||
uint16_t dev_info;
|
||||
uint16_t rtsp_port;
|
||||
uint16_t max_throughput;
|
||||
} dev_info;
|
||||
|
||||
struct {
|
||||
enum wfd_sube_id id;
|
||||
uint32_t cea;
|
||||
uint32_t vesa;
|
||||
uint32_t hh;
|
||||
uint8_t native;
|
||||
uint8_t profiles;
|
||||
uint8_t levels;
|
||||
uint8_t latency;
|
||||
uint16_t min_slice_size;
|
||||
uint16_t slice_enc_params;
|
||||
uint8_t video_frame_rate_ctl;
|
||||
} video_formats;
|
||||
|
||||
struct {
|
||||
enum wfd_sube_id id;
|
||||
uint32_t lpcm_modes;
|
||||
uint8_t lpcm_dec_latency;
|
||||
uint32_t aac_modes;
|
||||
uint8_t aac_dec_latency;
|
||||
uint32_t ac3_modes;
|
||||
uint8_t ac3_dec_latency;
|
||||
} audio_formats;
|
||||
|
||||
struct {
|
||||
enum wfd_sube_id id;
|
||||
uint16_t caps;
|
||||
} extended_caps;
|
||||
};
|
||||
|
||||
struct wfd_video_formats
|
||||
{
|
||||
uint8_t native;
|
||||
uint8_t pref_disp_mode_sup;
|
||||
size_t n_h264_codecs;
|
||||
struct {
|
||||
uint8_t profile;
|
||||
uint8_t level;
|
||||
uint32_t cea_sup;
|
||||
uint32_t vesa_sup;
|
||||
uint32_t hh_sup;
|
||||
uint8_t latency;
|
||||
uint16_t min_slice_size;
|
||||
uint16_t slice_enc_params;
|
||||
uint8_t frame_rate_ctrl_sup;
|
||||
uint16_t max_hres;
|
||||
uint16_t max_vres;
|
||||
} h264_codecs[0];
|
||||
};
|
||||
|
||||
struct wfd_audio_codecs
|
||||
{
|
||||
size_t n_caps;
|
||||
struct {
|
||||
enum wfd_audio_format format;
|
||||
uint32_t modes;
|
||||
uint8_t latency;
|
||||
} caps[0];
|
||||
};
|
||||
|
||||
struct wfd_resolution
|
||||
{
|
||||
uint16_t index;
|
||||
uint16_t hres;
|
||||
uint16_t vres;
|
||||
uint16_t fps;
|
||||
bool progressive: 1;
|
||||
};
|
||||
|
||||
void wfd_print_resolutions(char * prefix);
|
||||
int wfd_get_resolutions(enum wfd_resolution_standard std,
|
||||
int index,
|
||||
struct wfd_resolution *out);
|
||||
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 vfd_get_mask_from_resolution(int hres,
|
||||
int vres,
|
||||
enum wfd_resolution_standard *out_std,
|
||||
uint32_t *out_mask);
|
||||
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);
|
||||
int wfd_video_formats_from_string(const char *l,
|
||||
struct wfd_video_formats **out);
|
||||
static inline void wfd_video_formats_free(struct wfd_video_formats *f) { free(f); }
|
||||
int wfd_video_formats_to_string(struct wfd_video_formats *f, char **out);
|
||||
int wfd_audio_codecs_from_string(const char *l,
|
||||
struct wfd_audio_codecs **out);
|
||||
static inline void wfd_audio_codecs_free(struct wfd_audio_codecs *c) { free(c); }
|
||||
int wfd_audio_codecs_to_string(struct wfd_audio_codecs *c, char **out);
|
||||
int wfd_audio_format_from_string(const char *s, enum wfd_audio_format *f);
|
||||
const char * wfd_audio_format_to_string(enum wfd_audio_format f);
|
||||
|
||||
static inline int wfd_sube_device_get_type(const union wfd_sube *sube)
|
||||
{
|
||||
assert(wfd_sube_is_device_info(sube));
|
||||
|
||||
return (WFD_DEVINFO_DEV_TYPE_MASK & sube->dev_info.dev_info);
|
||||
}
|
||||
|
||||
static inline bool wfd_sube_device_is_source(const union wfd_sube *sube)
|
||||
{
|
||||
switch(wfd_sube_device_get_type(sube)) {
|
||||
case 0:
|
||||
case 3:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool wfd_sube_device_is_sink(const union wfd_sube *sube)
|
||||
{
|
||||
switch(wfd_sube_device_get_type(sube)) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline int wfd_sube_src_support_coupled_sink(const union wfd_sube *sube)
|
||||
{
|
||||
assert(WFD_SUBE_ID_DEVICE_INFO == sube->id);
|
||||
|
||||
return !!(WFD_DEVINFO_SRC_COUPLED_SINK_MASK & sube->dev_info.dev_info);
|
||||
}
|
||||
|
||||
static inline uint16_t wfd_sube_device_get_rtsp_port(const union wfd_sube *sube)
|
||||
{
|
||||
assert(wfd_sube_is_device_info(sube));
|
||||
|
||||
return sube->dev_info.rtsp_port;
|
||||
}
|
||||
|
||||
#endif /* WFD_H */
|
||||
|
|
|
|||
|
|
@ -240,6 +240,9 @@ static int cmd_set_friendly_name(char **args, unsigned int n)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (!l->managed)
|
||||
return log_EUNMANAGED();
|
||||
|
||||
return ctl_link_set_friendly_name(l, name);
|
||||
}
|
||||
|
||||
|
|
@ -310,10 +313,8 @@ static int cmd_p2p_scan(char **args, unsigned int n)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (!l->managed) {
|
||||
cli_printf("link %s not managed\n", l->label);
|
||||
return 0;
|
||||
}
|
||||
if (!l->managed)
|
||||
return log_EUNMANAGED();
|
||||
|
||||
return ctl_link_set_p2p_scanning(l, !stop);
|
||||
}
|
||||
|
|
@ -362,10 +363,8 @@ static int cmd_connect(char **args, unsigned int n)
|
|||
pin = "";
|
||||
}
|
||||
|
||||
if (!p->l->managed) {
|
||||
cli_printf("link %s not managed\n", p->l->label);
|
||||
return 0;
|
||||
}
|
||||
if (!p->l->managed)
|
||||
return log_EUNMANAGED();
|
||||
|
||||
return ctl_peer_connect(p, prov, pin);
|
||||
}
|
||||
|
|
@ -389,10 +388,8 @@ static int cmd_disconnect(char **args, unsigned int n)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (!p->l->managed) {
|
||||
cli_printf("link %s not managed\n", p->l->label);
|
||||
return 0;
|
||||
}
|
||||
if (!p->l->managed)
|
||||
return log_EUNMANAGED();
|
||||
|
||||
return ctl_peer_disconnect(p);
|
||||
}
|
||||
|
|
@ -605,7 +602,7 @@ static int parse_argv(int argc, char *argv[])
|
|||
while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
cli_fn_help();
|
||||
cli_fn_help();
|
||||
return 0;
|
||||
case ARG_HELP_COMMANDS:
|
||||
return cli_help(cli_cmds, 20);
|
||||
|
|
@ -638,22 +635,22 @@ int main(int argc, char **argv)
|
|||
|
||||
setlocale(LC_ALL, "");
|
||||
|
||||
GKeyFile* gkf = load_ini_file();
|
||||
GKeyFile* gkf = load_ini_file();
|
||||
|
||||
if (gkf) {
|
||||
gchar* log_level;
|
||||
log_level = g_key_file_get_string (gkf, "wifictl", "log-journal-level", NULL);
|
||||
if (log_level) {
|
||||
log_max_sev = log_parse_arg(log_level);
|
||||
g_free(log_level);
|
||||
}
|
||||
log_level = g_key_file_get_string (gkf, "wifictl", "log-level", NULL);
|
||||
if (log_level) {
|
||||
cli_max_sev = log_parse_arg(log_level);
|
||||
g_free(log_level);
|
||||
}
|
||||
g_key_file_free(gkf);
|
||||
}
|
||||
if (gkf) {
|
||||
gchar* log_level;
|
||||
log_level = g_key_file_get_string (gkf, "wifictl", "log-journal-level", NULL);
|
||||
if (log_level) {
|
||||
log_max_sev = log_parse_arg(log_level);
|
||||
g_free(log_level);
|
||||
}
|
||||
log_level = g_key_file_get_string (gkf, "wifictl", "log-level", NULL);
|
||||
if (log_level) {
|
||||
cli_max_sev = log_parse_arg(log_level);
|
||||
g_free(log_level);
|
||||
}
|
||||
g_key_file_free(gkf);
|
||||
}
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r < 0)
|
||||
|
|
|
|||
|
|
@ -456,7 +456,7 @@ static void init_packet(GDHCPServer *dhcp_server, struct dhcp_packet *packet,
|
|||
packet->gateway_nip = client_packet->gateway_nip;
|
||||
packet->ciaddr = client_packet->ciaddr;
|
||||
dhcp_add_option_uint32(packet, DHCP_SERVER_ID,
|
||||
dhcp_server->server_nip);
|
||||
get_be32(&dhcp_server->server_nip));
|
||||
}
|
||||
|
||||
static void add_option(gpointer key, gpointer value, gpointer user_data)
|
||||
|
|
@ -675,7 +675,7 @@ static gboolean listener_event(GIOChannel *channel, GIOCondition condition,
|
|||
|
||||
server_id_option = dhcp_get_option(&packet, DHCP_SERVER_ID);
|
||||
if (server_id_option) {
|
||||
uint32_t server_nid = get_be32(server_id_option);
|
||||
uint32_t server_nid = get_unaligned((uint32_t *) server_id_option);
|
||||
|
||||
if (server_nid != dhcp_server->server_nip)
|
||||
return TRUE;
|
||||
|
|
|
|||
43
src/disp/CMakeLists.txt
Normal file
43
src/disp/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
find_package(PkgConfig)
|
||||
pkg_check_modules (GLIB2 REQUIRED glib-2.0)
|
||||
link_directories( ${GLIB2_LIBRARY_DIRS})
|
||||
include_directories( ${GLIB2_INCLUDE_DIRS})
|
||||
find_package(Readline REQUIRED)
|
||||
pkg_check_modules (GLIB2 REQUIRED glib-2.0)
|
||||
|
||||
set(miracle-dispd_SRCS
|
||||
../ctl/ctl-cli.c
|
||||
../ctl/ctl-wifi.c
|
||||
../ctl/wfd.c
|
||||
dispd-sink.c
|
||||
dispd-dbus.c
|
||||
dispd-session.c
|
||||
dispd-out-session.c
|
||||
dispd.c
|
||||
dispd-encoder.c
|
||||
dispd-arg.c
|
||||
)
|
||||
|
||||
include_directories(${CMAKE_SOURCE_DIR}/src/ctl
|
||||
${CMAKE_BINARY_DIR}
|
||||
${CMAKE_SOURCE_DIR}/src
|
||||
${CMAKE_SOURCE_DIR}/src/shared
|
||||
)
|
||||
|
||||
add_executable(miracle-dispd ${miracle-dispd_SRCS})
|
||||
|
||||
install(TARGETS miracle-dispd DESTINATION bin)
|
||||
|
||||
if(READLINE_FOUND)
|
||||
message(STATUS "Compiling with Readline support")
|
||||
set_property(TARGET miracle-dispd
|
||||
APPEND
|
||||
PROPERTY COMPILE_DEFINITIONS HAVE_READLINE
|
||||
)
|
||||
target_link_libraries(miracle-dispd ${READLINE_LIBRARY})
|
||||
endif(READLINE_FOUND)
|
||||
|
||||
target_link_libraries(miracle-dispd miracle-shared)
|
||||
target_link_libraries(miracle-dispd m)
|
||||
target_link_libraries(miracle-dispd ${GLIB2_LIBRARIES})
|
||||
|
||||
61
src/disp/dispd-arg.c
Normal file
61
src/disp/dispd-arg.c
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
/*
|
||||
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||
*
|
||||
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||
*
|
||||
* MiracleCast is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MiracleCast is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <errno.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include "dispd-arg.h"
|
||||
|
||||
int dispd_arg_list_new(struct dispd_arg_list **out)
|
||||
{
|
||||
assert(out);
|
||||
|
||||
struct dispd_arg_list *l = calloc(1, sizeof(struct dispd_arg_list));
|
||||
if(!l) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
l->dynamic = true;
|
||||
|
||||
*out = l;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dispd_arg_list_clear(struct dispd_arg_list *l)
|
||||
{
|
||||
int i;
|
||||
struct dispd_arg *arg;
|
||||
|
||||
if(!l || !l->dynamic) {
|
||||
return;
|
||||
}
|
||||
|
||||
arg = l->discrete ? l->argv : l->args;
|
||||
for(i = 0; i < l->len; i ++) {
|
||||
if((DISPD_ARG_STR == arg->type || DISPD_ARG_PTR == arg->type)
|
||||
&& arg->ptr && arg->free) {
|
||||
(*arg->free)(arg->ptr);
|
||||
}
|
||||
}
|
||||
|
||||
if(l->discrete) {
|
||||
free(l->argv);
|
||||
}
|
||||
}
|
||||
|
||||
242
src/disp/dispd-arg.h
Normal file
242
src/disp/dispd-arg.h
Normal file
|
|
@ -0,0 +1,242 @@
|
|||
/*
|
||||
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||
*
|
||||
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||
*
|
||||
* MiracleCast is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MiracleCast is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include "shl_macro.h"
|
||||
|
||||
#ifndef DISPD_ARG_H
|
||||
#define DISPD_ARG_H
|
||||
|
||||
#define dispd_arg_i8(_v) { .type = DISPD_ARG_I8, .i8 = (_v) }
|
||||
#define dispd_arg_u8(_v) { .type = DISPD_ARG_U8, .u8 = (_v) }
|
||||
#define dispd_arg_i16(_v) { .type = DISPD_ARG_I16, .i16 = (_v) }
|
||||
#define dispd_arg_u16(_v) { .type = DISPD_ARG_U16, .u16 = (_v) }
|
||||
#define dispd_arg_i32(_v) { .type = DISPD_ARG_I32, .i32 = (_v) }
|
||||
#define dispd_arg_u32(_v) { .type = DISPD_ARG_U32, .u32 = (_v) }
|
||||
#define dispd_arg_i64(_v) { .type = DISPD_ARG_I64, .i64 = (_v) }
|
||||
#define dispd_arg_u64(_v) { .type = DISPD_ARG_U64, .u64 = (_v) }
|
||||
#define dispd_arg_cstr(_v) { .type = DISPD_ARG_CSTR, .ptr = (_v) }
|
||||
#define dispd_arg_cptr(_v) { .type = DISPD_ARG_CPTR, .ptr = (_v) }
|
||||
#define dispd_arg_arg_list(_v) { \
|
||||
.type = DISPD_ARG_ARG_LIST, \
|
||||
.ptr = &(struct dispd_arg_list) dispd_arg_list(_v) \
|
||||
}
|
||||
#define dispd_arg_dict(_k, _v) { \
|
||||
.type = DISPD_ARG_DICT, \
|
||||
.k = (struct dispd_arg[]){_k}, \
|
||||
.v = (struct dispd_arg[]){_v} \
|
||||
}
|
||||
|
||||
#if INT_MAX == INT64_MAX
|
||||
#define dispd_arg_i(_v) dispd_arg_i64(_v)
|
||||
#define dispd_arg_u(_v) dispd_arg_u64(_v)
|
||||
#elif INT_MAX == INT32_MAX
|
||||
#define dispd_arg_i(_v) dispd_arg_i32(_v)
|
||||
#define dispd_arg_u(_v) dispd_arg_u32(_v)
|
||||
#else
|
||||
#error unsupported int size
|
||||
#endif
|
||||
|
||||
#define dispd_arg_type_id(_t) _Generic((_t), \
|
||||
int8_t: DISPD_ARG_I8, \
|
||||
uint8_t: DISPD_ARG_U8, \
|
||||
int16_t: DISPD_ARG_I16, \
|
||||
uint16_t: DISPD_ARG_U16, \
|
||||
int32_t: DISPD_ARG_I32, \
|
||||
uint32_t: DISPD_ARG_U32, \
|
||||
int64_t: DISPD_ARG_I64, \
|
||||
uint64_t: DISPD_ARG_U64, \
|
||||
const char *: DISPD_ARG_CSTR, \
|
||||
const dispd_arg_list *: DISPD_ARG_ARG_LIST, \
|
||||
char *: DISPD_ARG_STR, \
|
||||
void *: DISPD_ARG_PTR, \
|
||||
default: DISPD_ARG_CPTR \
|
||||
)
|
||||
|
||||
#define dispd_arg_list(...) { \
|
||||
.argv = (struct dispd_arg[]) { \
|
||||
__VA_ARGS__ \
|
||||
}, \
|
||||
.discrete = true, \
|
||||
.dynamic = false, \
|
||||
.len = (sizeof((struct dispd_arg[]){ __VA_ARGS__ })/sizeof(struct dispd_arg)) \
|
||||
}
|
||||
|
||||
#define dispd_arg_get(_a, _v) ({ \
|
||||
*(_v) = _Generic(*(_v), \
|
||||
int8_t: dispd_arg_get_i8, \
|
||||
uint8_t: dispd_arg_get_u8, \
|
||||
int16_t: dispd_arg_get_i16, \
|
||||
uint16_t: dispd_arg_get_u16, \
|
||||
int32_t: dispd_arg_get_i32, \
|
||||
uint32_t: dispd_arg_get_u32, \
|
||||
int64_t: dispd_arg_get_i64, \
|
||||
uint64_t: dispd_arg_get_u64, \
|
||||
char *: dispd_arg_get_str, \
|
||||
const struct dispd_arg_list *: dispd_arg_get_arg_list, \
|
||||
const char *: dispd_arg_get_cstr, \
|
||||
void *: dispd_arg_get_ptr, \
|
||||
default: dispd_arg_get_cptr \
|
||||
)(_a); \
|
||||
})
|
||||
|
||||
#define dispd_arg_get_dictk(_a, _k) ({ \
|
||||
assert(_a); \
|
||||
assert(DISPD_ARG_DICT == (_a)->type); \
|
||||
dispd_arg_get((_a)->k, (_k)); \
|
||||
})
|
||||
|
||||
#define dispd_arg_get_dictv(_a, _v) ({ \
|
||||
assert(_a); \
|
||||
assert(DISPD_ARG_DICT == (_a)->type); \
|
||||
dispd_arg_get((_a)->v, (_v)); \
|
||||
})
|
||||
|
||||
#define dispd_arg_get_dict(_a, _k, _v) ({ \
|
||||
assert(_a); \
|
||||
assert(DISPD_ARG_DICT == (_a)->type); \
|
||||
dispd_arg_get_dictk(_a, _k); \
|
||||
dispd_arg_get_dictv(_a, _v); \
|
||||
})
|
||||
|
||||
#define dispd_arg_list_get(_l, _i, _v) ({ \
|
||||
dispd_arg_get(dispd_arg_list_at((_l), (_i)), (_v)); \
|
||||
})
|
||||
|
||||
#define dispd_arg_list_get_dictk(_l, _i, _k) ({ \
|
||||
dispd_arg_get_dictk(dispd_arg_list_at((_l), (_i)), (_k)); \
|
||||
})
|
||||
|
||||
#define dispd_arg_list_get_dictv(_l, _i, _v) ({ \
|
||||
dispd_arg_get_dictv(dispd_arg_list_at((_l), (_i)), (_v)); \
|
||||
})
|
||||
|
||||
#define dispd_arg_list_get_dict(_l, _i, _k, _v) ({ \
|
||||
dispd_arg_get_dict(dispd_arg_list_at((_l), (_i)), (_k), (_v)); \
|
||||
})
|
||||
|
||||
enum dispd_arg_type
|
||||
{
|
||||
DISPD_ARG_NONE,
|
||||
DISPD_ARG_I8,
|
||||
DISPD_ARG_I16,
|
||||
DISPD_ARG_I32,
|
||||
DISPD_ARG_I64,
|
||||
DISPD_ARG_U8,
|
||||
DISPD_ARG_U16,
|
||||
DISPD_ARG_U32,
|
||||
DISPD_ARG_U64,
|
||||
DISPD_ARG_STR,
|
||||
DISPD_ARG_CSTR,
|
||||
DISPD_ARG_PTR,
|
||||
DISPD_ARG_CPTR,
|
||||
DISPD_ARG_DICT,
|
||||
DISPD_ARG_ARG_LIST,
|
||||
};
|
||||
|
||||
struct dispd_arg
|
||||
{
|
||||
enum dispd_arg_type type;
|
||||
union
|
||||
{
|
||||
int8_t i8;
|
||||
uint8_t u8;
|
||||
int16_t i16;
|
||||
uint16_t u16;
|
||||
int32_t i32;
|
||||
uint32_t u32;
|
||||
int64_t i64;
|
||||
uint64_t u64;
|
||||
struct {
|
||||
void *ptr;
|
||||
void (*free)(void *);
|
||||
};
|
||||
struct {
|
||||
struct dispd_arg *k;
|
||||
struct dispd_arg *v;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
struct dispd_arg_list
|
||||
{
|
||||
size_t len: sizeof(size_t) - 2;
|
||||
bool discrete: 1;
|
||||
bool dynamic: 1;
|
||||
|
||||
union {
|
||||
struct dispd_arg * argv;
|
||||
struct dispd_arg args[0];
|
||||
};
|
||||
};
|
||||
|
||||
int dispd_arg_list_new(struct dispd_arg_list **out);
|
||||
void dispd_arg_list_clear(struct dispd_arg_list *l);
|
||||
static inline void dispd_arg_list_free(struct dispd_arg_list *l);
|
||||
static inline const struct dispd_arg * dispd_arg_list_at(const struct dispd_arg_list *l,
|
||||
int i);
|
||||
|
||||
static inline enum dispd_arg_type dispd_arg_get_type(struct dispd_arg *a);
|
||||
static inline void dispd_arg_free_ptr(struct dispd_arg *a);
|
||||
static inline void dispd_arg_clear(struct dispd_arg *a);
|
||||
|
||||
static inline int8_t dispd_arg_get_i8(const struct dispd_arg *a);
|
||||
static inline void dispd_arg_set_i8(struct dispd_arg *a, int8_t v);
|
||||
|
||||
static inline uint8_t dispd_arg_get_u8(const struct dispd_arg *a);
|
||||
static inline void dispd_arg_set_u8(struct dispd_arg *a, uint8_t v);
|
||||
|
||||
static inline int16_t dispd_arg_get_i16(const struct dispd_arg *a);
|
||||
static inline void dispd_arg_set_i16(struct dispd_arg *a, int16_t v);
|
||||
|
||||
static inline uint16_t dispd_arg_get_u16(const struct dispd_arg *a);
|
||||
static inline void dispd_arg_set_u16(struct dispd_arg *a, uint16_t v);
|
||||
|
||||
static inline int32_t dispd_arg_get_i32(const struct dispd_arg *a);
|
||||
static inline void dispd_arg_set_i32(struct dispd_arg *a, int32_t v);
|
||||
|
||||
static inline uint32_t dispd_arg_get_u32(const struct dispd_arg *a);
|
||||
static inline void dispd_arg_set_u32(struct dispd_arg *a, uint32_t v);
|
||||
|
||||
static inline int64_t dispd_arg_get_i64(const struct dispd_arg *a);
|
||||
static inline void dispd_arg_set_i64(struct dispd_arg *a, int64_t v);
|
||||
|
||||
static inline uint64_t dispd_arg_get_u64(const struct dispd_arg *a);
|
||||
static inline void dispd_arg_set_u64(struct dispd_arg *a, uint64_t v);
|
||||
|
||||
static inline const char * dispd_arg_get_cstr(const struct dispd_arg *a);
|
||||
static inline void dispd_arg_set_cstr(struct dispd_arg *a, const char * v);
|
||||
|
||||
static inline void dispd_arg_take_str(struct dispd_arg *a, char *v);
|
||||
static inline char * dispd_arg_get_str(const struct dispd_arg *a);
|
||||
static inline int dispd_arg_set_str(struct dispd_arg *a, const char *v);;
|
||||
|
||||
static inline const void * dispd_arg_get_cptr(const struct dispd_arg *a);
|
||||
static inline void dispd_arg_set_cptr(struct dispd_arg *a, const void * v);
|
||||
|
||||
static inline void dispd_arg_take_ptr(struct dispd_arg *a, void *v, void (*f)(void *));
|
||||
static inline void * dispd_arg_get_ptr(const struct dispd_arg *a);
|
||||
|
||||
static inline void dispd_arg_take_arg_list(struct dispd_arg *a, struct dispd_arg_list *l);
|
||||
static inline const struct dispd_arg_list * dispd_arg_get_arg_list(const struct dispd_arg *a);
|
||||
|
||||
#include "dispd-arg.inc"
|
||||
|
||||
#endif /* DISPD_ARG_H */
|
||||
192
src/disp/dispd-arg.inc
Normal file
192
src/disp/dispd-arg.inc
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
/*
|
||||
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||
*
|
||||
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||
*
|
||||
* MiracleCast is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MiracleCast is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#define dispd_arg_getter(_t, _s, _S) \
|
||||
static inline _t dispd_arg_get_##_s(const struct dispd_arg *a) \
|
||||
{ \
|
||||
assert(a); \
|
||||
assert(DISPD_ARG_##_S == a->type); \
|
||||
return a->_s; \
|
||||
}
|
||||
|
||||
#define dispd_arg_setter(_t, _s, _S) \
|
||||
static inline void dispd_arg_set_##_s(struct dispd_arg *a, _t v) \
|
||||
{ \
|
||||
assert(a); \
|
||||
assert(!a->type || DISPD_ARG_##_S == a->type); \
|
||||
*a = (struct dispd_arg) { .type = DISPD_ARG_##_S, ._s = v }; \
|
||||
}
|
||||
|
||||
dispd_arg_getter(int8_t, i8, I8)
|
||||
dispd_arg_setter(int8_t, i8, I8)
|
||||
dispd_arg_getter(uint8_t, u8, U8)
|
||||
dispd_arg_setter(uint8_t, u8, U8)
|
||||
dispd_arg_getter(int16_t, i16, I16)
|
||||
dispd_arg_setter(int16_t, i16, I16)
|
||||
dispd_arg_getter(uint16_t, u16, U16)
|
||||
dispd_arg_setter(uint16_t, u16, U16)
|
||||
dispd_arg_getter(int32_t, i32, I32)
|
||||
dispd_arg_setter(int32_t, i32, I32)
|
||||
dispd_arg_getter(uint32_t, u32, U32)
|
||||
dispd_arg_setter(uint32_t, u32, U32)
|
||||
dispd_arg_getter(int64_t, i64, I64)
|
||||
dispd_arg_setter(int64_t, i64, I64)
|
||||
dispd_arg_getter(uint64_t, u64, U64)
|
||||
dispd_arg_setter(uint64_t, u64, U64)
|
||||
|
||||
static inline void dispd_arg_list_free(struct dispd_arg_list *l)
|
||||
{
|
||||
dispd_arg_list_clear(l);
|
||||
free(l);
|
||||
}
|
||||
|
||||
static inline const struct dispd_arg * dispd_arg_list_at(const struct dispd_arg_list *l,
|
||||
int i)
|
||||
{
|
||||
assert(l);
|
||||
assert(i >= 0 && i < l->len);
|
||||
return l->discrete ? &l->argv[i] : &l->args[i];
|
||||
}
|
||||
|
||||
static inline enum dispd_arg_type dispd_arg_get_type(struct dispd_arg *a)
|
||||
{
|
||||
assert(a);
|
||||
|
||||
return a->type;
|
||||
}
|
||||
|
||||
static inline void dispd_arg_free_ptr(struct dispd_arg *a)
|
||||
{
|
||||
if(!a || (DISPD_ARG_STR != a->type && DISPD_ARG_PTR != a->type)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(a->ptr && a->free) {
|
||||
(*a->free)(a->ptr);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void dispd_arg_clear(struct dispd_arg *a)
|
||||
{
|
||||
if(a) {
|
||||
dispd_arg_free_ptr(a);
|
||||
memset(a, 0, sizeof(*a));
|
||||
}
|
||||
}
|
||||
|
||||
static inline const char * dispd_arg_get_cstr(const struct dispd_arg *a)
|
||||
{
|
||||
assert(a);
|
||||
assert(DISPD_ARG_CSTR == a->type || DISPD_ARG_STR == a->type);
|
||||
|
||||
return a->ptr;
|
||||
}
|
||||
|
||||
static inline void dispd_arg_set_cstr(struct dispd_arg *a, const char * v)
|
||||
{
|
||||
assert(a);
|
||||
assert(!a->type || DISPD_ARG_CSTR == a->type);
|
||||
|
||||
*a = (struct dispd_arg) { .type = DISPD_ARG_CSTR, .ptr = (void *) v };
|
||||
}
|
||||
|
||||
static inline char * dispd_arg_get_str(const struct dispd_arg *a)
|
||||
{
|
||||
assert(a);
|
||||
assert(DISPD_ARG_STR == a->type);
|
||||
|
||||
return a->ptr;
|
||||
}
|
||||
|
||||
static inline void dispd_arg_take_str(struct dispd_arg *a, char *v)
|
||||
{
|
||||
assert(a);
|
||||
assert(!a->type || DISPD_ARG_STR == a->type || DISPD_ARG_CSTR == a->type);
|
||||
|
||||
dispd_arg_free_ptr(a);
|
||||
*a = (struct dispd_arg) { .type = DISPD_ARG_STR, .ptr = v, .free = free };
|
||||
}
|
||||
|
||||
static inline int dispd_arg_set_str(struct dispd_arg *a, const char *v)
|
||||
{
|
||||
char *s;
|
||||
|
||||
assert(a);
|
||||
assert(!a->type || DISPD_ARG_STR == a->type);
|
||||
|
||||
s = strdup(v);
|
||||
if(!s) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
dispd_arg_take_str(a, s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline const void * dispd_arg_get_cptr(const struct dispd_arg *a)
|
||||
{
|
||||
assert(a);
|
||||
assert(DISPD_ARG_PTR <= a->type && DISPD_ARG_CPTR == a->type);
|
||||
|
||||
return a->ptr;
|
||||
}
|
||||
|
||||
static inline void dispd_arg_set_cptr(struct dispd_arg *a, const void * v)
|
||||
{
|
||||
assert(a);
|
||||
assert(!a->type || DISPD_ARG_CSTR == a->type);
|
||||
|
||||
*a = (struct dispd_arg) { .type = DISPD_ARG_CPTR, .ptr = (void *) v };
|
||||
}
|
||||
|
||||
static inline void * dispd_arg_get_ptr(const struct dispd_arg *a)
|
||||
{
|
||||
assert(a);
|
||||
assert(DISPD_ARG_PTR == a->type || DISPD_ARG_STR == a->type);
|
||||
|
||||
return a->ptr;
|
||||
}
|
||||
|
||||
static inline void dispd_arg_take_ptr(struct dispd_arg *a, void *v, void (*f)(void *))
|
||||
{
|
||||
assert(a);
|
||||
assert(!a->type || DISPD_ARG_PTR == a->type);
|
||||
|
||||
dispd_arg_free_ptr(a);
|
||||
*a = (struct dispd_arg) { .type = DISPD_ARG_PTR, .ptr = v, .free = f };
|
||||
}
|
||||
|
||||
static inline void dispd_arg_take_arg_list(struct dispd_arg *a, struct dispd_arg_list *l)
|
||||
{
|
||||
assert(a);
|
||||
assert(!a->type || DISPD_ARG_ARG_LIST == a->type);
|
||||
|
||||
dispd_arg_free_ptr(a);
|
||||
*a = (struct dispd_arg) { .type = DISPD_ARG_ARG_LIST,
|
||||
.ptr = l,
|
||||
.free = (void (*)(void *)) dispd_arg_list_free };
|
||||
}
|
||||
|
||||
static inline const struct dispd_arg_list * dispd_arg_get_arg_list(const struct dispd_arg *a)
|
||||
{
|
||||
assert(a);
|
||||
assert(DISPD_ARG_ARG_LIST == a->type);
|
||||
|
||||
return a->ptr;
|
||||
}
|
||||
943
src/disp/dispd-dbus.c
Normal file
943
src/disp/dispd-dbus.c
Normal file
|
|
@ -0,0 +1,943 @@
|
|||
/*
|
||||
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||
*
|
||||
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||
*
|
||||
* MiracleCast is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MiracleCast is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
#include "dispd.h"
|
||||
#include "util.h"
|
||||
#include "shl_log.h"
|
||||
#include "dispd-dbus.h"
|
||||
|
||||
#define dispd_dbus_object_added(o, argv...) ({ \
|
||||
const char *ifaces[] = { argv }; \
|
||||
_dispd_dbus_object_added(dispd_dbus_get(), \
|
||||
(o), \
|
||||
ifaces, \
|
||||
SHL_ARRAY_LENGTH(ifaces)); \
|
||||
})
|
||||
#define dispd_dbus_object_removed(o, argv...) ({ \
|
||||
const char *ifaces[] = { argv }; \
|
||||
_dispd_dbus_object_removed(dispd_dbus_get(), \
|
||||
(o), \
|
||||
ifaces, \
|
||||
SHL_ARRAY_LENGTH(ifaces)); \
|
||||
})
|
||||
|
||||
struct dispd_dbus
|
||||
{
|
||||
sd_bus *bus;
|
||||
sd_event *loop;
|
||||
|
||||
bool exposed : 1;
|
||||
};
|
||||
|
||||
int dispd_dbus_new(struct dispd_dbus **out, sd_event *loop, sd_bus *bus)
|
||||
{
|
||||
struct dispd_dbus *dispd_dbus = calloc(1, sizeof(struct dispd_dbus));
|
||||
if(!dispd_dbus) {
|
||||
return log_ENOMEM();
|
||||
}
|
||||
|
||||
dispd_dbus->bus = sd_bus_ref(bus);
|
||||
dispd_dbus->loop = sd_event_ref(loop);
|
||||
|
||||
*out = dispd_dbus;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dispd_dbus_free(struct dispd_dbus *dispd_dbus)
|
||||
{
|
||||
if(!dispd_dbus) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(dispd_dbus->exposed) {
|
||||
sd_bus_release_name(dispd_dbus->bus, "org.freedesktop.miracle.wfd");
|
||||
}
|
||||
|
||||
if(dispd_dbus->bus) {
|
||||
sd_bus_unref(dispd_dbus->bus);
|
||||
}
|
||||
|
||||
if(dispd_dbus->loop) {
|
||||
sd_event_unref(dispd_dbus->loop);
|
||||
}
|
||||
|
||||
free(dispd_dbus);
|
||||
}
|
||||
|
||||
static inline int dispd_dbus_get_sink_path(struct dispd_sink *s, char **out)
|
||||
{
|
||||
int r = sd_bus_path_encode("/org/freedesktop/miracle/wfd/sink",
|
||||
dispd_sink_get_label(s),
|
||||
out);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int dispd_dbus_get_session_path(struct dispd_session *s, char **out)
|
||||
{
|
||||
char buf[64];
|
||||
int r = snprintf(buf, sizeof(buf), "%u", dispd_session_get_id(s));
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
r = sd_bus_path_encode("/org/freedesktop/miracle/wfd/session",
|
||||
buf,
|
||||
out);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dispd_dbus_enum(sd_bus *bus,
|
||||
const char *path,
|
||||
void *userdata,
|
||||
char ***out,
|
||||
sd_bus_error *out_error)
|
||||
{
|
||||
int r = 0, i = 0;
|
||||
char **nodes, *node;
|
||||
struct dispd_sink *sink;
|
||||
struct dispd_session *session;
|
||||
struct dispd *dispd = dispd_get();
|
||||
|
||||
if(strcmp("/org/freedesktop/miracle/wfd", path)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!dispd->n_sinks) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
nodes = malloc((dispd->n_sinks + dispd->n_sessions + 1) * sizeof(char *));
|
||||
if(!nodes) {
|
||||
return log_ENOMEM();
|
||||
}
|
||||
|
||||
dispd_foreach_sink(sink, dispd) {
|
||||
r = dispd_dbus_get_sink_path(sink, &node);
|
||||
if(0 > r) {
|
||||
goto free_nodes;
|
||||
}
|
||||
nodes[i ++] = node;
|
||||
}
|
||||
|
||||
dispd_foreach_session(session, dispd) {
|
||||
r = dispd_dbus_get_session_path(session, &node);
|
||||
if(0 > r) {
|
||||
goto free_nodes;
|
||||
}
|
||||
nodes[i ++] = node;
|
||||
}
|
||||
|
||||
nodes[i ++] = NULL;
|
||||
*out = nodes;
|
||||
|
||||
return 0;
|
||||
|
||||
free_nodes:
|
||||
while(i --) {
|
||||
free(nodes[i]);
|
||||
}
|
||||
free(nodes);
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
int _dispd_dbus_object_removed(struct dispd_dbus *dispd_dbus,
|
||||
const char *path,
|
||||
const char **ifaces,
|
||||
size_t n_ifaces)
|
||||
{
|
||||
int i, r;
|
||||
_sd_bus_message_unref_ sd_bus_message *m = NULL;
|
||||
|
||||
if(!dispd_dbus) {
|
||||
return -ECANCELED;
|
||||
}
|
||||
|
||||
r = sd_bus_message_new_signal(dispd_dbus->bus,
|
||||
&m,
|
||||
"/org/freedesktop/miracle/wfd",
|
||||
"org.freedesktop.DBus.ObjectManager",
|
||||
"InterfacesRemoved");
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
r = sd_bus_message_append(m, "o", path);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
r = sd_bus_message_open_container(m, 'a', "s");
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
for(i = 0; i < n_ifaces; i ++) {
|
||||
r = sd_bus_message_append(m, "s", ifaces[i]);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
}
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
r = sd_bus_send(dispd_dbus->bus, m, NULL);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _dispd_dbus_object_added(struct dispd_dbus *dispd_dbus,
|
||||
const char *path,
|
||||
const char **ifaces,
|
||||
size_t n_ifaces)
|
||||
{
|
||||
int i, r;
|
||||
_sd_bus_message_unref_ sd_bus_message *m = NULL;
|
||||
|
||||
if(!dispd_dbus) {
|
||||
return -ECANCELED;
|
||||
}
|
||||
|
||||
r = sd_bus_message_new_signal(dispd_dbus->bus,
|
||||
&m,
|
||||
"/org/freedesktop/miracle/wfd",
|
||||
"org.freedesktop.DBus.ObjectManager",
|
||||
"InterfacesAdded");
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
r = sd_bus_message_append(m, "o", path);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
r = sd_bus_message_open_container(m, 'a', "{sa{sv}}");
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
for(i = 0; i < n_ifaces; i ++) {
|
||||
r = sd_bus_message_append(m, "{sa{sv}}", ifaces[i], 0);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
}
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
r = sd_bus_send(dispd_dbus->bus, m, NULL);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dispd_fn_sink_new(struct dispd_sink *s)
|
||||
{
|
||||
_shl_free_ char *path = NULL;
|
||||
int r = dispd_dbus_get_sink_path(s, &path);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
r = dispd_dbus_object_added(path, "org.freedesktop.miracle.wfd.Sink");
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dispd_fn_sink_free(struct dispd_sink *s)
|
||||
{
|
||||
_shl_free_ char *path = NULL;
|
||||
int r = dispd_dbus_get_sink_path(s, &path);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
r = dispd_dbus_object_removed(path, "org.freedesktop.miracle.wfd.Sink");
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int _dispd_fn_sink_properties_changed(struct dispd_sink *s, char **names)
|
||||
{
|
||||
_shl_free_ char *path = NULL;
|
||||
int r;
|
||||
struct dispd_dbus *dispd_dbus = dispd_dbus_get();
|
||||
|
||||
if(!dispd_dbus) {
|
||||
return log_ERR(-ECANCELED);
|
||||
}
|
||||
|
||||
r = dispd_dbus_get_sink_path(s, &path);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
r = sd_bus_emit_properties_changed_strv(dispd_dbus->bus,
|
||||
path,
|
||||
"org.freedesktop.miracle.wfd.Sink",
|
||||
names);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dispd_dbus_find_sink(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
void *userdata,
|
||||
void **ret_found,
|
||||
sd_bus_error *ret_error)
|
||||
{
|
||||
_shl_free_ char *node = NULL;
|
||||
struct dispd_sink *sink;
|
||||
int r = sd_bus_path_decode(path,
|
||||
"/org/freedesktop/miracle/wfd/sink",
|
||||
&node);
|
||||
if(0 >= r || !node) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = dispd_find_sink_by_label(dispd_get(), node, &sink);
|
||||
if(r) {
|
||||
*ret_found = sink;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int dispd_fn_session_new(struct dispd_session *s)
|
||||
{
|
||||
_shl_free_ char *path = NULL;
|
||||
int r = dispd_dbus_get_session_path(s, &path);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
r = dispd_dbus_object_added(path, "org.freedesktop.miracle.wfd.Session");
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dispd_fn_session_free(struct dispd_session *s)
|
||||
{
|
||||
_shl_free_ char *path = NULL;
|
||||
int r = dispd_dbus_get_session_path(s, &path);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
r = dispd_dbus_object_removed(path, "org.freedesktop.miracle.wfd.Session");
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dispd_dbus_find_session(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
void *userdata,
|
||||
void **ret_found,
|
||||
sd_bus_error *ret_error)
|
||||
{
|
||||
struct dispd_session *s;
|
||||
_shl_free_ char *node = NULL;
|
||||
int r = sd_bus_path_decode(path,
|
||||
"/org/freedesktop/miracle/wfd/session",
|
||||
&node);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
r = dispd_find_session_by_id(dispd_get(),
|
||||
strtoull(node, NULL, 10),
|
||||
&s);
|
||||
if(r) {
|
||||
*ret_found = s;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int get_user_runtime_path(char **out, sd_bus *bus, uid_t uid)
|
||||
{
|
||||
_sd_bus_message_unref_ sd_bus_message *rep = NULL;
|
||||
_sd_bus_error_free_ sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
char p[256];
|
||||
char *rp;
|
||||
int r;
|
||||
|
||||
assert_ret(out);
|
||||
assert_ret(bus);
|
||||
assert_ret(0 < uid);
|
||||
|
||||
r = snprintf(p, sizeof(p), "/org/freedesktop/login1/user/_%d", uid);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
r = sd_bus_call_method(bus,
|
||||
"org.freedesktop.login1",
|
||||
p,
|
||||
"org.freedesktop.DBus.Properties",
|
||||
"Get",
|
||||
&error,
|
||||
&rep,
|
||||
"ss",
|
||||
"org.freedesktop.login1.User",
|
||||
"RuntimePath");
|
||||
if(0 > r) {
|
||||
log_warning("%s: %s", error.name, error.message);
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sd_bus_message_read(rep, "v", "s", &rp);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
*out = strdup(rp);
|
||||
if(!*out) {
|
||||
return log_ENOMEM();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dispd_dbus_sink_start_session(sd_bus_message *m,
|
||||
void *userdata,
|
||||
sd_bus_error *ret_error)
|
||||
{
|
||||
_dispd_session_unref_ struct dispd_session *sess = NULL;
|
||||
_shl_free_ char *path = NULL, *disp_type_name = NULL, *disp_name = NULL;
|
||||
_sd_bus_creds_unref_ sd_bus_creds *creds = NULL;
|
||||
_shl_free_ char *runtime_path = NULL;
|
||||
struct dispd_sink *sink = userdata;
|
||||
char *disp_params;
|
||||
const char *disp, *disp_auth;
|
||||
const char *audio_dev;
|
||||
struct dispd_rectangle rect;
|
||||
pid_t pid;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_read(m,
|
||||
"ssuuuus",
|
||||
&disp_auth,
|
||||
&disp,
|
||||
&rect.x,
|
||||
&rect.y,
|
||||
&rect.width,
|
||||
&rect.height,
|
||||
&audio_dev);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
r = sscanf(disp, "%m[^:]://%ms",
|
||||
&disp_type_name,
|
||||
&disp_name);
|
||||
if(r != 2) {
|
||||
return log_EINVAL();
|
||||
}
|
||||
|
||||
if(strcmp("x", disp_type_name)) {
|
||||
return log_EINVAL();
|
||||
}
|
||||
|
||||
r = dispd_sink_create_session(sink, &sess);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
dispd_session_set_disp_type(sess, DISPD_DISPLAY_SERVER_TYPE_X);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
disp_params = strchr(disp_name, '?');
|
||||
if(disp_params) {
|
||||
*disp_params ++ = '\0';
|
||||
}
|
||||
|
||||
r = dispd_session_set_disp_name(sess, disp_name);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
r = dispd_session_set_disp_params(sess, disp_params);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
r = dispd_session_set_disp_auth(sess, disp_auth);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
r = dispd_session_set_disp_dimension(sess, &rect);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
if(*audio_dev) {
|
||||
r = dispd_session_set_audio_type(sess, DISPD_AUDIO_SERVER_TYPE_PULSE_AUDIO);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
r = dispd_session_set_audio_dev_name(sess, audio_dev);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
}
|
||||
|
||||
r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_PID, &creds);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
r = sd_bus_creds_get_pid(creds, &pid);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
sd_bus_creds_unref(creds);
|
||||
creds = NULL;
|
||||
|
||||
r = sd_bus_creds_new_from_pid(&creds,
|
||||
pid,
|
||||
SD_BUS_CREDS_UID | SD_BUS_CREDS_GID);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
dispd_session_set_client_pid(sess, gid);
|
||||
|
||||
r = sd_bus_creds_get_uid(creds, &uid);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
dispd_session_set_client_uid(sess, uid);
|
||||
|
||||
sd_bus_creds_get_gid(creds, &gid);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
dispd_session_set_client_gid(sess, gid);
|
||||
|
||||
r = get_user_runtime_path(&runtime_path,
|
||||
sd_bus_message_get_bus(m),
|
||||
uid);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
r = dispd_session_set_runtime_path(sess, runtime_path);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
r = dispd_session_start(sess);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
r = dispd_dbus_get_session_path(sess, &path);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
r = sd_bus_reply_method_return(m, "o", path);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dispd_dbus_sink_get_session(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *ret_error)
|
||||
{
|
||||
struct dispd_sink *s = userdata;
|
||||
_shl_free_ char *session_path = NULL;
|
||||
int r;
|
||||
|
||||
if(s->session) {
|
||||
r = dispd_dbus_get_session_path(s->session, &session_path);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
}
|
||||
else {
|
||||
session_path = strdup("/");
|
||||
if(!session_path) {
|
||||
return log_ENOMEM();
|
||||
}
|
||||
}
|
||||
|
||||
r = sd_bus_message_append(reply, "o", session_path);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dispd_dbus_sink_get_peer(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *ret_error)
|
||||
{
|
||||
struct dispd_sink *s = userdata;
|
||||
_shl_free_ char *peer_path = NULL;
|
||||
int r = sd_bus_path_encode("/org/freedesktop/miracle/wifi/peer",
|
||||
s->label,
|
||||
&peer_path);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
r = sd_bus_message_append(reply, "o", peer_path);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//static int dispd_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 dispd_dbus_session_resume(sd_bus_message *m,
|
||||
void *userdata,
|
||||
sd_bus_error *ret_error)
|
||||
{
|
||||
struct dispd_session *s = userdata;
|
||||
int r;
|
||||
|
||||
if(dispd_session_is_established(s)) {
|
||||
r = dispd_session_resume(s);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
}
|
||||
else {
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
r = sd_bus_reply_method_return(m, NULL);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dispd_dbus_session_pause(sd_bus_message *m,
|
||||
void *userdata,
|
||||
sd_bus_error *ret_error)
|
||||
{
|
||||
struct dispd_session *s = userdata;
|
||||
int r;
|
||||
|
||||
if(dispd_session_is_established(s)) {
|
||||
r = dispd_session_pause(s);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
}
|
||||
else {
|
||||
return -ENOTCONN;
|
||||
}
|
||||
|
||||
r = sd_bus_reply_method_return(m, NULL);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dispd_dbus_session_teardown(sd_bus_message *m,
|
||||
void *userdata,
|
||||
sd_bus_error *ret_error)
|
||||
{
|
||||
struct dispd_session *s = userdata;
|
||||
int r = 0;
|
||||
|
||||
if(dispd_session_is_established(s)) {
|
||||
r = dispd_session_teardown(s);
|
||||
}
|
||||
|
||||
if(0 > r) {
|
||||
dispd_session_destroy(s);
|
||||
}
|
||||
|
||||
r = sd_bus_reply_method_return(m, NULL);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dispd_dbus_session_get_sink(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *ret_error)
|
||||
{
|
||||
struct dispd_session *s = userdata;
|
||||
_shl_free_ char *sink_path = NULL;
|
||||
int r;
|
||||
|
||||
if(dispd_session_get_dir(s) != DISPD_SESSION_DIR_OUT) {
|
||||
sink_path = strdup("/");
|
||||
}
|
||||
else {
|
||||
dispd_dbus_get_sink_path(dispd_out_session_get_sink(s), &sink_path);
|
||||
}
|
||||
if(!sink_path) {
|
||||
return log_ENOMEM();
|
||||
}
|
||||
|
||||
r = sd_bus_message_append(reply, "o", sink_path);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dispd_dbus_get_session_presentation_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 dispd_session *s = userdata;
|
||||
int r = sd_bus_message_append(reply,
|
||||
"s",
|
||||
dispd_session_get_stream_url(s) ? : "");
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dispd_dbus_get_session_state(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *userdata,
|
||||
sd_bus_error *ret_error)
|
||||
{
|
||||
struct dispd_session *s = userdata;
|
||||
int r = sd_bus_message_append(reply, "i", dispd_session_get_state(s));
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int _dispd_fn_session_properties_changed(struct dispd_session *s, char **names)
|
||||
{
|
||||
_shl_free_ char *path = NULL;
|
||||
int r;
|
||||
struct dispd_dbus *dispd_dbus = dispd_dbus_get();
|
||||
|
||||
if(!dispd_dbus) {
|
||||
return -ECANCELED;
|
||||
}
|
||||
|
||||
r = dispd_dbus_get_session_path(s, &path);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
r = sd_bus_emit_properties_changed_strv(dispd_dbus_get()->bus,
|
||||
path,
|
||||
"org.freedesktop.miracle.wfd.Session",
|
||||
names);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int dispd_dbus_shutdown(sd_bus_message *m,
|
||||
void *userdata,
|
||||
sd_bus_error *ret_error)
|
||||
{
|
||||
int r;
|
||||
|
||||
dispd_shutdown(dispd_get());
|
||||
|
||||
r = sd_bus_reply_method_return(m, NULL);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const sd_bus_vtable dispd_dbus_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_METHOD("Shutdown", NULL, NULL, dispd_dbus_shutdown, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_VTABLE_END,
|
||||
};
|
||||
|
||||
static const sd_bus_vtable dispd_dbus_sink_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_METHOD("StartSession", "ssuuuus", "o", dispd_dbus_sink_start_session, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
/*SD_BUS_PROPERTY("AudioFormats", "a{sv}", dispd_dbus_sink_get_audio_formats, 0, SD_BUS_VTABLE_PROPERTY_CONST),*/
|
||||
/*SD_BUS_PROPERTY("VideoFormats", "a{sv}", dispd_dbus_sink_get_video_formats, 0, SD_BUS_VTABLE_PROPERTY_CONST),*/
|
||||
/*SD_BUS_PROPERTY("HasAudio", "b", dispd_dbus_sink_has_audio, 0, SD_BUS_VTABLE_PROPERTY_CONST),*/
|
||||
/*SD_BUS_PROPERTY("HasVideo", "b", dispd_dbus_sink_has_video, 0, SD_BUS_VTABLE_PROPERTY_CONST),*/
|
||||
SD_BUS_PROPERTY("Session", "o", dispd_dbus_sink_get_session, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("Peer", "o", dispd_dbus_sink_get_peer, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_VTABLE_END,
|
||||
};
|
||||
|
||||
static const sd_bus_vtable dispd_dbus_session_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_METHOD("Resume", NULL, NULL, dispd_dbus_session_resume, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("Pause", NULL, NULL, dispd_dbus_session_pause, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("Teardown", NULL, NULL, dispd_dbus_session_teardown, SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_PROPERTY("Sink", "o", dispd_dbus_session_get_sink, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Url", "s", dispd_dbus_get_session_presentation_url, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("State", "i", dispd_dbus_get_session_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_VTABLE_END,
|
||||
};
|
||||
|
||||
int dispd_dbus_expose(struct dispd_dbus *dispd_dbus)
|
||||
{
|
||||
int r = sd_bus_add_object_vtable(dispd_dbus->bus,
|
||||
NULL,
|
||||
"/org/freedesktop/miracle/wfd",
|
||||
"org.freedesktop.miracle.wfd",
|
||||
dispd_dbus_vtable,
|
||||
dispd_dbus);
|
||||
if(0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sd_bus_add_fallback_vtable(dispd_dbus->bus,
|
||||
NULL,
|
||||
"/org/freedesktop/miracle/wfd/sink",
|
||||
"org.freedesktop.miracle.wfd.Sink",
|
||||
dispd_dbus_sink_vtable,
|
||||
dispd_dbus_find_sink,
|
||||
dispd_dbus);
|
||||
if(0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sd_bus_add_fallback_vtable(dispd_dbus->bus,
|
||||
NULL,
|
||||
"/org/freedesktop/miracle/wfd/session",
|
||||
"org.freedesktop.miracle.wfd.Session",
|
||||
dispd_dbus_session_vtable,
|
||||
dispd_dbus_find_session,
|
||||
dispd_dbus);
|
||||
if(0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sd_bus_add_node_enumerator(dispd_dbus->bus,
|
||||
NULL,
|
||||
"/org/freedesktop/miracle/wfd",
|
||||
dispd_dbus_enum,
|
||||
dispd_dbus);
|
||||
if(0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sd_bus_add_object_manager(dispd_dbus->bus, NULL, "/org/freedesktop/miracle/wfd");
|
||||
if(0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = sd_bus_request_name(dispd_dbus->bus, "org.freedesktop.miracle.wfd", 0);
|
||||
if(0 < r) {
|
||||
dispd_dbus->exposed = true;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
47
src/disp/dispd-dbus.h
Normal file
47
src/disp/dispd-dbus.h
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||
*
|
||||
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||
*
|
||||
* MiracleCast is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MiracleCast is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <systemd/sd-bus.h>
|
||||
|
||||
#ifndef CTL_WFD_DBUS_H
|
||||
#define CTL_WFD_DBUS_H
|
||||
|
||||
#define dispd_fn_sink_properties_changed(s, namev...) ({ \
|
||||
char *names[] = { namev, NULL }; \
|
||||
_dispd_fn_sink_properties_changed((s), names); \
|
||||
})
|
||||
|
||||
#define dispd_fn_session_properties_changed(s, namev...) ({ \
|
||||
char *names[] = { namev, NULL }; \
|
||||
_dispd_fn_session_properties_changed((s), names); \
|
||||
})
|
||||
|
||||
struct dispd_dbus;
|
||||
struct dispd_session;
|
||||
struct dispd_sink;
|
||||
|
||||
struct dispd_dbus * dispd_dbus_get();
|
||||
int dispd_dbus_new(struct dispd_dbus **out, sd_event *loop, sd_bus *bus);
|
||||
void dispd_dbus_free(struct dispd_dbus *dispd_dbus);
|
||||
int dispd_dbus_expose(struct dispd_dbus *dispd_dbus);
|
||||
int _dispd_fn_sink_properties_changed(struct dispd_sink *s, char **names);
|
||||
int _dispd_fn_session_properties_changed(struct dispd_session *s, char **names);
|
||||
|
||||
#endif
|
||||
|
||||
982
src/disp/dispd-encoder.c
Normal file
982
src/disp/dispd-encoder.c
Normal file
|
|
@ -0,0 +1,982 @@
|
|||
/*
|
||||
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||
*
|
||||
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||
*
|
||||
* MiracleCast is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MiracleCast is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <systemd/sd-event.h>
|
||||
#include <systemd/sd-bus.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <fcntl.h>
|
||||
#include "dispd-encoder.h"
|
||||
#include "shl_macro.h"
|
||||
#include "shl_log.h"
|
||||
#include "dispd-session.h"
|
||||
#include "dispd.h"
|
||||
#include "util.h"
|
||||
|
||||
struct dispd_encoder
|
||||
{
|
||||
int ref;
|
||||
|
||||
sd_event *loop;
|
||||
sd_event_source *child_source;
|
||||
sd_event_source *child_term_time_source;
|
||||
sd_event_source *pipe_source;
|
||||
|
||||
sd_bus *bus;
|
||||
sd_bus_slot *name_disappeared_slot;
|
||||
sd_bus_slot *state_change_notify_slot;
|
||||
|
||||
uid_t bus_owner;
|
||||
uid_t bus_group;
|
||||
char *bus_name;
|
||||
|
||||
enum dispd_encoder_state state;
|
||||
dispd_encoder_state_change_handler handler;
|
||||
void *userdata;
|
||||
};
|
||||
|
||||
static int dispd_encoder_new(struct dispd_encoder **out,
|
||||
uid_t bus_owner,
|
||||
gid_t bus_group);
|
||||
static int on_bus_info_readable(sd_event_source *source,
|
||||
int fd,
|
||||
uint32_t events,
|
||||
void *userdata);
|
||||
static void dispd_encoder_set_state(struct dispd_encoder *e,
|
||||
enum dispd_encoder_state state);
|
||||
|
||||
static void dispd_encoder_exec(const char *cmd, int fd, struct dispd_session *s)
|
||||
{
|
||||
int r;
|
||||
sigset_t mask;
|
||||
char disp[16], runtime_path[256];
|
||||
|
||||
log_info("child forked with pid %d", getpid());
|
||||
|
||||
/* restore to default signal handler */
|
||||
sigemptyset(&mask);
|
||||
sigprocmask(SIG_SETMASK, &mask, NULL);
|
||||
|
||||
snprintf(disp, sizeof(disp), "DISPLAY=%s", dispd_session_get_disp_name(s));
|
||||
snprintf(runtime_path, sizeof(runtime_path), "XDG_RUNTIME_DIR=%s", dispd_session_get_runtime_path(s));
|
||||
|
||||
/* after encoder connected to DBus, write unique name to fd 3,
|
||||
* so we can controll it through DBus
|
||||
*/
|
||||
r = dup2(fd, 3);
|
||||
if(0 > r) {
|
||||
log_vERRNO();
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(fd != 3) {
|
||||
close(fd);
|
||||
}
|
||||
|
||||
// TODO drop caps and don't let user raises thier caps
|
||||
r = setgid(dispd_session_get_client_gid(s));
|
||||
if(0 > r) {
|
||||
log_vERRNO();
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = setuid(dispd_session_get_client_uid(s));
|
||||
if(0 > r) {
|
||||
log_vERRNO();
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
||||
if(0 > r) {
|
||||
log_vERRNO();
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = execvpe(cmd,
|
||||
(char *[]){ (char *) cmd, NULL },
|
||||
(char *[]){ disp,
|
||||
runtime_path,
|
||||
"G_MESSAGES_DEBUG=all",
|
||||
NULL
|
||||
});
|
||||
if(0 > r) {
|
||||
log_vERRNO();
|
||||
goto error;
|
||||
}
|
||||
|
||||
error:
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
static void dispd_encoder_close_pipe(struct dispd_encoder *e)
|
||||
{
|
||||
if(!e->pipe_source) {
|
||||
return;
|
||||
}
|
||||
|
||||
close(sd_event_source_get_io_fd(e->pipe_source));
|
||||
|
||||
sd_event_source_unref(e->pipe_source);
|
||||
e->pipe_source = NULL;
|
||||
|
||||
dispd_encoder_unref(e);
|
||||
}
|
||||
|
||||
static int dispd_encoder_kill_child(struct dispd_encoder *e)
|
||||
{
|
||||
int r;
|
||||
pid_t pid;
|
||||
|
||||
if(!e->child_source) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO add timer in case child can't be terminated by SIGTERM
|
||||
sd_event_source_get_child_pid(e->child_source, &pid);
|
||||
r = kill(pid, SIGTERM);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void dispd_encoder_notify_state_change(struct dispd_encoder *e,
|
||||
enum dispd_encoder_state state)
|
||||
{
|
||||
assert_vret(e);
|
||||
|
||||
if(!e->handler) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispd_encoder_ref(e);
|
||||
(*e->handler)(e, state, e->userdata);
|
||||
dispd_encoder_unref(e);
|
||||
}
|
||||
|
||||
static void dispd_encoder_cleanup(struct dispd_encoder *e)
|
||||
{
|
||||
if(e->child_source) {
|
||||
sd_event_source_unref(e->child_source);
|
||||
e->child_source = NULL;
|
||||
dispd_encoder_unref(e);
|
||||
}
|
||||
|
||||
if(e->child_term_time_source) {
|
||||
sd_event_source_unref(e->child_term_time_source);
|
||||
e->child_term_time_source = NULL;
|
||||
dispd_encoder_unref(e);
|
||||
}
|
||||
|
||||
if(e->child_source) {
|
||||
sd_event_source_unref(e->child_source);
|
||||
e->child_source = NULL;
|
||||
dispd_encoder_unref(e);
|
||||
}
|
||||
|
||||
dispd_encoder_close_pipe(e);
|
||||
|
||||
if(e->name_disappeared_slot) {
|
||||
sd_bus_slot_unref(e->name_disappeared_slot);
|
||||
e->name_disappeared_slot = NULL;
|
||||
dispd_encoder_unref(e);
|
||||
}
|
||||
|
||||
if(e->state_change_notify_slot) {
|
||||
sd_bus_slot_unref(e->state_change_notify_slot);
|
||||
e->state_change_notify_slot = NULL;
|
||||
dispd_encoder_unref(e);
|
||||
}
|
||||
}
|
||||
|
||||
static int on_child_terminated(sd_event_source *source,
|
||||
const siginfo_t *si,
|
||||
void *userdata)
|
||||
{
|
||||
struct dispd_encoder *e = userdata;
|
||||
|
||||
log_info("encoder process %d terminated", si->si_pid);
|
||||
|
||||
dispd_encoder_set_state(e, DISPD_ENCODER_STATE_TERMINATED);
|
||||
dispd_encoder_cleanup(e);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dispd_encoder_spawn(struct dispd_encoder **out, struct dispd_session *s)
|
||||
{
|
||||
_dispd_encoder_unref_ struct dispd_encoder *e = NULL;
|
||||
int fds[2] = { -1, -1 };
|
||||
pid_t pid;
|
||||
int r;
|
||||
|
||||
assert_ret(out);
|
||||
assert_ret(s);
|
||||
|
||||
r = dispd_encoder_new(&e,
|
||||
dispd_session_get_client_uid(s),
|
||||
dispd_session_get_client_gid(s));
|
||||
if(0 > r) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
r = pipe2(fds, O_NONBLOCK);
|
||||
if(0 > r) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
pid = fork();
|
||||
if(0 > pid) {
|
||||
r = log_ERRNO();
|
||||
goto kill_encoder;
|
||||
}
|
||||
else if(!pid) {
|
||||
close(fds[0]);
|
||||
|
||||
dispd_encoder_exec("gstencoder", fds[1], s);
|
||||
}
|
||||
|
||||
r = sd_event_add_child(dispd_get_loop(),
|
||||
&e->child_source,
|
||||
pid,
|
||||
WEXITED,
|
||||
on_child_terminated,
|
||||
dispd_encoder_ref(e));
|
||||
if(0 > r) {
|
||||
goto close_pipe;
|
||||
}
|
||||
|
||||
r = sd_event_add_io(dispd_get_loop(),
|
||||
&e->pipe_source,
|
||||
fds[0],
|
||||
EPOLLIN,
|
||||
on_bus_info_readable,
|
||||
dispd_encoder_ref(e));
|
||||
if(0 > r) {
|
||||
goto close_pipe;
|
||||
}
|
||||
|
||||
close(fds[1]);
|
||||
*out = dispd_encoder_ref(e);
|
||||
|
||||
return 0;
|
||||
|
||||
close_pipe:
|
||||
close(fds[0]);
|
||||
close(fds[1]);
|
||||
kill_encoder:
|
||||
// dispd will do the cleanup
|
||||
kill(pid, SIGKILL);
|
||||
end:
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
static int dispd_encoder_new(struct dispd_encoder **out,
|
||||
uid_t bus_owner,
|
||||
gid_t bus_group)
|
||||
{
|
||||
_dispd_encoder_unref_ struct dispd_encoder *e = NULL;
|
||||
|
||||
assert_ret(out);
|
||||
|
||||
e = calloc(1, sizeof(struct dispd_encoder));
|
||||
if(!e) {
|
||||
return log_ENOMEM();
|
||||
}
|
||||
|
||||
e->ref = 1;
|
||||
e->bus_owner = bus_owner;
|
||||
e->bus_group = bus_group;
|
||||
|
||||
*out = dispd_encoder_ref(e);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dispd_encoder * dispd_encoder_ref(struct dispd_encoder *e)
|
||||
{
|
||||
assert_retv(e, e);
|
||||
assert_retv(0 < e->ref, e);
|
||||
|
||||
++ e->ref;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
void dispd_encoder_unrefp(struct dispd_encoder **e)
|
||||
{
|
||||
if(*e) {
|
||||
dispd_encoder_unref(*e);
|
||||
}
|
||||
}
|
||||
|
||||
void dispd_encoder_unref(struct dispd_encoder *e)
|
||||
{
|
||||
assert_vret(e);
|
||||
assert_vret(0 < e->ref);
|
||||
|
||||
--e->ref;
|
||||
if(e->ref) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* since we encrease ref count at creation of every sources and slots,
|
||||
* once we get here, it means no sources and slots exist anymore */
|
||||
if(e->bus) {
|
||||
sd_bus_detach_event(e->bus);
|
||||
sd_bus_unref(e->bus);
|
||||
}
|
||||
|
||||
if(e->bus_name) {
|
||||
free(e->bus_name);
|
||||
}
|
||||
|
||||
// if(e->bus_addr) {
|
||||
// free(e->bus_addr);
|
||||
// }
|
||||
|
||||
free(e);
|
||||
}
|
||||
|
||||
void dispd_encoder_set_handler(struct dispd_encoder *e,
|
||||
dispd_encoder_state_change_handler handler,
|
||||
void *userdata)
|
||||
{
|
||||
assert_vret(e);
|
||||
|
||||
e->handler = handler;
|
||||
e->userdata = userdata;
|
||||
}
|
||||
|
||||
dispd_encoder_state_change_handler dispd_encoder_get_handler(struct dispd_encoder *e)
|
||||
{
|
||||
assert_retv(e, NULL);
|
||||
|
||||
return e->handler;
|
||||
}
|
||||
|
||||
enum dispd_encoder_state dispd_encoder_get_state(struct dispd_encoder *e)
|
||||
{
|
||||
assert_retv(e, DISPD_ENCODER_STATE_NULL);
|
||||
|
||||
return e->state;
|
||||
}
|
||||
|
||||
static const char * state_to_name(enum dispd_encoder_state s)
|
||||
{
|
||||
const char *names[] = {
|
||||
"NULL",
|
||||
"SPAWNED",
|
||||
"CONFIGURED",
|
||||
"READY",
|
||||
"STARTED",
|
||||
"PAUSED",
|
||||
"TERMINATED"
|
||||
};
|
||||
|
||||
if(0 > s || DISPD_ENCODER_STATE_TERMINATED < s) {
|
||||
return "unknown encoder state";
|
||||
}
|
||||
|
||||
return names[s];
|
||||
}
|
||||
|
||||
static void dispd_encoder_set_state(struct dispd_encoder *e,
|
||||
enum dispd_encoder_state state)
|
||||
{
|
||||
assert_vret(e);
|
||||
|
||||
if(e->state == state) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_debug("state change from %s to %s",
|
||||
state_to_name(e->state),
|
||||
state_to_name(state));
|
||||
|
||||
e->state = state;
|
||||
dispd_encoder_notify_state_change(e, state);
|
||||
}
|
||||
|
||||
static int on_encoder_properties_changed(sd_bus_message *m,
|
||||
void *userdata,
|
||||
sd_bus_error *ret_error)
|
||||
{
|
||||
struct dispd_encoder *e = userdata;
|
||||
const char *name;
|
||||
int value;
|
||||
enum dispd_encoder_state s;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_skip(m, "s");
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
r = sd_bus_message_enter_container(m, 'a', "{sv}");
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
while(!sd_bus_message_at_end(m, true)) {
|
||||
r = sd_bus_message_read(m, "{sv}", &name, "i", &value);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
if(strcmp("State", name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch(value) {
|
||||
case 0:
|
||||
s = DISPD_ENCODER_STATE_NULL;
|
||||
break;
|
||||
case 1:
|
||||
s = DISPD_ENCODER_STATE_CONFIGURED;
|
||||
break;
|
||||
case 2:
|
||||
s = DISPD_ENCODER_STATE_READY;
|
||||
break;
|
||||
case 3:
|
||||
s = DISPD_ENCODER_STATE_STARTED;
|
||||
break;
|
||||
case 4:
|
||||
s = DISPD_ENCODER_STATE_PAUSED;
|
||||
break;
|
||||
case 5:
|
||||
s = DISPD_ENCODER_STATE_TERMINATED;
|
||||
break;
|
||||
default:
|
||||
log_error("encoder enter unknown state: %d", value);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dispd_encoder_set_state(e, s);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_encoder_disappeared(sd_bus_message *m,
|
||||
void *userdata,
|
||||
sd_bus_error *ret_error)
|
||||
{
|
||||
struct dispd_encoder *e = userdata;
|
||||
int r;
|
||||
|
||||
log_info("encoder %s disappered from bus", e->bus_name);
|
||||
|
||||
r = dispd_encoder_kill_child(e);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
else if(r) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
dispd_encoder_cleanup(e);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int read_line(int fd, char *b, size_t len)
|
||||
{
|
||||
int r;
|
||||
char *p = b;
|
||||
|
||||
assert_ret(0 <= fd);
|
||||
assert_ret(b);
|
||||
assert_ret(len);
|
||||
|
||||
while((p - b) < (len - 1)) {
|
||||
r = read(fd, p, 1);
|
||||
if(0 > r) {
|
||||
if(EINTR == errno) {
|
||||
continue;
|
||||
}
|
||||
else if(EAGAIN == errno) {
|
||||
break;
|
||||
}
|
||||
return log_ERRNO();
|
||||
}
|
||||
else if(!r || '\n' == *p) {
|
||||
break;
|
||||
}
|
||||
|
||||
++ p;
|
||||
}
|
||||
|
||||
*p = '\0';
|
||||
|
||||
return p - b;
|
||||
}
|
||||
|
||||
static int on_bus_info_readable(sd_event_source *source,
|
||||
int fd,
|
||||
uint32_t events,
|
||||
void *userdata)
|
||||
{
|
||||
struct dispd_encoder *e = userdata;
|
||||
char buf[512];
|
||||
int r;
|
||||
|
||||
r = read_line(fd, buf, sizeof(buf) - 1);
|
||||
if(0 > r) {
|
||||
log_vERR(r);
|
||||
goto error;
|
||||
}
|
||||
else if(!r) {
|
||||
log_warning("no bus name returned from encoder");
|
||||
r = -ENOENT;
|
||||
goto error;
|
||||
}
|
||||
|
||||
// TODO remove heading and trailing speces from buf before strdup()
|
||||
log_info("got bus name from encoder: %s", buf);
|
||||
|
||||
e->bus_name = strdup(buf);
|
||||
if(!e->bus_name) {
|
||||
log_vERRNO();
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = read_line(fd, buf, sizeof(buf) - 1);
|
||||
if(0 > r) {
|
||||
log_vERR(r);
|
||||
goto error;
|
||||
}
|
||||
else if(!r) {
|
||||
log_warning("no bus address returned from encoder");
|
||||
r = -ENOENT;
|
||||
goto error;
|
||||
}
|
||||
|
||||
log_info("got bus address from encoder: %s", buf);
|
||||
|
||||
r = seteuid(e->bus_owner);
|
||||
if(0 > r) {
|
||||
log_vERRNO();
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = sd_bus_new(&e->bus);
|
||||
if(0 > r) {
|
||||
log_vERR(r);
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = sd_bus_set_address(e->bus, buf);
|
||||
if(0 > r) {
|
||||
log_vERR(r);
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = sd_bus_set_bus_client(e->bus, true);
|
||||
if(0 > r) {
|
||||
log_vERR(r);
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = sd_bus_start(e->bus);
|
||||
if(0 > r) {
|
||||
log_vERR(r);
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = sd_bus_attach_event(e->bus, dispd_get_loop(), 0);
|
||||
if(0 > r) {
|
||||
log_vERR(r);
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = seteuid(0);
|
||||
if(0 > r) {
|
||||
log_vERRNO();
|
||||
goto error;
|
||||
}
|
||||
|
||||
snprintf(buf, sizeof(buf),
|
||||
"type='signal',"
|
||||
"sender='%s',"
|
||||
"path='/org/freedesktop/miracle/encoder',"
|
||||
"interface='org.freedesktop.DBus.Properties',"
|
||||
"member='PropertiesChanged',"
|
||||
"arg0='org.freedesktop.miracle.encoder'",
|
||||
e->bus_name);
|
||||
if(0 > r) {
|
||||
log_vERRNO();
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = sd_bus_add_match(e->bus,
|
||||
&e->state_change_notify_slot,
|
||||
buf,
|
||||
on_encoder_properties_changed,
|
||||
dispd_encoder_ref(e));
|
||||
if(0 > r) {
|
||||
log_vERRNO();
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = snprintf(buf, sizeof(buf),
|
||||
"type='signal',"
|
||||
"sender='org.freedesktop.DBus',"
|
||||
"path='/org/freedesktop/DBus',"
|
||||
"interface='org.freedesktop.DBus',"
|
||||
"member='NameOwnerChanged',"
|
||||
"arg0namespace='%s'",
|
||||
e->bus_name);
|
||||
if(0 > r) {
|
||||
log_vERRNO();
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = sd_bus_add_match(e->bus,
|
||||
&e->name_disappeared_slot,
|
||||
buf,
|
||||
on_encoder_disappeared,
|
||||
dispd_encoder_ref(e));
|
||||
if(0 > r) {
|
||||
log_vERRNO();
|
||||
goto error;
|
||||
}
|
||||
|
||||
dispd_encoder_set_state(e, DISPD_ENCODER_STATE_SPAWNED);
|
||||
|
||||
goto end;
|
||||
|
||||
error:
|
||||
seteuid(0);
|
||||
dispd_encoder_kill_child(e);
|
||||
end:
|
||||
dispd_encoder_close_pipe(e);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int config_append(sd_bus_message *m,
|
||||
enum wfd_encoder_config k,
|
||||
const char *t,
|
||||
...)
|
||||
{
|
||||
int r;
|
||||
va_list argv;
|
||||
|
||||
assert_ret(m);
|
||||
assert_ret(t);
|
||||
|
||||
r = sd_bus_message_open_container(m, 'e', "iv");
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
r = sd_bus_message_append(m, "i", k);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
r = sd_bus_message_open_container(m, 'v', t);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
va_start(argv, t);
|
||||
switch(*t) {
|
||||
case 's':
|
||||
r = sd_bus_message_append(m, t, va_arg(argv, char *));
|
||||
break;
|
||||
case 'u':
|
||||
r = sd_bus_message_append(m, t, va_arg(argv, uint32_t));
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
va_end(argv);
|
||||
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
r = sd_bus_message_close_container(m);
|
||||
if(0 > r) {
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dispd_encoder_configure(struct dispd_encoder *e, struct dispd_session *s)
|
||||
{
|
||||
_cleanup_sd_bus_message_ sd_bus_message *call = NULL;
|
||||
_cleanup_sd_bus_message_ sd_bus_message *reply = NULL;
|
||||
_cleanup_sd_bus_error_ sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
const struct dispd_rectangle *rect;
|
||||
struct dispd_sink *sink;
|
||||
int r;
|
||||
|
||||
assert_ret(e);
|
||||
assert_ret(e->bus);
|
||||
assert_ret(s);
|
||||
assert_ret(dispd_is_out_session(s));
|
||||
|
||||
r = sd_bus_message_new_method_call(e->bus,
|
||||
&call,
|
||||
e->bus_name,
|
||||
"/org/freedesktop/miracle/encoder",
|
||||
"org.freedesktop.miracle.encoder",
|
||||
"Configure");
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
r = sd_bus_message_open_container(call, 'a', "{iv}");
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
sink = dispd_out_session_get_sink(s);
|
||||
r = config_append(call,
|
||||
WFD_ENCODER_CONFIG_PEER_ADDRESS,
|
||||
"s",
|
||||
sink->peer->remote_address);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
r = config_append(call,
|
||||
WFD_ENCODER_CONFIG_RTP_PORT0,
|
||||
"u",
|
||||
s->stream.rtp_port);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
if(s->stream.rtcp_port) {
|
||||
r = config_append(call,
|
||||
WFD_ENCODER_CONFIG_PEER_RTCP_PORT,
|
||||
"u",
|
||||
s->stream.rtcp_port);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
}
|
||||
|
||||
r = config_append(call,
|
||||
WFD_ENCODER_CONFIG_LOCAL_ADDRESS,
|
||||
"s",
|
||||
sink->peer->local_address);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
if(s->stream.rtcp_port) {
|
||||
r = config_append(call,
|
||||
WFD_ENCODER_CONFIG_LOCAL_RTCP_PORT,
|
||||
"u",
|
||||
s->stream.rtcp_port);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
}
|
||||
|
||||
rect = dispd_session_get_disp_dimension(s);
|
||||
if(rect) {
|
||||
r = config_append(call,
|
||||
WFD_ENCODER_CONFIG_X,
|
||||
"u",
|
||||
rect->x);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
r = config_append(call,
|
||||
WFD_ENCODER_CONFIG_Y,
|
||||
"u",
|
||||
rect->y);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
r = config_append(call,
|
||||
WFD_ENCODER_CONFIG_WIDTH,
|
||||
"u",
|
||||
rect->width);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
r = config_append(call,
|
||||
WFD_ENCODER_CONFIG_HEIGHT,
|
||||
"u",
|
||||
rect->height);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
}
|
||||
|
||||
if(dispd_session_has_audio(s) && dispd_session_get_audio_dev_name(s)) {
|
||||
r = config_append(call,
|
||||
WFD_ENCODER_CONFIG_AUDIO_TYPE,
|
||||
"u",
|
||||
dispd_session_get_audio_type(s));
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
r = config_append(call,
|
||||
WFD_ENCODER_CONFIG_AUDIO_DEV,
|
||||
"s",
|
||||
dispd_session_get_audio_dev_name(s));
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
}
|
||||
|
||||
r = sd_bus_message_close_container(call);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
if(0 > r) {
|
||||
log_vERR(r);
|
||||
goto end;
|
||||
}
|
||||
|
||||
r = sd_bus_call(e->bus, call, 0, &error, &reply);
|
||||
if(0 > r) {
|
||||
log_warning("%s: %s", error.name, error.message);
|
||||
log_vERR(r);
|
||||
}
|
||||
|
||||
end:
|
||||
return r;
|
||||
}
|
||||
|
||||
static int dispd_encoder_call(struct dispd_encoder *e, const char *method)
|
||||
{
|
||||
_cleanup_sd_bus_message_ sd_bus_message *call = NULL;
|
||||
_cleanup_sd_bus_message_ sd_bus_message *reply = NULL;
|
||||
_cleanup_sd_bus_error_ sd_bus_error error = SD_BUS_ERROR_NULL;
|
||||
int r;
|
||||
|
||||
assert_ret(e);
|
||||
assert_ret(method);
|
||||
assert_ret(e->bus);
|
||||
|
||||
r = sd_bus_message_new_method_call(e->bus,
|
||||
&call,
|
||||
e->bus_name,
|
||||
"/org/freedesktop/miracle/encoder",
|
||||
"org.freedesktop.miracle.encoder",
|
||||
method);
|
||||
if(0 > r) {
|
||||
log_vERR(r);
|
||||
goto error;
|
||||
}
|
||||
|
||||
if(0 > r) {
|
||||
log_vERR(r);
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = sd_bus_call(e->bus, call, 0, &error, &reply);
|
||||
if(0 > r) {
|
||||
log_warning("%s: %s", error.name, error.message);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
dispd_encoder_kill_child(e);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int dispd_encoder_start(struct dispd_encoder *e)
|
||||
{
|
||||
assert_ret(e);
|
||||
|
||||
return dispd_encoder_call(e, "Start");
|
||||
}
|
||||
|
||||
int dispd_encoder_pause(struct dispd_encoder *e)
|
||||
{
|
||||
assert_ret(e);
|
||||
|
||||
return dispd_encoder_call(e, "Pause");
|
||||
}
|
||||
|
||||
static int on_child_term_timeout(sd_event_source *s,
|
||||
uint64_t usec,
|
||||
void *userdata)
|
||||
{
|
||||
struct dispd_encoder *e = userdata;
|
||||
|
||||
dispd_encoder_kill_child(e);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dispd_encoder_stop(struct dispd_encoder *e)
|
||||
{
|
||||
uint64_t now;
|
||||
sd_event *loop;
|
||||
int r;
|
||||
|
||||
assert_ret(e);
|
||||
|
||||
r = dispd_encoder_call(e, "Stop");
|
||||
if(0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
loop = dispd_get_loop();
|
||||
r = sd_event_now(loop, CLOCK_MONOTONIC, &now);
|
||||
if(0 > r) {
|
||||
log_vERR(r);
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = sd_event_add_time(loop,
|
||||
&e->child_term_time_source,
|
||||
CLOCK_MONOTONIC,
|
||||
now + (1000 * 1000),
|
||||
0,
|
||||
on_child_term_timeout,
|
||||
dispd_encoder_ref(e));
|
||||
if(0 > r) {
|
||||
log_vERR(r);
|
||||
goto error;
|
||||
}
|
||||
|
||||
error:
|
||||
dispd_encoder_kill_child(e);
|
||||
return r;
|
||||
}
|
||||
87
src/disp/dispd-encoder.h
Normal file
87
src/disp/dispd-encoder.h
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||
*
|
||||
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||
*
|
||||
* MiracleCast is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MiracleCast is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "shl_macro.h"
|
||||
|
||||
#ifndef DISPD_ENCODER_H
|
||||
#define DISPD_ENCODER_H
|
||||
|
||||
#define _dispd_encoder_unref_ _shl_cleanup_(dispd_encoder_unrefp)
|
||||
|
||||
enum wfd_encoder_config
|
||||
{
|
||||
WFD_ENCODER_CONFIG_DISPLAY_TYPE, /* string */
|
||||
WFD_ENCODER_CONFIG_DISPLAY_NAME, /* string */
|
||||
WFD_ENCODER_CONFIG_MONITOR_NUM, /* uint32 */
|
||||
WFD_ENCODER_CONFIG_X, /* uint32 */
|
||||
WFD_ENCODER_CONFIG_Y, /* uint32 */
|
||||
WFD_ENCODER_CONFIG_WIDTH, /* uint32 */
|
||||
WFD_ENCODER_CONFIG_HEIGHT, /* uint32 */
|
||||
WFD_ENCODER_CONFIG_WINDOW_ID, /* uint32 */
|
||||
WFD_ENCODER_CONFIG_FRAMERATE, /* uint32 */
|
||||
WFD_ENCODER_CONFIG_SCALE_WIDTH, /* uint32 */
|
||||
WFD_ENCODER_CONFIG_SCALE_HEIGHT, /* uint32 */
|
||||
WFD_ENCODER_CONFIG_AUDIO_TYPE, /* string */
|
||||
WFD_ENCODER_CONFIG_AUDIO_DEV, /* string */
|
||||
WFD_ENCODER_CONFIG_PEER_ADDRESS, /* string */
|
||||
WFD_ENCODER_CONFIG_RTP_PORT0, /* uint32 */
|
||||
WFD_ENCODER_CONFIG_RTP_PORT1, /* uint32 */
|
||||
WFD_ENCODER_CONFIG_PEER_RTCP_PORT, /* uint32 */
|
||||
WFD_ENCODER_CONFIG_LOCAL_ADDRESS, /* uint32 */
|
||||
WFD_ENCODER_CONFIG_LOCAL_RTCP_PORT, /* uint32 */
|
||||
WFD_ENCODER_CONFIG_H264_PROFILE,
|
||||
WFD_ENCODER_CONFIG_H264_LEVEL,
|
||||
WFD_ENCODER_CONFIG_DEBUG_LEVEL,
|
||||
};
|
||||
|
||||
enum dispd_encoder_state
|
||||
{
|
||||
DISPD_ENCODER_STATE_NULL = 0,
|
||||
DISPD_ENCODER_STATE_SPAWNED,
|
||||
DISPD_ENCODER_STATE_CONFIGURED,
|
||||
DISPD_ENCODER_STATE_READY,
|
||||
DISPD_ENCODER_STATE_STARTED,
|
||||
DISPD_ENCODER_STATE_PAUSED,
|
||||
DISPD_ENCODER_STATE_TERMINATED,
|
||||
};
|
||||
|
||||
struct dispd_session;
|
||||
struct dispd_encoder;
|
||||
|
||||
typedef void (*dispd_encoder_state_change_handler)(struct dispd_encoder *e,
|
||||
enum dispd_encoder_state state,
|
||||
void *userdata);
|
||||
|
||||
int dispd_encoder_spawn(struct dispd_encoder **out, struct dispd_session *s);
|
||||
struct dispd_encoder * dispd_encoder_ref(struct dispd_encoder *e);
|
||||
void dispd_encoder_unref(struct dispd_encoder *e);
|
||||
void dispd_encoder_unrefp(struct dispd_encoder **e);
|
||||
|
||||
int dispd_encoder_configure(struct dispd_encoder *e, struct dispd_session *s);
|
||||
int dispd_encoder_start(struct dispd_encoder *e);
|
||||
int dispd_encoder_pause(struct dispd_encoder *e);
|
||||
int dispd_encoder_stop(struct dispd_encoder *e);
|
||||
|
||||
void dispd_encoder_set_handler(struct dispd_encoder *e,
|
||||
dispd_encoder_state_change_handler handler,
|
||||
void *userdata);
|
||||
dispd_encoder_state_change_handler dispd_encoder_get_handler(struct dispd_encoder *e);
|
||||
enum dispd_encoder_state dispd_encoder_get_state(struct dispd_encoder *e);
|
||||
|
||||
#endif /* DISPD_ENCODER_H */
|
||||
1004
src/disp/dispd-out-session.c
Normal file
1004
src/disp/dispd-out-session.c
Normal file
File diff suppressed because it is too large
Load diff
1091
src/disp/dispd-session.c
Normal file
1091
src/disp/dispd-session.c
Normal file
File diff suppressed because it is too large
Load diff
146
src/disp/dispd-session.h
Normal file
146
src/disp/dispd-session.h
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||
*
|
||||
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||
*
|
||||
* MiracleCast is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MiracleCast is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <unistd.h>
|
||||
#include "dispd.h"
|
||||
#include "dispd-arg.h"
|
||||
|
||||
#ifndef DISPD_SESSION_H
|
||||
#define DISPD_SESSION_H
|
||||
|
||||
#define dispd_out_session(s) (assert(dispd_is_out_session(s)), (struct dispd_out_session *) (s))
|
||||
#define dispd_in_session(s) (assert(dispd_is_in_session(s)), (struct dispd_in_session *) (s))
|
||||
|
||||
struct dispd_session;
|
||||
struct dispd_sink;
|
||||
struct rtsp;
|
||||
struct rtsp_message;
|
||||
|
||||
enum rtsp_message_id
|
||||
{
|
||||
RTSP_M_UNKNOWN,
|
||||
RTSP_M1_REQUEST_SINK_OPTIONS,
|
||||
RTSP_M2_REQUEST_SRC_OPTIONS,
|
||||
RTSP_M3_GET_PARAMETER,
|
||||
RTSP_M4_SET_PARAMETER,
|
||||
RTSP_M5_TRIGGER,
|
||||
RTSP_M6_SETUP,
|
||||
RTSP_M7_PLAY,
|
||||
RTSP_M8_TEARDOWN,
|
||||
RTSP_M9_PAUSE,
|
||||
RTSP_M10_SET_ROUTE,
|
||||
RTSP_M11_SET_CONNECTOR_TYPE,
|
||||
RTSP_M12_SET_STANDBY,
|
||||
RTSP_M13_REQUEST_IDR,
|
||||
RTSP_M14_ESTABLISH_UIBC,
|
||||
RTSP_M15_ENABLE_UIBC,
|
||||
RTSP_M16_KEEPALIVE,
|
||||
};
|
||||
|
||||
enum dispd_stream_id
|
||||
{
|
||||
DISPD_STREAM_ID_PRIMARY,
|
||||
DISPD_STREAM_ID_SECONDARY,
|
||||
};
|
||||
|
||||
enum dispd_session_arg_id
|
||||
{
|
||||
DISPD_SESSION_ARG_NEXT_REQUEST,
|
||||
DISPD_SESSION_ARG_NEW_STATE,
|
||||
DISPD_SESSION_ARG_REQUEST_ARGS,
|
||||
};
|
||||
|
||||
struct rtsp_dispatch_entry
|
||||
{
|
||||
union {
|
||||
int (*request)(struct dispd_session *s,
|
||||
struct rtsp *bus,
|
||||
const struct dispd_arg_list *args,
|
||||
struct rtsp_message **out);
|
||||
int (*handle_request)(struct dispd_session *s,
|
||||
struct rtsp_message *req,
|
||||
struct rtsp_message **out_rep);
|
||||
};
|
||||
int (*handle_reply)(struct dispd_session *s,
|
||||
struct rtsp_message *m);
|
||||
struct dispd_arg_list rule;
|
||||
};
|
||||
|
||||
struct dispd_session_vtable
|
||||
{
|
||||
int (*initiate_io)(struct dispd_session *s, int *out_fd, uint32_t *out_mask);
|
||||
int (*handle_io)(struct dispd_session *s, int error, int *out_fd);
|
||||
int (*initiate_request)(struct dispd_session *s);
|
||||
int (*resume)(struct dispd_session *);
|
||||
int (*pause)(struct dispd_session *);
|
||||
int (*teardown)(struct dispd_session *);
|
||||
void (*destroy)(struct dispd_session *s);
|
||||
};
|
||||
|
||||
struct dispd_session
|
||||
{
|
||||
int ref;
|
||||
enum dispd_session_dir dir;
|
||||
enum dispd_session_state state;
|
||||
enum rtsp_message_id last_request;
|
||||
const struct rtsp_dispatch_entry *rtsp_disp_tbl;
|
||||
|
||||
unsigned int id;
|
||||
struct rtsp *rtsp;
|
||||
uint64_t req_cookie;
|
||||
uint16_t rtp_ports[2];
|
||||
struct wfd_video_formats *vformats;
|
||||
struct wfd_audio_codecs *acodecs;
|
||||
|
||||
struct {
|
||||
enum dispd_stream_id id;
|
||||
char *url;
|
||||
uint16_t rtp_port;
|
||||
uint16_t rtcp_port;
|
||||
} stream;
|
||||
|
||||
enum dispd_display_server_type disp_type;
|
||||
char *disp_name;
|
||||
char *disp_params;
|
||||
char *disp_auth;
|
||||
struct dispd_rectangle disp_dimen;
|
||||
enum dispd_audio_server_type audio_type;
|
||||
char *audio_dev_name;
|
||||
|
||||
uid_t client_uid;
|
||||
gid_t client_gid;
|
||||
gid_t client_pid;
|
||||
char *runtime_path;
|
||||
};
|
||||
|
||||
int dispd_session_init(struct dispd_session *s,
|
||||
unsigned int id,
|
||||
enum dispd_session_dir dir,
|
||||
const struct rtsp_dispatch_entry *disp_tbl);
|
||||
int dispd_session_gen_stream_url(struct dispd_session *s,
|
||||
const char *local_addr,
|
||||
enum dispd_stream_id id);
|
||||
int dispd_session_request(struct dispd_session *s,
|
||||
enum rtsp_message_id id,
|
||||
const struct dispd_arg_list *args);
|
||||
void dispd_session_end(struct dispd_session *s);
|
||||
struct dispd_sink * dispd_out_session_get_sink(struct dispd_session *s);
|
||||
void dispd_session_set_state(struct dispd_session *s,
|
||||
enum dispd_session_state state);
|
||||
|
||||
#endif /* DISPD_SESSION_H */
|
||||
173
src/disp/dispd-sink.c
Normal file
173
src/disp/dispd-sink.c
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
/*
|
||||
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||
*
|
||||
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||
*
|
||||
* MiracleCast is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MiracleCast is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <time.h>
|
||||
#include <systemd/sd-event.h>
|
||||
#include "ctl.h"
|
||||
#include "dispd.h"
|
||||
#include "dispd-dbus.h"
|
||||
|
||||
static int dispd_sink_set_session(struct dispd_sink *sink,
|
||||
struct dispd_session *session)
|
||||
{
|
||||
int r;
|
||||
|
||||
assert_ret(sink);
|
||||
|
||||
if(sink->session == session) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(session) {
|
||||
r = dispd_add_session(dispd_get(), session);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
}
|
||||
|
||||
if(sink->session) {
|
||||
dispd_fn_sink_detach(sink);
|
||||
dispd_remove_session_by_id(dispd_get(),
|
||||
dispd_session_get_id(sink->session),
|
||||
NULL);
|
||||
dispd_session_unref(sink->session);
|
||||
}
|
||||
|
||||
sink->session = session ? dispd_session_ref(session) : NULL;
|
||||
dispd_fn_sink_properties_changed(sink, "Session");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dispd_sink_new(struct dispd_sink **out,
|
||||
struct ctl_peer *peer,
|
||||
union wfd_sube *sube)
|
||||
{
|
||||
struct dispd_sink *sink;
|
||||
|
||||
assert_ret(out);
|
||||
assert_ret(peer);
|
||||
assert_ret(sube);
|
||||
assert_ret(wfd_sube_device_is_sink(sube));
|
||||
|
||||
sink = calloc(1, sizeof(struct dispd_sink));
|
||||
if(!sink) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
sink->label = strdup(peer->label);
|
||||
if(!sink->label) {
|
||||
dispd_sink_free(sink);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
sink->peer = peer;
|
||||
sink->dev_info = *sube;
|
||||
|
||||
*out = sink;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dispd_sink_free(struct dispd_sink *sink)
|
||||
{
|
||||
if(!sink) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispd_sink_set_session(sink, NULL);
|
||||
|
||||
if(sink->label) {
|
||||
free(sink->label);
|
||||
}
|
||||
|
||||
free(sink);
|
||||
}
|
||||
|
||||
const char * dispd_sink_get_label(struct dispd_sink *sink)
|
||||
{
|
||||
assert_retv(sink, NULL);
|
||||
|
||||
return sink->label;
|
||||
}
|
||||
|
||||
const union wfd_sube * dispd_sink_get_dev_info(struct dispd_sink *sink)
|
||||
{
|
||||
assert_retv(sink, NULL);
|
||||
|
||||
return &sink->dev_info;
|
||||
}
|
||||
|
||||
struct ctl_peer * dispd_sink_get_peer(struct dispd_sink *sink)
|
||||
{
|
||||
assert_retv(sink, NULL);
|
||||
|
||||
return sink->peer;
|
||||
}
|
||||
|
||||
int dispd_sink_create_session(struct dispd_sink *sink, struct dispd_session **out)
|
||||
{
|
||||
int r;
|
||||
_dispd_session_unref_ struct dispd_session *sess = NULL;
|
||||
|
||||
assert_ret(sink);
|
||||
assert_ret(out);
|
||||
|
||||
if(dispd_sink_is_session_started(sink)) {
|
||||
return -EALREADY;
|
||||
}
|
||||
|
||||
r = dispd_out_session_new(&sess,
|
||||
dispd_alloc_session_id(dispd_get()),
|
||||
sink);
|
||||
if(0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
r = dispd_sink_set_session(sink, sess);
|
||||
if(0 > r) {
|
||||
return r;
|
||||
}
|
||||
|
||||
*out = dispd_session_ref(sess);
|
||||
|
||||
dispd_fn_sink_properties_changed(sink, "Session");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dispd_fn_out_session_ended(struct dispd_session *s)
|
||||
{
|
||||
struct dispd_sink *sink;
|
||||
|
||||
assert_ret(dispd_is_out_session(s));
|
||||
|
||||
sink = dispd_out_session_get_sink(s);
|
||||
if(sink) {
|
||||
dispd_sink_set_session(sink, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool dispd_sink_is_session_started(struct dispd_sink *sink)
|
||||
{
|
||||
assert_retv(sink, false);
|
||||
|
||||
return NULL != sink->session;
|
||||
}
|
||||
545
src/disp/dispd.c
Normal file
545
src/disp/dispd.c
Normal file
|
|
@ -0,0 +1,545 @@
|
|||
/*
|
||||
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||
*
|
||||
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||
*
|
||||
* MiracleCast is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MiracleCast is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#define LOG_SUBSYSTEM "dispd"
|
||||
|
||||
#include <locale.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <systemd/sd-event.h>
|
||||
#include <systemd/sd-daemon.h>
|
||||
#include "ctl.h"
|
||||
#include "dispd.h"
|
||||
#include "wfd.h"
|
||||
#include "dispd-dbus.h"
|
||||
#include "config.h"
|
||||
|
||||
#define CLI_PROMPT "\001" CLI_BLUE "\002" "[dispctl] # " "\001" CLI_DEFAULT "\002"
|
||||
|
||||
#define HISTORY_FILENAME ".miracle-disp.history"
|
||||
|
||||
static int dispd_init(struct dispd *dispd, sd_bus *bus);
|
||||
static void dispd_free(struct dispd *dispd);
|
||||
|
||||
static struct dispd_dbus *dispd_dbus = NULL;
|
||||
|
||||
char* get_cli_prompt()
|
||||
{
|
||||
return CLI_PROMPT;
|
||||
}
|
||||
|
||||
/*
|
||||
* get history filename
|
||||
*/
|
||||
|
||||
char* get_history_filename()
|
||||
{
|
||||
return HISTORY_FILENAME;
|
||||
}
|
||||
|
||||
struct dispd_dbus * dispd_dbus_get()
|
||||
{
|
||||
return dispd_dbus;
|
||||
}
|
||||
static struct dispd *dispd = NULL;
|
||||
|
||||
struct ctl_wifi *get_wifi()
|
||||
{
|
||||
return dispd->wifi;
|
||||
}
|
||||
|
||||
struct dispd * dispd_get()
|
||||
{
|
||||
return dispd;
|
||||
}
|
||||
|
||||
int dispd_new(struct dispd **out, sd_event *loop, sd_bus *bus)
|
||||
{
|
||||
int r;
|
||||
struct dispd *dispd = calloc(1, sizeof(struct dispd));
|
||||
if(!dispd) {
|
||||
r = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
|
||||
shl_htable_init_str(&dispd->sinks);
|
||||
shl_htable_init_uint(&dispd->sessions);
|
||||
dispd->loop = sd_event_ref(loop);
|
||||
|
||||
r = dispd_init(dispd, bus);
|
||||
if(0 > r) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
*out = dispd;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
dispd_free(dispd);
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
static void dispd_free(struct dispd *dispd)
|
||||
{
|
||||
if(!dispd) {
|
||||
return;
|
||||
}
|
||||
|
||||
ctl_wifi_free(dispd->wifi);
|
||||
dispd->wifi = NULL;
|
||||
shl_htable_clear_str(&dispd->sinks, NULL, NULL);
|
||||
shl_htable_clear_uint(&dispd->sessions, NULL, NULL);
|
||||
|
||||
if(dispd->loop) {
|
||||
sd_event_unref(dispd->loop);
|
||||
}
|
||||
|
||||
free(dispd);
|
||||
}
|
||||
|
||||
static int dispd_handle_shutdown(sd_event_source *s,
|
||||
uint64_t usec,
|
||||
void *userdata)
|
||||
{
|
||||
struct dispd *dispd = userdata;
|
||||
|
||||
sd_event_exit(dispd->loop, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void dispd_shutdown(struct dispd *dispd)
|
||||
{
|
||||
uint64_t now;
|
||||
int r = sd_event_now(dispd_get_loop(), CLOCK_MONOTONIC, &now);
|
||||
if(0 > r) {
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = sd_event_add_time(dispd_get_loop(),
|
||||
NULL,
|
||||
CLOCK_MONOTONIC,
|
||||
now + 100 * 1000,
|
||||
0,
|
||||
dispd_handle_shutdown,
|
||||
dispd);
|
||||
if(0 <= r) {
|
||||
return;
|
||||
}
|
||||
|
||||
error:
|
||||
sd_event_exit(dispd->loop, 0);
|
||||
}
|
||||
|
||||
int dispd_add_sink(struct dispd *dispd,
|
||||
struct ctl_peer *p,
|
||||
union wfd_sube *sube,
|
||||
struct dispd_sink **out)
|
||||
{
|
||||
_dispd_sink_free_ struct dispd_sink *s = NULL;
|
||||
int r = shl_htable_lookup_str(&dispd->sinks,
|
||||
p->label,
|
||||
NULL,
|
||||
NULL);
|
||||
if(r) {
|
||||
return -EEXIST;
|
||||
}
|
||||
|
||||
r = dispd_sink_new(&s, p, sube);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
r = shl_htable_insert_str(&dispd->sinks,
|
||||
dispd_sink_to_htable(s),
|
||||
NULL);
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
++dispd->n_sinks;
|
||||
*out = s;
|
||||
s = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dispd_find_sink_by_label(struct dispd *dispd,
|
||||
const char *label,
|
||||
struct dispd_sink **out)
|
||||
{
|
||||
char **entry;
|
||||
int r = shl_htable_lookup_str(&dispd->sinks, label, NULL, &entry);
|
||||
if(r && out) {
|
||||
*out = dispd_sink_from_htable(entry);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int dispd_remove_sink_by_label(struct dispd *dispd,
|
||||
const char *label,
|
||||
struct dispd_sink **out)
|
||||
{
|
||||
char **entry;
|
||||
int r = shl_htable_remove_str(&dispd->sinks, label, NULL, &entry);
|
||||
if(!r) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
--dispd->n_sinks;
|
||||
|
||||
if(out) {
|
||||
*out = dispd_sink_from_htable(entry);
|
||||
}
|
||||
|
||||
end:
|
||||
return r;
|
||||
}
|
||||
|
||||
unsigned int dispd_alloc_session_id(struct dispd *dispd)
|
||||
{
|
||||
return ++dispd->id_pool;
|
||||
}
|
||||
|
||||
int dispd_add_session(struct dispd *dispd, struct dispd_session *s)
|
||||
{
|
||||
int r;
|
||||
|
||||
assert(dispd);
|
||||
assert(s && dispd_session_get_id(s));
|
||||
assert(!dispd_find_session_by_id(dispd, dispd_session_get_id(s), NULL));
|
||||
|
||||
r = shl_htable_insert_uint(&dispd->sessions, dispd_session_to_htable(s));
|
||||
if(0 > r) {
|
||||
return log_ERR(r);
|
||||
}
|
||||
|
||||
++dispd->n_sessions;
|
||||
|
||||
dispd_fn_session_new(s);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dispd_find_session_by_id(struct dispd *dispd,
|
||||
unsigned int id,
|
||||
struct dispd_session **out)
|
||||
{
|
||||
unsigned int *entry;
|
||||
int r = shl_htable_lookup_uint(&dispd->sessions, id, &entry);
|
||||
if(r && out) {
|
||||
*out = dispd_session_from_htable(entry);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int dispd_remove_session_by_id(struct dispd *dispd,
|
||||
unsigned int id,
|
||||
struct dispd_session **out)
|
||||
{
|
||||
unsigned int *entry;
|
||||
struct dispd_session *s;
|
||||
int r = shl_htable_remove_uint(&dispd->sessions, id, &entry);
|
||||
if(!r) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
--dispd->n_sessions;
|
||||
|
||||
s = dispd_session_from_htable(entry);
|
||||
dispd_fn_session_free(s);
|
||||
if(out) {
|
||||
*out = s;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int dispd_fetch_info(sd_event_source *s, void *userdata)
|
||||
{
|
||||
struct dispd *dispd = userdata;
|
||||
int r;
|
||||
|
||||
sd_event_source_unref(s);
|
||||
|
||||
r = ctl_wifi_fetch(dispd->wifi);
|
||||
if(0 > r) {
|
||||
log_warning("failed to fetch information about links and peers: %s",
|
||||
strerror(errno));
|
||||
sd_event_exit(dispd->loop, r);
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int dispd_handle_signal(sd_event_source *s,
|
||||
const struct signalfd_siginfo *ssi,
|
||||
void *userdata)
|
||||
{
|
||||
int r;
|
||||
siginfo_t siginfo;
|
||||
struct dispd *dispd = userdata;
|
||||
|
||||
if(ssi->ssi_signo == SIGCHLD) {
|
||||
r = waitid(P_PID, ssi->ssi_pid, &siginfo, WNOHANG | WEXITED);
|
||||
if(0 > r) {
|
||||
log_warning("failed to reaping child %d", ssi->ssi_pid);
|
||||
}
|
||||
else {
|
||||
log_info("child %d exit: %d",
|
||||
ssi->ssi_pid,
|
||||
siginfo.si_code);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sd_event_exit(dispd->loop, 0);
|
||||
}
|
||||
|
||||
static int dispd_init(struct dispd *dispd, sd_bus *bus)
|
||||
{
|
||||
int i, r;
|
||||
const int signals[] = {
|
||||
SIGINT, SIGHUP, SIGQUIT, SIGTERM, SIGCHLD,
|
||||
};
|
||||
struct ctl_wifi *wifi;
|
||||
|
||||
for(i = 0; i < SHL_ARRAY_LENGTH(signals); i ++) {
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, signals[i]);
|
||||
r = sigprocmask(SIG_BLOCK, &mask, NULL);
|
||||
if(0 > r) {
|
||||
break;
|
||||
}
|
||||
|
||||
r = sd_event_add_signal(dispd->loop,
|
||||
NULL,
|
||||
signals[i],
|
||||
dispd_handle_signal,
|
||||
dispd);
|
||||
if(0 > r) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
r = ctl_wifi_new(&wifi, bus);
|
||||
if(0 > r) {
|
||||
log_vENOMEM();
|
||||
goto end;
|
||||
}
|
||||
|
||||
r = sd_event_add_defer(dispd->loop, NULL, dispd_fetch_info, dispd);
|
||||
if(0 > r) {
|
||||
log_vERRNO();
|
||||
|
||||
ctl_wifi_free(wifi);
|
||||
goto end;
|
||||
}
|
||||
|
||||
dispd->wifi = wifi;
|
||||
|
||||
end:
|
||||
return r;
|
||||
}
|
||||
|
||||
void ctl_fn_peer_new(struct ctl_peer *p)
|
||||
{
|
||||
struct dispd_sink *s;
|
||||
union wfd_sube sube;
|
||||
int r;
|
||||
|
||||
log_debug("new peer %s (%s) shows up, wfd_subelems: '%s'",
|
||||
p->label,
|
||||
p->friendly_name,
|
||||
p->wfd_subelements);
|
||||
|
||||
if(!p->wfd_subelements || !*p->wfd_subelements) {
|
||||
log_info("peer %s has no wfd_subelems, ignore it", p->label);
|
||||
return;
|
||||
}
|
||||
|
||||
r = wfd_sube_parse(p->wfd_subelements, &sube);
|
||||
if(0 > r) {
|
||||
log_debug("peer %s has no valid subelement, ignore it", p->label);
|
||||
return;
|
||||
}
|
||||
|
||||
if(wfd_sube_device_is_sink(&sube)) {
|
||||
r = dispd_add_sink(dispd_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 log_vERRNO();
|
||||
}
|
||||
|
||||
r = dispd_fn_sink_new(s);
|
||||
if(0 > r) {
|
||||
log_warning("failed to publish newly added sink (%s): %s",
|
||||
dispd_sink_get_label(s),
|
||||
strerror(errno));
|
||||
return log_vERRNO();
|
||||
}
|
||||
|
||||
log_info("sink %s added", s->label);
|
||||
}
|
||||
|
||||
if(wfd_sube_device_is_source(&sube)) {
|
||||
log_info("source %s ignired", p->label);
|
||||
}
|
||||
}
|
||||
|
||||
void ctl_fn_peer_free(struct ctl_peer *p)
|
||||
{
|
||||
struct dispd_sink *s;
|
||||
int r;
|
||||
|
||||
r = dispd_remove_sink_by_label(dispd, p->label, &s);
|
||||
if(r) {
|
||||
dispd_fn_sink_free(s);
|
||||
log_info("sink %s removed", s->label);
|
||||
dispd_sink_free(s);
|
||||
}
|
||||
|
||||
log_info("peer %s down", p->label);
|
||||
}
|
||||
|
||||
void ctl_fn_peer_provision_discovery(struct ctl_peer *p,
|
||||
const char *prov,
|
||||
const char *pin)
|
||||
{
|
||||
}
|
||||
|
||||
void ctl_fn_peer_go_neg_request(struct ctl_peer *p,
|
||||
const char *prov,
|
||||
const char *pin)
|
||||
{
|
||||
}
|
||||
|
||||
void ctl_fn_peer_formation_failure(struct ctl_peer *p, const char *reason)
|
||||
{
|
||||
}
|
||||
|
||||
void ctl_fn_peer_connected(struct ctl_peer *p)
|
||||
{
|
||||
}
|
||||
|
||||
void ctl_fn_peer_disconnected(struct ctl_peer *p)
|
||||
{
|
||||
}
|
||||
|
||||
void ctl_fn_link_new(struct ctl_link *l)
|
||||
{
|
||||
}
|
||||
|
||||
void ctl_fn_link_free(struct ctl_link *l)
|
||||
{
|
||||
}
|
||||
|
||||
void cli_fn_help()
|
||||
{
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int r;
|
||||
sd_event *event;
|
||||
sd_bus *bus;
|
||||
|
||||
setlocale(LC_ALL, "");
|
||||
setlocale(LC_TIME, "en_US.UTF-8");
|
||||
|
||||
if(getenv("LOG_LEVEL")) {
|
||||
log_max_sev = log_parse_arg(getenv("LOG_LEVEL"));
|
||||
}
|
||||
|
||||
r = sd_event_default(&event);
|
||||
if(0 > r) {
|
||||
log_warning("can't create default event loop");
|
||||
goto end;
|
||||
}
|
||||
|
||||
r = sd_event_set_watchdog(event, true);
|
||||
if (0 > r) {
|
||||
log_warning("unable to start automatic watchdog support: %s", strerror(errno));
|
||||
goto unref_event;
|
||||
}
|
||||
|
||||
r = sd_bus_default_system(&bus);
|
||||
if(0 > r) {
|
||||
log_warning("unable to connect to system DBus: %s", strerror(errno));
|
||||
goto disable_watchdog;
|
||||
}
|
||||
|
||||
r = sd_bus_attach_event(bus, event, 0);
|
||||
if(0 > r) {
|
||||
goto unref_bus;
|
||||
}
|
||||
|
||||
r = dispd_dbus_new(&dispd_dbus, event, bus);
|
||||
if(0 > r) {
|
||||
goto bus_detach_event;
|
||||
}
|
||||
|
||||
r = dispd_new(&dispd, event, bus);
|
||||
if(0 > r) {
|
||||
goto free_dispd_dbus;
|
||||
}
|
||||
|
||||
r = dispd_dbus_expose(dispd_dbus);
|
||||
if(0 > r) {
|
||||
log_warning("unable to publish WFD service: %s", strerror(errno));
|
||||
goto free_dispd;
|
||||
}
|
||||
|
||||
r = sd_notify(false, "READY=1\n"
|
||||
"STATUS=Running..");
|
||||
if (0 > r) {
|
||||
log_warning("unable to notify systemd that we are ready: %s", strerror(errno));
|
||||
goto free_dispd_dbus;
|
||||
}
|
||||
|
||||
sd_event_loop(event);
|
||||
|
||||
sd_notify(false, "STATUS=Exiting..");
|
||||
|
||||
free_dispd:
|
||||
dispd_free(dispd);
|
||||
free_dispd_dbus:
|
||||
dispd_dbus_free(dispd_dbus);
|
||||
bus_detach_event:
|
||||
sd_bus_detach_event(bus);
|
||||
unref_bus:
|
||||
sd_bus_flush_close_unref(bus);
|
||||
disable_watchdog:
|
||||
sd_event_set_watchdog(event, false);
|
||||
unref_event:
|
||||
sd_event_unref(event);
|
||||
end:
|
||||
return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
223
src/disp/dispd.h
Normal file
223
src/disp/dispd.h
Normal file
|
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
* MiracleCast - Wifi-Display/Miracast Implementation
|
||||
*
|
||||
* Copyright (c) 2013-2014 David Herrmann <dh.herrmann@gmail.com>
|
||||
*
|
||||
* MiracleCast is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation; either version 2.1 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* MiracleCast is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with MiracleCast; If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <systemd/sd-event.h>
|
||||
#include "shl_htable.h"
|
||||
#include "ctl.h"
|
||||
#include "wfd.h"
|
||||
|
||||
#ifndef DISP_DISP_H
|
||||
#define DISP_DISP_H
|
||||
|
||||
/* dispd session */
|
||||
#define dispd_session(s) ((struct dispd_session *) (s))
|
||||
#define dispd_is_session(s) ( \
|
||||
(s) && \
|
||||
(DISPD_SESSION_DIR_OUT == dispd_session(s)->dir || \
|
||||
DISPD_SESSION_DIR_IN == dispd_session(s)->dir) \
|
||||
)
|
||||
#define dispd_session_has_id(s) (0 < dispd_session_get_id(s))
|
||||
#define dispd_is_out_session(s) (DISPD_SESSION_DIR_OUT == dispd_session_get_dir(s))
|
||||
#define dispd_is_in_session(s) (DISPD_SESSION_DIR_IN == dispd_session_get_dir(s))
|
||||
#define _dispd_session_unref_ _shl_cleanup_(dispd_session_unrefp)
|
||||
|
||||
struct dispd_sink;
|
||||
struct dispd_session;
|
||||
struct rtsp_dispatch_entry;
|
||||
|
||||
enum dispd_session_dir
|
||||
{
|
||||
DISPD_SESSION_DIR_OUT,
|
||||
DISPD_SESSION_DIR_IN,
|
||||
};
|
||||
|
||||
enum dispd_session_state
|
||||
{
|
||||
DISPD_SESSION_STATE_NULL,
|
||||
DISPD_SESSION_STATE_CONNECTING,
|
||||
DISPD_SESSION_STATE_CAPS_EXCHANGING,
|
||||
DISPD_SESSION_STATE_ESTABLISHED,
|
||||
DISPD_SESSION_STATE_SETTING_UP,
|
||||
DISPD_SESSION_STATE_PAUSED,
|
||||
DISPD_SESSION_STATE_PLAYING,
|
||||
DISPD_SESSION_STATE_TEARING_DOWN,
|
||||
DISPD_SESSION_STATE_DESTROYED,
|
||||
};
|
||||
|
||||
struct dispd_rectangle
|
||||
{
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
enum dispd_display_server_type
|
||||
{
|
||||
DISPD_DISPLAY_SERVER_TYPE_UNKNOWN = 0,
|
||||
DISPD_DISPLAY_SERVER_TYPE_X,
|
||||
};
|
||||
|
||||
enum dispd_audio_server_type
|
||||
{
|
||||
DISPD_AUDIO_SERVER_TYPE_UNKNOWN = 0,
|
||||
DISPD_AUDIO_SERVER_TYPE_PULSE_AUDIO,
|
||||
};
|
||||
|
||||
int dispd_out_session_new(struct dispd_session **out,
|
||||
unsigned int id,
|
||||
struct dispd_sink *sink);
|
||||
struct dispd_session * _dispd_session_ref(struct dispd_session *s);
|
||||
#define dispd_session_ref(s) ( \
|
||||
log_debug("dispd_session_ref(%p): %d => %d", (s), *(int *) s, 1 + *(int *) s), \
|
||||
_dispd_session_ref(s) \
|
||||
)
|
||||
void _dispd_session_unref(struct dispd_session *s);
|
||||
#define dispd_session_unref(s) { \
|
||||
log_debug("dispd_session_unref(%p): %d => %d", (s), *(int *) s, *(int *) s - 1); \
|
||||
_dispd_session_unref(s); \
|
||||
}
|
||||
|
||||
void dispd_session_unrefp(struct dispd_session **s);
|
||||
unsigned int * dispd_session_to_htable(struct dispd_session *s);
|
||||
struct dispd_session * dispd_session_from_htable(unsigned int *e);
|
||||
|
||||
int dispd_session_start(struct dispd_session *s);
|
||||
int dispd_session_resume(struct dispd_session *s);
|
||||
int dispd_session_pause(struct dispd_session *s);
|
||||
int dispd_session_teardown(struct dispd_session *s);
|
||||
int dispd_session_destroy(struct dispd_session *s);
|
||||
|
||||
bool dispd_session_is_established(struct dispd_session *s);
|
||||
unsigned int dispd_session_get_id(struct dispd_session *s);
|
||||
const char * dispd_session_get_stream_url(struct dispd_session *s);
|
||||
bool dispd_session_is_state(struct dispd_session *s, enum dispd_session_state state);
|
||||
enum dispd_session_state dispd_session_get_state(struct dispd_session *s);
|
||||
enum dispd_session_dir dispd_session_get_dir(struct dispd_session *s);
|
||||
struct dispd_sink * dispd_out_session_get_sink(struct dispd_session *s);
|
||||
|
||||
enum dispd_display_server_type dispd_session_get_disp_type(struct dispd_session *s);
|
||||
bool dispd_session_has_disp(struct dispd_session *s);
|
||||
int dispd_session_set_disp_type(struct dispd_session *s, enum dispd_display_server_type);
|
||||
const char * dispd_session_get_disp_name(struct dispd_session *s);
|
||||
int dispd_session_set_disp_name(struct dispd_session *s, const char *disp_name);
|
||||
const char * dispd_session_get_disp_params(struct dispd_session *s);
|
||||
int dispd_session_set_disp_params(struct dispd_session *s, const char *disp_params);
|
||||
const char * dispd_session_get_disp_auth(struct dispd_session *s);
|
||||
int dispd_session_set_disp_auth(struct dispd_session *s, const char *disp_auth);
|
||||
const struct dispd_rectangle * dispd_session_get_disp_dimension(struct dispd_session *s);
|
||||
int dispd_session_set_disp_dimension(struct dispd_session *s, const struct dispd_rectangle *rect);
|
||||
enum dispd_audio_server_type dispd_session_get_audio_type(struct dispd_session *s);
|
||||
bool dispd_session_has_audio(struct dispd_session *s);
|
||||
int dispd_session_set_audio_type(struct dispd_session *s, enum dispd_audio_server_type audio_type);
|
||||
const char * dispd_session_get_audio_dev_name(struct dispd_session *s);
|
||||
int dispd_session_set_audio_dev_name(struct dispd_session *s, const char *audio_dev_name);
|
||||
const char * dispd_session_get_runtime_path(struct dispd_session *s);
|
||||
int dispd_session_set_runtime_path(struct dispd_session *s,
|
||||
const char *runtime_path);
|
||||
uid_t dispd_session_get_client_uid(struct dispd_session *s);
|
||||
int dispd_session_set_client_uid(struct dispd_session *s, uid_t uid);
|
||||
uid_t dispd_session_get_client_gid(struct dispd_session *s);
|
||||
int dispd_session_set_client_gid(struct dispd_session *s, uid_t gid);
|
||||
pid_t dispd_session_get_client_pid(struct dispd_session *s);
|
||||
int dispd_session_set_client_pid(struct dispd_session *s, pid_t pid);
|
||||
|
||||
/* dispd sink */
|
||||
#define _dispd_sink_free_ _shl_cleanup_(dispd_sink_freep)
|
||||
#define dispd_sink_to_htable(s) (&(s)->label)
|
||||
#define dispd_sink_from_htable(s) shl_htable_entry(s, struct dispd_sink, label)
|
||||
|
||||
struct dispd_sink
|
||||
{
|
||||
struct ctl_peer *peer;
|
||||
union wfd_sube dev_info;
|
||||
char *label;
|
||||
struct dispd_session *session;
|
||||
|
||||
sd_event_source *session_cleanup_source;
|
||||
};
|
||||
|
||||
int dispd_sink_new(struct dispd_sink **out,
|
||||
struct ctl_peer *peer,
|
||||
union wfd_sube *sube);
|
||||
void dispd_sink_free(struct dispd_sink *sink);
|
||||
static inline void dispd_sink_freep(struct dispd_sink **s)
|
||||
{
|
||||
dispd_sink_free(*s);
|
||||
*s = NULL;
|
||||
}
|
||||
|
||||
int dispd_sink_create_session(struct dispd_sink *sink, struct dispd_session **out);
|
||||
|
||||
const char * dispd_sink_get_label(struct dispd_sink *sink);
|
||||
const union wfd_sube * dispd_sink_get_dev_info(struct dispd_sink *sink);
|
||||
bool dispd_sink_is_session_started(struct dispd_sink *sink);
|
||||
|
||||
void dispd_sink_handle_session_ended(struct dispd_sink *sink);
|
||||
|
||||
/* wfd handling */
|
||||
#define dispd_foreach_sink(_i, _w) \
|
||||
SHL_HTABLE_FOREACH_MACRO(_i, \
|
||||
&(_w)->sinks, \
|
||||
dispd_sink_from_htable)
|
||||
#define dispd_foreach_session(_i, _w) \
|
||||
SHL_HTABLE_FOREACH_MACRO(_i, \
|
||||
&(_w)->sessions, \
|
||||
dispd_session_from_htable)
|
||||
|
||||
struct dispd
|
||||
{
|
||||
sd_event *loop;
|
||||
struct ctl_wifi *wifi;
|
||||
struct shl_htable sinks;
|
||||
size_t n_sinks;
|
||||
struct shl_htable sessions;
|
||||
size_t n_sessions;
|
||||
unsigned int id_pool;
|
||||
};
|
||||
|
||||
struct dispd * dispd_get();
|
||||
void dispd_shutdown(struct dispd *wfd);
|
||||
|
||||
static inline struct sd_event * dispd_get_loop()
|
||||
{
|
||||
return dispd_get()->loop;
|
||||
}
|
||||
|
||||
int dispd_find_sink_by_label(struct dispd *wfd,
|
||||
const char *label,
|
||||
struct dispd_sink **out);
|
||||
int dispd_add_session(struct dispd *wfd, struct dispd_session *s);
|
||||
int dispd_find_session_by_id(struct dispd *wfd,
|
||||
unsigned int id,
|
||||
struct dispd_session **out);
|
||||
int dispd_remove_session_by_id(struct dispd *wfd,
|
||||
unsigned int id,
|
||||
struct dispd_session **out);
|
||||
unsigned int dispd_alloc_session_id(struct dispd *wfd);
|
||||
|
||||
int dispd_fn_session_new(struct dispd_session *s);
|
||||
int dispd_fn_session_free(struct dispd_session *s);
|
||||
int dispd_fn_out_session_ended(struct dispd_session *s);
|
||||
|
||||
int dispd_fn_sink_new(struct dispd_sink *s);
|
||||
int dispd_fn_sink_free(struct dispd_sink *s);
|
||||
int dispd_fn_sink_detach(struct dispd_sink *s);
|
||||
|
||||
#endif /* DISP_DISP_H */
|
||||
25
src/disp/meson.build
Normal file
25
src/disp/meson.build
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
inc = include_directories('../..', '../ctl',)
|
||||
gst1 = dependency('gstreamer-1.0')
|
||||
gst1_base = dependency('gstreamer-base-1.0')
|
||||
deps = [libsystemd, libmiracle_shared_dep, gst1, gst1_base]
|
||||
if readline.found()
|
||||
deps += [readline]
|
||||
endif
|
||||
|
||||
miracle_dispd_src = ['../ctl/ctl-cli.c',
|
||||
'../ctl/ctl-wifi.c',
|
||||
'../ctl/wfd.c',
|
||||
'dispd.c',
|
||||
'dispd-sink.c',
|
||||
'dispd-dbus.c',
|
||||
'dispd-session.c',
|
||||
'dispd-out-session.c',
|
||||
'dispd-arg.c',
|
||||
'dispd-encoder.c'
|
||||
]
|
||||
executable('miracle-dispd',
|
||||
miracle_dispd_src,
|
||||
install: true,
|
||||
include_directories: inc,
|
||||
dependencies: deps
|
||||
)
|
||||
|
|
@ -1,8 +1,11 @@
|
|||
inc_shared = include_directories('shared')
|
||||
|
||||
subdir('shared')
|
||||
subdir('wifi')
|
||||
subdir('dhcp')
|
||||
subdir('ctl')
|
||||
subdir('uibc')
|
||||
subdir('disp')
|
||||
|
||||
executable('miracled', 'miracled.c',
|
||||
dependencies: [libmiracle_shared_dep, m],
|
||||
|
|
|
|||
|
|
@ -231,6 +231,11 @@ extern const char *LOG_SUBSYSTEM;
|
|||
#define log_vEPIPE() \
|
||||
((void)log_EPIPE())
|
||||
|
||||
#define log_EPROTO() \
|
||||
(log_error("protocol error"), -EPROTO)
|
||||
#define log_vEPROTO() \
|
||||
((void)log_EPROTO())
|
||||
|
||||
#define log_ERRNO() \
|
||||
(log_error("syscall failed (%d): %m", errno), -errno)
|
||||
#define log_vERRNO() \
|
||||
|
|
@ -246,4 +251,13 @@ extern const char *LOG_SUBSYSTEM;
|
|||
#define log_vEUNMANAGED() \
|
||||
((void)log_EUNMANAGED())
|
||||
|
||||
#define assert_ret(c) \
|
||||
if(!(c)) return (log_error("assertion " #c " failed"), -EINVAL)
|
||||
|
||||
#define assert_retv(c, v) \
|
||||
if(!(c)) return (log_error("assertion " #c " failed"), (v))
|
||||
|
||||
#define assert_vret(c) \
|
||||
if(!(c)) { log_error("assertion " #c " failed"); return; }
|
||||
|
||||
#endif /* SHL_LOG_H */
|
||||
|
|
|
|||
|
|
@ -57,6 +57,11 @@ static inline void cleanup_sd_bus_message(sd_bus_message **ptr)
|
|||
sd_bus_message_unref(*ptr);
|
||||
}
|
||||
|
||||
static inline void cleanup_sd_bus_creds(sd_bus_creds **ptr)
|
||||
{
|
||||
sd_bus_creds_unref(*ptr);
|
||||
}
|
||||
|
||||
static inline void cleanup_udev_device(struct udev_device **ptr)
|
||||
{
|
||||
udev_device_unref(*ptr);
|
||||
|
|
@ -71,6 +76,7 @@ static inline void cleanup_udev_enumerate(struct udev_enumerate **ptr)
|
|||
#define _sd_bus_error_free_ _shl_cleanup_(sd_bus_error_free)
|
||||
#define _cleanup_sd_bus_message_ _shl_cleanup_(cleanup_sd_bus_message)
|
||||
#define _sd_bus_message_unref_ _shl_cleanup_(cleanup_sd_bus_message)
|
||||
#define _sd_bus_creds_unref_ _shl_cleanup_(cleanup_sd_bus_creds)
|
||||
#define _cleanup_udev_device_ _shl_cleanup_(cleanup_udev_device)
|
||||
#define _cleanup_udev_enumerate_ _shl_cleanup_(cleanup_udev_enumerate)
|
||||
|
||||
|
|
|
|||
|
|
@ -261,12 +261,12 @@ static const sd_bus_vtable peer_dbus_vtable[] = {
|
|||
"ss",
|
||||
NULL,
|
||||
peer_dbus_connect,
|
||||
0),
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("Disconnect",
|
||||
NULL,
|
||||
NULL,
|
||||
peer_dbus_disconnect,
|
||||
0),
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_PROPERTY("Link",
|
||||
"o",
|
||||
peer_dbus_get_link,
|
||||
|
|
@ -513,6 +513,24 @@ static int link_dbus_get_interface_index(sd_bus *bus,
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int link_dbus_get_mac_addr(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *reply,
|
||||
void *data,
|
||||
sd_bus_error *err)
|
||||
{
|
||||
struct link *l = data;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_append_basic(reply, 's', l->mac_addr);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int link_dbus_get_interface_name(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
|
|
@ -589,22 +607,46 @@ static int link_dbus_get_managed(sd_bus *bus,
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int link_dbus_set_managed(sd_bus *bus,
|
||||
static int link_dbus_manage(sd_bus_message *msg,
|
||||
void *data,
|
||||
sd_bus_error *err)
|
||||
{
|
||||
struct link *l = data;
|
||||
int r = link_manage(l, true);
|
||||
if(r < 0)
|
||||
return r;
|
||||
|
||||
return sd_bus_reply_method_return(msg, NULL);
|
||||
}
|
||||
|
||||
static int link_dbus_unmanage(sd_bus_message *msg,
|
||||
void *data,
|
||||
sd_bus_error *err)
|
||||
{
|
||||
struct link *l = data;
|
||||
int r = link_manage(l, false);
|
||||
if(r < 0)
|
||||
return r;
|
||||
|
||||
return sd_bus_reply_method_return(msg, NULL);
|
||||
}
|
||||
|
||||
static int link_dbus_get_p2p_state(sd_bus *bus,
|
||||
const char *path,
|
||||
const char *interface,
|
||||
const char *property,
|
||||
sd_bus_message *value,
|
||||
sd_bus_message *reply,
|
||||
void *data,
|
||||
sd_bus_error *err)
|
||||
{
|
||||
struct link *l = data;
|
||||
int val, r;
|
||||
int r;
|
||||
|
||||
r = sd_bus_message_read(value, "b", &val);
|
||||
r = sd_bus_message_append(reply, "i", link_get_p2p_state(l));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return link_set_managed(l, val);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int link_dbus_get_p2p_scanning(sd_bus *bus,
|
||||
|
|
@ -682,11 +724,26 @@ static int link_dbus_set_wfd_subelements(sd_bus *bus,
|
|||
|
||||
static const sd_bus_vtable link_dbus_vtable[] = {
|
||||
SD_BUS_VTABLE_START(0),
|
||||
SD_BUS_METHOD("Manage",
|
||||
NULL,
|
||||
NULL,
|
||||
link_dbus_manage,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("Unmanage",
|
||||
NULL,
|
||||
NULL,
|
||||
link_dbus_unmanage,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_PROPERTY("InterfaceIndex",
|
||||
"u",
|
||||
link_dbus_get_interface_index,
|
||||
0,
|
||||
SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("MACAddress",
|
||||
"s",
|
||||
link_dbus_get_mac_addr,
|
||||
0,
|
||||
SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("InterfaceName",
|
||||
"s",
|
||||
link_dbus_get_interface_name,
|
||||
|
|
@ -697,11 +754,15 @@ static const sd_bus_vtable link_dbus_vtable[] = {
|
|||
link_dbus_get_friendly_name,
|
||||
link_dbus_set_friendly_name,
|
||||
0,
|
||||
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_WRITABLE_PROPERTY("Managed",
|
||||
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE | SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_PROPERTY("Managed",
|
||||
"b",
|
||||
link_dbus_get_managed,
|
||||
link_dbus_set_managed,
|
||||
0,
|
||||
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("P2PState",
|
||||
"i",
|
||||
link_dbus_get_p2p_state,
|
||||
0,
|
||||
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_WRITABLE_PROPERTY("P2PScanning",
|
||||
|
|
@ -709,13 +770,13 @@ static const sd_bus_vtable link_dbus_vtable[] = {
|
|||
link_dbus_get_p2p_scanning,
|
||||
link_dbus_set_p2p_scanning,
|
||||
0,
|
||||
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE | SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_WRITABLE_PROPERTY("WfdSubelements",
|
||||
"s",
|
||||
link_dbus_get_wfd_subelements,
|
||||
link_dbus_set_wfd_subelements,
|
||||
0,
|
||||
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE | SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_VTABLE_END
|
||||
};
|
||||
|
||||
|
|
@ -829,14 +890,17 @@ static int manager_dbus_enumerate(sd_bus *bus,
|
|||
struct peer *p;
|
||||
size_t i, peer_cnt;
|
||||
char **nodes, *node;
|
||||
int r;
|
||||
|
||||
if(strcmp("/org/freedesktop/miracle/wifi", path)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
peer_cnt = 0;
|
||||
MANAGER_FOREACH_LINK(l, m)
|
||||
if (l->public)
|
||||
peer_cnt += l->peer_cnt;
|
||||
|
||||
nodes = malloc(sizeof(*nodes) * (m->link_cnt + peer_cnt + 2));
|
||||
nodes = malloc(sizeof(*nodes) * (m->link_cnt + peer_cnt + 1));
|
||||
if (!nodes)
|
||||
return log_ENOMEM();
|
||||
|
||||
|
|
@ -875,13 +939,6 @@ static int manager_dbus_enumerate(sd_bus *bus,
|
|||
}
|
||||
}
|
||||
|
||||
node = strdup("/org/freedesktop/miracle/wifi");
|
||||
if (!node) {
|
||||
r = log_ENOMEM();
|
||||
goto error;
|
||||
}
|
||||
|
||||
nodes[i++] = node;
|
||||
nodes[i] = NULL;
|
||||
*out = nodes;
|
||||
|
||||
|
|
@ -891,7 +948,7 @@ error:
|
|||
while (i--)
|
||||
free(nodes[i]);
|
||||
free(nodes);
|
||||
return r;
|
||||
return log_ENOMEM();
|
||||
}
|
||||
|
||||
int manager_dbus_connect(struct manager *m)
|
||||
|
|
|
|||
|
|
@ -57,6 +57,7 @@ struct peer *link_find_peer_by_label(struct link *l, const char *label)
|
|||
int link_new(struct manager *m,
|
||||
unsigned int ifindex,
|
||||
const char *ifname,
|
||||
const char *mac_addr,
|
||||
struct link **out)
|
||||
{
|
||||
struct link *l;
|
||||
|
|
@ -84,6 +85,12 @@ int link_new(struct manager *m,
|
|||
goto error;
|
||||
}
|
||||
|
||||
l->mac_addr = strdup(mac_addr);
|
||||
if (!l->mac_addr) {
|
||||
r = log_ENOMEM();
|
||||
goto error;
|
||||
}
|
||||
|
||||
r = supplicant_new(l, &l->s);
|
||||
if (r < 0)
|
||||
goto error;
|
||||
|
|
@ -116,7 +123,10 @@ void link_free(struct link *l)
|
|||
|
||||
log_debug("free link: %s (%u)", l->ifname, l->ifindex);
|
||||
|
||||
link_set_managed(l, false);
|
||||
link_manage(l, false);
|
||||
|
||||
link_dbus_removed(l);
|
||||
l->public = false;
|
||||
|
||||
link_dbus_removed(l);
|
||||
l->public = false;
|
||||
|
|
@ -128,9 +138,10 @@ void link_free(struct link *l)
|
|||
|
||||
supplicant_free(l->s);
|
||||
|
||||
/* link_set_managed(l, false) already removed all peers */
|
||||
/* link_manage(l, false) already removed all peers */
|
||||
shl_htable_clear_str(&l->peers, NULL, NULL);
|
||||
|
||||
free(l->mac_addr);
|
||||
free(l->wfd_subelements);
|
||||
free(l->friendly_name);
|
||||
free(l->ifname);
|
||||
|
|
@ -181,12 +192,18 @@ int link_set_ip_binary(struct link *l, const char *ip_binary)
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int link_get_p2p_state(struct link *l)
|
||||
{
|
||||
return l->p2p_state;
|
||||
}
|
||||
|
||||
bool link_get_managed(struct link *l)
|
||||
{
|
||||
return l->managed;
|
||||
}
|
||||
|
||||
int link_set_managed(struct link *l, bool set)
|
||||
int link_manage(struct link *l, bool set)
|
||||
{
|
||||
int r;
|
||||
|
||||
|
|
@ -196,22 +213,33 @@ int link_set_managed(struct link *l, bool set)
|
|||
return 0;
|
||||
|
||||
if (set) {
|
||||
log_info("manage link %s", l->ifname);
|
||||
|
||||
r = supplicant_start(l->s);
|
||||
if (r < 0) {
|
||||
log_error("cannot start supplicant on %s", l->ifname);
|
||||
return -EFAULT;
|
||||
}
|
||||
log_info("acquiring link ownership %s", l->ifname);
|
||||
} else {
|
||||
log_info("link %s no longer managed", l->ifname);
|
||||
log_info("droping link ownership %s", l->ifname);
|
||||
supplicant_stop(l->s);
|
||||
}
|
||||
|
||||
l->managed = set;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void link_supplicant_p2p_state_known(struct link *l, int state)
|
||||
{
|
||||
if (!l)
|
||||
return log_vEINVAL();
|
||||
if (l->p2p_state == state)
|
||||
return;
|
||||
if(-1 > state || 1 < state)
|
||||
return log_vEINVAL();
|
||||
|
||||
l->p2p_state = state;
|
||||
link_dbus_properties_changed(l, "P2PState", NULL);
|
||||
}
|
||||
|
||||
int link_renamed(struct link *l, const char *ifname)
|
||||
{
|
||||
char *t;
|
||||
|
|
@ -244,6 +272,9 @@ int link_set_friendly_name(struct link *l, const char *name)
|
|||
if (!l || !name || !*name)
|
||||
return log_EINVAL();
|
||||
|
||||
if (!l->managed)
|
||||
return log_EUNMANAGED();
|
||||
|
||||
t = strdup(name);
|
||||
if (!t)
|
||||
return log_ENOMEM();
|
||||
|
|
@ -339,18 +370,36 @@ bool link_get_p2p_scanning(struct link *l)
|
|||
return supplicant_p2p_scanning(l->s);
|
||||
}
|
||||
|
||||
const char *link_get_mac_addr(struct link *l)
|
||||
{
|
||||
if (!l)
|
||||
return NULL;
|
||||
|
||||
return l->mac_addr;
|
||||
}
|
||||
|
||||
void link_supplicant_started(struct link *l)
|
||||
{
|
||||
if(l && !l->managed) {
|
||||
l->managed = true;
|
||||
link_dbus_properties_changed(l, "Managed", NULL);
|
||||
}
|
||||
|
||||
if (!l || l->public)
|
||||
return;
|
||||
|
||||
if (l->m->friendly_name && l->managed)
|
||||
link_set_friendly_name(l, l->m->friendly_name);
|
||||
if (l->m->friendly_name && l->managed)
|
||||
link_set_friendly_name(l, l->m->friendly_name);
|
||||
log_info("link %s managed", l->ifname);
|
||||
}
|
||||
|
||||
void link_supplicant_stopped(struct link *l)
|
||||
{
|
||||
if(l && l->managed) {
|
||||
l->managed = false;
|
||||
link_dbus_properties_changed(l, "Managed", NULL);
|
||||
}
|
||||
|
||||
if (!l || !l->public)
|
||||
return;
|
||||
|
||||
|
|
|
|||
|
|
@ -218,8 +218,10 @@ static void supplicant_group_free(struct supplicant_group *g)
|
|||
}
|
||||
|
||||
LINK_FOREACH_PEER(p, g->s->l)
|
||||
if (p->sp->g == g)
|
||||
supplicant_peer_drop_group(p->sp);
|
||||
if (p->sp->g == g) {
|
||||
supplicant_peer_drop_group(p->sp);
|
||||
return;
|
||||
}
|
||||
|
||||
shl_dlist_unlink(&g->list);
|
||||
|
||||
|
|
@ -901,7 +903,13 @@ static void supplicant_parse_peer(struct supplicant *s,
|
|||
* parse it we _definitely_ have to provide proper data. */
|
||||
r = wpas_message_dict_read(m, "wfd_dev_info", 's', &val);
|
||||
if (r >= 0) {
|
||||
t = strdup(val);
|
||||
/* remove "0x" from the start of wfd_dev_info and prepend
|
||||
* subelement ID and lenght to it to make it compliant
|
||||
* with the format of wfd_subelems */
|
||||
char buf[19] = "000006";
|
||||
t = strdup(strncmp("0x", val, 2)
|
||||
? val
|
||||
: strncat(buf, val + 2, sizeof(buf) - 7));
|
||||
if (!t) {
|
||||
log_vENOMEM();
|
||||
} else {
|
||||
|
|
@ -1538,10 +1546,12 @@ static void supplicant_try_ready(struct supplicant *s)
|
|||
s->has_wfd = false;
|
||||
|
||||
s->running = true;
|
||||
link_supplicant_started(s->l);
|
||||
|
||||
LINK_FOREACH_PEER(p, s->l)
|
||||
peer_supplicant_started(p);
|
||||
|
||||
link_supplicant_started(s->l);
|
||||
link_supplicant_p2p_state_known(s->l, s->has_p2p ? 1 : -1);
|
||||
}
|
||||
|
||||
static int supplicant_p2p_set_disallow_freq_fn(struct wpas *w,
|
||||
|
|
@ -1716,8 +1726,10 @@ static int supplicant_status_fn(struct wpas *w,
|
|||
|
||||
if (!p2p_state) {
|
||||
log_warning("wpa_supplicant or driver does not support P2P");
|
||||
link_supplicant_p2p_state_known(s->l, -1);
|
||||
} else if (!strcmp(p2p_state, "DISABLED")) {
|
||||
log_warning("P2P support disabled on given interface");
|
||||
link_supplicant_p2p_state_known(s->l, -1);
|
||||
} else {
|
||||
s->has_p2p = true;
|
||||
|
||||
|
|
@ -1743,6 +1755,8 @@ static int supplicant_status_fn(struct wpas *w,
|
|||
NULL,
|
||||
0,
|
||||
NULL);
|
||||
wpas_message_unref(m);
|
||||
m = NULL;
|
||||
if (r < 0) {
|
||||
log_vERR(r);
|
||||
goto error;
|
||||
|
|
@ -1771,6 +1785,8 @@ static int supplicant_status_fn(struct wpas *w,
|
|||
s,
|
||||
0,
|
||||
NULL);
|
||||
wpas_message_unref(m);
|
||||
m = NULL;
|
||||
if (r < 0) {
|
||||
log_vERR(r);
|
||||
goto error;
|
||||
|
|
@ -1799,6 +1815,8 @@ static int supplicant_status_fn(struct wpas *w,
|
|||
s,
|
||||
0,
|
||||
NULL);
|
||||
wpas_message_unref(m);
|
||||
m = NULL;
|
||||
if (r < 0) {
|
||||
log_vERR(r);
|
||||
goto error;
|
||||
|
|
@ -1915,6 +1933,7 @@ static void supplicant_stopped(struct supplicant *s)
|
|||
|
||||
if (s->running) {
|
||||
s->running = false;
|
||||
link_supplicant_p2p_state_known(s->l, 0);
|
||||
link_supplicant_stopped(s->l);
|
||||
}
|
||||
}
|
||||
|
|
@ -2452,45 +2471,45 @@ static void supplicant_run(struct supplicant *s, const char *binary)
|
|||
|
||||
static int supplicant_find(char **binary)
|
||||
{
|
||||
_shl_free_ char *path = getenv("PATH");
|
||||
if(!path) {
|
||||
return -EINVAL;
|
||||
}
|
||||
_shl_free_ char *path = getenv("PATH");
|
||||
if(!path) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
path = strdup(path);
|
||||
if(!path) {
|
||||
return log_ENOMEM();
|
||||
}
|
||||
path = strdup(path);
|
||||
if(!path) {
|
||||
return log_ENOMEM();
|
||||
}
|
||||
|
||||
struct stat bin_stat;
|
||||
char *curr = path, *next;
|
||||
while(1) {
|
||||
curr = strtok_r(curr, ":", &next);
|
||||
if(!curr) {
|
||||
break;
|
||||
}
|
||||
struct stat bin_stat;
|
||||
char *curr = path, *next;
|
||||
while(1) {
|
||||
curr = strtok_r(curr, ":", &next);
|
||||
if(!curr) {
|
||||
break;
|
||||
}
|
||||
|
||||
_shl_free_ char *bin = shl_strcat(curr, "/wpa_supplicant");
|
||||
if (!bin)
|
||||
return log_ENOMEM();
|
||||
_shl_free_ char *bin = shl_strcat(curr, "/wpa_supplicant");
|
||||
if (!bin)
|
||||
return log_ENOMEM();
|
||||
|
||||
if(stat(bin, &bin_stat) < 0) {
|
||||
if(ENOENT == errno || ENOTDIR == errno) {
|
||||
goto end;
|
||||
}
|
||||
return log_ERRNO();
|
||||
}
|
||||
if(stat(bin, &bin_stat) < 0) {
|
||||
if(ENOENT == errno || ENOTDIR == errno) {
|
||||
goto end;
|
||||
}
|
||||
return log_ERRNO();
|
||||
}
|
||||
|
||||
if (!access(bin, X_OK)) {
|
||||
*binary = strdup(bin);
|
||||
return 0;
|
||||
}
|
||||
if (!access(bin, X_OK)) {
|
||||
*binary = strdup(bin);
|
||||
return 0;
|
||||
}
|
||||
|
||||
end:
|
||||
curr = NULL;
|
||||
}
|
||||
curr = NULL;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int supplicant_spawn(struct supplicant *s)
|
||||
|
|
@ -2506,16 +2525,16 @@ static int supplicant_spawn(struct supplicant *s)
|
|||
|
||||
log_debug("spawn supplicant of %s", s->l->ifname);
|
||||
|
||||
if (supplicant_find(&binary) < 0) {
|
||||
if (binary != NULL) {
|
||||
log_error("execution of wpas (%s) not possible: %m", binary);
|
||||
} else {
|
||||
log_error("execution of wpas not possible: %m");
|
||||
if (supplicant_find(&binary) < 0) {
|
||||
if (binary != NULL) {
|
||||
log_error("execution of wpas (%s) not possible: %m", binary);
|
||||
} else {
|
||||
log_error("execution of wpas not possible: %m");
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
log_info("wpa_supplicant found: %s", binary);
|
||||
log_info("wpa_supplicant found: %s", binary);
|
||||
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
|
|
|
|||
|
|
@ -86,6 +86,8 @@ static void manager_add_udev_link(struct manager *m,
|
|||
struct link *l;
|
||||
unsigned int ifindex;
|
||||
const char *ifname;
|
||||
const char *mac_addr;
|
||||
char buf[18];
|
||||
int r;
|
||||
|
||||
ifindex = ifindex_from_udev_device(d);
|
||||
|
|
@ -104,7 +106,11 @@ static void manager_add_udev_link(struct manager *m,
|
|||
if (shl_startswith(ifname, "p2p-"))
|
||||
return;
|
||||
|
||||
r = link_new(m, ifindex, ifname, &l);
|
||||
mac_addr = udev_device_get_property_value(d, "ID_NET_NAME_MAC");
|
||||
mac_addr = mac_addr + strlen(mac_addr) - 12;
|
||||
snprintf(buf, sizeof(buf), "%.2s:%.2s:%.2s:%.2s:%.2s:%.2s", mac_addr, mac_addr + 2, mac_addr + 4, mac_addr + 6, mac_addr + 8, mac_addr + 10);
|
||||
|
||||
r = link_new(m, ifindex, ifname, buf, &l);
|
||||
if (r < 0)
|
||||
return;
|
||||
|
||||
|
|
@ -124,7 +130,7 @@ static void manager_add_udev_link(struct manager *m,
|
|||
bool managed = (!interface_name || !strcmp(interface_name, ifname)) && !lazy_managed;
|
||||
#endif
|
||||
if (managed) {
|
||||
link_set_managed(l, true);
|
||||
link_manage(l, true);
|
||||
} else {
|
||||
log_debug("ignored device: %s", ifname);
|
||||
}
|
||||
|
|
@ -164,12 +170,12 @@ static int manager_udev_fn(sd_event_source *source,
|
|||
|
||||
#ifdef RELY_UDEV
|
||||
if (udev_device_has_tag(d, "miracle") && !lazy_managed)
|
||||
link_set_managed(l, true);
|
||||
link_manage(l, true);
|
||||
else
|
||||
link_set_managed(l, false);
|
||||
link_manage(l, false);
|
||||
#else
|
||||
if ((!interface_name || !strcmp(interface_name, ifname)) && !lazy_managed) {
|
||||
link_set_managed(l, true);
|
||||
link_manage(l, true);
|
||||
} else {
|
||||
log_debug("ignored device: %s", ifname);
|
||||
}
|
||||
|
|
@ -188,8 +194,9 @@ static int manager_signal_fn(sd_event_source *source,
|
|||
struct manager *m = data;
|
||||
|
||||
if (ssi->ssi_signo == SIGCHLD) {
|
||||
siginfo_t info;
|
||||
log_debug("caught SIGCHLD for %ld, reaping child", (long)ssi->ssi_pid);
|
||||
waitid(P_PID, ssi->ssi_pid, NULL, WNOHANG|WEXITED);
|
||||
waitid(P_PID, ssi->ssi_pid, &info, WNOHANG|WEXITED);
|
||||
return 0;
|
||||
} else if (ssi->ssi_signo == SIGPIPE) {
|
||||
/* ignore SIGPIPE */
|
||||
|
|
@ -595,17 +602,17 @@ int main(int argc, char **argv)
|
|||
|
||||
srand(time(NULL));
|
||||
|
||||
GKeyFile* gkf = load_ini_file();
|
||||
GKeyFile* gkf = load_ini_file();
|
||||
|
||||
if (gkf) {
|
||||
gchar* log_level;
|
||||
log_level = g_key_file_get_string (gkf, "wifid", "log-level", NULL);
|
||||
if (log_level) {
|
||||
log_max_sev = log_parse_arg(log_level);
|
||||
g_free(log_level);
|
||||
}
|
||||
g_key_file_free(gkf);
|
||||
}
|
||||
if (gkf) {
|
||||
gchar* log_level;
|
||||
log_level = g_key_file_get_string (gkf, "wifid", "log-level", NULL);
|
||||
if (log_level) {
|
||||
log_max_sev = log_parse_arg(log_level);
|
||||
g_free(log_level);
|
||||
}
|
||||
g_key_file_free(gkf);
|
||||
}
|
||||
|
||||
r = parse_argv(argc, argv);
|
||||
if (r < 0)
|
||||
|
|
@ -614,9 +621,9 @@ int main(int argc, char **argv)
|
|||
return EXIT_SUCCESS;
|
||||
|
||||
if (getuid() != 0) {
|
||||
r = EACCES;
|
||||
r = EACCES;
|
||||
log_notice("Must run as root");
|
||||
goto finish;
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = manager_new(&m);
|
||||
|
|
|
|||
|
|
@ -129,6 +129,8 @@ struct link {
|
|||
char *ifname;
|
||||
char *friendly_name;
|
||||
char *wfd_subelements;
|
||||
char *mac_addr;
|
||||
int p2p_state; /* 0: unknown, 1: supported, -1: unsupproted */
|
||||
char *config_methods;
|
||||
char *ip_binary;
|
||||
|
||||
|
|
@ -153,6 +155,7 @@ struct peer *link_find_peer_by_label(struct link *l, const char *label);
|
|||
int link_new(struct manager *m,
|
||||
unsigned int ifindex,
|
||||
const char *ifname,
|
||||
const char *mac_addr,
|
||||
struct link **out);
|
||||
void link_free(struct link *l);
|
||||
|
||||
|
|
@ -162,7 +165,7 @@ bool link_is_using_dev(struct link *l);
|
|||
|
||||
int link_set_ip_binary(struct link *l, const char *ip_binary);
|
||||
|
||||
int link_set_managed(struct link *l, bool set);
|
||||
int link_manage(struct link *l, bool set);
|
||||
bool link_get_managed(struct link *l);
|
||||
int link_renamed(struct link *l, const char *ifname);
|
||||
|
||||
|
|
@ -173,10 +176,14 @@ int link_set_wfd_subelements(struct link *l, const char *val);
|
|||
const char *link_get_wfd_subelements(struct link *l);
|
||||
int link_set_p2p_scanning(struct link *l, bool set);
|
||||
bool link_get_p2p_scanning(struct link *l);
|
||||
int link_get_p2p_state(struct link *l);
|
||||
const char *link_get_mac_addr(struct link *l);
|
||||
|
||||
void link_supplicant_started(struct link *l);
|
||||
void link_supplicant_stopped(struct link *l);
|
||||
void link_supplicant_p2p_scan_changed(struct link *l, bool new_value);
|
||||
/* 0: unknown, -1: unsupported, 1: supported */
|
||||
void link_supplicant_p2p_state_known(struct link *l, int state);
|
||||
|
||||
_shl_sentinel_
|
||||
void link_dbus_properties_changed(struct link *l, const char *prop, ...);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue