[Provingground] Touch firmware version updates.

Add ability to report coral device touch firmwares.
Fix issue with poppy/meowth boards where there's variable
weirdness.

BUG=None
TEST=ran it: new values appear and old values stay

Change-Id: Ie16bfe76f3d131a160d3165578d9dbf01a278e77
Reviewed-on: https://chromium-review.googlesource.com/1069427
Commit-Ready: Katherine Threlkeld <kathrelkeld@chromium.org>
Tested-by: Katherine Threlkeld <kathrelkeld@chromium.org>
Reviewed-by: Kalin Stoyanov <kalin@chromium.org>
diff --git a/provingground/touch_firmware_versions.py b/provingground/touch_firmware_versions.py
index e3c6575..c6d8c30 100755
--- a/provingground/touch_firmware_versions.py
+++ b/provingground/touch_firmware_versions.py
@@ -52,6 +52,18 @@
     self.fw_id = fwid
     self.fw_file = filename
 
+def create_error_info(device, path, error):
+  """Creates a firmwareInfo class with the given error message.
+
+  Args:
+      device: value for firmwareInfo.device.
+      path: value for firmwareInfo.path.
+      error: error message string.
+  """
+  error_values = firmwareInfo(device, path)
+  error_values.problem_found = True
+  error_values.error = error
+  return error_values
 
 def find_value_of_variable(var, text):
   """Returns the value as defined for the given variable in the given text.
@@ -75,7 +87,7 @@
 def reduce_string(link, text):
   """Finds the absolute value of the given string.
 
-  Assumes variables embedded in a string are in ${VAR} or \s$VAR\s format.
+  Assumes variables embedded in a string are in ${VAR} or $VAR format.
   E.g., given '"${PRODUCT_ID_TP}_${FIRMWARE_VERSION_TP}.bin"' return
   '85.0_7.0.bin', after following all variables.
   Assumes the entire string could be a varible of the form $VAR (no { }).
@@ -107,7 +119,7 @@
   return reduce_string(new_link, text)
 
 
-def find_info_from_line(info, line, text):
+def find_info_from_dosym_line(info, line, text):
   """Finds the info to be output for the given dosym line.
 
   Makes exceptions for known formatting problems.
@@ -141,14 +153,15 @@
     new_line = list(line)
     if bad_fmt in line[2]:
       new_line[2] = line[2].replace(bad_fmt, good_fmt)
-      find_info_from_line(info, new_line, text)
+      find_info_from_dosym_line(info, new_line, text)
   elif info.device == 'kip' and link_from.find('dummy') >= 0:
     info.hw_id = None
   elif info.device == 'sumo' and link_to and 'fw.bin' not in link_to:
     info.hw_id = None
+  elif (info.device == 'poppy' or info.device == 'meowth') and not info.fw_file:
+    info.fw_file = os.path.basename(line[2]).replace('"', '')
 
-
-def find_firmwares_in_file(path):
+def find_dosym_firmwares_in_file(path):
   """Finds all dosym lines in the file and outputs values as needed.
 
   If anything went wrong, outputs the devicename for manual inspection.
@@ -156,19 +169,10 @@
   Args:
     path: the path to the ebuild file.
   """
-  def create_error_info(device, path, error):
-    error_values = firmwareInfo(device, path)
-    error_values.problem_found = True
-    error_values.error = error
-    return error_values
 
-  search = re.search('/(overlay-)?(variant-)?(baseboard-)?([^/]*?)(-private)?/',
-                     path)
-  if not search:
-    return [create_error_info(
-        path, path, 'Project path did not match expected format!')]
-
-  device = search.group(4)
+  device = find_device_from_path(path)
+  if not device:
+    return [create_error_info(path, path, 'Project path error!')]
 
   with open(path) as fh:
     text = fh.read()
@@ -198,7 +202,7 @@
   values_list = []
   for line in search:
     values = firmwareInfo(device, path)
-    find_info_from_line(values, line, text)
+    find_info_from_dosym_line(values, line, text)
     if values.hw_id != None:
       if values.hw_id == '' or values.fw_id == '' or values.fw_file == '':
         values.problem_found = True
@@ -208,6 +212,55 @@
 
   return values_list
 
+def find_model_firmwares_in_file(path):
+  """Finds the touch firmwares described in dtsi files, e.g. for coral.
+
+  Args:
+      path: the path to the given dtsi file.
+  """
+  device = find_device_from_path(path)
+  if not device:
+    return [create_error_info(path, path, 'Project path error!')]
+
+  model = os.path.basename(os.path.split(path)[0])
+  device_model = '%s_%s' % (device, model)
+
+  with open(path) as fh:
+    text = fh.read()
+
+  # Find the touch section, if any.
+  search = re.search(r'touch .*', text, re.DOTALL)
+  if not search:
+    return []
+  touch = search.group(0)
+
+  search = re.findall(r'touch-type\s*=[<&\s]*([^\s;>]+)[>;\s\{\}]*pid\s*'
+                      r'=[\s"]*([^\s"]+)[;\s"]*version\s*=[\s"]*([^\s;"]+)',
+                      touch, re.DOTALL)
+  if not search:
+    return [create_error_info(device_model, path, 'No devices found!')]
+
+  value_list = []
+  for match in search:
+    value_info = firmwareInfo(device_model, path)
+    # hwid (pid), fwid (version), filename (touch-type)
+    value_info.set_ids(match[1], match[2], match[0])
+    value_list.append(value_info)
+
+  return value_list
+
+def find_device_from_path(path):
+  """Returns the device name, given the filepath to the device's overlay.
+
+  Args:
+      path: the path to the given overlay file.
+  """
+  search = re.search('/(overlay-)?(variant-)?(baseboard-)?([^/]*?)(-private)?/',
+                     path)
+  if not search:
+    return None
+
+  return search.group(4)
 
 def firmware_versions():
   """Finds all touch-firmware files and their firmware values."""
@@ -215,17 +268,26 @@
   src_dir = os.path.join(file_dir, SRC_DIR)
   os.chdir(src_dir)
 
+  values_list = []
+
   # Find ebuild files, e.g. chromeos-touch-firmware-caroline-0.0.1.ebuild
   find_output = ''
-  cmd = 'find %s -regex .*touch-firmware-.*-[0-9.]+\.ebuild'
+  cmd = r'find %s -regex .*touch-firmware-.*-[0-9.]+\.ebuild'
   for d in ['private-overlays/', 'overlays/']:
     find_output += subprocess.check_output((cmd % d).split(' '))
   ebuilds = find_output.split()
   ebuilds.sort()
-
-  values_list = []
   for path in ebuilds:
-    values_list += find_firmwares_in_file(path)
+    values_list += find_dosym_firmwares_in_file(path)
+
+  # Find the model.dtsi files used by platforms like coral.
+  find_output = ''
+  cmd = 'find %s -regex .*model.dtsi'
+  for d in ['private-overlays/', 'overlays/']:
+    find_output += subprocess.check_output((cmd % d).split(' '))
+  dtsis = find_output.split()
+  for path in dtsis:
+    values_list += find_model_firmwares_in_file(path)
 
   values_list.sort(key=lambda x: x.device)
   return values_list