Newer
Older
#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <libpicoevent.h>
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include "main.h"
#include "ubus.h"
static int audio_rx_fd;
static int audio_tx_fd;
static int event_fd;
static const char event_str[] = "/tmp/endpt_event";
static const char audio_tx_str[] = "/tmp/endpt_audio_tx";
static const char audio_rx_str[] = "/tmp/endpt_audio_rx";
static pe_stream_t *event_stream;
static pe_stream_t *audio_rx_stream;
static pe_list_t *event_list;
static pe_bus_t *audio_rx_bus;
static pe_bus_t *audio_tx_bus;
int picoBase = -1;
// Send audio data to asterisk
int send_audio_asterisk(uint8_t *buf, int size) {
pe_bus_send(audio_tx_bus, buf, size);
return 0;
}
// Handle audio stream from Asterisk on audio rx socket and
// re-posts it to internal audio rx bus.
static void audio_rx_stream_handler(pe_stream_t *stream __attribute__((unused)), pe_event_t *event) {
if (pe_bus_receive(audio_rx_bus, (pe_event_t *)event) < 0) {
exit_failure("audio_bus rx buffer full\n");
return; // Drop packets if we can't cope the pace
}
pe_bus_dispatch(audio_rx_bus);
}
// Notify main thread there is a new event. We need to
// make the ubus call from the main context. This function
// is executed by a libvoip thread.
int send_event_main(struct line_event_t *ev, char x) {
if (!event_list || !event_fd) return -1;
pe_list_add(event_list, (void*)ev);
/* Trigger event loop to read event from queue. */
write(event_fd, &x, sizeof(char));
return 0;
}
// Handler for events sent to the main thread (in
// general the events originates from libvoip).
static void event_stream_handler(pe_stream_t *stream __attribute__((unused)), pe_event_t *event) {
struct line_event_t *ev;
if(!event || !event->in || event->count <= 0) return;
switch(*event->in) {
case EVENT_MAIN_LINE:
ev = (struct line_event_t*) pe_list_get(event_list);
if(!ev) break;
ENDPT_DBG("Main event handler line %d event %s\n", ev->line, ev->name);
ubus_call_asterisk(ev);
ubus_broadcast_event(ev); // Also broadcast event to all
break;
case EVENT_MAIN_QUIT:
ENDPT_DBG("Shutting down on quit event\n");
ubus_disable_receive();
voice_connection_deinit();
line_dect_deinit();
voice_line_deinit();
voice_engine_shutdown();
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
break;
case EVENT_MAIN_WAKE: // A nop event. Do nothing but wake up.
break;
}
}
static void fifos_init(void) {
int ret;
/* Create event list for event fifo. */
event_list = pe_list_new();
if (!event_list)
exit_failure("pe_list_new\n");
/* Create event fifo. */
ret = mkfifo(event_str, 0666);
if ((ret == -1) && (errno != EEXIST)) exit_failure("Failed to create event pipe");
event_fd = open(event_str, O_RDWR|O_NONBLOCK);
if(event_fd == -1) exit_failure("Failed to open event pipe");
/* Create audio fifos. Both ends open in R/W mode to become
* independent of daemon startup order and peer restart
* recovery. However, later each end use unidirectional flow. */
ret = mkfifo(audio_tx_str, 0666);
if ((ret == -1) && (errno != EEXIST)) exit_failure("Failed to create tx pipe");
audio_tx_fd = open(audio_tx_str, O_RDWR|O_LARGEFILE|O_NONBLOCK);
if(audio_tx_fd == -1) exit_failure("Failed to open tx pipe");
ret = mkfifo(audio_rx_str, 0666);
if ((ret == -1) && (errno != EEXIST)) exit_failure("Failed to create rx pipe");
audio_rx_fd = open(audio_rx_str, O_RDWR|O_LARGEFILE|O_NONBLOCK);
if(audio_rx_fd == -1) exit_failure("Failed to open rx pipe");
}
static void streams_and_buses_init(void) {
/* Listen for callback data from libvoip in our main event loop.
We need to do it this way as ubus is not thread safe. */
event_stream = pe_stream_new(event_fd);
pe_stream_add_handler(event_stream, sizeof(char), event_stream_handler);
pe_base_add_stream(picoBase, event_stream);
/* Listen for audio frames from asterisk. */
audio_rx_stream = pe_stream_new(audio_rx_fd);
pe_stream_add_handler(audio_rx_stream, 4096, audio_rx_stream_handler);
pe_base_add_stream(picoBase, audio_rx_stream);
audio_rx_bus = pe_bus_new(audio_rx_fd);
if (!audio_rx_bus) exit_failure("Failed to create audio_rx bus");
pe_bus_add_handler(audio_rx_bus, lineAudioTx);
/* Send audio frames to asterisk. */
audio_tx_bus = pe_bus_new(audio_tx_fd);
if (!audio_tx_bus) exit_failure("Failed to create audio_tx bus");
}
// Posix signal handler. Wake up main event loop to exit prog.
static void signal_handler(int signum __attribute__((unused)), siginfo_t *info __attribute__((unused)), void *ucontext __attribute__((unused))) {
if(event_fd) send_event_main(NULL, EVENT_MAIN_QUIT);
}
// Setup Posix signal handling
static int signals_init(void) {
const int signals_handled[] = { SIGHUP, SIGINT, SIGQUIT, SIGTERM, SIGPWR };
struct sigaction action;
unsigned int i;
memset(&action, 0, sizeof(action));
action.sa_sigaction = signal_handler;
action.sa_flags = SA_SIGINFO | SA_RESTART;
sigemptyset(&action.sa_mask);
for(i = 0; i < ARRAY_SIZE(signals_handled); i++) {
sigaddset(&action.sa_mask, signals_handled[i]);
}
for(i = 0; i < ARRAY_SIZE(signals_handled); i++) {
if(sigaction(signals_handled[i], &action, NULL)) {
perror("Error trapping signals");
return -1;
}
}
return 0;
}
static void voicemngr_event_report(int line, const char *event, int data) {
/* Re-send the event to the main thread, which in turn re-send
* to Asterisk. We need to do this due to UBUS is not thread safe,
* so all UBUS calls must be done from a single thread. */
if(!DEBUG_LOOPBACK) { // Skip when debugging
struct line_event_t *msg = malloc(sizeof(struct line_event_t));
if (!msg) {
ENDPT_DBG("%s: out of memory\n", __func__);
return;
}
msg->name = event;
msg->data = (data < 0 ? 0 : data); // Discard negative values
msg->line = line;
send_event_main(msg, EVENT_MAIN_LINE);
}
}
int main(void) {
struct uci_context *context = NULL;
struct uci_package *package = NULL;
int res, has_dect;
const char *hw_board_has_dect;
// Load the UCI package
context = uci_alloc_context();
if (!context) {
ENDPT_DBG("uci_alloc_context() failed\n");
return EXIT_FAILURE;
}
res = uci_load(context, uciStrConfig, &package);
if (res != 0 || !package) {
uci_get_errorstr(context, &error, "");
ENDPT_DBG("Failed to load uci package %s, %s\n", uciStrConfig, error);
free(error);
uci_free_context(context);
context = NULL;
return EXIT_FAILURE;
}
/* Unbuffered stdout */
if(isatty(STDOUT_FILENO))
setbuf(stdout, NULL);
// Check that ASCII to DTMF convertion works.
if(!isdigit('0') || !isalpha('a')) {
ENDPT_DBG("Source has invalid encoding.\n");
goto err;
}
/* Does the HW has DECT populated? This is stored in the
* "db", but that can't be queried via the normal uci API so we
* export it as an environment var in the startup script. */
hw_board_has_dect = getenv("hw_board_hasDect");
has_dect = hw_board_has_dect && *hw_board_has_dect == '1';
// Call this first. Find out how many phone lines we have
if(voice_get_terminal_info(&terminal_info)) goto err;
if(voice_register_cb_event_report(voicemngr_event_report)) goto err;
if(signals_init()) goto err;
if((picoBase = pe_base_new()) < 0) goto err;
fifos_init();
streams_and_buses_init();
if(ubus_init()) goto err;
if(config_init(context, package)) goto err;
package = NULL; // the package has been unloaded by config_init()
if(voice_line_init(has_dect)) goto err;
if(voice_connection_init()) goto err;
if(perhaps_erase_unused_lines(context)) goto err;
if(ubus_enable_receive()) goto err;
// Free the memory
uci_free_context(context);
context = NULL;
ENDPT_DBG("voicemngr has started successfully\n");
/* Listen for events and dispatch them to handlers. Does not return. */
pe_base_dispatch(picoBase);
err:
if (package)
uci_unload(context, package);
if (context)
uci_free_context(context);
line_dect_deinit();
voice_line_deinit();
voice_engine_shutdown();
ubus_disable_receive();
ubus_close();
return EXIT_FAILURE;
}