Commit c2d65c26 authored by Bob Ham's avatar Bob Ham

Merge branch 'udev' into 'master'

Listen for the TTY port using udev

Closes #1

See merge request !3
parents 52435a39 bf0f91e3
Pipeline #1568 failed with stages
......@@ -7,6 +7,7 @@ Build-Depends:
dh-exec,
libpulse-dev,
libglib2.0-dev,
libudev-dev,
meson,
Standards-Version: 4.1.3
Homepage: https://source.puri.sm/Librem5/haegtesse
......
......@@ -4,11 +4,13 @@ Description=Hægtesse, a daemon for voice call audio
# Don't stop restarting the program
StartLimitIntervalSec=0
# We need PulseAudio
# We need udev
Requires=systemd-udevd.service
# and PulseAudio
Requires=pulseaudio.service
[Service]
ExecStart=/usr/bin/haegtesse -p /dev/ttyUSB4
ExecStart=/usr/bin/haegtesse
Restart=always
RestartSec=500ms
......
This diff is collapsed.
/*
* Copyright (C) 2018 Purism SPC
*
* This file is part of Hægtesse.
*
* Hægtesse is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* Hægtesse 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Hægtesse. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Bob Ham <bob.ham@puri.sm>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#ifndef HAEG_AUDIO_H__
#define HAEG_AUDIO_H__
#include <glib-object.h>
G_BEGIN_DECLS
#define HAEG_TYPE_AUDIO (haeg_audio_get_type ())
G_DECLARE_FINAL_TYPE (HaegAudio, haeg_audio, HAEG, AUDIO, GObject);
HaegAudio *haeg_audio_new (gsize fragment_size);
gboolean haeg_audio_is_corked (HaegAudio *audio);
void haeg_audio_cork (HaegAudio *audio);
void haeg_audio_uncork (HaegAudio *self);
void haeg_audio_begin_write (HaegAudio *self,
void **buf,
size_t *buf_size);
void haeg_audio_cancel_write (HaegAudio *self);
void haeg_audio_write (HaegAudio *self,
const void *buf,
size_t buf_size);
void haeg_audio_get_mic_buffer (HaegAudio *self,
void **data,
size_t *write_len);
void haeg_audio_update_mic_buffer (HaegAudio *self,
size_t written);
void haeg_audio_clear_mic_buffer (HaegAudio *self);
G_END_DECLS
#endif /* HAEG_AUDIO_H__ */
/*
* Copyright (C) 2018 Purism SPC
*
* This file is part of Hægtesse.
*
* Hægtesse is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* Hægtesse 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Hægtesse. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Bob Ham <bob.ham@puri.sm>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#include "haeg-port-monitor.h"
#include "util.h"
#include <glib/gi18n.h>
#include <glib-object.h>
#include <gio/gio.h>
#include <libudev.h>
#define ID_VENDOR "1e0e"
#define ID_PRODUCT "9001"
#define B_INTERFACE_NUMBER "04"
/**
* SECTION:haeg-port-monitor
* @short_description: Monitor for the appearance of an appropriate TTY port
* @Title: HaegPortMonitor
*/
struct _HaegPortMonitor
{
GObject parent_instance;
struct udev *ctx;
struct udev_monitor *monitor;
GIOChannel *channel;
guint watch_id;
gchar *port_node;
};
G_DEFINE_TYPE (HaegPortMonitor, haeg_port_monitor, G_TYPE_OBJECT);
enum {
SIGNAL_PORT,
SIGNAL_LAST_SIGNAL,
};
static guint signals [SIGNAL_LAST_SIGNAL];
static gboolean
check_device_sysattr(struct udev_device *device,
const char *sysattr,
const char *match_value)
{
const char *value;
value = udev_device_get_sysattr_value (device, sysattr);
if (!value)
{
return FALSE;
}
return strcmp (value, match_value) == 0;
}
static gboolean
check_device (struct udev_device *device)
{
struct udev_device *parent;
gboolean match;
/* Check USB vendor/product */
parent = udev_device_get_parent_with_subsystem_devtype
(device, "usb", "usb_device");
if (!parent)
{
//g_debug ("No usb_device parent");
return FALSE;
}
match = check_device_sysattr (parent, "idVendor", ID_VENDOR);
if (!match)
{
//g_debug ("idVendor does not match");
return FALSE;
}
match = check_device_sysattr (parent, "idProduct", ID_PRODUCT);
if (!match)
{
//g_debug ("idProduct does not match");
return FALSE;
}
/* Check USB interface number */
parent = udev_device_get_parent_with_subsystem_devtype
(device, "usb", "usb_interface");
if (!parent)
{
//g_debug ("No usb_interface parent");
return FALSE;
}
return check_device_sysattr (parent, "bInterfaceNumber",
B_INTERFACE_NUMBER);
}
static void
check_monitor_device (HaegPortMonitor *self,
struct udev_device *device)
{
const char *action;
gboolean match;
const char *devnode;
action = udev_device_get_action (device);
if (!action)
{
g_warning ("No action from udev device");
return;
}
if (strcmp (action, "add") != 0 &&
strcmp (action, "remove") != 0)
{
return;
}
devnode = udev_device_get_devnode (device);
if (strcmp (action, "add") == 0)
{
match = check_device (device);
if (!match)
{
//g_debug ("udev device `%s' is not what we're looking for",
// devnode);
return;
}
if (!self->port_node)
{
g_debug ("TTY port `%s' added", devnode);
self->port_node = g_strdup (devnode);
g_signal_emit (self, signals[SIGNAL_PORT],
0, self->port_node);
}
else
{
g_warning ("Extraneous TTY port `%s' addition from udev, ignoring", devnode);
}
}
else
{
if (!self->port_node
|| strcmp (self->port_node, devnode) != 0)
{
return;
}
g_debug ("TTY port `%s' removed", devnode);
g_free (self->port_node);
self->port_node = NULL;
}
}
static void
read_device (HaegPortMonitor *self)
{
struct udev_device *device;
device = udev_monitor_receive_device (self->monitor);
if (!device)
{
haeg_error ("Error receiving udev device from monitor");
}
check_monitor_device (self, device);
udev_device_unref (device);
}
static gboolean
watch_cb (GIOChannel *source,
GIOCondition condition,
HaegPortMonitor *self)
{
const gchar *err_msg = NULL;
/* Check what happened */
switch (condition)
{
case G_IO_IN:
case G_IO_PRI:
read_device (self);
case G_IO_OUT:
return TRUE;
case G_IO_ERR:
err_msg = "error condition";
break;
case G_IO_HUP:
err_msg = "file hung up";
break;
case G_IO_NVAL:
err_msg = "invalid request";
break;
}
haeg_error ("Error during udev monitor IO watch: %s", err_msg);
return FALSE;
}
static void
enumerate (HaegPortMonitor *self)
{
struct udev_enumerate *enumerator;
int err;
struct udev_list_entry *devices, *node;
const gchar *name;
struct udev_device *device;
gboolean match;
enumerator = udev_enumerate_new (self->ctx);
if (!enumerator)
{
haeg_error ("Error creating udev enumerator");
}
err = udev_enumerate_add_match_subsystem (enumerator, "tty");
if (err < 0)
{
haeg_error ("Error adding subsystem match to udev enumerator");
}
err = udev_enumerate_scan_devices (enumerator);
if (err < 0)
{
haeg_error ("Error enumerating udev devices");
}
devices = udev_enumerate_get_list_entry (enumerator);
udev_list_entry_foreach (node, devices)
{
name = udev_list_entry_get_name (node);
device = udev_device_new_from_syspath (self->ctx, name);
if (!device)
{
haeg_error ("Error creating udev device from name `%s'",
name);
}
match = check_device (device);
if (match)
{
const char *devnode;
devnode = udev_device_get_devnode (device);
g_debug ("Found TTY port `%s' while enumerating devices",
devnode);
self->port_node = g_strdup (devnode);
}
udev_device_unref (device);
if (match)
{
break;
}
}
udev_enumerate_unref (enumerator);
}
static void
haeg_port_monitor_init (HaegPortMonitor *self)
{
}
static void
constructed (GObject *object)
{
GObjectClass *parent_class = g_type_class_peek (G_TYPE_OBJECT);
HaegPortMonitor *self = HAEG_PORT_MONITOR (object);
// Create context
self->ctx = udev_new();
if (!self->ctx)
{
haeg_error ("Error creating udev context");
}
// Create monitor
self->monitor = udev_monitor_new_from_netlink
(self->ctx, "udev");
if (!self->monitor)
{
haeg_error ("Error creating udev monitor");
}
udev_monitor_filter_add_match_subsystem_devtype
(self->monitor, "tty", NULL);
udev_monitor_enable_receiving (self->monitor);
// Create channel
self->channel = g_io_channel_unix_new
(udev_monitor_get_fd (self->monitor));
self->watch_id =
g_io_add_watch (self->channel, G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL,
(GIOFunc)watch_cb, self);
// Enumerate
enumerate (self);
parent_class->constructed (object);
}
static void
dispose (GObject *object)
{
GObjectClass *parent_class = g_type_class_peek (G_TYPE_OBJECT);
HaegPortMonitor *self = HAEG_PORT_MONITOR (object);
if (self->watch_id)
{
g_source_remove (self->watch_id);
self->watch_id = 0;
}
if (self->channel)
{
g_io_channel_unref (self->channel);
self->channel = NULL;
}
if (self->monitor)
{
udev_monitor_unref (self->monitor);
self->monitor = NULL;
}
if (self->ctx)
{
udev_unref (self->ctx);
self->ctx = NULL;
}
parent_class->dispose (object);
}
static void
finalize (GObject *object)
{
GObjectClass *parent_class = g_type_class_peek (G_TYPE_OBJECT);
HaegPortMonitor *self = HAEG_PORT_MONITOR (object);
g_free (self->port_node);
parent_class->finalize (object);
}
static void
haeg_port_monitor_class_init (HaegPortMonitorClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->constructed = constructed;
object_class->dispose = dispose;
object_class->finalize = finalize;
/**
* HaegPortMonitor::port:
* @self: The #HaegPortMonitor instance.
*
* The TTY port has been added or requested.
*/
signals[SIGNAL_PORT] =
g_signal_new ("port",
G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST,
0, NULL, NULL, NULL,
G_TYPE_NONE,
1,
G_TYPE_STRING);
}
HaegPortMonitor *
haeg_port_monitor_new ()
{
return g_object_new (HAEG_TYPE_PORT_MONITOR, NULL);
}
static gboolean
request_port (HaegPortMonitor *self)
{
if (self->port_node)
{
g_signal_emit (self, signals[SIGNAL_PORT],
0, self->port_node);
}
return FALSE;
}
void
haeg_port_monitor_request_port (HaegPortMonitor *self)
{
g_idle_add ((GSourceFunc)request_port, self);
}
/*
* Copyright (C) 2018 Purism SPC
*
* This file is part of Hægtesse.
*
* Hægtesse is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* Hægtesse 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Hægtesse. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Bob Ham <bob.ham@puri.sm>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#ifndef HAEG_PORT_MONITOR_H__
#define HAEG_PORT_MONITOR_H__
#include <glib-object.h>
G_BEGIN_DECLS
#define HAEG_TYPE_PORT_MONITOR (haeg_port_monitor_get_type ())
G_DECLARE_FINAL_TYPE (HaegPortMonitor, haeg_port_monitor, HAEG, PORT_MONITOR, GObject);
HaegPortMonitor *haeg_port_monitor_new ();
void haeg_port_monitor_request_port (HaegPortMonitor *self);
G_END_DECLS
#endif /* HAEG_PORT_MONITOR_H__ */
This diff is collapsed.
/*
* Copyright (C) 2018 Purism SPC
*
* This file is part of Hægtesse.
*
* Hægtesse is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* Hægtesse 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
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Hægtesse. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Bob Ham <bob.ham@puri.sm>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#ifndef HAEG_PORT_H__
#define HAEG_PORT_H__
#include <glib-object.h>
G_BEGIN_DECLS
#define HAEG_TYPE_PORT (haeg_port_get_type ())
G_DECLARE_FINAL_TYPE (HaegPort, haeg_port, HAEG, PORT, GObject);
HaegPort *haeg_port_new (const gchar *file_name,
gsize sample_len,
GError **error);
void haeg_port_start (HaegPort *port);
void haeg_port_stop (HaegPort *self);
gsize haeg_port_read (HaegPort *self,
void *buf,
size_t buf_size,
GError **error);
gsize haeg_port_write (HaegPort *self,
void *buf,
size_t buf_size,
gboolean *not_ready,
GError **error);
G_END_DECLS
#endif /* HAEG_PORT_H__ */
......@@ -26,7 +26,7 @@
#include "util.h"
#include <glib/gi18n.h>
#include <gmodule.h>
#include <gio/gio.h>
#include <unistd.h>
#include <errno.h>
......@@ -98,6 +98,7 @@ dispose (GObject *object)
HaegSampleBuffer *self = HAEG_SAMPLE_BUFFER (object);
g_byte_array_unref (self->partial);
self->partial = NULL;
parent_class->dispose (object);
}
......@@ -140,7 +141,9 @@ haeg_sample_buffer_new (gint fd, gsize sample_len)
gsize
haeg_sample_buffer_read (HaegSampleBuffer *self,
gchar *buf, gsize buf_len)
gchar *buf,
gsize buf_len,
GError **error)
{
guint partial_len = self->partial->len;
gsize total;
......@@ -183,8 +186,17 @@ haeg_sample_buffer_read (HaegSampleBuffer *self,
bytes_read = read (self->fd, buf + total, buf_len - total);
if (bytes_read == -1)
{
haeg_error ("Error reading from TTY port: %s",
g_strerror (errno));
g_set_error (error,
G_IO_ERROR,
g_io_error_from_errno (errno),
"Error reading from TTY port: %s",
g_strerror (errno));
return 0;
}
else if (bytes_read == 0)
{
// EOF
return 0;
}
g_debug ("Read %i bytes from TTY port", bytes_read);
......
......@@ -33,9 +33,12 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (HaegSampleBuffer, haeg_sample_buffer, HAEG, SAMPLE_BUFFER, GObject);
HaegSampleBuffer *haeg_sample_buffer_new (gint fd, gsize sample_len);
gsize haeg_sample_buffer_read (HaegSampleBuffer *buffer,
gchar *buf, gsize buf_len);
HaegSampleBuffer * haeg_sample_buffer_new (gint fd,
gsize sample_len);
gsize haeg_sample_buffer_read (HaegSampleBuffer *buffer,
gchar *buf,
gsize buf_len,
GError **error);
G_END_DECLS
......
This diff is collapsed.
......@@ -20,14 +20,19 @@
#
haegtesse_deps = [ dependency('gobject-2.0'),
dependency('gio-unix-2.0'),
dependency('libpulse'),
dependency('libpulse-mainloop-glib'),
dependency('libudev'),
]
executable('haegtesse',
['main.c',
'util.h', 'util.c',
'haeg-sample-buffer.h', 'haeg-sample-buffer.c'
'haeg-sample-buffer.h', 'haeg-sample-buffer.c',
'haeg-port.h', 'haeg-port.c',
'haeg-audio.h', 'haeg-audio.c',
'haeg-port-monitor.h', 'haeg-port-monitor.c',
],
dependencies : haegtesse_deps,
include_directories : include_directories('..'),
......
......@@ -27,6 +27,9 @@
#include <glib.h>
#define APPLICATION_NAME "H\u00E6gtesse"
#define APPLICATION_ID "sm.puri.Haegtesse"
G_BEGIN_DECLS
void haeg_error (const gchar *format, ...);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment