blob: 2d4d5fb059b91a348909032de943f62ffd87308f [file] [log] [blame]
David Burger7fd1dbe2020-03-26 09:26:55 -06001#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3# Copyright 2020 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6"""Transforms config from /config/proto/api proto format to platform JSON."""
7
8import argparse
9import json
10import pprint
C Shapiro90fda252020-04-17 14:34:57 -050011import os
David Burger7fd1dbe2020-03-26 09:26:55 -060012import sys
C Shapiro90fda252020-04-17 14:34:57 -050013import re
C Shapiro5bf23a72020-04-24 11:40:17 -050014import xml.etree.ElementTree as etree
C Shapiro9a3ac8c2020-04-25 07:49:21 -050015import xml.dom.minidom as minidom
David Burger7fd1dbe2020-03-26 09:26:55 -060016
17from collections import namedtuple
18
Andrew Lambcd33f702020-06-11 10:45:16 -060019from google.protobuf import json_format
20
Prathmesh Prabhu72f8a002020-04-10 09:57:53 -070021from chromiumos.config.api import device_brand_pb2
David Burger92609a32020-04-23 10:38:50 -060022from chromiumos.config.api import topology_pb2
C Shapiro5bf23a72020-04-24 11:40:17 -050023from chromiumos.config.payload import config_bundle_pb2
Prathmesh Prabhu72f8a002020-04-10 09:57:53 -070024from chromiumos.config.api.software import brand_config_pb2
David Burger7fd1dbe2020-03-26 09:26:55 -060025
Andrew Lamb2413c982020-05-29 12:15:36 -060026Config = namedtuple('Config', [
27 'program', 'hw_design', 'odm', 'hw_design_config', 'device_brand',
28 'device_signer_config', 'oem', 'sw_config', 'brand_config', 'build_target'
29])
David Burger7fd1dbe2020-03-26 09:26:55 -060030
Andrew Lamb2413c982020-05-29 12:15:36 -060031ConfigFiles = namedtuple(
David Burger52c9d322020-06-09 07:16:18 -060032 'ConfigFiles', ['bluetooth', 'arc_hw_features', 'touch_fw', 'dptf_map'])
C Shapiro5bf23a72020-04-24 11:40:17 -050033
David Burger52c9d322020-06-09 07:16:18 -060034DPTF_PATH = 'sw_build_config/platform/chromeos-config/thermal'
35DPTF_FILE = 'dptf.dv'
C Shapiro2b6d5332020-05-06 17:51:35 -050036TOUCH_PATH = 'sw_build_config/platform/chromeos-config/touch'
David Burger7fd1dbe2020-03-26 09:26:55 -060037
Andrew Lamb2413c982020-05-29 12:15:36 -060038
Andrew Lambcd33f702020-06-11 10:45:16 -060039def parse_args(argv):
David Burger7fd1dbe2020-03-26 09:26:55 -060040 """Parse the available arguments.
41
42 Invalid arguments or -h cause this function to print a message and exit.
43
44 Args:
45 argv: List of string arguments (excluding program name / argv[0])
46
47 Returns:
48 argparse.Namespace object containing the attributes.
49 """
50 parser = argparse.ArgumentParser(
51 description='Converts source proto config into platform JSON config.')
52 parser.add_argument(
53 '-c',
54 '--project_configs',
55 nargs='+',
56 type=str,
57 help='Space delimited list of source protobinary project config files.')
58 parser.add_argument(
59 '-p',
60 '--program_config',
61 type=str,
62 help='Path to the source program-level protobinary file')
63 parser.add_argument(
Andrew Lamb2413c982020-05-29 12:15:36 -060064 '-o', '--output', type=str, help='Output file that will be generated')
David Burger7fd1dbe2020-03-26 09:26:55 -060065 return parser.parse_args(argv)
66
67
Andrew Lambcd33f702020-06-11 10:45:16 -060068def _set(field, target, target_name):
Sam McNally9a873f72020-06-05 19:47:22 +100069 if field or field == 0:
David Burger7fd1dbe2020-03-26 09:26:55 -060070 target[target_name] = field
71
72
Andrew Lambcd33f702020-06-11 10:45:16 -060073def _build_arc(config, config_files):
74 if not config.build_target.arc:
75 return None
76
77 build_properties = {
78 'device': config.build_target.arc.device,
79 'first-api-level': config.build_target.arc.first_api_level,
80 'marketing-name': config.device_brand.brand_name,
81 'metrics-tag': config.hw_design.name.lower(),
82 'product': config.build_target.id.value,
83 }
84 if config.oem:
85 build_properties['oem'] = config.oem.name
86 result = {'build-properties': build_properties}
87 feature_id = _arc_hardware_feature_id(config.hw_design_config)
88 if feature_id in config_files.arc_hw_features:
89 result['hardware-features'] = config_files.arc_hw_features[feature_id]
90 topology = config.hw_design_config.hardware_topology
91 ppi = topology.screen.hardware_feature.screen.panel_properties.pixels_per_in
92 # Only set for high resolution displays
93 if ppi and ppi > 250:
94 result['scale'] = ppi
95 return result
David Burger7fd1dbe2020-03-26 09:26:55 -060096
Andrew Lamb2413c982020-05-29 12:15:36 -060097
Andrew Lambcd33f702020-06-11 10:45:16 -060098def _build_bluetooth(config, bluetooth_files):
C Shapiro90fda252020-04-17 14:34:57 -050099 bt_flags = config.sw_config.bluetooth_config.flags
100 # Convert to native map (from proto wrapper)
101 bt_flags_map = dict(bt_flags)
102 result = {}
103 if bt_flags_map:
104 result['flags'] = bt_flags_map
C Shapiro74da76e2020-05-04 13:02:20 -0500105 bt_comp = config.hw_design_config.hardware_features.bluetooth.component.usb
C Shapiro90fda252020-04-17 14:34:57 -0500106 if bt_comp.vendor_id:
Andrew Lambcd33f702020-06-11 10:45:16 -0600107 bt_id = _bluetooth_id(config.hw_design.name.lower(), bt_comp)
C Shapiro90fda252020-04-17 14:34:57 -0500108 if bt_id in bluetooth_files:
109 result['config'] = bluetooth_files[bt_id]
110 return result
111
David Burger7fd1dbe2020-03-26 09:26:55 -0600112
Andrew Lambcd33f702020-06-11 10:45:16 -0600113def _build_fingerprint(hw_topology):
114 if not hw_topology.HasField('fingerprint'):
115 return None
116
117 fp = hw_topology.fingerprint.hardware_feature.fingerprint
118 result = {}
119 if fp.location != topology_pb2.HardwareFeatures.Fingerprint.NOT_PRESENT:
120 location = fp.Location.DESCRIPTOR.values_by_number[fp.location].name
121 result['sensor-location'] = location.lower().replace('_', '-')
122 if fp.board:
123 result['board'] = fp.board
124 return result
David Burger7fd1dbe2020-03-26 09:26:55 -0600125
126
Andrew Lambcd33f702020-06-11 10:45:16 -0600127def _fw_bcs_path(payload):
David Burger7fd1dbe2020-03-26 09:26:55 -0600128 if payload and payload.firmware_image_name:
Andrew Lamb2413c982020-05-29 12:15:36 -0600129 return 'bcs://%s.%d.%d.0.tbz2' % (payload.firmware_image_name,
130 payload.version.major,
131 payload.version.minor)
David Burger7fd1dbe2020-03-26 09:26:55 -0600132
Andrew Lambcd33f702020-06-11 10:45:16 -0600133 return None
David Burger7fd1dbe2020-03-26 09:26:55 -0600134
Andrew Lambcd33f702020-06-11 10:45:16 -0600135
136def _fw_build_target(payload):
David Burger7fd1dbe2020-03-26 09:26:55 -0600137 if payload:
138 return payload.build_target_name
139
Andrew Lambcd33f702020-06-11 10:45:16 -0600140 return None
David Burger7fd1dbe2020-03-26 09:26:55 -0600141
Andrew Lambcd33f702020-06-11 10:45:16 -0600142
143def _build_firmware(config):
David Burgerb70b6762020-05-21 12:14:59 -0600144 """Returns firmware config, or None if no build targets."""
Andrew Lamb3da156d2020-04-16 16:00:56 -0600145 fw_payload_config = config.sw_config.firmware
146 fw_build_config = config.sw_config.firmware_build_config
147 main_ro = fw_payload_config.main_ro_payload
148 main_rw = fw_payload_config.main_rw_payload
149 ec_ro = fw_payload_config.ec_ro_payload
150 pd_ro = fw_payload_config.pd_ro_payload
David Burger7fd1dbe2020-03-26 09:26:55 -0600151
152 build_targets = {}
Andrew Lamb3da156d2020-04-16 16:00:56 -0600153
Andrew Lambcd33f702020-06-11 10:45:16 -0600154 _set(fw_build_config.build_targets.depthcharge, build_targets, 'depthcharge')
155 _set(fw_build_config.build_targets.coreboot, build_targets, 'coreboot')
156 _set(fw_build_config.build_targets.ec, build_targets, 'ec')
157 _set(
Andrew Lambf8954ee2020-04-21 10:24:40 -0600158 list(fw_build_config.build_targets.ec_extras), build_targets, 'ec_extras')
Andrew Lambcd33f702020-06-11 10:45:16 -0600159 _set(fw_build_config.build_targets.libpayload, build_targets, 'libpayload')
David Burger7fd1dbe2020-03-26 09:26:55 -0600160
David Burgerb70b6762020-05-21 12:14:59 -0600161 if not build_targets:
162 return None
163
David Burger7fd1dbe2020-03-26 09:26:55 -0600164 result = {
165 'bcs-overlay': config.build_target.overlay_name,
166 'build-targets': build_targets,
David Burger7fd1dbe2020-03-26 09:26:55 -0600167 }
Andrew Lamb883fa042020-04-06 11:37:22 -0600168
Andrew Lambcd33f702020-06-11 10:45:16 -0600169 _set(main_ro.firmware_image_name.lower(), result, 'image-name')
Andrew Lamb883fa042020-04-06 11:37:22 -0600170
Andrew Lambcd33f702020-06-11 10:45:16 -0600171 _set(_fw_bcs_path(main_ro), result, 'main-ro-image')
172 _set(_fw_bcs_path(main_rw), result, 'main-rw-image')
173 _set(_fw_bcs_path(ec_ro), result, 'ec-ro-image')
174 _set(_fw_bcs_path(pd_ro), result, 'pd-ro-image')
David Burger7fd1dbe2020-03-26 09:26:55 -0600175
Andrew Lambcd33f702020-06-11 10:45:16 -0600176 _set(
Andrew Lambf39fbe82020-04-13 16:14:33 -0600177 config.hw_design_config.hardware_features.fw_config.value,
178 result,
179 'firmware-config',
180 )
181
David Burger7fd1dbe2020-03-26 09:26:55 -0600182 return result
183
184
Andrew Lambcd33f702020-06-11 10:45:16 -0600185def _build_fw_signing(config):
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500186 if config.sw_config.firmware and config.device_signer_config:
David Burger68e0d142020-05-15 17:29:33 -0600187 hw_design = config.hw_design.name.lower()
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500188 return {
189 'key-id': config.device_signer_config.key_id,
C Shapiro10e9a612020-05-19 17:06:43 -0500190 # TODO(shapiroc): Need to fix for whitelabel.
191 # Whitelabel will collide on unique signature-id values.
David Burger68e0d142020-05-15 17:29:33 -0600192 'signature-id': hw_design,
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500193 }
194 return {}
David Burger7fd1dbe2020-03-26 09:26:55 -0600195
196
Andrew Lambcd33f702020-06-11 10:45:16 -0600197def _file(source, destination):
Andrew Lamb2413c982020-05-29 12:15:36 -0600198 return {'destination': destination, 'source': source}
David Burger7fd1dbe2020-03-26 09:26:55 -0600199
200
Andrew Lambcd33f702020-06-11 10:45:16 -0600201def _build_audio(config):
David Burger7fd1dbe2020-03-26 09:26:55 -0600202 alsa_path = '/usr/share/alsa/ucm'
203 cras_path = '/etc/cras'
204 project_name = config.hw_design.name.lower()
David Burger43250662020-05-07 11:21:50 -0600205 program_name = config.program.name.lower()
Andrew Lamb7d536782020-04-07 10:23:55 -0600206 if not config.sw_config.HasField('audio_config'):
David Burger7fd1dbe2020-03-26 09:26:55 -0600207 return {}
208 audio = config.sw_config.audio_config
209 card = audio.card_name
David Burger599ff7b2020-04-06 16:29:31 -0600210 card_with_suffix = audio.card_name
211 if audio.ucm_suffix:
212 card_with_suffix += '.' + audio.ucm_suffix
David Burger7fd1dbe2020-03-26 09:26:55 -0600213 files = []
214 if audio.ucm_file:
Andrew Lamb2413c982020-05-29 12:15:36 -0600215 files.append(
Andrew Lambcd33f702020-06-11 10:45:16 -0600216 _file(audio.ucm_file,
Andrew Lamb2413c982020-05-29 12:15:36 -0600217 '%s/%s/HiFi.conf' % (alsa_path, card_with_suffix)))
David Burger7fd1dbe2020-03-26 09:26:55 -0600218 if audio.ucm_master_file:
Andrew Lamb2413c982020-05-29 12:15:36 -0600219 files.append(
Andrew Lambcd33f702020-06-11 10:45:16 -0600220 _file(audio.ucm_master_file, '%s/%s/%s.conf' %
Andrew Lamb2413c982020-05-29 12:15:36 -0600221 (alsa_path, card_with_suffix, card_with_suffix)))
David Burger7fd1dbe2020-03-26 09:26:55 -0600222 if audio.card_config_file:
Andrew Lamb2413c982020-05-29 12:15:36 -0600223 files.append(
Andrew Lambcd33f702020-06-11 10:45:16 -0600224 _file(audio.card_config_file,
Andrew Lamb2413c982020-05-29 12:15:36 -0600225 '%s/%s/%s' % (cras_path, project_name, card)))
David Burger7fd1dbe2020-03-26 09:26:55 -0600226 if audio.dsp_file:
227 files.append(
Andrew Lambcd33f702020-06-11 10:45:16 -0600228 _file(audio.dsp_file, '%s/%s/dsp.ini' % (cras_path, project_name)))
David Burgere1a37492020-05-06 09:29:24 -0600229 if audio.module_file:
230 files.append(
Andrew Lambcd33f702020-06-11 10:45:16 -0600231 _file(audio.module_file, '/etc/modprobe.d/alsa-%s.conf' % program_name))
David Burgere1a37492020-05-06 09:29:24 -0600232 if audio.board_file:
233 files.append(
Andrew Lambcd33f702020-06-11 10:45:16 -0600234 _file(audio.board_file, '%s/%s/board.ini' % (cras_path, project_name)))
David Burger599ff7b2020-04-06 16:29:31 -0600235
236 result = {
David Burger7fd1dbe2020-03-26 09:26:55 -0600237 'main': {
238 'cras-config-dir': project_name,
239 'files': files,
240 }
241 }
David Burger599ff7b2020-04-06 16:29:31 -0600242 if audio.ucm_suffix:
David Burger03cdcbd2020-04-13 13:54:48 -0600243 result['main']['ucm-suffix'] = audio.ucm_suffix
David Burger599ff7b2020-04-06 16:29:31 -0600244
245 return result
David Burger7fd1dbe2020-03-26 09:26:55 -0600246
247
Andrew Lambcd33f702020-06-11 10:45:16 -0600248def _build_camera(hw_topology):
David Burger8aa8fa32020-04-14 08:30:34 -0600249 if hw_topology.HasField('camera'):
250 camera = hw_topology.camera.hardware_feature.camera
251 result = {}
252 if camera.count.value:
253 result['count'] = camera.count.value
254 return result
255
Andrew Lambcd33f702020-06-11 10:45:16 -0600256 return None
David Burger8aa8fa32020-04-14 08:30:34 -0600257
Andrew Lambcd33f702020-06-11 10:45:16 -0600258
259def _build_identity(hw_scan_config, program, brand_scan_config=None):
David Burger7fd1dbe2020-03-26 09:26:55 -0600260 identity = {}
Andrew Lambcd33f702020-06-11 10:45:16 -0600261 _set(hw_scan_config.firmware_sku, identity, 'sku-id')
262 _set(hw_scan_config.smbios_name_match, identity, 'smbios-name-match')
Andrew Lamb7806ce92020-04-07 10:22:17 -0600263 # 'platform-name' is needed to support 'mosys platform name'. Clients should
264 # longer require platform name, but set it here for backwards compatibility.
Andrew Lambcd33f702020-06-11 10:45:16 -0600265 _set(program.name, identity, 'platform-name')
David Burger7fd1dbe2020-03-26 09:26:55 -0600266 # ARM architecture
Andrew Lambcd33f702020-06-11 10:45:16 -0600267 _set(hw_scan_config.device_tree_compatible_match, identity,
David Burger7fd1dbe2020-03-26 09:26:55 -0600268 'device-tree-compatible-match')
269
270 if brand_scan_config:
Andrew Lambcd33f702020-06-11 10:45:16 -0600271 _set(brand_scan_config.whitelabel_tag, identity, 'whitelabel-tag')
David Burger7fd1dbe2020-03-26 09:26:55 -0600272
273 return identity
274
275
Andrew Lambcd33f702020-06-11 10:45:16 -0600276def _lookup(id_value, id_map):
277 if not id_value.value:
278 return None
279
280 key = id_value.value
281 if key in id_map:
282 return id_map[id_value.value]
283 error = 'Failed to lookup %s with value: %s' % (
284 id_value.__class__.__name__.replace('Id', ''), key)
285 print(error)
286 print('Check the config contents provided:')
287 printer = pprint.PrettyPrinter(indent=4)
288 printer.pprint(id_map)
289 raise Exception(error)
David Burger7fd1dbe2020-03-26 09:26:55 -0600290
291
Andrew Lambcd33f702020-06-11 10:45:16 -0600292def _build_touch_file_config(config, project_name):
293 partners = {x.id.value: x for x in config.partners.value}
C Shapiro2b6d5332020-05-06 17:51:35 -0500294 files = []
295 for comp in config.components:
C Shapiro4813be62020-05-13 17:31:58 -0500296 touch = comp.touchscreen
297 # Everything is the same for Touch screen/pad, except different fields
298 if comp.HasField('touchpad'):
299 touch = comp.touchpad
300 if touch.product_id:
Andrew Lambcd33f702020-06-11 10:45:16 -0600301 vendor = _lookup(comp.manufacturer_id, partners)
C Shapiro2b6d5332020-05-06 17:51:35 -0500302 if not vendor:
Andrew Lamb2413c982020-05-29 12:15:36 -0600303 raise Exception("Manufacturer must be set for touch device %s" %
304 comp.id.value)
C Shapiro2b6d5332020-05-06 17:51:35 -0500305
C Shapiro4813be62020-05-13 17:31:58 -0500306 product_id = touch.product_id
307 fw_version = touch.fw_version
C Shapiro2b6d5332020-05-06 17:51:35 -0500308
C Shapiro5c6fc212020-05-13 16:32:09 -0500309 touch_vendor = vendor.touch_vendor
310 sym_link = touch_vendor.fw_file_format.format(
Andrew Lamb2413c982020-05-29 12:15:36 -0600311 vendor_name=vendor.name,
312 vendor_id=touch_vendor.vendor_id,
313 product_id=product_id,
314 fw_version=fw_version,
315 product_series=touch.product_series)
C Shapiro2b6d5332020-05-06 17:51:35 -0500316
317 file_name = "%s_%s.bin" % (product_id, fw_version)
318 fw_file_path = os.path.join(TOUCH_PATH, vendor.name, file_name)
319
320 if not os.path.exists(fw_file_path):
Andrew Lamb2413c982020-05-29 12:15:36 -0600321 raise Exception("Touchscreen fw bin file doesn't exist at: %s" %
322 fw_file_path)
C Shapiro2b6d5332020-05-06 17:51:35 -0500323
324 files.append({
Andrew Lamb2413c982020-05-29 12:15:36 -0600325 "destination":
326 "/opt/google/touch/firmware/%s_%s" % (vendor.name, file_name),
327 "source":
328 os.path.join(project_name, fw_file_path),
329 "symlink":
330 os.path.join("/lib/firmware", sym_link),
C Shapiro2b6d5332020-05-06 17:51:35 -0500331 })
332
333 result = {}
Andrew Lambcd33f702020-06-11 10:45:16 -0600334 _set(files, result, 'files')
C Shapiro2b6d5332020-05-06 17:51:35 -0500335 return result
336
337
Andrew Lambcd33f702020-06-11 10:45:16 -0600338def _transform_build_configs(config, config_files=ConfigFiles({}, {}, {},
339 None)):
340 # pylint: disable=too-many-locals,too-many-branches
341 partners = {x.id.value: x for x in config.partners.value}
342 programs = {x.id.value: x for x in config.programs.value}
David Burger7fd1dbe2020-03-26 09:26:55 -0600343 sw_configs = list(config.software_configs)
Andrew Lambcd33f702020-06-11 10:45:16 -0600344 brand_configs = {x.brand_id.value: x for x in config.brand_configs}
David Burger7fd1dbe2020-03-26 09:26:55 -0600345
C Shapiroa0b766c2020-03-31 08:35:28 -0500346 if len(config.build_targets) != 1:
347 # Artifact of sharing the config_bundle for analysis and transforms.
348 # Integrated analysis of multiple programs/projects it the only time
349 # having multiple build targets would be valid.
350 raise Exception('Single build_target required for transform')
351
David Burger7fd1dbe2020-03-26 09:26:55 -0600352 results = {}
353 for hw_design in config.designs.value:
354 if config.device_brands.value:
Andrew Lamb2413c982020-05-29 12:15:36 -0600355 device_brands = [
356 x for x in config.device_brands.value
357 if x.design_id.value == hw_design.id.value
358 ]
David Burger7fd1dbe2020-03-26 09:26:55 -0600359 else:
360 device_brands = [device_brand_pb2.DeviceBrand()]
361
362 for device_brand in device_brands:
363 # Brand config can be empty since platform JSON config allows it
364 brand_config = brand_config_pb2.BrandConfig()
365 if device_brand.id.value in brand_configs:
366 brand_config = brand_configs[device_brand.id.value]
367
368 for hw_design_config in hw_design.configs:
369 design_id = hw_design_config.id.value
Andrew Lamb2413c982020-05-29 12:15:36 -0600370 sw_config_matches = [
371 x for x in sw_configs if x.design_config_id.value == design_id
372 ]
David Burger7fd1dbe2020-03-26 09:26:55 -0600373 if len(sw_config_matches) == 1:
374 sw_config = sw_config_matches[0]
375 elif len(sw_config_matches) > 1:
376 raise Exception('Multiple software configs found for: %s' % design_id)
377 else:
378 raise Exception('Software config is required for: %s' % design_id)
379
Andrew Lambcd33f702020-06-11 10:45:16 -0600380 program = _lookup(hw_design.program_id, programs)
C Shapiroadefd7c2020-05-19 16:37:21 -0500381 signer_configs_by_design = {}
382 signer_configs_by_brand = {}
383 for signer_config in program.device_signer_configs:
384 design_id = signer_config.design_id.value
385 brand_id = signer_config.brand_id.value
386 if design_id:
387 signer_configs_by_design[design_id] = signer_config
388 elif brand_id:
389 signer_configs_by_brand[brand_id] = signer_config
390 else:
391 raise Exception('No ID found for signer config: %s' % signer_config)
392
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500393 device_signer_config = None
C Shapiroadefd7c2020-05-19 16:37:21 -0500394 if signer_configs_by_design or signer_configs_by_brand:
395 design_id = hw_design.id.value
396 brand_id = device_brand.id.value
397 if design_id in signer_configs_by_design:
398 device_signer_config = signer_configs_by_design[design_id]
399 elif brand_id in signer_configs_by_brand:
400 device_signer_config = signer_configs_by_brand[brand_id]
401 else:
402 # Assume that if signer configs are set, every config is setup
Andrew Lamb2413c982020-05-29 12:15:36 -0600403 raise Exception('Signer config missing for design: %s, brand: %s' %
404 (design_id, brand_id))
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500405
Andrew Lambcd33f702020-06-11 10:45:16 -0600406 transformed_config = _transform_build_config(
C Shapiro90fda252020-04-17 14:34:57 -0500407 Config(
408 program=program,
409 hw_design=hw_design,
Andrew Lambcd33f702020-06-11 10:45:16 -0600410 odm=_lookup(hw_design.odm_id, partners),
C Shapiro90fda252020-04-17 14:34:57 -0500411 hw_design_config=hw_design_config,
412 device_brand=device_brand,
413 device_signer_config=device_signer_config,
Andrew Lambcd33f702020-06-11 10:45:16 -0600414 oem=_lookup(device_brand.oem_id, partners),
C Shapiro90fda252020-04-17 14:34:57 -0500415 sw_config=sw_config,
416 brand_config=brand_config,
Andrew Lamb2413c982020-05-29 12:15:36 -0600417 build_target=config.build_targets[0]), config_files)
David Burger7fd1dbe2020-03-26 09:26:55 -0600418
Andrew Lamb2413c982020-05-29 12:15:36 -0600419 config_json = json.dumps(
420 transformed_config,
421 sort_keys=True,
422 indent=2,
423 separators=(',', ': '))
David Burger7fd1dbe2020-03-26 09:26:55 -0600424
425 if config_json not in results:
426 results[config_json] = transformed_config
427
428 return list(results.values())
429
430
Andrew Lambcd33f702020-06-11 10:45:16 -0600431def _transform_build_config(config, config_files):
David Burger7fd1dbe2020-03-26 09:26:55 -0600432 """Transforms Config instance into target platform JSON schema.
433
434 Args:
435 config: Config namedtuple
C Shapiro5bf23a72020-04-24 11:40:17 -0500436 config_files: Map to look up the generated config files.
David Burger7fd1dbe2020-03-26 09:26:55 -0600437
438 Returns:
439 Unique config payload based on the platform JSON schema.
440 """
441 result = {
Andrew Lamb2413c982020-05-29 12:15:36 -0600442 'identity':
Andrew Lambcd33f702020-06-11 10:45:16 -0600443 _build_identity(config.sw_config.id_scan_config, config.program,
444 config.brand_config.scan_config),
Andrew Lamb2413c982020-05-29 12:15:36 -0600445 'name':
446 config.hw_design.name.lower(),
David Burger7fd1dbe2020-03-26 09:26:55 -0600447 }
448
Andrew Lambcd33f702020-06-11 10:45:16 -0600449 _set(_build_arc(config, config_files), result, 'arc')
450 _set(_build_audio(config), result, 'audio')
451 _set(_build_bluetooth(config, config_files.bluetooth), result, 'bluetooth')
452 _set(config.device_brand.brand_code, result, 'brand-code')
453 _set(
454 _build_camera(config.hw_design_config.hardware_topology), result,
455 'camera')
456 _set(_build_firmware(config), result, 'firmware')
457 _set(_build_fw_signing(config), result, 'firmware-signing')
458 _set(
459 _build_fingerprint(config.hw_design_config.hardware_topology), result,
Andrew Lamb2413c982020-05-29 12:15:36 -0600460 'fingerprint')
David Burger7fd1dbe2020-03-26 09:26:55 -0600461 power_prefs = config.sw_config.power_config.preferences
462 power_prefs_map = dict(
Andrew Lamb2413c982020-05-29 12:15:36 -0600463 (x.replace('_', '-'), power_prefs[x]) for x in power_prefs)
Andrew Lambcd33f702020-06-11 10:45:16 -0600464 _set(power_prefs_map, result, 'power')
David Burger52c9d322020-06-09 07:16:18 -0600465 if config_files.dptf_map:
466 # Prefer design specific if found, if not fall back to project wide config
467 # mapped under the empty string.
468 if config_files.dptf_map.get(config.hw_design.name):
469 dptf_file = config_files.dptf_map[config.hw_design.name]
470 else:
471 dptf_file = config_files.dptf_map.get('')
472 _set(dptf_file, result, 'thermal')
Andrew Lambcd33f702020-06-11 10:45:16 -0600473 _set(config_files.touch_fw, result, 'touch')
David Burger7fd1dbe2020-03-26 09:26:55 -0600474
475 return result
476
477
Andrew Lambcd33f702020-06-11 10:45:16 -0600478def write_output(configs, output=None):
David Burger7fd1dbe2020-03-26 09:26:55 -0600479 """Writes a list of configs to platform JSON format.
480
481 Args:
482 configs: List of config dicts defined in cros_config_schema.yaml
483 output: Target file output (if None, prints to stdout)
484 """
Andrew Lamb2413c982020-05-29 12:15:36 -0600485 json_output = json.dumps({'chromeos': {
486 'configs': configs,
487 }},
488 sort_keys=True,
489 indent=2,
490 separators=(',', ': '))
David Burger7fd1dbe2020-03-26 09:26:55 -0600491 if output:
492 with open(output, 'w') as output_stream:
493 # Using print function adds proper trailing newline.
494 print(json_output, file=output_stream)
495 else:
496 print(json_output)
497
498
Andrew Lambcd33f702020-06-11 10:45:16 -0600499def _bluetooth_id(project_name, bt_comp):
Andrew Lamb2413c982020-05-29 12:15:36 -0600500 return '_'.join(
501 [project_name, bt_comp.vendor_id, bt_comp.product_id, bt_comp.bcd_device])
C Shapiro90fda252020-04-17 14:34:57 -0500502
503
Andrew Lambcd33f702020-06-11 10:45:16 -0600504def _feature(name, present):
C Shapiro5bf23a72020-04-24 11:40:17 -0500505 attrib = {'name': name}
506 if present:
507 return etree.Element('feature', attrib=attrib)
Andrew Lambcd33f702020-06-11 10:45:16 -0600508
509 return etree.Element('unavailable-feature', attrib=attrib)
C Shapiro5bf23a72020-04-24 11:40:17 -0500510
511
Andrew Lambcd33f702020-06-11 10:45:16 -0600512def _any_present(features):
Andrew Lamb2413c982020-05-29 12:15:36 -0600513 return topology_pb2.HardwareFeatures.PRESENT in features
C Shapiro5bf23a72020-04-24 11:40:17 -0500514
515
Andrew Lambcd33f702020-06-11 10:45:16 -0600516def _arc_hardware_feature_id(design_config):
C Shapiro5bf23a72020-04-24 11:40:17 -0500517 return design_config.id.value.lower().replace(':', '_')
518
519
Andrew Lambcd33f702020-06-11 10:45:16 -0600520def _write_arc_hardware_feature_file(output_dir, file_name, config_content):
David Burger77a1d312020-05-23 16:05:45 -0600521 output_dir += '/arc'
522 os.makedirs(output_dir, exist_ok=True)
523 output = '%s/%s' % (output_dir, file_name)
Andrew Lamb2413c982020-05-29 12:15:36 -0600524 file_content = minidom.parseString(config_content).toprettyxml(
525 indent=' ', encoding='utf-8')
C Shapiroea33cff2020-05-11 13:32:05 -0500526
527 with open(output, 'wb') as f:
528 f.write(file_content)
529
530
Andrew Lambcd33f702020-06-11 10:45:16 -0600531def _write_arc_hardware_feature_files(config, output_dir, build_root_dir):
C Shapiro5bf23a72020-04-24 11:40:17 -0500532 """Writes ARC hardware_feature.xml files for each config
533
534 Args:
535 config: Source ConfigBundle to process.
536 output_dir: Path to the generated output.
C Shapiro5c877992020-04-29 12:11:28 -0500537 build_root_path: Path to the config file from portage's perspective.
C Shapiro5bf23a72020-04-24 11:40:17 -0500538 Returns:
539 dict that maps the design_config_id onto the correct file.
540 """
Andrew Lambcd33f702020-06-11 10:45:16 -0600541 # pylint: disable=too-many-locals
C Shapiro5bf23a72020-04-24 11:40:17 -0500542 result = {}
C Shapiroea33cff2020-05-11 13:32:05 -0500543 configs_by_design = {}
C Shapiro5bf23a72020-04-24 11:40:17 -0500544 for hw_design in config.designs.value:
545 for design_config in hw_design.configs:
546 hw_features = design_config.hardware_features
547 multi_camera = hw_features.camera.count == 2
Andrew Lambcd33f702020-06-11 10:45:16 -0600548 touchscreen = _any_present([hw_features.screen.touch_support])
C Shapiro5bf23a72020-04-24 11:40:17 -0500549 acc = hw_features.accelerometer
550 gyro = hw_features.gyroscope
551 compass = hw_features.magnetometer
Andrew Lambcd33f702020-06-11 10:45:16 -0600552 light_sensor = hw_features.light_sensor
C Shapiro5bf23a72020-04-24 11:40:17 -0500553 root = etree.Element('permissions')
554 root.extend([
Andrew Lambcd33f702020-06-11 10:45:16 -0600555 _feature('android.hardware.camera', multi_camera),
556 _feature('android.hardware.camera.autofocus', multi_camera),
557 _feature(
558 'android.hardware.sensor.accelerometer',
559 _any_present([acc.lid_accelerometer, acc.base_accelerometer])),
560 _feature('android.hardware.sensor.gyroscope',
561 _any_present([gyro.lid_gyroscope, gyro.base_gyroscope])),
562 _feature(
Andrew Lamb2413c982020-05-29 12:15:36 -0600563 'android.hardware.sensor.compass',
Andrew Lambcd33f702020-06-11 10:45:16 -0600564 _any_present(
565 [compass.lid_magnetometer, compass.base_magnetometer])),
566 _feature(
567 'android.hardware.sensor.light',
568 _any_present(
569 [light_sensor.lid_lightsensor,
570 light_sensor.base_lightsensor])),
571 _feature('android.hardware.touchscreen', touchscreen),
572 _feature('android.hardware.touchscreen.multitouch', touchscreen),
573 _feature('android.hardware.touchscreen.multitouch.distinct',
Andrew Lamb2413c982020-05-29 12:15:36 -0600574 touchscreen),
Andrew Lambcd33f702020-06-11 10:45:16 -0600575 _feature('android.hardware.touchscreen.multitouch.jazzhand',
Andrew Lamb2413c982020-05-29 12:15:36 -0600576 touchscreen),
C Shapiro5bf23a72020-04-24 11:40:17 -0500577 ])
578
C Shapiroea33cff2020-05-11 13:32:05 -0500579 design_name = hw_design.name.lower()
C Shapiro5bf23a72020-04-24 11:40:17 -0500580
C Shapiroea33cff2020-05-11 13:32:05 -0500581 # Constructs the following map:
582 # design_name -> config -> design_configs
583 # This allows any of the following file naming schemes:
584 # - All configs within a design share config (design_name prefix only)
585 # - Nobody shares (full design_name and config id prefix needed)
586 #
587 # Having shared configs when possible makes code reviews easier around
588 # the configs and makes debugging easier on the platform side.
589 config_content = etree.tostring(root)
590 arc_configs = configs_by_design.get(design_name, {})
591 design_configs = arc_configs.get(config_content, [])
592 design_configs.append(design_config)
593 arc_configs[config_content] = design_configs
594 configs_by_design[design_name] = arc_configs
C Shapiro9a3ac8c2020-04-25 07:49:21 -0500595
C Shapiroea33cff2020-05-11 13:32:05 -0500596 for design_name, unique_configs in configs_by_design.items():
597 for file_content, design_configs in unique_configs.items():
Andrew Lamb2413c982020-05-29 12:15:36 -0600598 file_name = 'hardware_features_%s.xml' % design_name
599 if len(unique_configs) == 1:
Andrew Lambcd33f702020-06-11 10:45:16 -0600600 _write_arc_hardware_feature_file(output_dir, file_name, file_content)
C Shapiro9a3ac8c2020-04-25 07:49:21 -0500601
Andrew Lamb2413c982020-05-29 12:15:36 -0600602 for design_config in design_configs:
Andrew Lambcd33f702020-06-11 10:45:16 -0600603 feature_id = _arc_hardware_feature_id(design_config)
Andrew Lamb2413c982020-05-29 12:15:36 -0600604 if len(unique_configs) > 1:
605 file_name = 'hardware_features_%s.xml' % feature_id
Andrew Lambcd33f702020-06-11 10:45:16 -0600606 _write_arc_hardware_feature_file(output_dir, file_name, file_content)
Andrew Lamb2413c982020-05-29 12:15:36 -0600607 result[feature_id] = {
608 'build-path': '%s/arc/%s' % (build_root_dir, file_name),
609 'system-path': '/etc/%s' % file_name,
610 }
C Shapiro5bf23a72020-04-24 11:40:17 -0500611 return result
612
613
Andrew Lambcd33f702020-06-11 10:45:16 -0600614def _write_bluetooth_config_files(config, output_dir, build_root_path):
C Shapiro90fda252020-04-17 14:34:57 -0500615 """Writes bluetooth conf files for every unique bluetooth chip.
616
617 Args:
618 config: Source ConfigBundle to process.
619 output_dir: Path to the generated output.
C Shapiro5c877992020-04-29 12:11:28 -0500620 build_root_path: Path to the config file from portage's perspective.
C Shapiro90fda252020-04-17 14:34:57 -0500621 Returns:
622 dict that maps the bluetooth component id onto the file config.
623 """
David Burger77a1d312020-05-23 16:05:45 -0600624 output_dir += '/bluetooth'
C Shapiro90fda252020-04-17 14:34:57 -0500625 result = {}
626 for hw_design in config.designs.value:
627 project_name = hw_design.name.lower()
628 for design_config in hw_design.configs:
C Shapiro74da76e2020-05-04 13:02:20 -0500629 bt_comp = design_config.hardware_features.bluetooth.component.usb
C Shapiro90fda252020-04-17 14:34:57 -0500630 if bt_comp.vendor_id:
Andrew Lambcd33f702020-06-11 10:45:16 -0600631 bt_id = _bluetooth_id(project_name, bt_comp)
C Shapiro90fda252020-04-17 14:34:57 -0500632 result[bt_id] = {
C Shapiro5c877992020-04-29 12:11:28 -0500633 'build-path': '%s/bluetooth/%s.conf' % (build_root_path, bt_id),
C Shapiro90fda252020-04-17 14:34:57 -0500634 'system-path': '/etc/bluetooth/%s/main.conf' % bt_id,
635 }
636 bt_content = '''[General]
Andrew Lamb2413c982020-05-29 12:15:36 -0600637DeviceID = bluetooth:%s:%s:%s''' % (bt_comp.vendor_id, bt_comp.product_id,
C Shapiro90fda252020-04-17 14:34:57 -0500638 bt_comp.bcd_device)
639
David Burger77a1d312020-05-23 16:05:45 -0600640 os.makedirs(output_dir, exist_ok=True)
641 output = '%s/%s.conf' % (output_dir, bt_id)
C Shapiro90fda252020-04-17 14:34:57 -0500642 with open(output, 'w') as output_stream:
643 # Using print function adds proper trailing newline.
644 print(bt_content, file=output_stream)
645 return result
646
647
Andrew Lambcd33f702020-06-11 10:45:16 -0600648def _read_config(path):
David Burgerd4f32962020-05-02 12:07:40 -0600649 """Reads a ConfigBundle proto from a json pb file.
David Burgere6f76222020-04-27 11:08:01 -0600650
651 Args:
David Burgerd4f32962020-05-02 12:07:40 -0600652 path: Path to the file encoding the json pb proto.
David Burgere6f76222020-04-27 11:08:01 -0600653 """
654 config = config_bundle_pb2.ConfigBundle()
655 with open(path, 'r') as f:
656 return json_format.Parse(f.read(), config)
657
658
Andrew Lambcd33f702020-06-11 10:45:16 -0600659def _merge_configs(configs):
David Burger7fd1dbe2020-03-26 09:26:55 -0600660 result = config_bundle_pb2.ConfigBundle()
661 for config in configs:
662 result.MergeFrom(config)
663
664 return result
665
666
David Burger52c9d322020-06-09 07:16:18 -0600667def _dptf_map(configs, project_name):
668 """Produces a dptf map for the given configs.
669
670 Produces a map that maps from design name to the dptf file config for that
671 design. It looks for the dptf files at:
672 DPTF_PATH + DPTF_FILE
673 for a project wide config, that it maps under the empty string, and at:
674 DPTF_PATH + design_name + DPTF_FILE
675 for design specific configs that it maps under the design name.
676
677 Args:
678 configs: Source ConfigBundle to process.
679 project_name: Name of project processing for.
680
681 Returns:
682 map from design name or empty string (project wide), to dptf config
683 """
684 result = {}
685 project_dptf_path = os.path.join(project_name, 'dptf.dv')
686 # Looking at top level for project wide, and then for each design name
687 # for design specific.
688 dirs = [""] + [d.name for d in configs.designs.value]
689 for directory in dirs:
690 if os.path.exists(os.path.join(DPTF_PATH, directory, DPTF_FILE)):
691 dptf_file = {
692 'dptf-dv':
693 project_dptf_path,
694 'files': [
695 _file(
696 os.path.join(project_name, DPTF_PATH, directory, DPTF_FILE),
697 os.path.join('/etc/dptf', project_dptf_path))
698 ]
699 }
700 result[directory] = dptf_file
701 return result
702
703
Andrew Lambcd33f702020-06-11 10:45:16 -0600704def Main(project_configs, program_config, output): # pylint: disable=invalid-name
David Burger7fd1dbe2020-03-26 09:26:55 -0600705 """Transforms source proto config into platform JSON.
706
707 Args:
708 project_configs: List of source project configs to transform.
709 program_config: Program config for the given set of projects.
710 output: Output file that will be generated by the transform.
711 """
Andrew Lambcd33f702020-06-11 10:45:16 -0600712 configs = _merge_configs([_read_config(program_config)] +
713 [_read_config(config) for config in project_configs])
C Shapiro5bf23a72020-04-24 11:40:17 -0500714 bluetooth_files = {}
715 arc_hw_feature_files = {}
C Shapiro2b6d5332020-05-06 17:51:35 -0500716 touch_fw = {}
David Burger52c9d322020-06-09 07:16:18 -0600717 dptf_map = {}
C Shapiro5bf23a72020-04-24 11:40:17 -0500718 output_dir = os.path.dirname(output)
C Shapiro5c877992020-04-29 12:11:28 -0500719 build_root_dir = output_dir
C Shapiro5c877992020-04-29 12:11:28 -0500720 if 'sw_build_config' in output_dir:
721 full_path = os.path.realpath(output)
Andrew Lamb2413c982020-05-29 12:15:36 -0600722 project_name = re.match(r'.*/(\w*)/sw_build_config/.*',
723 full_path).groups(1)[0]
C Shapiro5c877992020-04-29 12:11:28 -0500724 # Projects don't know about each other until they are integrated into the
725 # build system. When this happens, the files need to be able to co-exist
726 # without any collisions. This prefixes the project name (which is how
727 # portage maps in the project), so project files co-exist and can be
728 # installed together.
729 # This is necessary to allow projects to share files at the program level
730 # without having portage file installation collisions.
731 build_root_dir = os.path.join(project_name, output_dir)
C Shapiro6830e6c2020-04-29 13:29:56 -0500732
David Burger52c9d322020-06-09 07:16:18 -0600733 dptf_map = _dptf_map(configs, project_name)
734
C Shapiro2b6d5332020-05-06 17:51:35 -0500735 if os.path.exists(TOUCH_PATH):
Andrew Lambcd33f702020-06-11 10:45:16 -0600736 touch_fw = _build_touch_file_config(configs, project_name)
737 bluetooth_files = _write_bluetooth_config_files(configs, output_dir,
738 build_root_dir)
739 arc_hw_feature_files = _write_arc_hardware_feature_files(
740 configs, output_dir, build_root_dir)
C Shapiro5bf23a72020-04-24 11:40:17 -0500741 config_files = ConfigFiles(
742 bluetooth=bluetooth_files,
743 arc_hw_features=arc_hw_feature_files,
C Shapiro2b6d5332020-05-06 17:51:35 -0500744 touch_fw=touch_fw,
David Burger52c9d322020-06-09 07:16:18 -0600745 dptf_map=dptf_map)
Andrew Lambcd33f702020-06-11 10:45:16 -0600746 write_output(_transform_build_configs(configs, config_files), output)
David Burger7fd1dbe2020-03-26 09:26:55 -0600747
748
749def main(argv=None):
750 """Main program which parses args and runs
751
752 Args:
753 argv: List of command line arguments, if None uses sys.argv.
754 """
755 if argv is None:
756 argv = sys.argv[1:]
Andrew Lambcd33f702020-06-11 10:45:16 -0600757 opts = parse_args(argv)
David Burger7fd1dbe2020-03-26 09:26:55 -0600758 Main(opts.project_configs, opts.program_config, opts.output)
759
760
761if __name__ == '__main__':
762 sys.exit(main(sys.argv[1:]))