1
/*
2
 * Copyright (C) 2010 Nokia Corporation.
3
 *
4
 * Contact: Maemo MMF Audio <mmf-audio@projects.maemo.org>
5
 *          or Jyri Sarha <jyri.sarha@nokia.com>
6
 *
7
 * These PulseAudio Modules are free software; you can redistribute
8
 * it and/or modify it under the terms of the GNU Lesser General Public
9
 * License as published by the Free Software Foundation
10
 * version 2.1 of the License.
11
 *
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
 * Lesser General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU Lesser General Public
18
 * License along with this library; if not, write to the Free Software
19
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
20
 * USA.
21
 */
22
#include "cmtspeech-connection.h"
23
24
#include "cmtspeech-mainloop-handler.h"
25
#include "module-voice-api.h"
26
#include "cmtspeech-sink-input.h"
27
#include <pulsecore/rtpoll.h>
28
#include <pulsecore/core-rtclock.h>
29
#include <pulse/rtclock.h>
30
#include <pulse/timeval.h>
31
#include <poll.h>
32
#include <errno.h>
33
34
#include <cmtspeech.h>
35
36
/* TODO: Get rid of this and use asyncmsgq instead. */
37
enum cmt_speech_thread_state {
38
    CMT_UNINITIALIZED = 0,
39
    CMT_STARTING,
40
    CMT_RUNNING,
41
    CMT_ASK_QUIT,
42
    CMT_QUIT
43
};
44
45
/* This should be only used for memblock free cb - cmtspeech_free_cb - below
46
   and it is initialized in cmtspeech_connection_init(). */
47
static struct userdata *userdata = NULL;
48
49
static uint ul_frame_count = 0;
50
51
#define CMTSPEECH_CLEANUP_TIMER_TIMEOUT ((pa_usec_t)(5 * PA_USEC_PER_SEC))
52
53
enum cmtspeech_cleanup_state_name {
54
    CMTSPEECH_CLEANUP_TIMER_INACTIVE = 0,
55
    CMTSPEECH_CLEANUP_TIMER_ACTIVE,
56
    CMTSPEECH_CLEANUP_IN_PROGRESS
57
};
58
59
enum {
60
    CMTSPEECH_HANDLER_CLOSE_CONNECTION,
61
};
62
63
typedef struct cmtspeech_handler {
64
    pa_msgobject parent;
65
    struct userdata *u;
66
} cmtspeech_handler;
67
68
PA_DEFINE_PRIVATE_CLASS(cmtspeech_handler, pa_msgobject);
69
#define CMTSPEECH_HANDLER(o) cmtspeech_handler_cast(o)
70
71
static void cmtspeech_handler_free(pa_object *o) {
72
    cmtspeech_handler *h = CMTSPEECH_HANDLER(o);
73
74
    pa_log_info("Free called");
75
    pa_xfree(h);
76
}
77
78
static void close_cmtspeech_on_error(struct userdata *u);
79
80
static int cmtspeech_handler_process_msg(pa_msgobject *o, int code, void *ud, int64_t offset, pa_memchunk *chunk) {
81
    cmtspeech_handler *h = CMTSPEECH_HANDLER(o);
82
    struct userdata *u;
83
84
    cmtspeech_handler_assert_ref(h);
85
    pa_assert_se(u = h->u);
86
87
    switch (code) {
88
        case CMTSPEECH_HANDLER_CLOSE_CONNECTION:
89
            pa_log_debug("CMTSPEECH_HANDLER_CLOSE_CONNECTION");
90
            close_cmtspeech_on_error(u);
91
            return 0;
92
        default:
93
            pa_log_error("Unknown message code %d", code);
94
            return -1;
95
    }
96
}
97
98
static pa_msgobject *cmtspeech_handler_new(struct userdata *u) {
99
    cmtspeech_handler *h;
100
101
    pa_assert(u);
102
    pa_assert(u->core);
103
    pa_assert_se(h = pa_msgobject_new(cmtspeech_handler));
104
105
    h->parent.parent.free = cmtspeech_handler_free;
106
    h->parent.process_msg = cmtspeech_handler_process_msg;
107
    h->u = u;
108
109
    return (pa_msgobject *)h;
110
}
111
112
/* This is usually called from sink IO-thread */
113
static void cmtspeech_free_cb(void *p) {
114
    cmtspeech_t *cmtspeech;
115
116
    if (!p)
117
        return;
118
119
    if (!userdata) {
120
        pa_log_error("userdata not set, cmtspeech buffer %p was not freed!", p);
121
        return;
122
    }
123
124
    pa_mutex_lock(userdata->cmt_connection.cmtspeech_mutex);
125
    cmtspeech = userdata->cmt_connection.cmtspeech;
126
    if (!cmtspeech) {
127
        pa_log_error("cmtspeech not open, cmtspeech buffer %p was not freed!", p);
128
    } else {
129
        int ret;
130
        cmtspeech_buffer_t *buf = cmtspeech_dl_buffer_find_with_data(cmtspeech, (uint8_t*)p);
131
        if (buf != NULL) {
132
            if ((ret = cmtspeech_dl_buffer_release(cmtspeech, buf))) {
133
                pa_log_error("cmtspeech_dl_buffer_release(%p) failed return value %d.", (void *)buf, ret);
134
            }
135
        } else {
136
            pa_log_error("cmtspeech_dl_buffer_find_with_data() returned NULL, releasing buffer failed.");
137
        }
138
    }
139
    pa_mutex_unlock(userdata->cmt_connection.cmtspeech_mutex);
140
}
141
142
/* Called from sink IO-thread */
143
/* NOTE: If you ever see a seqfault when accessing these libcmtspeechdata owned
144
 * memblocks, then just free the cmtframes here after coping them to regular
145
 * pa_memblocks. The performance penalty should not be too severe. */
146
int cmtspeech_buffer_to_memchunk(struct userdata *u, cmtspeech_buffer_t *buf, pa_memchunk *chunk) {
147
    pa_assert_fp(u);
148
    pa_assert_fp(chunk);
149
    pa_assert_fp(buf);
150
151
    if (!buf->data) {
152
        pa_log_warn("No data in cmtspeech_buffer");
153
        if (cmtspeech_dl_buffer_release(u->cmt_connection.cmtspeech, buf))
154
            pa_log_warn("cmtspeech_dl_buffer_release() failed");
155
        return -1;
156
    }
157
158
    chunk->memblock = pa_memblock_new_user(u->core->mempool, buf->data, (size_t) buf->size, cmtspeech_free_cb, TRUE);
159
    chunk->index = CMTSPEECH_DATA_HEADER_LEN;
160
    chunk->length = buf->count - CMTSPEECH_DATA_HEADER_LEN;
161
162
    return 0;
163
}
164
165
/* cmtspeech thread */
166
static inline
167
int push_cmtspeech_buffer_to_dl_queue(struct userdata *u, cmtspeech_dl_buf_t *buf) {
168
    pa_assert_fp(u);
169
    pa_assert_fp(buf);
170
171
    if (pa_asyncq_push(u->cmt_connection.dl_frame_queue, (void *)buf, FALSE)) {
172
        int ret;
173
        struct cmtspeech_connection *c = &u->cmt_connection;
174
175
        pa_log_error("Failed to push dl frame to asyncq");
176
        pa_mutex_lock(c->cmtspeech_mutex);
177
        if ((ret = cmtspeech_dl_buffer_release(u->cmt_connection.cmtspeech, buf)))
178
            pa_log_error("cmtspeech_dl_buffer_release(%p) failed return value %d.", (void *)buf, ret);
179
        pa_mutex_unlock(c->cmtspeech_mutex);
180
        return -1;
181
    }
182
183
    ONDEBUG_TOKENS(fprintf(stderr, "D"));
184
    return 0;
185
}
186
187
/* cmtspeech thread */
188
static void update_uplink_frame_timing(struct userdata *u, cmtspeech_event_t *cmtevent) {
189
    int deadline_us;
190
    int64_t usec;
191
192
    pa_log_debug("msec= %d usec=%d rtclock=%d.%09ld",
193
                 (int)cmtevent->msg.timing_config_ntf.msec,
194
                 (int)cmtevent->msg.timing_config_ntf.usec,
195
                 (int)cmtevent->msg.timing_config_ntf.tstamp.tv_sec,
196
                 cmtevent->msg.timing_config_ntf.tstamp.tv_nsec);
197
198
    deadline_us = (cmtevent->msg.timing_config_ntf.msec % 20) * 1000 + cmtevent->msg.timing_config_ntf.usec;
199
200
    usec = ((int64_t) cmtevent->msg.timing_config_ntf.tstamp.tv_sec * 1000000) +
201
        (cmtevent->msg.timing_config_ntf.tstamp.tv_nsec/1000) + deadline_us;
202
203
    pa_log_debug("deadline at %lld (%d usec from msg receival)", usec, deadline_us);
204
205
    if (u->source && PA_SOURCE_IS_LINKED(u->source->state))
206
        pa_asyncmsgq_post(u->source->asyncmsgq, PA_MSGOBJECT(u->source),
207
                          VOICE_SOURCE_SET_UL_DEADLINE, NULL, usec, NULL, NULL);
208
    else
209
        pa_log_error("No destination where to send timing info");
210
}
211
212
static void reset_call_stream_states(struct userdata *u) {
213
    struct cmtspeech_connection *c = &u->cmt_connection;
214
215
    pa_assert(u);
216
217
    if (c->streams_created) {
218
        pa_log_warn("DL/UL streams existed at reset, closing");
219
        pa_asyncmsgq_post(pa_thread_mq_get()->outq, u->mainloop_handler,
220
                          CMTSPEECH_MAINLOOP_HANDLER_DELETE_STREAMS, NULL, 0, NULL, NULL);
221
        c->streams_created = FALSE;
222
    }
223
    if (c->playback_running) {
224
        pa_log_warn("DL stream was open, closing");
225
        c->playback_running = FALSE;
226
    }
227
    if (c->record_running) {
228
        pa_log_warn("UL stream was open, closing");
229
        c->record_running = FALSE;
230
        ul_frame_count = 0;
231
    }
232
}
233
234
/* cmtspeech thread */
235
static int mainloop_cmtspeech(struct userdata *u) {
236
    int retsockets = 0;
237
    struct cmtspeech_connection *c = &u->cmt_connection;
238
    struct pollfd *pollfd;
239
240
    pa_assert(u);
241
242
    if (!c->cmt_poll_item)
243
        return 0;
244
245
    if (pa_atomic_load(&u->cmtspeech_server_status))
246
        pa_rtpoll_set_timer_absolute(c->rtpoll, pa_rtclock_now() + CMTSPEECH_CLEANUP_TIMER_TIMEOUT);
247
248
    pollfd = pa_rtpoll_item_get_pollfd(c->cmt_poll_item, NULL);
249
    if (pollfd->revents & POLLIN) {
250
        cmtspeech_t *cmtspeech;
251
        int flags = 0, i = CMTSPEECH_CTRL_LEN;
252
        int res;
253
254
        /* locking note: hot path lock */
255
        pa_mutex_lock(c->cmtspeech_mutex);
256
257
        cmtspeech = c->cmtspeech;
258
259
        res = cmtspeech_check_pending(cmtspeech, &flags);
260
        if (res >= 0)
261
            retsockets = 1;
262
263
        pa_mutex_unlock(c->cmtspeech_mutex);
264
265
        if (res > 0) {
266
            if (flags & CMTSPEECH_EVENT_CONTROL) {
267
                cmtspeech_event_t cmtevent;
268
269
                /* locking note: this path is taken only very rarely */
270
                pa_mutex_lock(c->cmtspeech_mutex);
271
272
                i = cmtspeech_read_event(cmtspeech, &cmtevent);
273
274
                pa_mutex_unlock(c->cmtspeech_mutex);
275
276
                pa_log_debug("read cmtspeech event: state %d -> %d (type %d, ret %d).",
277
                             cmtevent.prev_state, cmtevent.state, cmtevent.msg_type, i);
278
279
                if (i != 0) {
280
                    pa_log_error("ERROR: unable to read event.");
281
282
                } else if (cmtevent.prev_state == CMTSPEECH_STATE_DISCONNECTED &&
283
                           cmtevent.state == CMTSPEECH_STATE_CONNECTED) {
284
                    pa_log_debug("call starting.");
285
                    reset_call_stream_states(u);
286
287
                    pa_asyncmsgq_post(pa_thread_mq_get()->outq, u->mainloop_handler,
288
                                      CMTSPEECH_MAINLOOP_HANDLER_CREATE_STREAMS, NULL, 0, NULL, NULL);
289
290
                    c->streams_created = TRUE;
291
                } else if (cmtevent.prev_state == CMTSPEECH_STATE_CONNECTED &&
292
                           cmtevent.state == CMTSPEECH_STATE_ACTIVE_DL &&
293
                           cmtevent.msg_type == CMTSPEECH_SPEECH_CONFIG_REQ) {
294
                    pa_log_notice("speech start: srate=%u, format=%u, stream=%u",
295
                                  cmtevent.msg.speech_config_req.sample_rate,
296
                                  cmtevent.msg.speech_config_req.data_format,
297
                                  cmtevent.msg.speech_config_req.speech_data_stream);
298
299
                     /* Ul is turned on when timing information is received */
300
301
                    pa_log_debug("enabling DL");
302
                    pa_asyncmsgq_post(pa_thread_mq_get()->outq, u->mainloop_handler,
303
                                      CMTSPEECH_MAINLOOP_HANDLER_CMT_DL_CONNECT, NULL, 0, NULL, NULL);
304
                    c->playback_running = true;
305
306
                     // start waiting for first dl frame
307
                     c->first_dl_frame_received = false;
308
                } else if (cmtevent.prev_state == CMTSPEECH_STATE_ACTIVE_DLUL &&
309
                           cmtevent.state == CMTSPEECH_STATE_ACTIVE_DL &&
310
                           cmtevent.msg_type == CMTSPEECH_SPEECH_CONFIG_REQ) {
311
312
                    pa_log_notice("speech update: srate=%u, format=%u, stream=%u",
313
                                  cmtevent.msg.speech_config_req.sample_rate,
314
                                  cmtevent.msg.speech_config_req.data_format,
315
                                  cmtevent.msg.speech_config_req.speech_data_stream);
316
317
                } else if (cmtevent.prev_state == CMTSPEECH_STATE_ACTIVE_DL &&
318
                           cmtevent.state == CMTSPEECH_STATE_ACTIVE_DLUL) {
319
                    pa_log_debug("enabling UL");
320
321
                    pa_asyncmsgq_post(pa_thread_mq_get()->outq, u->mainloop_handler,
322
                                    CMTSPEECH_MAINLOOP_HANDLER_CMT_UL_CONNECT, NULL, 0, NULL, NULL);
323
                    c->record_running = true;
324
325
                } else if (cmtevent.state == CMTSPEECH_STATE_ACTIVE_DLUL &&
326
                           cmtevent.msg_type == CMTSPEECH_TIMING_CONFIG_NTF) {
327
                    update_uplink_frame_timing(u, &cmtevent);
328
                    pa_log_debug("updated UL timing params");
329
330
                } else if ((cmtevent.prev_state == CMTSPEECH_STATE_ACTIVE_DL ||
331
                            cmtevent.prev_state == CMTSPEECH_STATE_ACTIVE_DLUL) &&
332
                           cmtevent.state == CMTSPEECH_STATE_CONNECTED) {
333
                    pa_log_notice("speech stop: stream=%u",
334
                                  cmtevent.msg.speech_config_req.speech_data_stream);
335
                    pa_asyncmsgq_post(pa_thread_mq_get()->outq, u->mainloop_handler,
336
                                      CMTSPEECH_MAINLOOP_HANDLER_CMT_DL_DISCONNECT, NULL, 0, NULL, NULL);
337
                    c->playback_running = FALSE;
338
                    pa_asyncmsgq_post(pa_thread_mq_get()->outq, u->mainloop_handler,
339
                                      CMTSPEECH_MAINLOOP_HANDLER_CMT_UL_DISCONNECT, NULL, 0, NULL, NULL);
340
                    c->record_running = FALSE;
341
                    ul_frame_count = 0;
342
343
                } else if (cmtevent.prev_state == CMTSPEECH_STATE_CONNECTED &&
344
                         cmtevent.state == CMTSPEECH_STATE_DISCONNECTED) {
345
                    pa_log_debug("call terminated.");
346
                    pa_asyncmsgq_post(pa_thread_mq_get()->outq, u->mainloop_handler,
347
                                      CMTSPEECH_MAINLOOP_HANDLER_DELETE_STREAMS, NULL, 0, NULL, NULL);
348
                    c->streams_created = FALSE;
349
                    reset_call_stream_states(u);
350
351
                } else if (cmtevent.msg_type == CMTSPEECH_EVENT_RESET) {
352
                    pa_log_warn("modem reset detected");
353
                    close_cmtspeech_on_error(u);
354
                    /* cmtspeech handle now null so return immediately */
355
                    return retsockets;
356
357
                } else {
358
                    pa_log_error("Unrecognized cmtspeech event: state %d -> %d (type %d, ret %d).",
359
                                 cmtevent.prev_state, cmtevent.state, cmtevent.msg_type, i);
360
                    if (cmtevent.state == CMTSPEECH_STATE_DISCONNECTED)
361
                        reset_call_stream_states(u);
362
                }
363
            }
364
365
            /* step: check for SSI data events */
366
            if (flags & CMTSPEECH_EVENT_DL_DATA) {
367
                cmtspeech_buffer_t *buf;
368
                static int counter = 0;
369
                bool cmtspeech_active = false;
370
371
                counter++;
372
                if (counter < 10)
373
                    pa_log_debug("SSI: DL frame available, read %d bytes.", i);
374
375
                /* locking note: another hot path lock */
376
                pa_mutex_lock(c->cmtspeech_mutex);
377
                cmtspeech_active = cmtspeech_is_active(c->cmtspeech);
378
                i = cmtspeech_dl_buffer_acquire(cmtspeech, &buf);
379
                pa_mutex_unlock(c->cmtspeech_mutex);
380
381
                if (i < 0) {
382
                    pa_log_error("Invalid DL frame received, cmtspeech_dl_buffer_acquire returned %d", i);
383
                } else {
384
                    if (counter < 10 )
385
                        pa_log_debug("DL (audio len %d) frame's first bytes %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
386
                                     buf->count - CMTSPEECH_DATA_HEADER_LEN,
387
                                     buf->data[0], buf->data[1], buf->data[1], buf->data[3],
388
                                     buf->data[4], buf->data[5], buf->data[6], buf->data[7]);
389
390
                    if (c->playback_running) {
391
                        if (c->first_dl_frame_received != true) {
392
                            c->first_dl_frame_received = true;
393
                            pa_log_debug("DL frame received, turn DL routing on...");
394
                        }
395
                        (void)push_cmtspeech_buffer_to_dl_queue(u, buf);
396
397
                    } else if (cmtspeech_active != true) {
398
                        pa_log_debug("DL frame received before ACTIVE_DL state, dropping...");
399
                    }
400
                }
401
            }
402
        }
403
    } else {
404
        /* pollfd timer expired and no events. */
405
        if (pa_atomic_cmpxchg(&u->cmtspeech_cleanup_state, CMTSPEECH_CLEANUP_TIMER_ACTIVE, CMTSPEECH_CLEANUP_IN_PROGRESS)) {
406
            pa_mutex_lock(c->cmtspeech_mutex);
407
            if (!pa_atomic_load(&u->cmtspeech_server_status) && c->cmtspeech) {
408
                if (u->server_inactive_timeout <= pa_rtclock_now()) {
409
                    pa_log_debug("cmtspeech cleanup timer checking server status.");
410
                    if (cmtspeech_is_active(c->cmtspeech)) {
411
                        pa_log_debug("cmtspeech still active, forcing cleanup");
412
                        pa_asyncmsgq_post(pa_thread_mq_get()->outq, u->mainloop_handler,
413
                                          CMTSPEECH_MAINLOOP_HANDLER_CMT_DL_DISCONNECT, NULL, 0, NULL, NULL);
414
                        pa_asyncmsgq_post(pa_thread_mq_get()->outq, u->mainloop_handler,
415
                                          CMTSPEECH_MAINLOOP_HANDLER_CMT_UL_DISCONNECT, NULL, 0, NULL, NULL);
416
                        cmtspeech_state_change_error(c->cmtspeech);
417
                    }
418
                    pa_rtpoll_set_timer_disabled(c->rtpoll);
419
                    pa_atomic_store(&u->cmtspeech_cleanup_state, CMTSPEECH_CLEANUP_TIMER_INACTIVE);
420
                    pa_log_debug("cmtspeech cleanup timer inactive in cmtspeech mainloop.");
421
                } else {
422
                    pa_rtpoll_set_timer_relative(c->rtpoll, CMTSPEECH_CLEANUP_TIMER_TIMEOUT);
423
                    pa_atomic_store(&u->cmtspeech_cleanup_state, CMTSPEECH_CLEANUP_TIMER_ACTIVE);
424
                    pa_log_debug("cmtspeech cleanup timer timeout updated in cmtspeech mainloop.");
425
                }
426
            } else {
427
                pa_rtpoll_set_timer_disabled(c->rtpoll);
428
                pa_atomic_store(&u->cmtspeech_cleanup_state, CMTSPEECH_CLEANUP_TIMER_INACTIVE);
429
                pa_log_debug("cmtspeech cleanup timer inactive in cmtspeech mainloop (call active or cmtspeech closed).");
430
            }
431
            pa_mutex_unlock(c->cmtspeech_mutex);
432
        } else if (!pa_atomic_load(&u->cmtspeech_server_status) && c->cmtspeech == NULL) {
433
            pa_log_debug("cmtspeech cleanup timer inactive in cmtspeech mainloop (2).");
434
            pa_rtpoll_set_timer_disabled(c->rtpoll);
435
        }
436
    }
437
438
    return retsockets;
439
}
440
441
/* cmtspeech thread */
442
static int check_cmtspeech_connection(struct cmtspeech_connection *c) {
443
    static uint counter = 0;
444
445
    if (c->cmtspeech)
446
        return 0;
447
448
    /* locking note: not on the hot path */
449
450
    pa_mutex_lock(c->cmtspeech_mutex);
451
452
    c->cmtspeech = cmtspeech_open();
453
454
    pa_mutex_unlock(c->cmtspeech_mutex);
455
456
    if (!c->cmtspeech) {
457
        if (counter++ < 5)
458
            pa_log_error("cmtspeech_open() failed");
459
        return -1;
460
    } else if (counter > 0) {
461
        pa_log_debug("cmtspeech_open() OK");
462
        counter = 0;
463
    }
464
    return 0;
465
}
466
467
/* cmtspeech thread */
468
static void pollfd_update(struct cmtspeech_connection *c) {
469
    if (c->cmt_poll_item) {
470
        pa_rtpoll_item_free(c->cmt_poll_item);
471
        c->cmt_poll_item = NULL;
472
    }
473
    if (c->cmtspeech) {
474
        pa_rtpoll_item *i = pa_rtpoll_item_new(c->rtpoll, PA_RTPOLL_NEVER, 1);
475
        struct pollfd *pollfd = pa_rtpoll_item_get_pollfd(i, NULL);
476
        /* locking note: a hot path lock */
477
        pa_mutex_lock(c->cmtspeech_mutex);
478
        pollfd->fd = cmtspeech_descriptor(c->cmtspeech);
479
        pa_mutex_unlock(c->cmtspeech_mutex);
480
        pollfd->events = POLLIN;
481
        pollfd->revents = 0;
482
483
        c->cmt_poll_item = i;
484
485
    } else {
486
        pa_log_debug("No cmtspeech connection");
487
    }
488
489
    if (c->thread_state_poll_item) {
490
        pa_rtpoll_item_free(c->thread_state_poll_item);
491
        c->thread_state_poll_item = NULL;
492
    }
493
494
    c->thread_state_poll_item = pa_rtpoll_item_new_fdsem(c->rtpoll, PA_RTPOLL_NORMAL, c->thread_state_change);
495
}
496
497
/**
498
 * Closes the cmtspeech instance after an unrecoverable
499
 * error has been detected.
500
 *
501
 * In most cases, the connection to the modem has been lost and its
502
 * state is unknown. As a recovery mechanism, we close the library
503
 * instance and restart from a known state.
504
 */
505
/* cmtspeech thread */
506
static void close_cmtspeech_on_error(struct userdata *u)
507
{
508
    struct cmtspeech_connection *c = &u->cmt_connection;
509
    pa_bool_t was_active = c->streams_created;
510
511
    pa_assert(u);
512
513
    pa_log_debug("closing the modem instance");
514
515
    reset_call_stream_states(u);
516
517
    if (u->sink_input && PA_SINK_INPUT_IS_LINKED(u->sink_input->state) &&
518
        u->sink_input->sink && u->sink_input->sink->asyncmsgq) {
519
        pa_assert_se(pa_asyncmsgq_send(u->sink_input->sink->asyncmsgq, PA_MSGOBJECT(u->sink_input),
520
                                       PA_SINK_INPUT_MESSAGE_FLUSH_DL, NULL, 0, NULL) == 0);
521
    } else {
522
        cmtspeech_buffer_t *buf;
523
        pa_log_debug("DL stream not connected. Flushing the queue locally");
524
        while((buf = pa_asyncq_pop(c->dl_frame_queue, 0))) {
525
            if (cmtspeech_dl_buffer_release(c->cmtspeech, buf)) {
526
                pa_log_error("Freeing cmtspeech buffer failed!");
527
            }
528
        }
529
    }
530
531
    pa_mutex_lock(c->cmtspeech_mutex);
532
    if (was_active == TRUE)
533
        pa_log_error("closing modem instance when interface still active");
534
    if (cmtspeech_close(c->cmtspeech))
535
        pa_log_error("cmtspeech_close() failed");
536
    c->cmtspeech = NULL;
537
    pa_mutex_unlock(c->cmtspeech_mutex);
538
}
539
540
/* cmtspeech thread */
541
static void thread_func(void *udata) {
542
    struct userdata *u = udata;
543
    struct cmtspeech_connection *c = &u->cmt_connection;
544
545
    pa_assert(u);
546
547
    pa_log_debug("cmtspeech thread starting up");
548
549
    if (u->core->realtime_scheduling)
550
        pa_make_realtime(u->core->realtime_priority - 1);
551
552
    pa_thread_mq_install(&c->thread_mq);
553
554
    c->cmtspeech = cmtspeech_open();
555
556
    pa_assert_se(pa_atomic_cmpxchg(&c->thread_state, CMT_STARTING, CMT_RUNNING));
557
558
    while(1) {
559
        int ret;
560
561
        if (check_cmtspeech_connection(c)) {
562
            pa_rtpoll_set_timer_relative(c->rtpoll, 500000);
563
        }
564
565
        pollfd_update(c);
566
567
        if (0 > (ret = pa_rtpoll_run(c->rtpoll, TRUE))) {
568
            pa_log_error("running rtpoll failed (%d) (fd %d)", ret, cmtspeech_descriptor(c->cmtspeech));
569
            close_cmtspeech_on_error(u);
570
        }
571
572
        if (pa_atomic_load(&c->thread_state) == CMT_ASK_QUIT) {
573
            pa_log_debug("cmtspeech thread quiting");
574
            goto finish;
575
        }
576
577
        /* note: cmtspeech can be closed in DBus thread */
578
        if (c->cmtspeech == NULL) {
579
            pa_log_notice("closing and reopening cmtspeech device");
580
            continue;
581
        }
582
583
        if (0 > mainloop_cmtspeech(u)) {
584
            goto fail;
585
        }
586
587
    }
588
589
590
/**/
591
fail:
592
    pa_log_error("Trying to unload myself");
593
    pa_asyncmsgq_post(c->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
594
595
    pa_log_debug("Waiting for quit command...");
596
    pa_fdsem_wait(c->thread_state_change);
597
    pa_assert(pa_atomic_load(&c->thread_state) == CMT_ASK_QUIT);
598
599
finish:
600
    close_cmtspeech_on_error(u);
601
602
    pa_assert_se(pa_atomic_cmpxchg(&c->thread_state, CMT_ASK_QUIT, CMT_QUIT));
603
604
    pa_log_debug("cmtspeech thread ended");
605
}
606
607
static int priv_cmtspeech_to_pa_prio(int cmtspprio)
608
{
609
    if (cmtspprio == CMTSPEECH_TRACE_ERROR)
610
       return PA_LOG_ERROR;
611
612
    if (cmtspprio == CMTSPEECH_TRACE_INFO)
613
       return PA_LOG_INFO;
614
615
    return PA_LOG_DEBUG;
616
}
617
618
static void priv_cmtspeech_trace_handler_f(int priority, const char *message, va_list args)
619
{
620
    pa_log_levelv_meta(priv_cmtspeech_to_pa_prio(priority),
621
                      "libcmtspeechdata",
622
                      0,
623
                      NULL,
624
                      message,
625
                      args);
626
}
627
628
/* Main thread */
629
int cmtspeech_connection_init(struct userdata *u)
630
{
631
    struct cmtspeech_connection *c = &u->cmt_connection;
632
633
    pa_assert(u);
634
    pa_assert(!userdata); /* To make sure we are the only instance running. */
635
636
    /* Initialized static pointer for memblock free function */
637
    userdata = u;
638
639
    c->cmt_handler = cmtspeech_handler_new(u);
640
    pa_atomic_store(&c->thread_state, CMT_STARTING);
641
    c->thread_state_change = pa_fdsem_new();
642
    c->rtpoll = pa_rtpoll_new();
643
    c->cmt_poll_item = NULL;
644
    pa_thread_mq_init(&c->thread_mq, u->core->mainloop, c->rtpoll);
645
    c->dl_frame_queue = pa_asyncq_new(4);
646
647
    c->cmtspeech = NULL;
648
    c->cmtspeech_mutex = pa_mutex_new(FALSE, FALSE);
649
650
    cmtspeech_init();
651
    cmtspeech_trace_toggle(CMTSPEECH_TRACE_ERROR, true);
652
    cmtspeech_trace_toggle(CMTSPEECH_TRACE_INFO, true);
653
    cmtspeech_trace_toggle(CMTSPEECH_TRACE_STATE_CHANGE, true);
654
    cmtspeech_trace_toggle(CMTSPEECH_TRACE_IO, true);
655
    cmtspeech_trace_toggle(CMTSPEECH_TRACE_DEBUG, true);
656
    cmtspeech_set_trace_handler(priv_cmtspeech_trace_handler_f);
657
658
    c->call_ul = FALSE;
659
    c->call_dl = FALSE;
660
    c->call_emergency = FALSE;
661
662
    c->first_dl_frame_received = FALSE;
663
    c->record_running = FALSE;
664
    c->playback_running = FALSE;
665
    c->streams_created = FALSE;
666
667
    if (!(c->thread = pa_thread_new("cmtspeech", thread_func, u))) {
668
        pa_log_error("Failed to create thread.");
669
        pa_atomic_store(&c->thread_state, CMT_QUIT);
670
        cmtspeech_connection_unload(u);
671
        return -1;
672
    }
673
674
    return 0;
675
}
676
677
/* Main thread */
678
void cmtspeech_connection_unload(struct userdata *u)
679
{
680
    struct cmtspeech_connection *c = &u->cmt_connection;
681
682
    pa_assert(u);
683
684
    switch (pa_atomic_load(&c->thread_state)) {
685
        default:
686
            pa_log_error("Undefined thread_state value: %d", pa_atomic_load(&c->thread_state));
687
            // fall trough
688
        case CMT_UNINITIALIZED:
689
            pa_log_debug("No CMT connection to unload");
690
            return;
691
        case CMT_STARTING:
692
            while (pa_atomic_load(&c->thread_state) == CMT_STARTING) {
693
                pa_log_debug("CMT connection not up yet, waiting...");
694
                usleep(200000);
695
            }
696
            // fall trough
697
        case CMT_RUNNING:
698
            pa_assert_se(pa_atomic_cmpxchg(&c->thread_state, CMT_RUNNING, CMT_ASK_QUIT));
699
            pa_fdsem_post(c->thread_state_change);
700
            // fall trough
701
        case CMT_ASK_QUIT:
702
            while (pa_atomic_load(&c->thread_state) == CMT_ASK_QUIT) {
703
                pa_log_debug("Waiting for CMT connection thread to quit...");
704
                usleep(200000);
705
            }
706
            pa_log_debug("cmtspeech thread has ended");
707
            // fall trough
708
        case CMT_QUIT:
709
            break;
710
    }
711
712
    pa_atomic_store(&c->thread_state, CMT_UNINITIALIZED);
713
    if (c->cmt_handler) {
714
        c->cmt_handler->parent.free((pa_object *)c->cmt_handler);
715
        c->cmt_handler = NULL;
716
    }
717
    if (c->thread_state_change) {
718
        pa_fdsem_free(c->thread_state_change);
719
        c->thread_state_change = NULL;
720
    }
721
    pa_rtpoll_free(c->rtpoll);
722
    c->rtpoll = NULL;
723
    pa_thread_mq_done(&c->thread_mq);
724
725
    if (c->cmtspeech) {
726
        pa_log_error("CMT speech connection up when shutting down");
727
    }
728
    pa_asyncq_free(c->dl_frame_queue, NULL);
729
    pa_mutex_free(c->cmtspeech_mutex);
730
    userdata = NULL;
731
    pa_log_debug("CMT connection unloaded");
732
}
733
734
/**
735
 * Sends an UL frame using SSI audio interface 'sal'.
736
 *
737
 * Return zero on success, -1 on error.
738
 */
739
/* Source IO-thread */
740
int cmtspeech_send_ul_frame(struct userdata *u, uint8_t *buf, size_t bytes)
741
{
742
    cmtspeech_buffer_t *salbuf;
743
    int res = -1;
744
    struct cmtspeech_connection *c = &u->cmt_connection;
745
746
    pa_assert(u);
747
748
    /* locking note: hot path lock */
749
    pa_mutex_lock(c->cmtspeech_mutex);
750
751
    if (!c->cmtspeech) {
752
        pa_mutex_unlock(c->cmtspeech_mutex);
753
        return -EIO;
754
    }
755
756
    if (cmtspeech_is_active(c->cmtspeech) == true)
757
        res = cmtspeech_ul_buffer_acquire(c->cmtspeech, &salbuf);
758
759
    if (res == 0) {
760
        if (ul_frame_count++ < 10)
761
            pa_log_debug("Sending ul frame # %d", ul_frame_count);
762
763
        /* note: 'bytes' must match the fixed size of frames */
764
        pa_assert(bytes == (size_t)salbuf->pcount);
765
        memcpy(salbuf->payload, buf, bytes);
766
        res = cmtspeech_ul_buffer_release(c->cmtspeech, salbuf);
767
        if (res < 0) {
768
          pa_log_error("cmtspeech_ul_buffer_release(%p) failed return value %d.", (void *)salbuf, res);
769
          if (res == -EIO) {
770
              /* note: a severe error has occured, close the modem
771
               *       instance */
772
              pa_mutex_unlock(c->cmtspeech_mutex);
773
              pa_log_error("A severe error has occured, close the modem instance.");
774
              close_cmtspeech_on_error(u);
775
              pa_mutex_lock(c->cmtspeech_mutex);
776
          }
777
        }
778
        ONDEBUG_TOKENS(fprintf(stderr, "U"));
779
    } else {
780
        static uint count = 0;
781
        if (count++ < 10)
782
            pa_log_error("cmtspeech_ul_buffer_acquire failed %d", res);
783
    }
784
785
    pa_mutex_unlock(c->cmtspeech_mutex);
786
787
    return res;
788
}
789
790
/* This is called form pulseaudio main thread. */
791
DBusHandlerResult cmtspeech_dbus_filter(DBusConnection *conn, DBusMessage *msg, void *arg)
792
{
793
    DBusMessageIter args;
794
    int type;
795
    struct userdata *u = arg;
796
    struct cmtspeech_connection *c = &u->cmt_connection;
797
798
    pa_assert(u);
799
800
    DBusError dbus_error;
801
    dbus_error_init(&dbus_error);
802
803
    if (dbus_message_is_signal(msg, CMTSPEECH_DBUS_CSCALL_CONNECT_IF, CMTSPEECH_DBUS_CSCALL_CONNECT_SIG)) {
804
        dbus_bool_t ulflag, dlflag, emergencyflag;
805
806
        dbus_message_get_args(msg, &dbus_error,
807
                              DBUS_TYPE_BOOLEAN, &ulflag,
808
                              DBUS_TYPE_BOOLEAN, &dlflag,
809
                              DBUS_TYPE_BOOLEAN, &emergencyflag,
810
                              DBUS_TYPE_INVALID);
811
812
        if (dbus_error_is_set(&dbus_error) != TRUE) {
813
            pa_log_debug("received AudioConnect with params %d, %d, %d", ulflag, dlflag, emergencyflag);
814
815
            c->call_ul = (ulflag == TRUE ? TRUE : FALSE);
816
            c->call_dl = (dlflag == TRUE ? TRUE : FALSE);
817
            c->call_emergency = (emergencyflag == TRUE ? TRUE : FALSE);
818
819
            /* note: very rarely taken code path */
820
            pa_mutex_lock(c->cmtspeech_mutex);
821
            if (c->cmtspeech)
822
                cmtspeech_state_change_call_connect(c->cmtspeech, dlflag == TRUE);
823
            pa_mutex_unlock(c->cmtspeech_mutex);
824
825
        } else
826
            pa_log_error("received %s with invalid parameters", CMTSPEECH_DBUS_CSCALL_CONNECT_SIG);
827
828
        return DBUS_HANDLER_RESULT_HANDLED;
829
830
    } else if (dbus_message_is_signal(msg, CMTSPEECH_DBUS_CSCALL_STATUS_IF, CMTSPEECH_DBUS_CSCALL_STATUS_SIG)) {
831
        pa_log_debug("Received ServerStatus");
832
833
        if (dbus_message_iter_init(msg, &args) == TRUE) {
834
            type = dbus_message_iter_get_arg_type(&args);
835
            dbus_bool_t val;
836
            if (type == DBUS_TYPE_BOOLEAN) {
837
                dbus_message_iter_get_basic(&args, &val);
838
839
                pa_log_debug("Set ServerStatus to %d.", val == TRUE);
840
841
                /* note: very rarely taken code path */
842
                pa_mutex_lock(c->cmtspeech_mutex);
843
                if (c->cmtspeech) {
844
                    cmtspeech_state_change_call_status(c->cmtspeech, val == TRUE);
845
                    if (val) {
846
                        /* Call in progress, pause cleanup timer. */
847
                        pa_atomic_store(&u->cmtspeech_server_status, 1);
848
                        if (pa_atomic_cmpxchg(&u->cmtspeech_cleanup_state, CMTSPEECH_CLEANUP_TIMER_ACTIVE,
849
                                                                           CMTSPEECH_CLEANUP_TIMER_INACTIVE)) {
850
                            pa_log_warn("cmtspeech cleanup timer changed to inactive in DBus thread.");
851
                        }
852
                    } else {
853
                        /* Call ended, set cleanup timer timeout. */
854
                        u->server_inactive_timeout = pa_rtclock_now() + CMTSPEECH_CLEANUP_TIMER_TIMEOUT;
855
                        if (pa_atomic_cmpxchg(&u->cmtspeech_cleanup_state, CMTSPEECH_CLEANUP_TIMER_INACTIVE,
856
                                                                           CMTSPEECH_CLEANUP_TIMER_ACTIVE)) {
857
                            pa_log_debug("cmtspeech cleanup timer timeout set in DBus thread.");
858
                        } else {
859
                            pa_log_debug("cmtspeech cleanup timer is already active or cleanup in progress.");
860
                        }
861
                        pa_atomic_store(&u->cmtspeech_server_status, 0);
862
                    }
863
                }
864
                pa_mutex_unlock(c->cmtspeech_mutex);
865
            } else
866
                pa_log_warn("received %s with invalid arguments.", CMTSPEECH_DBUS_CSCALL_STATUS_SIG);
867
        } else
868
            pa_log_error("received %s with invalid parameters", CMTSPEECH_DBUS_CSCALL_STATUS_SIG);
869
870
        return DBUS_HANDLER_RESULT_HANDLED;
871
872
    } else if (dbus_message_is_signal(msg, CMTSPEECH_DBUS_PHONE_SSC_STATE_IF, CMTSPEECH_DBUS_PHONE_SSC_STATE_SIG)) {
873
        const char* modemstate = NULL;
874
875
        dbus_message_get_args(msg, &dbus_error,
876
                              DBUS_TYPE_STRING, &modemstate,
877
                              DBUS_TYPE_INVALID);
878
879
        if (dbus_error_is_set(&dbus_error) != TRUE) {
880
            pa_log_debug("modem state change: %s", modemstate);
881
        }
882
    }
883
884
    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
885
}