init
This commit is contained in:
commit
03ec206995
30
Makefile
Normal file
30
Makefile
Normal file
@ -0,0 +1,30 @@
|
||||
CC=gcc
|
||||
#INC=-I/usr/include/dbus-1.0/ -I/usr/lib/$(shell uname -m)-linux-gnu/dbus-1.0/include/
|
||||
INC=$(shell pkg-config --cflags dbus-1)
|
||||
CFLAGS=-Wall $(INC)
|
||||
LDFLAGS=-ltinyalsa -ldbus-1 -ldl
|
||||
SOURCES=src/q6voiced.c
|
||||
OBJECTS=$(SOURCES:.c=.o)
|
||||
NAME=q6voiced
|
||||
EXECUTABLE=$(NAME)
|
||||
|
||||
all: $(SOURCES) $(EXECUTABLE)
|
||||
|
||||
$(EXECUTABLE): $(OBJECTS)
|
||||
$(CC) $(OBJECTS) -o $@ $(LDFLAGS)
|
||||
|
||||
install:
|
||||
install -Dm755 $(EXECUTABLE) /usr/bin/$(EXECUTABLE)
|
||||
install -Dm644 $(NAME).service /etc/systemd/system
|
||||
test -f /etc/$(NAME).conf || install -Dm644 $(NAME).conf /etc/$(NAME).conf
|
||||
systemctl enable /etc/systemd/system/$(NAME).service
|
||||
systemctl start $(NAME)
|
||||
|
||||
uninstall:
|
||||
systemctl stop $(NAME)
|
||||
systemctl disable $(NAME)
|
||||
rm /etc/systemd/system/$(NAME).service
|
||||
rm /usr/bin/$(EXECUTABLE)
|
||||
|
||||
clean:
|
||||
rm -r $(EXECUTABLE) $(OBJECTS)
|
52
README.md
Normal file
52
README.md
Normal file
@ -0,0 +1,52 @@
|
||||
# Forked from https://gitlab.com/postmarketOS/q6voiced
|
||||
|
||||
# Instalation
|
||||
make
|
||||
make install
|
||||
|
||||
# Dependency
|
||||
tinyalsa
|
||||
|
||||
# Instalation on Mobian guide
|
||||
apt install libdbus-1-dev
|
||||
|
||||
git clone https://github.com/tinyalsa/tinyalsa.git
|
||||
cd tinyalsa
|
||||
make
|
||||
sudo make install
|
||||
sudo ldconfig
|
||||
|
||||
git clone https://github.com/alxpvl/q6voiced.git
|
||||
cd q6voiced
|
||||
make
|
||||
sudo make install
|
||||
|
||||
|
||||
# q6voiced
|
||||
q6voiced is a userspace daemon for the QDSP6 voice call audio driver.
|
||||
Voice call audio is directly routed from the modem to the input/output audio
|
||||
devices, but something needs to start the audio streams for that to happen.
|
||||
|
||||
q6voiced listens on dbus for signals from oFono, and opens/closes the
|
||||
PCM device when a call is initiated/ended in oFono. This essentially
|
||||
makes voice call audio work out of the box (provided that the audio
|
||||
routing, e.g. Earpiece and a microphone is set up appropriately).
|
||||
|
||||
The q6voiced patches can be currently found in the [msm8916-mainline/linux]
|
||||
repository (look for `ASoC: qdsp6:`) commits). It may also work for downstream
|
||||
since the currently implemented approach is very similar.
|
||||
|
||||
## Note
|
||||
It is expected that this daemon will be obsolete eventually.
|
||||
Different approaches exists for activating such audio streams that are not
|
||||
processed directly by Linux. (See [Hostless PCM streams]) At the moment the
|
||||
kernel driver implements the "Hostless FE" approach (as on downstream), but
|
||||
eventually this should be replaced by a Codec <-> Codec link.
|
||||
|
||||
In that case the audio streams would be activated by setting some ALSA mixers
|
||||
(e.g. through ALSA UCM) and this daemon could be removed. However, there is
|
||||
quite some work involved to make that work properly. Until then, this daemon
|
||||
allows voice call audio to work without activating audio manually.
|
||||
|
||||
[msm8916-mainline/linux]: https://github.com/msm8916-mainline/linux
|
||||
[Hostless PCM streams]: https://www.kernel.org/doc/html/latest/sound/soc/dpcm.html#hostless-pcm-streams
|
3
q6voiced.conf
Normal file
3
q6voiced.conf
Normal file
@ -0,0 +1,3 @@
|
||||
q6voice_card=0
|
||||
q6voice_device=3
|
||||
|
17
q6voiced.service
Normal file
17
q6voiced.service
Normal file
@ -0,0 +1,17 @@
|
||||
[Unit]
|
||||
Description=Enable q6voice audio when call is performed with oFono or ModemManager
|
||||
After=dbus.service
|
||||
|
||||
[Service]
|
||||
#Type=Simple
|
||||
# Note: q6voice_card/q6voice_device need to be set in /etc/q6voiced.conf
|
||||
EnvironmentFile=/etc/q6voiced.conf
|
||||
ExecStart=/usr/bin/q6voiced hw:${q6voice_card},${q6voice_device}
|
||||
User=nobody
|
||||
Group=audio
|
||||
|
||||
[Install]
|
||||
#WantedBy=sockets.target
|
||||
#Also=dbus.service
|
||||
WantedBy=multi-user.target
|
||||
|
21
src/LICENSE
Normal file
21
src/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2020 postmarketOS
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
158
src/q6voiced.c
Normal file
158
src/q6voiced.c
Normal file
@ -0,0 +1,158 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <dbus/dbus.h>
|
||||
#include <tinyalsa/asoundlib.h>
|
||||
|
||||
/* Note: These parameters have little relevance (no audio data written) */
|
||||
struct pcm_config pcm_config_voice_call = {
|
||||
.channels = 1,
|
||||
.rate = 8000,
|
||||
.period_size = 160,
|
||||
.period_count = 2,
|
||||
.format = PCM_FORMAT_S16_LE,
|
||||
};
|
||||
|
||||
struct q6voiced {
|
||||
unsigned int card, device;
|
||||
struct pcm *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.
|
||||
*/
|
||||
v->tx = pcm_open(v->card, v->device, PCM_IN, &pcm_config_voice_call);
|
||||
v->rx = pcm_open(v->card, v->device, PCM_OUT, &pcm_config_voice_call);
|
||||
if (!pcm_is_ready(v->tx) || pcm_prepare(v->tx))
|
||||
perror("Failed to open tx");
|
||||
if (!pcm_is_ready(v->rx) || pcm_prepare(v->rx))
|
||||
perror("Failed to open rx");
|
||||
|
||||
printf("PCM devices were opened.\n");
|
||||
}
|
||||
|
||||
static void q6voiced_close(struct q6voiced *v)
|
||||
{
|
||||
if (!v->tx)
|
||||
return; /* Not active */
|
||||
|
||||
pcm_close(v->rx);
|
||||
pcm_close(v->tx);
|
||||
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 || sscanf(argv[1], "hw:%u,%u", &v.card, &v.device) != 2) {
|
||||
fprintf(stderr, "Usage: q6voiced hw:<card>,<device>\n");
|
||||
return 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user