1
0
Fork 0
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:
Alberto Fanjul 2022-11-01 09:11:47 +01:00
parent 439dac09c5
commit fba5eb4a11
72 changed files with 9577 additions and 399 deletions

View file

@ -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})

View file

@ -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)

View file

@ -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)

View file

@ -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) {\

View file

@ -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,

View file

@ -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 */

View file

@ -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;

View file

@ -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;
}

View file

@ -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 */

View file

@ -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)

View file

@ -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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load diff

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
View 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
View 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
View 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
View 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
View 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
)

View file

@ -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],

View file

@ -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 */

View file

@ -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)

View file

@ -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)

View file

@ -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;

View file

@ -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) {

View file

@ -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);

View file

@ -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, ...);