]> git.alsa-project.org Git - alsa-tools.git/commitdiff
hdajackretask: Add "hints" functionality
authorDavid Henningsson <david.henningsson@canonical.com>
Fri, 1 Aug 2014 14:20:30 +0000 (16:20 +0200)
committerTakashi Iwai <tiwai@suse.de>
Mon, 4 Aug 2014 09:07:01 +0000 (11:07 +0200)
The kernel driver has some hints you can send to it that changes
parser behaviour. This patch exposes that functionality to the user.

(This patch also includes minor fixes for documentation, GTK warnings
and whitespace.)

Signed-off-by: David Henningsson <david.henningsson@canonical.com>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
hdajackretask/Makefile.am
hdajackretask/README
hdajackretask/apply-changes.c
hdajackretask/apply-changes.h
hdajackretask/main-gtk.c
hdajackretask/sysfs-pin-configs.c
hdajackretask/sysfs-pin-configs.h

index 3479b839b1ba1d1fc922b29ec6a11d8e06f37d69..e31315913764bea3858d9ce73faa98f50bbab186 100644 (file)
@@ -1,5 +1,5 @@
 EXTRA_DIST = gitcompile README
-AM_CFLAGS = @GTK3_CFLAGS@
+AM_CFLAGS = @GTK3_CFLAGS@ -Wno-deprecated-declarations
 bin_PROGRAMS = hdajackretask
 man_MANS =
 hdajackretask_SOURCES = main-gtk.c sysfs-pin-configs.c apply-changes.c 
index 68f62b166d2a8411f027f89ed9913d8e0ee58996..209e3322e555983e9bc1e9d4d776bebc93f769e6 100644 (file)
@@ -1,12 +1,12 @@
-Documentation for hda-jack-retask
-=================================
+Documentation for hdajackretask
+===============================
 
 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.
+Start the application "hdajackretask" 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.
@@ -44,9 +44,11 @@ Options
 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.
+Some codecs, especially older ones and on kernels 3.8 and below, 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 )
+http://www.intel.com/content/dam/www/public/us/en/documents/product-specifications/high-definition-audio-specification.pdf )
 
+ * Parser hints
+This enables you to send special "hints" to the driver that causes parsing to behave differently. Leave them at the "default" setting unless you have read the driver documentation. ( Which, at the time of this writing, is available here: https://www.kernel.org/doc/Documentation/sound/alsa/HD-Audio.txt - see the "Hint strings" section. )
index 840f649c66759181dbdaefb512eae150aaf2dae5..aa291ce2d86eb3f86146d9a415dbd4a0196c3aba 100644 (file)
@@ -15,6 +15,11 @@ static gchar* tempdir = NULL;
 static gchar* scriptfile = NULL;
 static gchar* errorfile = NULL;
 
+static GQuark quark()
+{
+    return g_quark_from_static_string("hda-jack-retask-error");
+}
+
 static gboolean ensure_tempdir(GError** err)
 {
     if (!tempdir) {
@@ -29,10 +34,10 @@ static gboolean ensure_tempdir(GError** err)
 }
 
 static gboolean create_reconfig_script(pin_configs_t* pins, int entries, int card, int device, 
-    const char* model, GError** err)
+    const char* model, const char* hints, GError** err)
 {
     gchar* hwdir = g_strdup_printf("/sys/class/sound/hwC%dD%d", card, device);
-    gchar destbuf[120*40] = "#!/bin/sh\n";
+    gchar destbuf[150*40] = "#!/bin/sh\n";
     int bufleft = sizeof(destbuf) - strlen(destbuf);
     gboolean ok = FALSE;
     gchar* s = destbuf + strlen(destbuf);
@@ -44,9 +49,16 @@ static gboolean create_reconfig_script(pin_configs_t* pins, int entries, int car
         int l = g_snprintf(s, bufleft, "echo \"%s\" | tee %s/modelname 2>>%s\n",
             model, hwdir, errorfile);
         bufleft-=l;
-        s+=l;           
+        s+=l;
     }
-    
+
+    if (hints) {
+        int l = g_snprintf(s, bufleft, "echo \"%s\" | tee %s/hints 2>>%s\n",
+            hints, 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);
@@ -57,7 +69,7 @@ static gboolean create_reconfig_script(pin_configs_t* pins, int entries, int car
     }    
 
     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__);
+        g_set_error(err, quark(), 0, "Bug in %s:%d!", __FILE__, __LINE__);
         goto cleanup;
     }
 
@@ -85,7 +97,7 @@ gboolean run_sudo_script(const gchar* script_name, GError** err)
     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);
+        g_set_error(err, quark(), 0, "%s", errfilecontents);
         ok = FALSE;
     }
     else ok = TRUE;
@@ -130,7 +142,7 @@ static gboolean kill_pulseaudio(gboolean* was_killed, int card, GError** err)
 
     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"
+        g_set_error(err, quark(), 0, "Cannot block PulseAudio from respawning:\n"
             "Please either remove '%s' or kill PulseAudio manually.", clientconf);
         goto cleanup;
     }
@@ -153,7 +165,7 @@ 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_set_error(err, quark(), 0, "%s", g_strerror(errno));
         g_free(clientconf);
         return FALSE;
     }
@@ -162,7 +174,7 @@ static gboolean restore_pulseaudio(gboolean was_killed, GError** err)
 }
 
 gboolean apply_changes_reconfig(pin_configs_t* pins, int entries, int card, int device, 
-    const char* model, GError** err)
+    const char* model, const char* hints, GError** err)
 {
     gboolean result = FALSE;
 //    gchar* script_name = NULL;
@@ -172,7 +184,7 @@ gboolean apply_changes_reconfig(pin_configs_t* pins, int entries, int card, int
     if (!kill_pulseaudio(&pa_killed, card, err))
         goto cleanup;
     /* Create script */
-    if (!create_reconfig_script(pins, entries, card, device, model, err))
+    if (!create_reconfig_script(pins, entries, card, device, model, hints, err))
         goto cleanup;
     /* Run script as root */
     if (!run_sudo_script(scriptfile, err))
@@ -187,10 +199,10 @@ cleanup:
 }
 
 static gboolean create_firmware_file(pin_configs_t* pins, int entries, int card, int device, 
-    const char* model, GError** err)
+    const char* model, const char* hints, GError** err)
 {
     gboolean ok;
-    gchar destbuf[40*40] = "";
+    gchar destbuf[40*40+40*24] = "";
     gchar* s = destbuf;
     gchar* filename = g_strdup_printf("%s/hda-jack-retask.fw", tempdir);
     unsigned int address, codec_vendorid, codec_ssid;
@@ -216,6 +228,12 @@ static gboolean create_firmware_file(pin_configs_t* pins, int entries, int card,
         s+=l;
     }
 
+    if (hints) {
+        int l = g_snprintf(s, bufleft, "\n[hints]\n%s\n", hints);
+        bufleft-=l;
+        s+=l;
+    }
+
     ok = g_file_set_contents(filename, destbuf, -1, err);
     g_free(filename);
     return ok;
@@ -238,14 +256,14 @@ static const gchar* install_script =
 "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)
+    const char* model, const char* hints, GError** err)
 {
     gchar *s;
 
     if (!ensure_tempdir(err))
         return FALSE;
 
-    if (!create_firmware_file(pins, entries, card, device, model, err))
+    if (!create_firmware_file(pins, entries, card, device, model, hints, err))
         return FALSE;
 
     /* Create hda-jack-retask.conf */
@@ -277,7 +295,7 @@ gboolean reset_changes_boot(GError** err)
     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.");
+        g_set_error(err, quark(), 0, "No boot override is currently installed, nothing to remove.");
         return FALSE;
     }
 
index 2507a6ad1500476f72629326a92f09ea8ad10da9..e08d66d3c7cd431d0792b6eca98f7e7c15422206 100644 (file)
@@ -5,10 +5,10 @@
 #include <glib.h>
 
 gboolean apply_changes_reconfig(pin_configs_t* pins, int entries, int card, int device, 
-    const char* model, GError** err);
+    const char* model, const char* hints, GError** err);
 
 gboolean apply_changes_boot(pin_configs_t* pins, int entries, int card, int device, 
-    const char* model, GError** err);
+    const char* model, const char* hints, GError** err);
 gboolean reset_changes_boot();
 
 #endif
index 9101af4d7c2788e44d0c086d39e84a60b0eb7ec2..f5ff6e4de694568d989eb446be67c590e1295fdd 100644 (file)
@@ -20,6 +20,13 @@ typedef struct pin_ui_data_t {
     ui_data_t* owner;
 } pin_ui_data_t;
 
+typedef struct hints_ui_data_t {
+    gboolean visible;
+    GtkWidget *frame;
+    GtkListStore *store;
+    gchar *values;
+} hints_ui_data_t;
+
 struct ui_data_t {
     GList* pin_ui_data;
     GtkWidget *main_window;
@@ -35,6 +42,8 @@ struct ui_data_t {
     gboolean trust_codec;
     gboolean trust_defcfg;
     gboolean model_auto;
+
+    hints_ui_data_t hints;
 };
 
 static void update_user_pin_config(ui_data_t* ui, pin_configs_t* cfg);
@@ -229,14 +238,44 @@ static void update_all_user_pin_config(ui_data_t* ui)
         update_user_pin_config(ui, &ui->sysfs_pins[i]);
 }
 
+static gboolean update_one_hint(GtkTreeModel *model, GtkTreePath *path,
+                            GtkTreeIter *iter, gpointer userdata)
+{
+    gchar *name, *value;
+    ui_data_t *ui = userdata;
+    gtk_tree_model_get(GTK_TREE_MODEL(ui->hints.store), iter, 0, &name, 1, &value, -1);
+    if (g_strcmp0(value, "default")) {
+        gchar *s = g_strconcat(name, "=", value, "\n", ui->hints.values, NULL);
+        g_free(ui->hints.values);
+        ui->hints.values = s;
+    }
+    g_free(name);
+    g_free(value);
+    return FALSE;
+}
+
+static void update_hints(ui_data_t* ui)
+{
+    g_free(ui->hints.values);
+    ui->hints.values = NULL;
+    if (ui->hints.visible)
+        gtk_tree_model_foreach(GTK_TREE_MODEL(ui->hints.store), update_one_hint, ui);
+}
+
+static GQuark quark()
+{
+    return g_quark_from_static_string("hda-jack-retask-error");
+}
+
 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!");
+        g_set_error(err, quark(), 0, "You must first select a codec!");
         return FALSE;
     }
+    update_hints(ui);
     update_all_user_pin_config(ui);
     if (ui->free_overrides)
         return TRUE;
@@ -249,21 +288,44 @@ static gboolean validate_user_pin_config(ui_data_t* ui, GError** err)
         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.");
+            g_set_error(err, quark(), 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.");
+            g_set_error(err, quark(), 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.");
+            g_set_error(err, quark(), 0, "This surround setup also requires a \"Center/LFE\" channel override.");
             return FALSE;
         }
     }
     return TRUE;
 }
 
+static gboolean update_tree_one_hint(GtkTreeModel *model, GtkTreePath *path,
+                            GtkTreeIter *iter, gpointer userdata)
+{
+    gchar *name;
+    ui_data_t *ui = userdata;
+    gtk_tree_model_get(GTK_TREE_MODEL(ui->hints.store), iter, 0, &name, -1);
+    gchar *s = strstr(ui->hints.values, name);
+    if (!s) {
+        g_free(name);
+        gtk_list_store_set(ui->hints.store, iter, 1, "default", -1);
+        return FALSE;
+    }
+    s += strlen(name);
+    while (*s == ' ' || *s == '=') s++;
+    gchar *s2 = s;
+    while (*s != '\n' && *s != '\0') s++;
+    s2 = g_strndup(s2, s - s2);
+    gtk_list_store_set(ui->hints.store, iter, 1, s2, -1);
+    g_free(s2);
+    g_free(name);
+    return FALSE;
+}
+
 static void show_action_result(ui_data_t* ui, GError* err, const gchar* ok_msg)
 {
     GtkWidget* dialog;
@@ -285,7 +347,7 @@ static void apply_now_clicked(GtkButton* button, gpointer user_data)
     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); 
+            ui->model_auto ? "auto" : NULL, ui->hints.values, &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.)");
@@ -299,8 +361,8 @@ static void apply_boot_clicked(GtkButton* button, gpointer user_data)
     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, 
+            ui->model_auto ? "auto" : NULL, ui->hints.values, &err);
+    show_action_result(ui, err,
         "Ok, now reboot to test to see if it actually worked!\n"
         "(Remember, this stuff is still experimental.)");
 }
@@ -359,15 +421,24 @@ static void update_codec_ui(ui_data_t* ui, bool codec_change)
     if (codec_index < 0)
         return;
     ui->current_codec = &ui->sysfs_codec_names[codec_index];
-    if (codec_change)
+    if (codec_change) {
         ui->sysfs_pincount = get_pin_configs_list(ui->sysfs_pins, 32, ui->current_codec->card, ui->current_codec->device);
+        ui->hints.values = get_hint_overrides(ui->current_codec->card, ui->current_codec->device);
+        gtk_tree_model_foreach(GTK_TREE_MODEL(ui->hints.store), update_tree_one_hint, ui);
+    }
     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));
+
+    if (ui->hints.visible)
+        gtk_widget_show_all(ui->hints.frame);
+    else
+        gtk_widget_hide(ui->hints.frame);
+
     resize_main_window(ui);
 }
 
@@ -394,6 +465,32 @@ static void free_override_toggled(GtkWidget* sender, ui_data_t* ui_data)
     update_codec_ui(ui_data, false);
 }
 
+static void hints_toggled(GtkWidget* sender, ui_data_t* ui_data)
+{
+    ui_data->hints.visible = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(sender));
+    update_codec_ui(ui_data, false);
+}
+
+static void hints_row_activated(GtkTreeView *tree_view, GtkTreePath *path,
+                                GtkTreeViewColumn *column, ui_data_t* ui_data)
+{
+    GtkTreeIter iter;
+    gchar *value;
+    const gchar *newvalue = "default";
+
+    gtk_tree_model_get_iter(GTK_TREE_MODEL(ui_data->hints.store), &iter, path);
+    gtk_tree_model_get(GTK_TREE_MODEL(ui_data->hints.store), &iter, 1, &value, -1);
+
+    if (!g_strcmp0(value, "default"))
+        newvalue = "yes";
+    else if (!g_strcmp0(value, "yes"))
+        newvalue = "no";
+    gtk_list_store_set(ui_data->hints.store, &iter, 1, newvalue, -1);
+
+    g_free(value);
+}
+
+
 static const char* readme_text = 
 #include "README.generated.h"
 ;
@@ -480,6 +577,31 @@ static ui_data_t* create_ui()
         gtk_box_set_child_packing(GTK_BOX(toplevel_2ndbox), frame, TRUE, TRUE, 2, GTK_PACK_START);
     }
 
+    /* Create hints */
+    {
+        GtkWidget* frame = gtk_frame_new("Hints");
+        ui->hints.frame = frame;
+
+        GtkListStore *store = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_STRING);
+        ui->hints.store = store;
+        const gchar** names = get_standard_hint_names();
+        for (; *names; names++) {
+            GtkTreeIter iter;
+            gtk_list_store_append(store, &iter);
+            gtk_list_store_set(store, &iter, 0, *names, 1, "default", -1);
+        }
+
+        GtkWidget *tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
+        gtk_tree_view_append_column(GTK_TREE_VIEW(tree), gtk_tree_view_column_new_with_attributes
+            ("Name", gtk_cell_renderer_text_new(), "text", 0, NULL));
+        gtk_tree_view_append_column(GTK_TREE_VIEW(tree), gtk_tree_view_column_new_with_attributes
+            ("Value", gtk_cell_renderer_text_new(), "text", 1, NULL));
+        g_signal_connect(tree, "row-activated", G_CALLBACK(hints_row_activated), ui);
+
+        gtk_container_add(GTK_CONTAINER(frame), tree);
+        gtk_container_add(toplevel_2ndbox, frame);
+    }
+
     /* Create settings */
     {
         GtkWidget* frame = gtk_frame_new("Options");
@@ -498,6 +620,10 @@ static ui_data_t* create_ui()
         g_signal_connect(check, "toggled", G_CALLBACK(free_override_toggled), ui);
         gtk_container_add(box, check);
 
+        check = gtk_check_button_new_with_label("Parser hints");
+        g_signal_connect(check, "toggled", G_CALLBACK(hints_toggled), ui);
+        gtk_container_add(box, check);
+
         gtk_container_add(GTK_CONTAINER(frame), GTK_WIDGET(box));
         gtk_container_add(rightside_box, frame);
     }
index 5bae0f9cd3a4b4fb055f66e46c69a2e6fa79a679..592ffe25d3f4be49eb44bf9ae6975c33d9bae03b 100644 (file)
@@ -7,6 +7,21 @@
 #include "sysfs-pin-configs.h"
 #include "apply-changes.h"
 
+const gchar *hint_names[25] = {
+"jack_detect",  "inv_jack_detect", "trigger_sense", "inv_eapd",
+"pcm_format_first", "sticky_stream", "spdif_status_reset",
+"pin_amp_workaround", "single_adc_amp", "auto_mute", "auto_mic",
+"line_in_auto_switch", "auto_mute_via_amp", "need_dac_fix", "primary_hp",
+"multi_io", "multi_cap_vol", "inv_dmic_split", "indep_hp",
+"add_stereo_mix_input", "add_jack_modes", "power_down_unused", "add_hp_mic",
+"hp_mic_detect", NULL };
+
+const gchar** get_standard_hint_names()
+{
+    return hint_names;
+}
+
+
 int get_codec_name_list(codec_name_t* names, int entries)
 {
     GDir* sysdir = g_dir_open("/sys/class/sound", 0, NULL);
@@ -117,6 +132,16 @@ static void get_pin_caps(pin_configs_t* pins, int entries, int card, int device)
     g_free(contents);
 }
 
+gchar *get_hint_overrides(int card, int device)
+{
+    gchar* filename = g_strdup_printf("/sys/class/sound/hwC%dD%d/hints", card, device);
+    gchar* contents = NULL;
+    int ok = g_file_get_contents(filename, &contents, NULL, NULL);
+    g_free(filename);
+    if (!ok)
+        return NULL;
+    return contents;
+}
 
 static void read_pin_overrides(pin_configs_t* pins, int entries, int card, int device, gboolean is_user)
 {
@@ -126,7 +151,7 @@ static void read_pin_overrides(pin_configs_t* pins, int entries, int card, int d
     int count = 0;
     int ok = g_file_get_contents(filename, &contents, NULL, NULL);
     g_free(filename);
-    if (!ok) 
+    if (!ok)
         return;
     line_iterator = lines = g_strsplit(contents, "\n", entries);
     while (count < entries && *line_iterator) {
@@ -151,6 +176,7 @@ static void read_pin_overrides(pin_configs_t* pins, int entries, int card, int d
         g_strfreev(line); 
     }    
     g_strfreev(lines);
+    g_free(contents);
 }
 
 int get_pin_configs_list(pin_configs_t* pins, int entries, int card, int device)
index 9a0a90283f64f8b6daf1c79f53708d90a0cbed94..d198f8b6e602503a9ca40da7912e68cc162950ac 100644 (file)
@@ -48,6 +48,10 @@ gchar* get_config_description(unsigned long config);
 
 gchar* get_caps_description(unsigned long pin_caps);
 
+const gchar** get_standard_hint_names();
+gchar *get_hint_overrides(int card, int device);
+
+
 /* 0 = Jack, 1 = N/A, 2 = Internal, 3 = Both (?!) */
 int get_port_conn(unsigned long config);