File: q6voiced.c

package info (click to toggle)
qcom-phone-utils 0.4.2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 212 kB
  • sloc: sh: 437; ansic: 123; makefile: 14
file content (169 lines) | stat: -rw-r--r-- 4,295 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
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
81
82
83
84
85
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
// SPDX-License-Identifier: MIT
#include <stdbool.h>
#include <stdio.h>
#include <dbus/dbus.h>
#include <string.h>
#include <alsa/asoundlib.h>

struct q6voiced {
	char card[64];
	snd_pcm_t *tx, *rx;
};

static void q6voiced_open(struct q6voiced *v)
{
	if (v->tx)
		return; /* Already active */

	/*
	 * Opening the PCM devices starts the stream.
	 * This should be replaced by a codec2codec link probably.
	 */
	if (snd_pcm_open(&v->tx, v->card, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK) < 0 ||
	    snd_pcm_set_params(v->tx, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, 1, 8000, 0, 500000) < 0 ||
	    snd_pcm_prepare(v->tx) < 0) {
		perror("Failed to open tx");
		goto error;
	}
	if (snd_pcm_open(&v->rx, v->card, SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK) < 0 ||
	    snd_pcm_set_params(v->rx, SND_PCM_FORMAT_S16_LE, SND_PCM_ACCESS_RW_INTERLEAVED, 1, 8000, 0, 500000) < 0 ||
	    snd_pcm_prepare(v->rx) < 0) {
		perror("Failed to open rx");
		goto error;
	}

	printf("PCM devices were opened.\n");
	return;

error:
	if (v->tx) {
		snd_pcm_close(v->tx);
		v->tx = NULL;
	}
	if (v->rx) {
		snd_pcm_close(v->rx);
		v->rx = NULL;
	}
}

static void q6voiced_close(struct q6voiced *v)
{
	if (!v->tx)
		return; /* Not active */

	snd_pcm_close(v->tx);
	snd_pcm_close(v->rx);
	v->rx = v->tx = NULL;

	printf("PCM devices were closed.\n");
}

/* See ModemManager-enums.h */
enum MMCallState {
	MM_CALL_STATE_DIALING		= 1,
	MM_CALL_STATE_RINGING_OUT	= 2,
	MM_CALL_STATE_ACTIVE		= 4,
};

static bool mm_state_is_active(int state)
{
	/*
	 * Some modems seem to be incapable of reporting DIALING -> ACTIVE.
	 * Therefore we also consider DIALING/RINGING_OUT as active.
	 */
	switch (state) {
	case MM_CALL_STATE_DIALING:
	case MM_CALL_STATE_RINGING_OUT:
	case MM_CALL_STATE_ACTIVE:
		return true;
	default:
		return false;
	}
}

static void handle_signal(struct q6voiced *v, DBusMessage *msg, DBusError *err)
{
	// Check if the message is a signal from the correct interface and with the correct name
	// TODO: Should we also check the call state for oFono?
	if (dbus_message_is_signal(msg, "org.ofono.VoiceCallManager", "CallAdded")) {
		q6voiced_open(v);
	} else if (dbus_message_is_signal(msg, "org.ofono.VoiceCallManager", "CallRemoved")) {
		q6voiced_close(v);
	} else if (dbus_message_is_signal(msg, "org.freedesktop.ModemManager1.Call", "StateChanged")) {
		/*
		 * For ModemManager call objects are created in advance
		 * and not necessarily immediately started.
		 * Need to listen for call state changes.
		 */
		int old_state, new_state;

		if (!dbus_message_get_args(msg, err,
					   DBUS_TYPE_INT32, &old_state,
					   DBUS_TYPE_INT32, &new_state,
					   DBUS_TYPE_INVALID))
			return;

		if (old_state == new_state)
			return; /* No change */

		if (mm_state_is_active(new_state))
			q6voiced_open(v);
		else if (mm_state_is_active(old_state) && !mm_state_is_active(new_state))
			q6voiced_close(v);
	}
}

int main(int argc, char **argv)
{
	struct q6voiced v = {0};

	DBusMessage *msg;
	DBusConnection *conn;
	DBusError err;

	if (argc != 2) {
		fprintf(stderr, "Usage: q6voiced hw:<card>,<device>\n");
		return 1;
	}

	strlcpy(v.card, argv[1], sizeof(v.card) - 1);

	// See: http://web.archive.org/web/20100309103206/http://dbus.freedesktop.org/doc/dbus/libdbus-tutorial.html
	// "Receiving a Signal"

	dbus_error_init(&err);

	conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
	if (dbus_error_is_set(&err)) {
		fprintf(stderr, "Connection error: %s\n", err.message);
		dbus_error_free(&err);
		return 1;
	}
	if (!conn)
		return 1;

	dbus_bus_add_match(conn, "type='signal',interface='org.ofono.VoiceCallManager'", &err);
	dbus_bus_add_match(conn, "type='signal',interface='org.freedesktop.ModemManager1.Call'", &err);
	dbus_connection_flush(conn);
	if (dbus_error_is_set(&err)) {
		fprintf(stderr, "Match error: %s\n", err.message);
		dbus_error_free(&err);
		return 1;
	}

	// Loop listening for signals being emmitted
	while (dbus_connection_read_write(conn, -1)) {
		// We need to process all received messages
		while (msg = dbus_connection_pop_message(conn)) {
			handle_signal(&v, msg, &err);
			if (dbus_error_is_set(&err)) {
				fprintf(stderr, "Failed to handle signal: %s\n", err.message);
				dbus_error_free(&err);
			}

			dbus_message_unref(msg);
		}
	}

	return 0;
}