chrontel: add a thread to report hdmi audio availability to cras.

There was a race that if chrontel reports the hdmi audio state before
cras is ready to accept it, the state is lost. Now we will retry after
five seconds.

BUG=chromium:257099
TEST=plug hdmi cable, boot zgb, verify audio goes to hdmi monitor.

Change-Id: Ice407a57a5da0d05c73d10e10618f08fccfe1c20
Reviewed-on: https://gerrit.chromium.org/gerrit/62809
Reviewed-by: Dylan Reid <dgreid@chromium.org>
Commit-Queue: Chih-Chung Chang <chihchung@chromium.org>
Tested-by: Chih-Chung Chang <chihchung@chromium.org>
diff --git a/Makefile b/Makefile
index c8350dc..a677ac3 100644
--- a/Makefile
+++ b/Makefile
@@ -8,7 +8,7 @@
 PKG_CONFIG ?= pkg-config
 
 INCLUDE_DIRS = $(shell $(PKG_CONFIG) --cflags x11 xrandr libcras)
-LIB_DIRS = $(shell $(PKG_CONFIG) --libs x11 xrandr xext libcras)
+LIB_DIRS = $(shell $(PKG_CONFIG) --libs x11 xrandr xext libcras) -lpthread
 
 MON=ch7036_monitor
 BUG=ch7036_debug
diff --git a/audio_utils.c b/audio_utils.c
index 3a75a75..b226100 100644
--- a/audio_utils.c
+++ b/audio_utils.c
@@ -5,6 +5,7 @@
 #include <assert.h>
 #include <dirent.h>
 #include <linux/input.h>
+#include <pthread.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/ioctl.h>
@@ -18,6 +19,10 @@
 
 static const size_t MAX_IONODES = 20;
 static int aud_verbose = 0;
+
+/* These variables are shared with the report thread. */
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
 static int hdmi_audio_available;
 
 static struct cras_client *client;
@@ -60,7 +65,7 @@
   case CLIENT_THREAD_RUN:
     err = cras_client_connected_wait(client);
     if (err) {
-      printf(stderr, "Audio: failed to wait connected.\n");
+      printf("Audio: failed to wait connected.\n");
       return err;
     }
     client_status = CLIENT_READY;
@@ -72,35 +77,23 @@
   }
 }
 
-int audio_init(int verbose)
-{
-  int err = 0;
-  aud_verbose = verbose;
-  if (aud_verbose) printf("Audio: init\n");
-  connect_to_server();
-  return 0;
-}
-
-void audio_to_hdmi(int available)
+/* Sends the availability message to server. Returns 0 if successful, -1
+ * otherwise. */
+static int send_state_to_server(int available)
 {
   size_t num_devs = 0;
   size_t num_nodes = MAX_IONODES;
   struct cras_ionode_info nodes[MAX_IONODES];
   int rc, i;
 
-  if (available == hdmi_audio_available)
-    return;
-  if (aud_verbose) printf("Audio: Set HDMI audio output to %s\n",
-                          available ? "Available" : "Unavailable");
-  hdmi_audio_available = available;
   if (connect_to_server() != 0)
-    return;
+    return -1;
 
   rc = cras_client_get_output_devices(client, NULL, nodes, &num_devs,
                                       &num_nodes);
   if (rc < 0) {
     printf("Audio: cannot get output nodes\n");
-    return;
+    return -1;
   }
 
   for (i = 0; i < num_nodes; i++) {
@@ -109,9 +102,58 @@
                                                  nodes[i].ionode_idx);
       cras_client_set_node_attr(client, node_id, IONODE_ATTR_PLUGGED,
                                 available);
-      return;
+      return 0;
     }
   }
 
-  printf("Audio: IEC959 node is not found");
+  printf("Audio: IEC959 node is not found\n");
+  return -1;
+}
+
+/* A thread which reports the hdmi audio state to the server. */
+static void *report_thread(void *arg)
+{
+  /* The state that has been reported to the server successfully. */
+  int reported = 0;
+  int available;
+
+  while (1)
+  {
+    /* Wait until the availablity changes */
+    pthread_mutex_lock(&mutex);
+    while (hdmi_audio_available == reported)
+      pthread_cond_wait(&cond, &mutex);
+    available = hdmi_audio_available;
+    pthread_mutex_unlock(&mutex);
+
+    /* Report the new state */
+    if (send_state_to_server(available) == 0) {
+      reported = available;
+    } else {
+      /* If we failed to connect to the server, sleep for five seconds and
+       * retry. */
+      sleep(5);
+    }
+  }
+}
+
+int audio_init(int verbose)
+{
+  pthread_t thread;
+  aud_verbose = verbose;
+  if (aud_verbose) printf("Audio: init\n");
+  pthread_create(&thread, NULL, report_thread, NULL);
+  return 0;
+}
+
+void audio_to_hdmi(int available)
+{
+  if (available == hdmi_audio_available)
+    return;
+  if (aud_verbose) printf("Audio: Set HDMI audio output to %s\n",
+                          available ? "Available" : "Unavailable");
+  pthread_mutex_lock(&mutex);
+  hdmi_audio_available = available;
+  pthread_cond_signal(&cond);
+  pthread_mutex_unlock(&mutex);
 }