|
Packit Service |
b98cfc |
/* Copyright 2011 David Henningsson, Canonical Ltd.
|
|
Packit Service |
b98cfc |
License: GPLv2+
|
|
Packit Service |
b98cfc |
*/
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
#include <stdio.h>
|
|
Packit Service |
b98cfc |
#include <unistd.h>
|
|
Packit Service |
b98cfc |
#include <string.h>
|
|
Packit Service |
b98cfc |
#include <glib.h>
|
|
Packit Service |
b98cfc |
#include <errno.h>
|
|
Packit Service |
b98cfc |
#include <glib/gstdio.h>
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
#include "apply-changes.h"
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
static gchar* tempdir = NULL;
|
|
Packit Service |
b98cfc |
static gchar* scriptfile = NULL;
|
|
Packit Service |
b98cfc |
static gchar* errorfile = NULL;
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
static GQuark quark()
|
|
Packit Service |
b98cfc |
{
|
|
Packit Service |
b98cfc |
return g_quark_from_static_string("hda-jack-retask-error");
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
static gboolean ensure_tempdir(GError** err)
|
|
Packit Service |
b98cfc |
{
|
|
Packit Service |
b98cfc |
if (!tempdir) {
|
|
Packit Service |
b98cfc |
tempdir = g_dir_make_tmp("hda-jack-retask-XXXXXX", err);
|
|
Packit Service |
b98cfc |
if (!tempdir)
|
|
Packit Service |
b98cfc |
return FALSE;
|
|
Packit Service |
b98cfc |
scriptfile = g_strdup_printf("%s/script.sh", tempdir);
|
|
Packit Service |
b98cfc |
errorfile = g_strdup_printf("%s/errors.log", tempdir);
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
g_unlink(errorfile); /* Ignore file does not exist error */
|
|
Packit Service |
b98cfc |
return TRUE;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
static gboolean create_reconfig_script(pin_configs_t* pins, int entries, int card, int device,
|
|
Packit Service |
b98cfc |
const char* model, const char* hints, GError** err)
|
|
Packit Service |
b98cfc |
{
|
|
Packit Service |
b98cfc |
gchar* hwdir = g_strdup_printf("/sys/class/sound/hwC%dD%d", card, device);
|
|
Packit Service |
b98cfc |
gchar destbuf[150*40] = "#!/bin/sh\n";
|
|
Packit Service |
b98cfc |
int bufleft = sizeof(destbuf) - strlen(destbuf);
|
|
Packit Service |
b98cfc |
gboolean ok = FALSE;
|
|
Packit Service |
b98cfc |
gchar* s = destbuf + strlen(destbuf);
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
if (!ensure_tempdir(err))
|
|
Packit Service |
b98cfc |
goto cleanup;
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
if (model) {
|
|
Packit Service |
b98cfc |
int l = g_snprintf(s, bufleft, "echo \"%s\" | tee %s/modelname 2>>%s\n",
|
|
Packit Service |
b98cfc |
model, hwdir, errorfile);
|
|
Packit Service |
b98cfc |
bufleft-=l;
|
|
Packit Service |
b98cfc |
s+=l;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
if (hints) {
|
|
Packit Service |
b98cfc |
int l = g_snprintf(s, bufleft, "echo \"%s\" | tee %s/hints 2>>%s\n",
|
|
Packit Service |
b98cfc |
hints, hwdir, errorfile);
|
|
Packit Service |
b98cfc |
bufleft-=l;
|
|
Packit Service |
b98cfc |
s+=l;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
while (entries) {
|
|
Packit Service |
b98cfc |
int l = g_snprintf(s, bufleft, "echo \"0x%02x 0x%08x\" | tee %s/user_pin_configs 2>>%s\n",
|
|
Packit Service |
b98cfc |
pins->nid, (unsigned int) actual_pin_config(pins), hwdir, errorfile);
|
|
Packit Service |
b98cfc |
bufleft-=l;
|
|
Packit Service |
b98cfc |
s+=l;
|
|
Packit Service |
b98cfc |
pins++;
|
|
Packit Service |
b98cfc |
entries--;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
if (bufleft < g_snprintf(s, bufleft, "echo 1 | tee %s/reconfig 2>>%s", hwdir, errorfile)) {
|
|
Packit Service |
b98cfc |
g_set_error(err, quark(), 0, "Bug in %s:%d!", __FILE__, __LINE__);
|
|
Packit Service |
b98cfc |
goto cleanup;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
if (!g_file_set_contents(scriptfile, destbuf, -1, err))
|
|
Packit Service |
b98cfc |
goto cleanup;
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
ok = TRUE;
|
|
Packit Service |
b98cfc |
cleanup:
|
|
Packit Service |
b98cfc |
g_free(hwdir);
|
|
Packit Service |
b98cfc |
return ok;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
//#define SUDO_COMMAND "gksudo --description \"Jack retasking\""
|
|
Packit Service |
b98cfc |
#define SUDO_COMMAND "pkexec"
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
gboolean run_sudo_script(const gchar* script_name, GError** err)
|
|
Packit Service |
b98cfc |
{
|
|
Packit Service |
b98cfc |
gchar* errfilecontents = NULL;
|
|
Packit Service |
b98cfc |
gchar* cmdline = g_strdup_printf("%s %s", SUDO_COMMAND, script_name);
|
|
Packit Service |
b98cfc |
int exit_status;
|
|
Packit Service |
b98cfc |
gsize errlen;
|
|
Packit Service |
b98cfc |
gboolean ok;
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
g_chmod(script_name, 0755);
|
|
Packit Service |
b98cfc |
g_spawn_command_line_sync(cmdline, NULL, NULL, &exit_status, NULL);
|
|
Packit Service |
b98cfc |
if (errorfile && g_file_get_contents(errorfile, &errfilecontents, &errlen, NULL) && errlen) {
|
|
Packit Service |
b98cfc |
g_set_error(err, quark(), 0, "%s", errfilecontents);
|
|
Packit Service |
b98cfc |
ok = FALSE;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
else ok = TRUE;
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
g_free(errfilecontents);
|
|
Packit Service |
b98cfc |
g_free(cmdline);
|
|
Packit Service |
b98cfc |
return ok;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
static gchar* get_pulseaudio_client_conf()
|
|
Packit Service |
b98cfc |
{
|
|
Packit Service |
b98cfc |
/* Reference: See src/pulsecore/core-util.c in pulseaudio */
|
|
Packit Service |
b98cfc |
gchar* fname;
|
|
Packit Service |
b98cfc |
gchar* dir = g_strdup_printf("%s/.pulse", g_get_home_dir());
|
|
Packit Service |
b98cfc |
if (access(dir, F_OK) < 0) {
|
|
Packit Service |
b98cfc |
const gchar* xch = g_getenv("XDG_CONFIG_HOME");
|
|
Packit Service |
b98cfc |
g_free(dir);
|
|
Packit Service |
b98cfc |
if (xch)
|
|
Packit Service |
b98cfc |
dir = g_strdup_printf("%s/pulse", xch);
|
|
Packit Service |
b98cfc |
else
|
|
Packit Service |
b98cfc |
dir = g_strdup_printf("%s/.config/pulse", g_get_home_dir());
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
fname = g_strdup_printf("%s/client.conf", dir);
|
|
Packit Service |
b98cfc |
g_free(dir);
|
|
Packit Service |
b98cfc |
return fname;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
static gboolean kill_pulseaudio(gboolean* was_killed, int card, GError** err)
|
|
Packit Service |
b98cfc |
{
|
|
Packit Service |
b98cfc |
gchar* fuser = NULL, *fuser2 = NULL;
|
|
Packit Service |
b98cfc |
gchar* s = NULL;
|
|
Packit Service |
b98cfc |
gchar* clientconf = NULL;
|
|
Packit Service |
b98cfc |
gboolean ok;
|
|
Packit Service |
b98cfc |
*was_killed = FALSE;
|
|
Packit Service |
b98cfc |
/* Is PA having a lock on the sound card? */
|
|
Packit Service |
b98cfc |
s = g_strdup_printf("fuser -v /dev/snd/controlC%d", card);
|
|
Packit Service |
b98cfc |
/* Due to some bug in fuser, stdout and stderr output is unclear. Better check both. */
|
|
Packit Service |
b98cfc |
if (!(ok = g_spawn_command_line_sync(s, &fuser, &fuser2, NULL, err)))
|
|
Packit Service |
b98cfc |
goto cleanup;
|
|
Packit Service |
b98cfc |
if ((ok = strstr(fuser, "pulseaudio") == NULL && strstr(fuser2, "pulseaudio") == NULL))
|
|
Packit Service |
b98cfc |
goto cleanup; // PulseAudio not locking the sound card
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
clientconf = get_pulseaudio_client_conf();
|
|
Packit Service |
b98cfc |
if (!(ok = !g_file_test(clientconf, G_FILE_TEST_EXISTS))) {
|
|
Packit Service |
b98cfc |
g_set_error(err, quark(), 0, "Cannot block PulseAudio from respawning:\n"
|
|
Packit Service |
b98cfc |
"Please either remove '%s' or kill PulseAudio manually.", clientconf);
|
|
Packit Service |
b98cfc |
goto cleanup;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
if (!(ok = g_file_set_contents(clientconf, "autospawn=no\n", -1, err)))
|
|
Packit Service |
b98cfc |
goto cleanup;
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
*was_killed = TRUE;
|
|
Packit Service |
b98cfc |
ok = g_spawn_command_line_sync("pulseaudio -k", NULL, NULL, NULL, err);
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
cleanup:
|
|
Packit Service |
b98cfc |
g_free(clientconf);
|
|
Packit Service |
b98cfc |
g_free(fuser);
|
|
Packit Service |
b98cfc |
g_free(fuser2);
|
|
Packit Service |
b98cfc |
g_free(s);
|
|
Packit Service |
b98cfc |
return ok;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
static gboolean restore_pulseaudio(gboolean was_killed, GError** err)
|
|
Packit Service |
b98cfc |
{
|
|
Packit Service |
b98cfc |
gchar* clientconf = get_pulseaudio_client_conf();
|
|
Packit Service |
b98cfc |
if (was_killed && g_unlink(clientconf) != 0) {
|
|
Packit Service |
b98cfc |
g_set_error(err, quark(), 0, "%s", g_strerror(errno));
|
|
Packit Service |
b98cfc |
g_free(clientconf);
|
|
Packit Service |
b98cfc |
return FALSE;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
g_free(clientconf);
|
|
Packit Service |
b98cfc |
return TRUE;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
gboolean apply_changes_reconfig(pin_configs_t* pins, int entries, int card, int device,
|
|
Packit Service |
b98cfc |
const char* model, const char* hints, GError** err)
|
|
Packit Service |
b98cfc |
{
|
|
Packit Service |
b98cfc |
gboolean result = FALSE;
|
|
Packit Service |
b98cfc |
// gchar* script_name = NULL;
|
|
Packit Service |
b98cfc |
gboolean pa_killed = FALSE;
|
|
Packit Service |
b98cfc |
/* Check for users of the sound card */
|
|
Packit Service |
b98cfc |
/* Kill pulseaudio if necessary (and possible) */
|
|
Packit Service |
b98cfc |
if (!kill_pulseaudio(&pa_killed, card, err))
|
|
Packit Service |
b98cfc |
goto cleanup;
|
|
Packit Service |
b98cfc |
/* Create script */
|
|
Packit Service |
b98cfc |
if (!create_reconfig_script(pins, entries, card, device, model, hints, err))
|
|
Packit Service |
b98cfc |
goto cleanup;
|
|
Packit Service |
b98cfc |
/* Run script as root */
|
|
Packit Service |
b98cfc |
if (!run_sudo_script(scriptfile, err))
|
|
Packit Service |
b98cfc |
goto cleanup;
|
|
Packit Service |
b98cfc |
result = TRUE;
|
|
Packit Service |
b98cfc |
cleanup:
|
|
Packit Service |
b98cfc |
if (!restore_pulseaudio(pa_killed, result ? err : NULL)) {
|
|
Packit Service |
b98cfc |
result = FALSE;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
// g_free(script_name);
|
|
Packit Service |
b98cfc |
return result;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
static gboolean create_firmware_file(pin_configs_t* pins, int entries, int card, int device,
|
|
Packit Service |
b98cfc |
const char* model, const char* hints, GError** err)
|
|
Packit Service |
b98cfc |
{
|
|
Packit Service |
b98cfc |
gboolean ok;
|
|
Packit Service |
b98cfc |
gchar destbuf[40*40+40*24] = "";
|
|
Packit Service |
b98cfc |
gchar* s = destbuf;
|
|
Packit Service |
b98cfc |
gchar* filename = g_strdup_printf("%s/hda-jack-retask.fw", tempdir);
|
|
Packit Service |
b98cfc |
unsigned int address, codec_vendorid, codec_ssid;
|
|
Packit Service |
b98cfc |
int bufleft = sizeof(destbuf);
|
|
Packit Service |
b98cfc |
int l;
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
get_codec_header(card, device, &address, &codec_vendorid, &codec_ssid);
|
|
Packit Service |
b98cfc |
l = g_snprintf(s, bufleft, "[codec]\n0x%08x 0x%08x %u\n\n[pincfg]\n", codec_vendorid, codec_ssid, address);
|
|
Packit Service |
b98cfc |
bufleft -= l;
|
|
Packit Service |
b98cfc |
s += l;
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
while (entries) {
|
|
Packit Service |
b98cfc |
l = g_snprintf(s, bufleft, "0x%02x 0x%08x\n", pins->nid, (unsigned int) actual_pin_config(pins));
|
|
Packit Service |
b98cfc |
bufleft -= l;
|
|
Packit Service |
b98cfc |
s += l;
|
|
Packit Service |
b98cfc |
pins++;
|
|
Packit Service |
b98cfc |
entries--;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
if (model) {
|
|
Packit Service |
b98cfc |
int l = g_snprintf(s, bufleft, "\n[model]\n%s\n", model);
|
|
Packit Service |
b98cfc |
bufleft-=l;
|
|
Packit Service |
b98cfc |
s+=l;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
if (hints) {
|
|
Packit Service |
b98cfc |
int l = g_snprintf(s, bufleft, "\n[hints]\n%s\n", hints);
|
|
Packit Service |
b98cfc |
bufleft-=l;
|
|
Packit Service |
b98cfc |
s+=l;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
ok = g_file_set_contents(filename, destbuf, -1, err);
|
|
Packit Service |
b98cfc |
g_free(filename);
|
|
Packit Service |
b98cfc |
return ok;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
static const gchar* remove_script =
|
|
Packit Service |
b98cfc |
"#!/bin/sh\n"
|
|
Packit Service |
b98cfc |
"rm /etc/modprobe.d/hda-jack-retask.conf 2>>%s\n"
|
|
Packit Service |
b98cfc |
"rm /lib/firmware/hda-jack-retask.fw 2>>%s\n";
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
static const gchar* retask_conf =
|
|
Packit Service |
b98cfc |
"# This file was added by the program 'hda-jack-retask'.\n"
|
|
Packit Service |
b98cfc |
"# If you want to revert the changes made by this program, you can simply erase this file and reboot your computer.\n"
|
|
Packit Service |
b98cfc |
"options snd-hda-intel patch=hda-jack-retask.fw,hda-jack-retask.fw,hda-jack-retask.fw,hda-jack-retask.fw\n";
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
static const gchar* install_script =
|
|
Packit Service |
b98cfc |
"#!/bin/sh\n"
|
|
Packit Service |
b98cfc |
"mv %s/hda-jack-retask.fw /lib/firmware/hda-jack-retask.fw\n 2>>%s\n"
|
|
Packit Service |
b98cfc |
"mv %s/hda-jack-retask.conf /etc/modprobe.d/hda-jack-retask.conf 2>>%s\n";
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
gboolean apply_changes_boot(pin_configs_t* pins, int entries, int card, int device,
|
|
Packit Service |
b98cfc |
const char* model, const char* hints, GError** err)
|
|
Packit Service |
b98cfc |
{
|
|
Packit Service |
b98cfc |
gchar *s;
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
if (!ensure_tempdir(err))
|
|
Packit Service |
b98cfc |
return FALSE;
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
if (!create_firmware_file(pins, entries, card, device, model, hints, err))
|
|
Packit Service |
b98cfc |
return FALSE;
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
/* Create hda-jack-retask.conf */
|
|
Packit Service |
b98cfc |
s = g_strdup_printf("%s/hda-jack-retask.conf", tempdir);
|
|
Packit Service |
b98cfc |
if (!g_file_set_contents(s, retask_conf, -1, err)) {
|
|
Packit Service |
b98cfc |
g_free(s);
|
|
Packit Service |
b98cfc |
return FALSE;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
g_free(s);
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
/* Create install script */
|
|
Packit Service |
b98cfc |
s = g_strdup_printf(install_script, tempdir, errorfile, tempdir, errorfile);
|
|
Packit Service |
b98cfc |
if (!g_file_set_contents(scriptfile, s, -1, err)) {
|
|
Packit Service |
b98cfc |
g_free(s);
|
|
Packit Service |
b98cfc |
return FALSE;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
g_free(s);
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
/* Run script as root */
|
|
Packit Service |
b98cfc |
if (!run_sudo_script(scriptfile, err))
|
|
Packit Service |
b98cfc |
return FALSE;
|
|
Packit Service |
b98cfc |
return TRUE;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
gboolean reset_changes_boot(GError** err)
|
|
Packit Service |
b98cfc |
{
|
|
Packit Service |
b98cfc |
gchar *s;
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
if ((g_file_test("/etc/modprobe.d/hda-jack-retask.conf", G_FILE_TEST_EXISTS) == 0) &&
|
|
Packit Service |
b98cfc |
(g_file_test("/lib/firmware/hda-jack-retask.fw", G_FILE_TEST_EXISTS) == 0))
|
|
Packit Service |
b98cfc |
{
|
|
Packit Service |
b98cfc |
g_set_error(err, quark(), 0, "No boot override is currently installed, nothing to remove.");
|
|
Packit Service |
b98cfc |
return FALSE;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
if (!ensure_tempdir(err))
|
|
Packit Service |
b98cfc |
return FALSE;
|
|
Packit Service |
b98cfc |
s = g_strdup_printf(remove_script, errorfile, errorfile);
|
|
Packit Service |
b98cfc |
if (!g_file_set_contents(scriptfile, s, -1, err)) {
|
|
Packit Service |
b98cfc |
g_free(s);
|
|
Packit Service |
b98cfc |
return FALSE;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
g_free(s);
|
|
Packit Service |
b98cfc |
|
|
Packit Service |
b98cfc |
/* Run script as root */
|
|
Packit Service |
b98cfc |
if (!run_sudo_script(scriptfile, err))
|
|
Packit Service |
b98cfc |
return FALSE;
|
|
Packit Service |
b98cfc |
return TRUE;
|
|
Packit Service |
b98cfc |
}
|
|
Packit Service |
b98cfc |
|