Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
openSUSE:12.2:ARM
alsa-tools
0002-hdajackretask-Add-new-program-for-easy-ret...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 0002-hdajackretask-Add-new-program-for-easy-retask-of-hda.patch of Package alsa-tools
From 7aa4a74f88d74de26886ce9203a5de2b8a905a84 Mon Sep 17 00:00:00 2001 From: David Henningsson <david.henningsson@canonical.com> Date: Thu, 22 Mar 2012 17:47:02 +0100 Subject: [PATCH 2/3] hdajackretask: Add new program for easy retask of hda pin nodes Most HDA Intel soundcards are to some degree retaskable, i e can be used for more than one thing. This tool is a GUI to make it easy to retask your jacks - e g, turn your Mic jack into an extra Headphone, or why not make them both line outs and connect them to your surround receiver? Signed-off-by: David Henningsson <david.henningsson@canonical.com> --- Makefile | 2 +- hdajackretask/Makefile | 18 ++ hdajackretask/README | 52 +++ hdajackretask/apply-changes.c | 285 +++++++++++++++++ hdajackretask/apply-changes.h | 15 + hdajackretask/gitcompile | 3 + hdajackretask/main-gtk.c | 523 +++++++++++++++++++++++++++++++ hdajackretask/sysfs-pin-configs.c | 626 +++++++++++++++++++++++++++++++++++++ hdajackretask/sysfs-pin-configs.h | 63 ++++ 9 files changed, 1586 insertions(+), 1 deletion(-) create mode 100644 hdajackretask/Makefile create mode 100644 hdajackretask/README create mode 100644 hdajackretask/apply-changes.c create mode 100644 hdajackretask/apply-changes.h create mode 100755 hdajackretask/gitcompile create mode 100644 hdajackretask/main-gtk.c create mode 100644 hdajackretask/sysfs-pin-configs.c create mode 100644 hdajackretask/sysfs-pin-configs.h diff --git a/Makefile b/Makefile index 3bfeac7..893b394 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ TOP = . SUBDIRS = ac3dec as10k1 envy24control hdsploader hdspconf hdspmixer \ mixartloader pcxhrloader rmedigicontrol sb16_csp seq sscape_ctl \ us428control usx2yloader vxloader echomixer ld10k1 qlo10k1 \ - hwmixvolume + hwmixvolume hdajackretask all: @for i in $(SUBDIRS); do \ diff --git a/hdajackretask/Makefile b/hdajackretask/Makefile new file mode 100644 index 0000000..c9c4043 --- /dev/null +++ b/hdajackretask/Makefile @@ -0,0 +1,18 @@ +SOURCES = main-gtk.c sysfs-pin-configs.c apply-changes.c + +build: + $(shell sed 's/\\/\\\\/g;s/"/\\"/g;s/^/"/;s/$$/\\n"/;' < README > README.generated.h) + gcc -g -O0 -Wall -o hda-jack-retask $(SOURCES) $(shell pkg-config --cflags --libs gtk+-3.0) + +clean: + -rm hda-jack-retask + -rm README.generated.h + +install: + install -Dm755 hda-jack-retask $(DESTDIR)/usr/bin/hda-jack-retask + install -Dm644 README $(DESTDIR)/usr/share/doc/hda-jack-retask/README + +uninstall: + -rm $(DESTDIR)/usr/bin/hda-jack-retask + +.PHONY: build install uninstall diff --git a/hdajackretask/README b/hdajackretask/README new file mode 100644 index 0000000..68f62b1 --- /dev/null +++ b/hdajackretask/README @@ -0,0 +1,52 @@ +Documentation for hda-jack-retask +================================= + +Most HDA Intel soundcards are to some degree retaskable, i e can be used for more than one thing. This tool is a GUI to make it easy to retask your jacks - e g, turn your Mic jack into an extra Headphone, or why not make them both line outs and connect them to your surround receiver? + +Quickstart +========== + +Start the application "hda-jack-retask" from the command line. +Select a codec in the top bar; some people have only one - if you have more than one, one is the "main" one, and the rest are probably HDMI codecs. + +All jacks (and other inputs/outputs) are shown under "Pin configuration". To override one of your jacks, click the "Override" checkbox for that pin and select the desired function. +Note: If you select a "Line out (back)" override, you should also have a "Line out (front)" override. A "Line out (Center/LFE)" override will only work with the previous two overrides, and so on. + +You can try out your new configuration by clicking "Apply now". To make your new configuration the boot up default, click "Install boot override". +Doing any of these requires root privileges, so you will likely be asked for your password. + +Quickend +======== + +Did you screw up? Click the "Remove boot override" button, and once the process has completed, shut down your computer, wait a little while and turn it on again. You should now be back where you started. + +Did it work? +============ + +If it did, be happy, share your joy, do something nice! + +If it didn't, here are some things to try: + + * Go into alsamixer and see if there are new volume controls, and see if they are unmuted and set to a reasonable value. + + * Sometimes the driver does not shut off unconnected pins (if they're unconnected, why worry?). You might be able to fix this by turning your computer completely off for a little while. + + * See the "model=auto" option below + +If this does not help, chances are high you're running into either a driver or a hardware limitation. You might run into driver bugs (maybe nobody has ever tested four headphones before!). If you do, feel free to report them on the alsa-devel mailinglist: however, due to lack of manpower, chances are this will be dealt with at a very low priority (having sound working out of the box for a standard configuration, is more important than supporting strange configurations). However, if you are a kernel hacker and come up with a patch that solves your problem without screwing up anything else, chances are pretty high it will be merged. + +If you suspect the problem is with this GUI application rather than the kernel driver, feel free to write me an email (and if you like, include a patch!). If so, write to diwic@ubuntu.com. + +Options +======= + + * Show unconnected pins +Your BIOS is responsible for setting up what pins on the codec that are actually connected to something and which ones are not. Sometimes BIOS is buggy, and will not show all your jacks. If you have a jack your BIOS says you haven't, you can try enabling random pins and see if it works. + + * Set Model=auto +Some codecs, especially older ones, are hard-coded to use a specific model, and thus will not care about your overrides. In many cases and with a reasonably new kernel, the auto parser now works well for these codecs as well. You can force the auto parser to be used by checking this box. In some cases, though, the explicit model is there for a reason, if so, you're stuck. + + * Advanced override +This is for the experts only. It makes you select each configuration field individually, instead of just a few predefined values that make sense. Note that most combinations here are invalid in one way or the other, so you should probably not mess with this unless you have read and understood the "Configuration Default" section of the HD Audio specification. (Which, at the time of this writing, is available here: +http://www.intel.com/content/dam/doc/product-specification/high-definition-audio-specification.pdf ) + diff --git a/hdajackretask/apply-changes.c b/hdajackretask/apply-changes.c new file mode 100644 index 0000000..eda404e --- /dev/null +++ b/hdajackretask/apply-changes.c @@ -0,0 +1,285 @@ +/* Copyright 2011 David Henningsson, Canonical Ltd. + License: GPLv2+ +*/ + +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <glib.h> +#include <errno.h> +#include <glib/gstdio.h> + +#include "apply-changes.h" + +static gchar* tempdir = NULL; +static gchar* scriptfile = NULL; +static gchar* errorfile = NULL; + +static gboolean ensure_tempdir(GError** err) +{ + if (!tempdir) { + tempdir = g_dir_make_tmp("hda-jack-retask-XXXXXX", err); + if (!tempdir) + return FALSE; + scriptfile = g_strdup_printf("%s/script.sh", tempdir); + errorfile = g_strdup_printf("%s/errors.log", tempdir); + } + g_unlink(errorfile); /* Ignore file does not exist error */ + return TRUE; +} + +static gboolean create_reconfig_script(pin_configs_t* pins, int entries, int card, int device, + const char* model, GError** err) +{ + gchar* hwdir = g_strdup_printf("/sys/class/sound/hwC%dD%d", card, device); + gchar destbuf[120*40] = "#!/bin/sh\n"; + int bufleft = sizeof(destbuf) - strlen(destbuf); + gboolean ok = FALSE; + gchar* s = destbuf + strlen(destbuf); + + if (!ensure_tempdir(err)) + goto cleanup; + + if (model) { + int l = g_snprintf(s, bufleft, "echo \"%s\" | tee %s/modelname 2>>%s\n", + model, hwdir, errorfile); + bufleft-=l; + s+=l; + } + + while (entries) { + int l = g_snprintf(s, bufleft, "echo \"0x%02x 0x%08x\" | tee %s/user_pin_configs 2>>%s\n", + pins->nid, (unsigned int) actual_pin_config(pins), hwdir, errorfile); + bufleft-=l; + s+=l; + pins++; + entries--; + } + + if (bufleft < g_snprintf(s, bufleft, "echo 1 | tee %s/reconfig 2>>%s", hwdir, errorfile)) { + g_set_error(err, 0, 0, "Bug in %s:%d!", __FILE__, __LINE__); + goto cleanup; + } + + if (!g_file_set_contents(scriptfile, destbuf, -1, err)) + goto cleanup; + + ok = TRUE; +cleanup: + g_free(hwdir); + return ok; +} + + +//#define SUDO_COMMAND "gksudo --description \"Jack retasking\"" +#define SUDO_COMMAND "pkexec" + +gboolean run_sudo_script(const gchar* script_name, GError** err) +{ + gchar* errfilecontents = NULL; + gchar* cmdline = g_strdup_printf("%s %s", SUDO_COMMAND, script_name); + int exit_status; + gsize errlen; + gboolean ok; + + g_chmod(script_name, 0755); + g_spawn_command_line_sync(cmdline, NULL, NULL, &exit_status, NULL); + if (errorfile && g_file_get_contents(errorfile, &errfilecontents, &errlen, NULL) && errlen) { + g_set_error(err, 0, 0, "%s", errfilecontents); + ok = FALSE; + } + else ok = TRUE; + + g_free(errfilecontents); + g_free(cmdline); + return ok; +} + +static gchar* get_pulseaudio_client_conf() +{ + return g_strdup_printf("%s/.pulse/client.conf", g_get_home_dir()); +} + +static gboolean kill_pulseaudio(gboolean* was_killed, int card, GError** err) +{ + gchar* fuser = NULL, *fuser2 = NULL; + gchar* s = NULL; + gchar* clientconf = NULL; + gboolean ok; + *was_killed = FALSE; + /* Is PA having a lock on the sound card? */ + s = g_strdup_printf("fuser -v /dev/snd/controlC%d", card); + /* Due to some bug in fuser, stdout and stderr output is unclear. Better check both. */ + if (!(ok = g_spawn_command_line_sync(s, &fuser, &fuser2, NULL, err))) + goto cleanup; + if ((ok = strstr(fuser, "pulseaudio") == NULL && strstr(fuser2, "pulseaudio") == NULL)) + goto cleanup; // PulseAudio not locking the sound card + + clientconf = get_pulseaudio_client_conf(); + if (!(ok = !g_file_test(clientconf, G_FILE_TEST_EXISTS))) { + g_set_error(err, 0, 0, "Cannot block PulseAudio from respawning:\n" + "Please either remove '%s' or kill PulseAudio manually.", clientconf); + goto cleanup; + } + + if (!(ok = g_file_set_contents(clientconf, "autospawn=no\n", -1, err))) + goto cleanup; + + *was_killed = TRUE; + ok = g_spawn_command_line_sync("pulseaudio -k", NULL, NULL, NULL, err); + +cleanup: + g_free(clientconf); + g_free(fuser); + g_free(fuser2); + g_free(s); + return ok; +} + +static gboolean restore_pulseaudio(gboolean was_killed, GError** err) +{ + gchar* clientconf = get_pulseaudio_client_conf(); + if (was_killed && g_unlink(clientconf) != 0) { + g_set_error(err, 0, 0, "%s", g_strerror(errno)); + g_free(clientconf); + return FALSE; + } + g_free(clientconf); + return TRUE; +} + +gboolean apply_changes_reconfig(pin_configs_t* pins, int entries, int card, int device, + const char* model, GError** err) +{ + gboolean result = FALSE; +// gchar* script_name = NULL; + gboolean pa_killed = FALSE; + /* Check for users of the sound card */ + /* Kill pulseaudio if necessary (and possible) */ + if (!kill_pulseaudio(&pa_killed, card, err)) + goto cleanup; + /* Create script */ + if (!create_reconfig_script(pins, entries, card, device, model, err)) + goto cleanup; + /* Run script as root */ + if (!run_sudo_script(scriptfile, err)) + goto cleanup; + result = TRUE; +cleanup: + if (!restore_pulseaudio(pa_killed, result ? err : NULL)) { + result = FALSE; + } +// g_free(script_name); + return result; +} + +static gboolean create_firmware_file(pin_configs_t* pins, int entries, int card, int device, + const char* model, GError** err) +{ + gboolean ok; + gchar destbuf[40*40] = ""; + gchar* s = destbuf; + gchar* filename = g_strdup_printf("%s/hda-jack-retask.fw", tempdir); + unsigned int address, codec_vendorid, codec_ssid; + int bufleft = sizeof(destbuf); + int l; + + get_codec_header(card, device, &address, &codec_vendorid, &codec_ssid); + l = g_snprintf(s, bufleft, "[codec]\n0x%08x 0x%08x %u\n\n[pincfg]\n", codec_vendorid, codec_ssid, address); + bufleft -= l; + s += l; + + while (entries) { + l = g_snprintf(s, bufleft, "0x%02x 0x%08x\n", pins->nid, (unsigned int) actual_pin_config(pins)); + bufleft -= l; + s += l; + pins++; + entries--; + } + + if (model) { + int l = g_snprintf(s, bufleft, "\n[model]\n%s\n", model); + bufleft-=l; + s+=l; + } + + ok = g_file_set_contents(filename, destbuf, -1, err); + g_free(filename); + return ok; +} + + +static const gchar* remove_script = +"#!/bin/sh\n" +"rm /etc/modprobe.d/hda-jack-retask.conf 2>>%s\n" +"rm /lib/firmware/hda-jack-retask.fw 2>>%s\n"; + +static const gchar* retask_conf = +"# This file was added by the program 'hda-jack-retask'.\n" +"# If you want to revert the changes made by this program, you can simply erase this file and reboot your computer.\n" +"options snd-hda-intel patch=hda-jack-retask.fw,hda-jack-retask.fw,hda-jack-retask.fw,hda-jack-retask.fw\n"; + +static const gchar* install_script = +"#!/bin/sh\n" +"mv %s/hda-jack-retask.fw /lib/firmware/hda-jack-retask.fw\n 2>>%s\n" +"mv %s/hda-jack-retask.conf /etc/modprobe.d/hda-jack-retask.conf 2>>%s\n"; + +gboolean apply_changes_boot(pin_configs_t* pins, int entries, int card, int device, + const char* model, GError** err) +{ + gchar *s; + + if (!ensure_tempdir(err)) + return FALSE; + + if (!create_firmware_file(pins, entries, card, device, model, err)) + return FALSE; + + /* Create hda-jack-retask.conf */ + s = g_strdup_printf("%s/hda-jack-retask.conf", tempdir); + if (!g_file_set_contents(s, retask_conf, -1, err)) { + g_free(s); + return FALSE; + } + g_free(s); + + /* Create install script */ + s = g_strdup_printf(install_script, tempdir, errorfile, tempdir, errorfile); + if (!g_file_set_contents(scriptfile, s, -1, err)) { + g_free(s); + return FALSE; + } + g_free(s); + + /* Run script as root */ + if (!run_sudo_script(scriptfile, err)) + return FALSE; + return TRUE; +} + +gboolean reset_changes_boot(GError** err) +{ + gchar *s; + + if ((g_file_test("/etc/modprobe.d/hda-jack-retask.conf", G_FILE_TEST_EXISTS) == 0) && + (g_file_test("/lib/firmware/hda-jack-retask.fw", G_FILE_TEST_EXISTS) == 0)) + { + g_set_error(err, 0, 0, "No boot override is currently installed, nothing to remove."); + return FALSE; + } + + if (!ensure_tempdir(err)) + return FALSE; + s = g_strdup_printf(remove_script, errorfile, errorfile); + if (!g_file_set_contents(scriptfile, s, -1, err)) { + g_free(s); + return FALSE; + } + g_free(s); + + /* Run script as root */ + if (!run_sudo_script(scriptfile, err)) + return FALSE; + return TRUE; +} + diff --git a/hdajackretask/apply-changes.h b/hdajackretask/apply-changes.h new file mode 100644 index 0000000..2507a6a --- /dev/null +++ b/hdajackretask/apply-changes.h @@ -0,0 +1,15 @@ +#ifndef __APPLY_CHANGES_H__ +#define __APPLY_CHANGES_H__ + +#include "sysfs-pin-configs.h" +#include <glib.h> + +gboolean apply_changes_reconfig(pin_configs_t* pins, int entries, int card, int device, + const char* model, GError** err); + +gboolean apply_changes_boot(pin_configs_t* pins, int entries, int card, int device, + const char* model, GError** err); +gboolean reset_changes_boot(); + +#endif + diff --git a/hdajackretask/gitcompile b/hdajackretask/gitcompile new file mode 100755 index 0000000..acc8825 --- /dev/null +++ b/hdajackretask/gitcompile @@ -0,0 +1,3 @@ +#!/bin/sh + +make GITCOMPILE_ARGS="$*" diff --git a/hdajackretask/main-gtk.c b/hdajackretask/main-gtk.c new file mode 100644 index 0000000..65d0f3a --- /dev/null +++ b/hdajackretask/main-gtk.c @@ -0,0 +1,523 @@ +/* Copyright 2011 David Henningsson, Canonical Ltd. + License: GPLv2+ +*/ + +#include <string.h> +#include <stdlib.h> +#include <gtk/gtk.h> + +#include "sysfs-pin-configs.h" +#include "apply-changes.h" + +typedef struct pin_ui_data_t { + pin_configs_t* pin_config; + typical_pins_t pins_info[32]; + GtkWidget *frame, *override, *jacktype; + + GtkWidget* free_override_cb[FREE_OVERRIDES_COUNT]; +} pin_ui_data_t; + +typedef struct ui_data_t { + GList* pin_ui_data; + GtkWidget *main_window; + GtkWidget *content_scroll_widget; + GtkWidget *content_inner_box; + GtkWidget *codec_selection_combo; + + codec_name_t* current_codec; + int sysfs_pincount; + codec_name_t sysfs_codec_names[128]; + pin_configs_t sysfs_pins[32]; + gboolean free_overrides; + gboolean trust_codec; + gboolean trust_defcfg; + gboolean model_auto; +} ui_data_t; + +static void override_toggled(GtkWidget* sender, pin_ui_data_t* data) +{ + int i; + gboolean checked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sender)); + gtk_widget_set_sensitive(data->jacktype, checked); + for (i = 0; i < FREE_OVERRIDES_COUNT; i++) + gtk_widget_set_sensitive(data->free_override_cb[i], checked); +} + +static GtkWidget* create_pin_ui(ui_data_t* ui, pin_configs_t* pin_cfg) +{ + GtkWidget* result; + GtkContainer* box; + pin_ui_data_t* data; + + int port_conn = get_port_conn(pin_cfg->init_pin_config); + /* Do not show unconnected pins */ + if (ui->trust_defcfg && port_conn == 1) + return NULL; + + data = calloc(1, sizeof(pin_ui_data_t)); + data->pin_config = pin_cfg; + + { /* Frame */ + gchar* d = get_config_description(pin_cfg->init_pin_config); + gchar* c = g_strdup_printf("Pin ID: 0x%02x", pin_cfg->nid); + GtkWidget* label = gtk_label_new(c); + result = gtk_frame_new(d); + data->frame = result; + box = GTK_CONTAINER(gtk_box_new(GTK_ORIENTATION_VERTICAL, 2)); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_container_add(box, label); + g_free(d); + g_free(c); + } + + { /* Capabilities + gchar* s = get_caps_description(pin_cfg->pin_caps); + gchar* s2 = g_strdup_printf("Capabilities: %s", strlen(s) > 2 ? s+2 : ""); // Hack for initial comma + GtkWidget* label = gtk_label_new(s2); + gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); + gtk_container_add(box, label); + g_free(s); + g_free(s2); */ + } + + { /* Override */ + GtkWidget* override = data->override = gtk_check_button_new_with_label("Override"); + GtkWidget* jacktype = data->jacktype = gtk_combo_box_text_new(); + GtkWidget* jacktype_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2); + int index = get_typical_pins(data->pins_info, 32, pin_cfg, ui->trust_codec); + typical_pins_t* current = data->pins_info; + while (current->name) { + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(jacktype), current->name); + current++; + } + gtk_combo_box_set_active(GTK_COMBO_BOX(jacktype), index); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(override), pin_cfg->user_override); + g_signal_connect(override, "toggled", G_CALLBACK(override_toggled), data); + + gtk_container_add(box, override); + gtk_container_add(GTK_CONTAINER(jacktype_box), jacktype); + if (!ui->free_overrides) + gtk_container_add(box, jacktype_box); + } + + /* Advanced override */ + { + int i; + GtkGrid* grid = GTK_GRID(gtk_grid_new()); + gtk_grid_set_row_spacing(grid, 2); + gtk_grid_set_column_spacing(grid, 4); + + for (i = 0; i < FREE_OVERRIDES_COUNT; i++) { + int index = -1; + int j = 0; + unsigned long act_pincfg = actual_pin_config(pin_cfg); + unsigned long mask = get_free_override_mask(i); + free_override_t* values = get_free_override_list(i); + data->free_override_cb[i] = gtk_combo_box_text_new(); + if (!values) + continue; + while (values->name) { + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(data->free_override_cb[i]), values->name); + if ((act_pincfg & mask) == values->value) + index = j; + values++; + j++; + } + if (index >= 0) + gtk_combo_box_set_active(GTK_COMBO_BOX(data->free_override_cb[i]), index); + } + + gtk_grid_attach(grid, gtk_label_new("Connectivity"), 0, 0, 1, 1); + gtk_grid_attach(grid, data->free_override_cb[0], 0, 1, 1, 1); + gtk_grid_attach(grid, gtk_label_new("Location"), 1, 0, 1, 1); + gtk_grid_attach(grid, data->free_override_cb[1], 1, 1, 1, 1); + gtk_grid_attach(grid, gtk_label_new("Device"), 2, 0, 1, 1); + gtk_grid_attach(grid, data->free_override_cb[2], 2, 1, 1, 1); + gtk_grid_attach(grid, gtk_label_new("Jack"), 3, 0, 1, 1); + gtk_grid_attach(grid, data->free_override_cb[3], 3, 1, 1, 1); + + gtk_grid_attach(grid, gtk_label_new("Color"), 0, 2, 1, 1); + gtk_grid_attach(grid, data->free_override_cb[4], 0, 3, 1, 1); + gtk_grid_attach(grid, gtk_label_new("Jack detection"), 1, 2, 1, 1); + gtk_grid_attach(grid, data->free_override_cb[5], 1, 3, 1, 1); + gtk_grid_attach(grid, gtk_label_new("Channel group"), 2, 2, 1, 1); + gtk_grid_attach(grid, data->free_override_cb[6], 2, 3, 1, 1); + gtk_grid_attach(grid, gtk_label_new("Channel (in group)"), 3, 2, 1, 1); + gtk_grid_attach(grid, data->free_override_cb[7], 3, 3, 1, 1); + + if (ui->free_overrides) + gtk_container_add(box, GTK_WIDGET(grid)); + } + override_toggled(data->override, data); + + gtk_container_add(GTK_CONTAINER(result), GTK_WIDGET(box)); + ui->pin_ui_data = g_list_prepend(ui->pin_ui_data, data); + return result; +} + +static void free_pin_ui_data(pin_ui_data_t* data) +{ + if (!data) + return; + if (data->frame) + gtk_widget_destroy(data->frame); + free(data); +} + +static gint pin_config_find(pin_ui_data_t* pin_ui, pin_configs_t* cfg) +{ + return pin_ui->pin_config == cfg ? 0 : 1; +} + +static void update_user_pin_config(ui_data_t* ui) +{ + int i; + for (i = 0; i < ui->sysfs_pincount; i++) { + pin_ui_data_t* pin_ui; + GList *pos = g_list_find_custom(ui->pin_ui_data, &ui->sysfs_pins[i], (GCompareFunc) pin_config_find); + ui->sysfs_pins[i].user_override = FALSE; + if (!pos) + continue; + pin_ui = pos->data; + if (!gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(pin_ui->override))) + continue; + + if (ui->free_overrides) { + int i; + int index; + unsigned long val = 0; + for (i = 0; i < FREE_OVERRIDES_COUNT; i++) { + index = gtk_combo_box_get_active(GTK_COMBO_BOX(pin_ui->jacktype)); + if (index < 0) + break; + val += get_free_override_list(i)[index].value; + } + if (index < 0) + continue; + ui->sysfs_pins[i].user_pin_config = val; + } else { + int index; + index = gtk_combo_box_get_active(GTK_COMBO_BOX(pin_ui->jacktype)); + if (index < 0) + continue; + ui->sysfs_pins[i].user_pin_config = pin_ui->pins_info[index].pin_set; + } + ui->sysfs_pins[i].user_override = TRUE; + } + +} + +static gboolean validate_user_pin_config(ui_data_t* ui, GError** err) +{ + int i; + + if (!ui->current_codec) { + g_set_error(err, 0, 0, "You must first select a codec!"); + return FALSE; + } + update_user_pin_config(ui); + if (ui->free_overrides) + return TRUE; + + /* Check surround configs */ + for (i = 0; i < ui->sysfs_pincount; i++) { + unsigned long v = ui->sysfs_pins[i].user_pin_config; + if (!ui->sysfs_pins[i].user_override) + continue; + if ((v & 0xf0) != 0x10) + continue; + if (((v & 0xf) != 0) && !find_pin_channel_match(ui->sysfs_pins, ui->sysfs_pincount, v & 0xf0)) { + g_set_error(err, 0, 0, "This surround setup also requires a \"front\" channel override."); + return FALSE; + } + if (((v & 0xf) >= 3) && !find_pin_channel_match(ui->sysfs_pins, ui->sysfs_pincount, 2 + (v & 0xf0))) { + g_set_error(err, 0, 0, "This surround setup also requires a \"back\" channel override."); + return FALSE; + } + if ((v & 0xf) >= 3 && !find_pin_channel_match(ui->sysfs_pins, ui->sysfs_pincount, 1 + (v & 0xf0))) { + g_set_error(err, 0, 0, "This surround setup also requires a \"Center/LFE\" channel override."); + return FALSE; + } + } + return TRUE; +} + +static void show_action_result(ui_data_t* ui, GError* err, const gchar* ok_msg) +{ + GtkWidget* dialog; + const gchar* msg = err ? err->message : ok_msg; + dialog = gtk_message_dialog_new (GTK_WINDOW(ui->main_window), + GTK_DIALOG_DESTROY_WITH_PARENT, err ? GTK_MESSAGE_ERROR : GTK_MESSAGE_INFO, + GTK_BUTTONS_CLOSE, "%s", msg); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + if (err) + g_error_free(err); +} + +static void apply_now_clicked(GtkButton* button, gpointer user_data) +{ + GError* err = NULL; + ui_data_t* ui = user_data; + gboolean ok = validate_user_pin_config(ui, &err); + if (ok) + apply_changes_reconfig(ui->sysfs_pins, ui->sysfs_pincount, + ui->current_codec->card, ui->current_codec->device, + ui->model_auto ? "auto" : NULL, &err); + show_action_result(ui, err, + "Ok, now go ahead and test to see if it actually worked!\n" + "(Remember, this stuff is still experimental.)"); +} + +static void apply_boot_clicked(GtkButton* button, gpointer user_data) +{ + GError* err = NULL; + ui_data_t* ui = user_data; + gboolean ok = validate_user_pin_config(ui, &err); + if (ok) + apply_changes_boot(ui->sysfs_pins, ui->sysfs_pincount, + ui->current_codec->card, ui->current_codec->device, + ui->model_auto ? "auto" : NULL, &err); + show_action_result(ui, err, + "Ok, now reboot to test to see if it actually worked!\n" + "(Remember, this stuff is still experimental.)"); +} + + +static void reset_boot_clicked(GtkButton* button, gpointer user_data) +{ + GError* err = NULL; + ui_data_t* ui = user_data; + reset_changes_boot(&err); + show_action_result(ui, err, + "The previous installed files (if any) of this program have been removed.\n" + "Reboot to finish the uninstallation."); +} + +static void resize_main_window(ui_data_t* ui) +{ + GtkAllocation a; + GtkRequisition r; + gint oldw, oldh, neww, newh, maxw, maxh; + GdkScreen* screen = gtk_window_get_screen(GTK_WINDOW(ui->main_window)); + gtk_widget_size_request(GTK_WIDGET(ui->content_inner_box), &r); + gtk_widget_get_allocation(ui->content_scroll_widget, &a); +// fprintf(stderr, "W: %d, H: %d, W: %d, H: %d\n", a.width, a.height, r.width, r.height); + gtk_window_get_size(GTK_WINDOW(ui->main_window), &oldw, &oldh); + maxw = screen ? (gdk_screen_get_width(screen)*3)/4 : INT_MAX / 4; + maxh = screen ? (gdk_screen_get_height(screen)*3)/4 : INT_MAX / 4; +// fprintf(stderr, "Before: W: %d, H: %d\n", oldw, oldh); + neww = oldw; + newh = oldh; + if (a.width < r.width) { + neww += 8 + r.width - a.width; + if (neww > maxw) + neww = maxw; + } + if (a.height < r.height) { + newh += 8 + r.height - a.height; + if (newh > maxh) + newh = maxh; + } + if (neww != oldw || newh != oldh) { + gtk_window_resize(GTK_WINDOW(ui->main_window), neww, newh); +// fprintf(stderr, "After: W: %d, H: %d\n", neww, newh); + } +} + +static void update_codec_ui(ui_data_t* ui) +{ + int codec_index = gtk_combo_box_get_active(GTK_COMBO_BOX(ui->codec_selection_combo)); + int i; + + g_list_free_full(ui->pin_ui_data, (GDestroyNotify) free_pin_ui_data); + ui->pin_ui_data = NULL; + ui->current_codec = NULL; + + if (codec_index < 0) + return; + ui->current_codec = &ui->sysfs_codec_names[codec_index]; + ui->sysfs_pincount = get_pin_configs_list(ui->sysfs_pins, 32, ui->current_codec->card, ui->current_codec->device); + for (i = 0; i < ui->sysfs_pincount; i++) { + GtkWidget *w = create_pin_ui(ui, &ui->sysfs_pins[i]); + if (w) + gtk_container_add(GTK_CONTAINER(ui->content_inner_box), w); + } + + gtk_widget_show_all(GTK_WIDGET(ui->content_inner_box)); + resize_main_window(ui); +} + +static void codec_selected(GtkComboBox* combo, gpointer user_data) +{ + update_codec_ui(user_data); +} + +static void showallpins_toggled(GtkWidget* sender, ui_data_t* ui_data) +{ + gboolean checked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sender)); + ui_data->trust_defcfg = !checked; + update_codec_ui(ui_data); +} + +static void automodel_toggled(GtkWidget* sender, ui_data_t* ui_data) +{ + ui_data->model_auto = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sender)); +} + +static void free_override_toggled(GtkWidget* sender, ui_data_t* ui_data) +{ + update_user_pin_config(ui_data); + ui_data->free_overrides = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sender)); + update_codec_ui(ui_data); +} + +static const char* readme_text = +#include "README.generated.h" +; + +static void documentation_clicked(GtkWidget* sender, ui_data_t* ui) +{ + GtkDialog* dlg = GTK_DIALOG(gtk_dialog_new_with_buttons("Jack retasking documentation", + GTK_WINDOW(ui->main_window), GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, + GTK_STOCK_CLOSE, GTK_RESPONSE_NONE, NULL)); + GtkTextView* textview = GTK_TEXT_VIEW(gtk_text_view_new()); + GtkContainer* content_area = GTK_CONTAINER(gtk_dialog_get_content_area(dlg)); + GtkScrolledWindow* content_scroll = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(NULL, NULL)); + + gtk_text_buffer_set_text(gtk_text_view_get_buffer(textview), readme_text, -1); + gtk_text_view_set_editable(textview, FALSE); + gtk_text_view_set_wrap_mode(textview, GTK_WRAP_WORD); + gtk_text_view_set_cursor_visible(textview, FALSE); + gtk_scrolled_window_add_with_viewport(content_scroll, GTK_WIDGET(textview)); + gtk_container_add(content_area, GTK_WIDGET(content_scroll)); + gtk_box_set_child_packing(GTK_BOX(content_area), GTK_WIDGET(content_scroll), TRUE, TRUE, 2, GTK_PACK_START); + + gtk_widget_show_all(GTK_WIDGET(content_area)); + + { /* Resize to fit screen */ + GdkScreen* screen = gtk_window_get_screen(GTK_WINDOW(ui->main_window)); + int neww = screen ? (gdk_screen_get_width(screen)*3)/4 : 800; + int newh = screen ? (gdk_screen_get_height(screen)*3)/4 : 600; + + gtk_window_set_default_size(GTK_WINDOW(dlg), neww, newh); + } + + gtk_dialog_run(dlg); + gtk_widget_destroy(GTK_WIDGET(dlg)); +} + +static ui_data_t* create_ui() +{ + ui_data_t* ui = calloc(sizeof(ui_data_t), 1); + GtkContainer* toplevel_box = GTK_CONTAINER(gtk_box_new(GTK_ORIENTATION_VERTICAL, 2)); + GtkContainer* toplevel_2ndbox = GTK_CONTAINER(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2)); + ui->content_inner_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 2); + GtkContainer* rightside_box = GTK_CONTAINER(gtk_box_new(GTK_ORIENTATION_VERTICAL, 2)); + ui->main_window = gtk_window_new(GTK_WINDOW_TOPLEVEL); + gtk_window_set_title(GTK_WINDOW(ui->main_window), "Jack retasking for HDA Intel sound cards"); + g_signal_connect (ui->main_window, "destroy", G_CALLBACK (gtk_main_quit), NULL); + + ui->trust_codec = TRUE; + ui->trust_defcfg = TRUE; + + /* Select codec to work with */ + { + GtkWidget* combo = ui->codec_selection_combo = gtk_combo_box_text_new(); + GtkWidget* box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 2); + codec_name_t *n = ui->sysfs_codec_names; + get_codec_name_list(ui->sysfs_codec_names, 128); + while (n->card != -1) { + gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(combo), n->name); + n++; + } + /* Select the first codec */ + if (ui->sysfs_codec_names->card != -1) { + gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0); + g_signal_connect(combo, "changed", G_CALLBACK(codec_selected), ui); + gtk_container_add(GTK_CONTAINER(box), gtk_label_new("Select a codec:")); + gtk_container_add(GTK_CONTAINER(box), combo); + } + else { + gtk_container_add(GTK_CONTAINER(box), gtk_label_new("No codecs found. Sorry.")); + gtk_widget_destroy(combo); + } + + gtk_container_add(toplevel_box, box); + } + + /* Add pin content area */ + { + GtkWidget* frame = gtk_frame_new("Pin configuration"); + GtkScrolledWindow* content_scroll = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(NULL, NULL)); + ui->content_scroll_widget = GTK_WIDGET(content_scroll); + gtk_frame_set_label_align(GTK_FRAME(frame), 0.5, 0.5); + gtk_scrolled_window_add_with_viewport(content_scroll, ui->content_inner_box); + gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(content_scroll)); + gtk_container_add(toplevel_2ndbox, frame); + gtk_box_set_child_packing(GTK_BOX(toplevel_2ndbox), frame, TRUE, TRUE, 2, GTK_PACK_START); + } + + /* Create settings */ + { + GtkWidget* frame = gtk_frame_new("Options"); + GtkContainer* box = GTK_CONTAINER(gtk_button_box_new(GTK_ORIENTATION_VERTICAL)); + GtkWidget* check; + + check = gtk_check_button_new_with_label("Show unconnected pins"); + g_signal_connect(check, "toggled", G_CALLBACK(showallpins_toggled), ui); + gtk_container_add(box, check); + + check = gtk_check_button_new_with_label("Set model=auto"); + g_signal_connect(check, "toggled", G_CALLBACK(automodel_toggled), ui); + gtk_container_add(box, check); + + check = gtk_check_button_new_with_label("Advanced override"); + g_signal_connect(check, "toggled", G_CALLBACK(free_override_toggled), ui); + gtk_container_add(box, check); + + gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(box)); + gtk_container_add(rightside_box, frame); + } + + /* Create bottom right buttons */ + { + GtkWidget* button; + GtkWidget* box = gtk_button_box_new(GTK_ORIENTATION_VERTICAL); + button = gtk_button_new_with_label("Read documentation"); + g_signal_connect(button, "clicked", G_CALLBACK(documentation_clicked), ui); + gtk_container_add(GTK_CONTAINER(box), button); + button = gtk_button_new_with_label("Apply now"); + g_signal_connect(button, "clicked", G_CALLBACK(apply_now_clicked), ui); + gtk_container_add(GTK_CONTAINER(box), button); + button = gtk_button_new_with_label("Install boot override"); + g_signal_connect(button, "clicked", G_CALLBACK(apply_boot_clicked), ui); + gtk_container_add(GTK_CONTAINER(box), button); + button = gtk_button_new_with_label("Remove boot override"); + g_signal_connect(button, "clicked", G_CALLBACK(reset_boot_clicked), ui); + gtk_container_add(GTK_CONTAINER(box), button); + + gtk_container_add(rightside_box, box); + gtk_box_set_child_packing(GTK_BOX(rightside_box), box, FALSE, FALSE, 2, GTK_PACK_END); + } + + gtk_container_add(toplevel_2ndbox, GTK_WIDGET(rightside_box)); + + gtk_container_add(GTK_CONTAINER(toplevel_box), GTK_WIDGET(toplevel_2ndbox)); + gtk_box_set_child_packing(GTK_BOX(toplevel_box), GTK_WIDGET(toplevel_2ndbox), TRUE, TRUE, 2, GTK_PACK_END); + gtk_container_add(GTK_CONTAINER(ui->main_window), GTK_WIDGET(toplevel_box)); + + return ui; +} + +int main(int argc, char *argv[]) +{ + ui_data_t* ui; + gtk_init(&argc, &argv); + ui = create_ui(); + gtk_widget_show_all(ui->main_window); + if (ui->codec_selection_combo) + update_codec_ui(ui); + gtk_main(); + return 0; +} diff --git a/hdajackretask/sysfs-pin-configs.c b/hdajackretask/sysfs-pin-configs.c new file mode 100644 index 0000000..cd2897e --- /dev/null +++ b/hdajackretask/sysfs-pin-configs.c @@ -0,0 +1,626 @@ +/* Copyright 2011 David Henningsson, Canonical Ltd. + License: GPLv2+ +*/ + +#include <stdlib.h> +#include <string.h> +#include "sysfs-pin-configs.h" +#include "apply-changes.h" + +int get_codec_name_list(codec_name_t* names, int entries) +{ + GDir* sysdir = g_dir_open("/sys/class/sound", 0, NULL); + int count = 0; + if (!sysdir) + return 0; + + while (entries > 1) { + gchar** cd = NULL; + gboolean ok; + const gchar* dirname = g_dir_read_name(sysdir); + if (!dirname) + break; + cd = g_strsplit_set(dirname, "CD", 9); + ok = g_strv_length(cd) == 3; + if (ok) { + gchar* filetest = g_strdup_printf("/sys/class/sound/%s/init_pin_configs", dirname); + ok = g_file_test(filetest, G_FILE_TEST_IS_REGULAR); + g_free(filetest); + } + + if (ok) { + gchar* chip_name = NULL, *vendor_name = NULL; + gchar* chip_file = g_strdup_printf("/sys/class/sound/%s/chip_name", dirname); + gchar* vendor_file = g_strdup_printf("/sys/class/sound/%s/vendor_name", dirname); + ok = g_file_get_contents(chip_file, &chip_name, NULL, NULL); + ok &= g_file_get_contents(vendor_file, &vendor_name, NULL, NULL); + if (ok) { + names->name = g_strdup_printf("%s %s", g_strchomp(vendor_name), g_strchomp(chip_name)); + } + g_free(chip_name); + g_free(vendor_name); + g_free(chip_file); + g_free(vendor_file); + } + + if (ok) { + names->card = atoi(cd[1]); + names->device = atoi(cd[2]); + + names++; + count++; + entries--; + } + g_strfreev(cd); + } + if (entries) { + names->name = NULL; + names->card = -1; + } + g_dir_close(sysdir); + return count; +} + +static unsigned long read_header_value(gchar* contents, gchar* key) +{ + gchar* s = strstr(contents, key); + s += strlen(key); + return g_ascii_strtoull(s, NULL, 0); +} + +void get_codec_header(int card, int device, unsigned int* address, + unsigned int* codec_vendorid, unsigned int* codec_ssid) +{ + gchar* filename = g_strdup_printf("/proc/asound/card%d/codec#%d", card, device); + gchar* contents = NULL; + int ok = g_file_get_contents(filename, &contents, NULL, NULL); + g_free(filename); + if (!ok) + return; + *address = read_header_value(contents, "Address: "); + *codec_vendorid = read_header_value(contents, "Vendor Id: "); + *codec_ssid = read_header_value(contents, "Subsystem Id: "); + + g_free(contents); +} + +static void get_pin_caps(pin_configs_t* pins, int entries, int card, int device) +{ + gchar* filename = g_strdup_printf("/proc/asound/card%d/codec#%d", card, device); + gchar* contents = NULL; + int ok = g_file_get_contents(filename, &contents, NULL, NULL); + g_free(filename); + if (!ok) + return; + + for (; entries; entries--, pins++) { + gchar* nodestr = g_strdup_printf("Node 0x%02x [", pins->nid); + gchar* q = strstr(contents, nodestr); + g_free(nodestr); + if (!q) + continue; + + q = strstr(q, "wcaps 0x"); + if (!q) + continue; + q += strlen("wcaps "); + pins->wid_caps = g_ascii_strtoull(q, NULL, 0); + + q = strstr(q, "Pincap 0x"); + if (!q) + continue; + q += strlen("Pincap "); + pins->pin_caps = g_ascii_strtoull(q, NULL, 0); + } + g_free(contents); +} + + +static void read_pin_overrides(pin_configs_t* pins, int entries, int card, int device, gboolean is_user) +{ + gchar* filename = g_strdup_printf("/sys/class/sound/hwC%dD%d/%s_pin_configs", card, device, is_user ? "user" : "driver"); + gchar* contents = NULL; + gchar** lines = NULL, **line_iterator; + int count = 0; + int ok = g_file_get_contents(filename, &contents, NULL, NULL); + g_free(filename); + if (!ok) + return; + line_iterator = lines = g_strsplit(contents, "\n", entries); + while (count < entries && *line_iterator) { + gchar** line = g_strsplit(*line_iterator, " ", 0); + line_iterator++; + if (g_strv_length(line) == 2) { + int nid = g_ascii_strtoull(line[0], NULL, 0) & 0xff; + unsigned long config = g_ascii_strtoull(line[1], NULL, 0); + int i; + for (i=0; i < entries; i++) + if (nid == pins[i].nid) { + if (is_user) { + pins[i].user_override = FALSE; + pins[i].user_override = config != actual_pin_config(&pins[i]); + pins[i].user_pin_config = config; + } else { + pins[i].driver_override = config != pins[i].init_pin_config; + pins[i].driver_pin_config = config; + } + } + } + g_strfreev(line); + } + g_strfreev(lines); +} + +int get_pin_configs_list(pin_configs_t* pins, int entries, int card, int device) +{ + gchar* filename = g_strdup_printf("/sys/class/sound/hwC%dD%d/init_pin_configs", card, device); + gchar* contents = NULL; + gchar** lines = NULL, **line_iterator; + int count = 0; + int ok = g_file_get_contents(filename, &contents, NULL, NULL); + g_free(filename); + if (!ok) + return 0; + line_iterator = lines = g_strsplit(contents, "\n", entries); + g_free(contents); + while (count < entries && *line_iterator) { + gchar** line = g_strsplit(*line_iterator, " ", 0); + line_iterator++; + if (g_strv_length(line) == 2) { + pins[count].nid = g_ascii_strtoull(line[0], NULL, 0); + pins[count].init_pin_config = g_ascii_strtoull(line[1], NULL, 0) & 0xffffffff; + pins[count].driver_override = FALSE; + pins[count].user_override = FALSE; + count++; + } + g_strfreev(line); + } + g_strfreev(lines); + + read_pin_overrides(pins, count, card, device, FALSE); + read_pin_overrides(pins, count, card, device, TRUE); + get_pin_caps(pins, count, card, device); + + return count; +} + +unsigned long actual_pin_config(pin_configs_t* pins) +{ + if (pins->user_override) + return pins->user_pin_config; + if (pins->driver_override) + return pins->driver_pin_config; + return pins->init_pin_config; +} + +/*** Code below taken from sound/pci/hda/hda_proc.c and hda_codec.h, (C) Takashi Iwai, GPLv2+. ****/ + +#define u32 unsigned long + +#define AC_WCAP_STEREO (1<<0) /* stereo I/O */ +#define AC_WCAP_IN_AMP (1<<1) /* AMP-in present */ +#define AC_WCAP_OUT_AMP (1<<2) /* AMP-out present */ +#define AC_WCAP_AMP_OVRD (1<<3) /* AMP-parameter override */ +#define AC_WCAP_FORMAT_OVRD (1<<4) /* format override */ +#define AC_WCAP_STRIPE (1<<5) /* stripe */ +#define AC_WCAP_PROC_WID (1<<6) /* Proc Widget */ +#define AC_WCAP_UNSOL_CAP (1<<7) /* Unsol capable */ +#define AC_WCAP_CONN_LIST (1<<8) /* connection list */ +#define AC_WCAP_DIGITAL (1<<9) /* digital I/O */ +#define AC_WCAP_POWER (1<<10) /* power control */ +#define AC_WCAP_LR_SWAP (1<<11) /* L/R swap */ +#define AC_WCAP_CP_CAPS (1<<12) /* content protection */ +#define AC_WCAP_CHAN_CNT_EXT (7<<13) /* channel count ext */ +#define AC_WCAP_DELAY (0xf<<16) +#define AC_WCAP_DELAY_SHIFT 16 +#define AC_WCAP_TYPE (0xf<<20) +#define AC_WCAP_TYPE_SHIFT 20 + +#define AC_PINCAP_IMP_SENSE (1<<0) /* impedance sense capable */ +#define AC_PINCAP_TRIG_REQ (1<<1) /* trigger required */ +#define AC_PINCAP_PRES_DETECT (1<<2) /* presence detect capable */ +#define AC_PINCAP_HP_DRV (1<<3) /* headphone drive capable */ +#define AC_PINCAP_OUT (1<<4) /* output capable */ +#define AC_PINCAP_IN (1<<5) /* input capable */ +#define AC_PINCAP_BALANCE (1<<6) /* balanced I/O capable */ +/* Note: This LR_SWAP pincap is defined in the Realtek ALC883 specification, + * but is marked reserved in the Intel HDA specification. + */ +#define AC_PINCAP_LR_SWAP (1<<7) /* L/R swap */ +/* Note: The same bit as LR_SWAP is newly defined as HDMI capability + * in HD-audio specification + */ +#define AC_PINCAP_HDMI (1<<7) /* HDMI pin */ +#define AC_PINCAP_DP (1<<24) /* DisplayPort pin, can + * coexist with AC_PINCAP_HDMI + */ +#define AC_PINCAP_VREF (0x37<<8) +#define AC_PINCAP_VREF_SHIFT 8 +#define AC_PINCAP_EAPD (1<<16) /* EAPD capable */ +#define AC_PINCAP_HBR (1<<27) /* High Bit Rate */ +/* Vref status (used in pin cap) */ +#define AC_PINCAP_VREF_HIZ (1<<0) /* Hi-Z */ +#define AC_PINCAP_VREF_50 (1<<1) /* 50% */ +#define AC_PINCAP_VREF_GRD (1<<2) /* ground */ +#define AC_PINCAP_VREF_80 (1<<4) /* 80% */ +#define AC_PINCAP_VREF_100 (1<<5) /* 100% */ + + +/* configuration default - 32bit */ +#define AC_DEFCFG_SEQUENCE (0xf<<0) +#define AC_DEFCFG_DEF_ASSOC (0xf<<4) +#define AC_DEFCFG_ASSOC_SHIFT 4 +#define AC_DEFCFG_MISC (0xf<<8) +#define AC_DEFCFG_MISC_SHIFT 8 +#define AC_DEFCFG_MISC_NO_PRESENCE (1<<0) +#define AC_DEFCFG_COLOR (0xf<<12) +#define AC_DEFCFG_COLOR_SHIFT 12 +#define AC_DEFCFG_CONN_TYPE (0xf<<16) +#define AC_DEFCFG_CONN_TYPE_SHIFT 16 +#define AC_DEFCFG_DEVICE (0xf<<20) +#define AC_DEFCFG_DEVICE_SHIFT 20 +#define AC_DEFCFG_LOCATION (0x3f<<24) +#define AC_DEFCFG_LOCATION_SHIFT 24 +#define AC_DEFCFG_PORT_CONN (0x3<<30) +#define AC_DEFCFG_PORT_CONN_SHIFT 30 + +static const char *get_jack_color(u32 cfg) +{ + static char *names[16] = { + "", "Black", "Grey", "Blue", + "Green", "Red", "Orange", "Yellow", + "Purple", "Pink", NULL, NULL, + NULL, NULL, "White", "Other", + }; + cfg = (cfg & AC_DEFCFG_COLOR) >> AC_DEFCFG_COLOR_SHIFT; + if (names[cfg]) + return names[cfg]; + else + return "UNKNOWN"; +} + +static const char *get_jack_type(u32 cfg) +{ + static char *jack_types[16] = { + "Line Out", "Speaker", "Headphone", "CD", + "SPDIF Out", "Digital Out", "Modem Line", "Modem Hand", + "Line In", "Aux", "Mic", "Telephony", + "SPDIF In", "Digital In", "Reserved", "Other" + }; + + return jack_types[(cfg & AC_DEFCFG_DEVICE) + >> AC_DEFCFG_DEVICE_SHIFT]; +} + +static const char *get_jack_location(u32 cfg) +{ + static char *bases[7] = { + "", ", Rear side", ", Front side", ", Left side", ", Right side", ", Top", ", Bottom", + }; + static unsigned char specials_idx[] = { + 0x07, 0x08, + 0x17, 0x18, 0x19, + 0x37, 0x38 + }; + static char *specials[] = { + ", Rear Panel", ", Drive Bar", + ", Riser", ", HDMI", ", ATAPI", + ", Mobile-In", ", Mobile-Out" + }; + int i; + cfg = (cfg & AC_DEFCFG_LOCATION) >> AC_DEFCFG_LOCATION_SHIFT; + if ((cfg & 0x0f) < 7) + return bases[cfg & 0x0f]; + for (i = 0; i < 7; i++) { + if (cfg == specials_idx[i]) + return specials[i]; + } + return "UNKNOWN"; +} + + +/**** Borrowed code end *****/ + +static free_override_t pc_arr[] = { + {"Not connected", 1 << AC_DEFCFG_PORT_CONN_SHIFT}, + {"Jack", 0}, + {"Internal", 2 << AC_DEFCFG_PORT_CONN_SHIFT}, + {"Both", 3 << AC_DEFCFG_PORT_CONN_SHIFT}, + {} +}; + +static free_override_t location_arr[] = { + {"External", 0}, + {"Rear", 1 << AC_DEFCFG_LOCATION_SHIFT}, + {"Front", 2 << AC_DEFCFG_LOCATION_SHIFT}, + {"Left", 3 << AC_DEFCFG_LOCATION_SHIFT}, + {"Right", 4 << AC_DEFCFG_LOCATION_SHIFT}, + {"Top", 5 << AC_DEFCFG_LOCATION_SHIFT}, + {"Bottom", 6 << AC_DEFCFG_LOCATION_SHIFT}, + {"Rear panel", 7 << AC_DEFCFG_LOCATION_SHIFT}, + {"Drive bay", 8 << AC_DEFCFG_LOCATION_SHIFT}, + + {"Internal", 16 << AC_DEFCFG_LOCATION_SHIFT}, + {"Internal riser", (16+7) << AC_DEFCFG_LOCATION_SHIFT}, + {"Internal display", (16+8) << AC_DEFCFG_LOCATION_SHIFT}, + {"Internal ATAPI", (16+9) << AC_DEFCFG_LOCATION_SHIFT}, + + {"Dock", 32 << AC_DEFCFG_LOCATION_SHIFT}, + {"Dock Rear", 33 << AC_DEFCFG_LOCATION_SHIFT}, + {"Dock Front", 34 << AC_DEFCFG_LOCATION_SHIFT}, + {"Dock Left", 35 << AC_DEFCFG_LOCATION_SHIFT}, + {"Dock Right", 36 << AC_DEFCFG_LOCATION_SHIFT}, + {"Dock Top", 37 << AC_DEFCFG_LOCATION_SHIFT}, + {"Dock Bottom", 38 << AC_DEFCFG_LOCATION_SHIFT}, + + {"Other", 48 << AC_DEFCFG_LOCATION_SHIFT}, + {"Other bottom", (48+7) << AC_DEFCFG_LOCATION_SHIFT}, + {"Inside mobile lid", (48+8) << AC_DEFCFG_LOCATION_SHIFT}, + {"Outside mobile lid", (48+9) << AC_DEFCFG_LOCATION_SHIFT}, + + {} +}; + +static free_override_t device_arr[] = { + {"Line Out", 0}, + {"Speaker", 1 << AC_DEFCFG_DEVICE_SHIFT}, + {"Headphone", 2 << AC_DEFCFG_DEVICE_SHIFT}, + {"CD", 3 << AC_DEFCFG_DEVICE_SHIFT}, + {"SPDIF Out", 4 << AC_DEFCFG_DEVICE_SHIFT}, + {"Digital Out", 5 << AC_DEFCFG_DEVICE_SHIFT}, + {"Modem (Line side)", 6 << AC_DEFCFG_DEVICE_SHIFT}, + {"Modem (Handset side)", 7 << AC_DEFCFG_DEVICE_SHIFT}, + {"Line In", 8 << AC_DEFCFG_DEVICE_SHIFT}, + {"Aux", 9 << AC_DEFCFG_DEVICE_SHIFT}, + {"Microphone", 10 << AC_DEFCFG_DEVICE_SHIFT}, + {"Telephony", 11 << AC_DEFCFG_DEVICE_SHIFT}, + {"SPDIF In", 12 << AC_DEFCFG_DEVICE_SHIFT}, + {"Other Digital In", 13 << AC_DEFCFG_DEVICE_SHIFT}, + {"Other", 15 << AC_DEFCFG_DEVICE_SHIFT}, + {} +}; + +static free_override_t jack_arr[] = { + {"Unknown", 0}, + {"3.5 mm", 1 << AC_DEFCFG_CONN_TYPE_SHIFT}, + {"6.3 mm", 2 << AC_DEFCFG_CONN_TYPE_SHIFT}, + {"ATAPI", 3 << AC_DEFCFG_CONN_TYPE_SHIFT}, + {"RCA", 4 << AC_DEFCFG_CONN_TYPE_SHIFT}, + {"Optical", 5 << AC_DEFCFG_CONN_TYPE_SHIFT}, + {"Other Digital", 6 << AC_DEFCFG_CONN_TYPE_SHIFT}, + {"Other Analog", 7 << AC_DEFCFG_CONN_TYPE_SHIFT}, + {"DIN", 8 << AC_DEFCFG_CONN_TYPE_SHIFT}, + {"XLR", 9 << AC_DEFCFG_CONN_TYPE_SHIFT}, + {"RJ-11 (Modem)", 10 << AC_DEFCFG_CONN_TYPE_SHIFT}, + {"Combination", 11 << AC_DEFCFG_CONN_TYPE_SHIFT}, + {"Other", 15 << AC_DEFCFG_CONN_TYPE_SHIFT}, + {} +}; + +static free_override_t color_arr[] = { + {"Unknown", 0}, + {"Black", 1 << AC_DEFCFG_COLOR_SHIFT}, + {"Grey", 2 << AC_DEFCFG_COLOR_SHIFT}, + {"Blue", 3 << AC_DEFCFG_COLOR_SHIFT}, + {"Green", 4 << AC_DEFCFG_COLOR_SHIFT}, + {"Red", 5 << AC_DEFCFG_COLOR_SHIFT}, + {"Orange", 6 << AC_DEFCFG_COLOR_SHIFT}, + {"Yellow", 7 << AC_DEFCFG_COLOR_SHIFT}, + {"Purple", 8 << AC_DEFCFG_COLOR_SHIFT}, + {"Pink", 9 << AC_DEFCFG_COLOR_SHIFT}, + {"White", 14 << AC_DEFCFG_COLOR_SHIFT}, + {"Other", 15 << AC_DEFCFG_COLOR_SHIFT}, + {} +}; + +static free_override_t no_presence_arr[] = { + {"Present", 0}, + {"Not present", 1 << AC_DEFCFG_MISC_SHIFT}, + {} +}; + +static free_override_t group_nr_arr[] = { + {"1", 1 << AC_DEFCFG_ASSOC_SHIFT}, + {"2", 2 << AC_DEFCFG_ASSOC_SHIFT}, + {"3", 3 << AC_DEFCFG_ASSOC_SHIFT}, + {"4", 4 << AC_DEFCFG_ASSOC_SHIFT}, + {"5", 5 << AC_DEFCFG_ASSOC_SHIFT}, + {"6", 6 << AC_DEFCFG_ASSOC_SHIFT}, + {"7", 7 << AC_DEFCFG_ASSOC_SHIFT}, + {"8", 8 << AC_DEFCFG_ASSOC_SHIFT}, + {"9", 9 << AC_DEFCFG_ASSOC_SHIFT}, + {"10", 10 << AC_DEFCFG_ASSOC_SHIFT}, + {"11", 11 << AC_DEFCFG_ASSOC_SHIFT}, + {"12", 12 << AC_DEFCFG_ASSOC_SHIFT}, + {"13", 13 << AC_DEFCFG_ASSOC_SHIFT}, + {"14", 14 << AC_DEFCFG_ASSOC_SHIFT}, + {"15", 15 << AC_DEFCFG_ASSOC_SHIFT}, + {} +}; + +static free_override_t channel_arr[] = { + {"Front", 0}, + {"Center/LFE", 1}, + {"Back", 2}, + {"Side", 3}, + {"Channel 8 & 9", 4}, + {"Channel 10 & 11", 5}, + {"Channel 12 & 13", 6}, + {"Channel 14 & 15", 7}, + {"Channel 16 & 17", 8}, + {"Channel 18 & 19", 9}, + {"Channel 20 & 21", 10}, + {"Channel 22 & 23", 11}, + {"Channel 24 & 25", 12}, + {"Channel 26 & 27", 13}, + {"Channel 28 & 29", 14}, + {"Channel 30 & 31", 15}, + {} +}; + + +static free_override_t* type_order[FREE_OVERRIDES_COUNT] = { + pc_arr, location_arr, device_arr, jack_arr, + color_arr, no_presence_arr, group_nr_arr, channel_arr +}; + +unsigned long get_free_override_mask(int type) +{ + static unsigned long masks[FREE_OVERRIDES_COUNT] = { + AC_DEFCFG_PORT_CONN, + AC_DEFCFG_LOCATION, + AC_DEFCFG_DEVICE, + AC_DEFCFG_CONN_TYPE, + AC_DEFCFG_COLOR, + AC_DEFCFG_MISC & 1, + AC_DEFCFG_DEF_ASSOC, + AC_DEFCFG_SEQUENCE, + }; + + return masks[type]; +} + +free_override_t* get_free_override_list(int type) +{ + return type_order[type]; +} + + +int get_port_conn(unsigned long config) +{ + return (config & AC_DEFCFG_PORT_CONN) >> AC_DEFCFG_PORT_CONN_SHIFT; +} + +gchar* get_config_description(unsigned long config) +{ + int port_conn = get_port_conn(config); + if (port_conn == 1) + return g_strdup("Not connected"); + return g_strdup_printf("%s %s%s%s", port_conn == 2 ? "Internal" : get_jack_color(config), + get_jack_type(config), + ((config >> (AC_DEFCFG_LOCATION_SHIFT+4)) & 3) == 2 ? ", Docking station" : "", + get_jack_location(config)); +} + +/* +gchar* get_caps_description(unsigned long pin_caps) +{ + int vref = (pin_caps & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; + gboolean linein = (pin_caps & AC_PINCAP_IN) && (vref & AC_PINCAP_VREF_HIZ); + gboolean lineout = pin_caps & AC_PINCAP_OUT; + gboolean hp = pin_caps & AC_PINCAP_HP_DRV; + gboolean mic = (pin_caps & AC_PINCAP_IN) && (vref & AC_PINCAP_VREF_50 || vref & AC_PINCAP_VREF_80); + return g_strjoin("", + lineout ? ", Line out": "", + hp ? ", Headphone": "", + linein ? ", Line in": "", + mic ? ", Microphone": "", NULL); + +} +*/ + +static gboolean extmic_caps(unsigned long pin_caps, unsigned long wid_caps) +{ + int vref = (pin_caps & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; + return (pin_caps & AC_PINCAP_IN) && (vref & AC_PINCAP_VREF_50 || vref & AC_PINCAP_VREF_80); +} + +static gboolean headphone_caps(unsigned long pin_caps, unsigned long wid_caps) +{ + return pin_caps & AC_PINCAP_HP_DRV; +} + +static gboolean lineout_caps(unsigned long pin_caps, unsigned long wid_caps) +{ + return (pin_caps & AC_PINCAP_OUT) && !(wid_caps & AC_WCAP_DIGITAL); +} + +static gboolean spdifout_caps(unsigned long pin_caps, unsigned long wid_caps) +{ + return (pin_caps & AC_PINCAP_OUT) && (wid_caps & AC_WCAP_DIGITAL) && !(pin_caps & AC_PINCAP_HDMI); +} + +static gboolean hdmi_caps(unsigned long pin_caps, unsigned long wid_caps) +{ + return pin_caps & AC_PINCAP_HDMI; +} + +static gboolean intmic_caps(unsigned long pin_caps, unsigned long wid_caps) +{ + return (pin_caps & AC_PINCAP_IN) && !(wid_caps & AC_WCAP_DIGITAL); +} + +static gboolean spdifin_caps(unsigned long pin_caps, unsigned long wid_caps) +{ + return (pin_caps & AC_PINCAP_IN) && (wid_caps & AC_WCAP_DIGITAL); +} + +static gboolean linein_caps(unsigned long pin_caps, unsigned long wid_caps) +{ + int vref = (pin_caps & AC_PINCAP_VREF) >> AC_PINCAP_VREF_SHIFT; + return (pin_caps & AC_PINCAP_IN) && (vref & AC_PINCAP_VREF_HIZ); +} + +static gboolean disabled_caps(unsigned long pin_caps, unsigned long wid_caps) +{ + return TRUE; +} + +static typical_pins_t simple_typical_pins[] = { + {"Headphone", headphone_caps, 0x0321403f, AC_DEFCFG_PORT_CONN | AC_DEFCFG_DEVICE, }, + {"Microphone", extmic_caps, 0x03a19020, AC_DEFCFG_PORT_CONN | AC_DEFCFG_DEVICE,}, + {"Line out (Front)", lineout_caps, 0x01014410, AC_DEFCFG_PORT_CONN | AC_DEFCFG_DEVICE | AC_DEFCFG_SEQUENCE,}, + {"Line out (Center/LFE)", lineout_caps, 0x01014411, AC_DEFCFG_PORT_CONN | AC_DEFCFG_DEVICE | AC_DEFCFG_SEQUENCE,}, + {"Line out (Back)", lineout_caps, 0x01014412, AC_DEFCFG_PORT_CONN | AC_DEFCFG_DEVICE | AC_DEFCFG_SEQUENCE,}, + {"Line out (Side)", lineout_caps, 0x01014413, AC_DEFCFG_PORT_CONN | AC_DEFCFG_DEVICE | AC_DEFCFG_SEQUENCE,}, + {"Line in", linein_caps, 0x0181344f, AC_DEFCFG_PORT_CONN | AC_DEFCFG_DEVICE,}, + {"Internal speaker", lineout_caps, 0x90170150, AC_DEFCFG_PORT_CONN | AC_DEFCFG_DEVICE | AC_DEFCFG_SEQUENCE, }, + {"Internal speaker (LFE)", lineout_caps, 0x90170151, AC_DEFCFG_PORT_CONN | AC_DEFCFG_DEVICE | AC_DEFCFG_SEQUENCE, }, + {"Internal speaker (Back)", lineout_caps, 0x90170152, AC_DEFCFG_PORT_CONN | AC_DEFCFG_DEVICE | AC_DEFCFG_SEQUENCE, }, + {"Internal mic", intmic_caps, 0x90a60160, AC_DEFCFG_PORT_CONN | AC_DEFCFG_DEVICE,}, + {"HDMI", hdmi_caps, 0x18560070, AC_DEFCFG_PORT_CONN | AC_DEFCFG_LOCATION,}, + {"SPDIF out", spdifout_caps, 0x014b1180, AC_DEFCFG_PORT_CONN | AC_DEFCFG_DEVICE,}, + {"SPDIF in", spdifin_caps, 0x01cb6190, AC_DEFCFG_PORT_CONN | AC_DEFCFG_DEVICE,}, + {"Not connected", disabled_caps, 0x40f000f0, AC_DEFCFG_PORT_CONN,}, + {} +}; + + + +int get_typical_pins(typical_pins_t* result, int entries, pin_configs_t* pin_cfg, int caps_limit) +{ + int count = 0; + int index = -1; + unsigned long actual = actual_pin_config(pin_cfg); + typical_pins_t* src; + for (src = simple_typical_pins; src->name && entries; src++) { + if (caps_limit && !src->caps_limit(pin_cfg->pin_caps, pin_cfg->wid_caps)) + continue; + if ((actual & src->match_mask) == (src->pin_set & src->match_mask)) + index = count; + *result = *src; + result++; + count++; + entries--; + } + if (entries) { + result->name = NULL; + } + return index; +} + +gboolean find_pin_channel_match(pin_configs_t* pins, int count, unsigned long pinval) +{ + int i; + pinval &= (AC_DEFCFG_DEF_ASSOC + AC_DEFCFG_SEQUENCE); + for (i = 0; i < count; i++, pins++) { + unsigned long val2 = actual_pin_config(pins); + if (get_port_conn(val2) == 1) + continue; + if (pinval == (val2 & (AC_DEFCFG_DEF_ASSOC + AC_DEFCFG_SEQUENCE))) + return TRUE; + } + return FALSE; +} + + diff --git a/hdajackretask/sysfs-pin-configs.h b/hdajackretask/sysfs-pin-configs.h new file mode 100644 index 0000000..9a0a902 --- /dev/null +++ b/hdajackretask/sysfs-pin-configs.h @@ -0,0 +1,63 @@ +#ifndef __SYSFS_PIN_CONFIGS_H__ +#define __SYSFS_PIN_CONFIGS_H__ + +#include <glib.h> + +typedef struct codec_name_t { + int card; + int device; + gchar* name; +} codec_name_t; + +typedef struct pin_configs_t { + int nid; + unsigned long init_pin_config; + unsigned long driver_pin_config; + unsigned long user_pin_config; + unsigned long pin_caps; + unsigned long wid_caps; + gboolean user_override; + gboolean driver_override; +} pin_configs_t; + +typedef gboolean(*caps_limit_func_t)(unsigned long, unsigned long); + +typedef struct typical_pins_t { + char* name; + caps_limit_func_t caps_limit; + unsigned long pin_set; + unsigned long match_mask; + unsigned long pin_set_mask; +} typical_pins_t; + +typedef struct free_override_t { + const char* name; + unsigned long value; +} free_override_t; + +#define FREE_OVERRIDES_COUNT 8 + +free_override_t* get_free_override_list(int type); +unsigned long get_free_override_mask(int type); + +int get_codec_name_list(codec_name_t* names, int entries); + +int get_pin_configs_list(pin_configs_t* pins, int entries, int card, int device); + +gchar* get_config_description(unsigned long config); + +gchar* get_caps_description(unsigned long pin_caps); + +/* 0 = Jack, 1 = N/A, 2 = Internal, 3 = Both (?!) */ +int get_port_conn(unsigned long config); + +int get_typical_pins(typical_pins_t* result, int entries, pin_configs_t* pin_cfg, int caps_limit); + +unsigned long actual_pin_config(pin_configs_t* pins); + +void get_codec_header(int card, int device, unsigned int* address, + unsigned int* codec_vendorid, unsigned int* codec_ssid); + +gboolean find_pin_channel_match(pin_configs_t* pins, int count, unsigned long pinval); + +#endif -- 1.7.9.2
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor