blob: 39a08dd8a61a9d7dbb84ede70178f83a1ea7a95d [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
Prathmesh Prabhu72f8a002020-04-10 09:57:53 -070019from chromiumos.config.api import device_brand_pb2
David Burger92609a32020-04-23 10:38:50 -060020from chromiumos.config.api import topology_pb2
C Shapiro5bf23a72020-04-24 11:40:17 -050021from chromiumos.config.payload import config_bundle_pb2
Prathmesh Prabhu72f8a002020-04-10 09:57:53 -070022from chromiumos.config.api.software import brand_config_pb2
David Burger7fd1dbe2020-03-26 09:26:55 -060023
David Burgere6f76222020-04-27 11:08:01 -060024from google.protobuf import json_format
25
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(
32 'ConfigFiles', ['bluetooth', 'arc_hw_features', 'touch_fw', 'dptf_file'])
C Shapiro5bf23a72020-04-24 11:40:17 -050033
C Shapiro6830e6c2020-04-29 13:29:56 -050034DPTF_PATH = 'sw_build_config/platform/chromeos-config/thermal/dptf.dv'
C Shapiro2b6d5332020-05-06 17:51:35 -050035TOUCH_PATH = 'sw_build_config/platform/chromeos-config/touch'
David Burger7fd1dbe2020-03-26 09:26:55 -060036
Andrew Lamb2413c982020-05-29 12:15:36 -060037
David Burger7fd1dbe2020-03-26 09:26:55 -060038def ParseArgs(argv):
39 """Parse the available arguments.
40
41 Invalid arguments or -h cause this function to print a message and exit.
42
43 Args:
44 argv: List of string arguments (excluding program name / argv[0])
45
46 Returns:
47 argparse.Namespace object containing the attributes.
48 """
49 parser = argparse.ArgumentParser(
50 description='Converts source proto config into platform JSON config.')
51 parser.add_argument(
52 '-c',
53 '--project_configs',
54 nargs='+',
55 type=str,
56 help='Space delimited list of source protobinary project config files.')
57 parser.add_argument(
58 '-p',
59 '--program_config',
60 type=str,
61 help='Path to the source program-level protobinary file')
62 parser.add_argument(
Andrew Lamb2413c982020-05-29 12:15:36 -060063 '-o', '--output', type=str, help='Output file that will be generated')
David Burger7fd1dbe2020-03-26 09:26:55 -060064 return parser.parse_args(argv)
65
66
67def _Set(field, target, target_name):
68 if field:
69 target[target_name] = field
70
71
C Shapiro5bf23a72020-04-24 11:40:17 -050072def _BuildArc(config, config_files):
David Burger7fd1dbe2020-03-26 09:26:55 -060073 if config.build_target.arc:
74 build_properties = {
75 'device': config.build_target.arc.device,
76 'first-api-level': config.build_target.arc.first_api_level,
77 'marketing-name': config.device_brand.brand_name,
78 'metrics-tag': config.hw_design.name.lower(),
Andrew Lambb47b7dc2020-04-07 10:20:32 -060079 'product': config.build_target.id.value,
David Burger7fd1dbe2020-03-26 09:26:55 -060080 }
81 if config.oem:
82 build_properties['oem'] = config.oem.name
Andrew Lamb2413c982020-05-29 12:15:36 -060083 result = {'build-properties': build_properties}
C Shapiro5bf23a72020-04-24 11:40:17 -050084 feature_id = _ArcHardwareFeatureId(config.hw_design_config)
85 if feature_id in config_files.arc_hw_features:
86 result['hardware-features'] = config_files.arc_hw_features[feature_id]
C Shapiroa3f202d2020-05-19 08:18:45 -050087 topology = config.hw_design_config.hardware_topology
88 ppi = topology.screen.hardware_feature.screen.panel_properties.pixels_per_in
89 # Only set for high resolution displays
90 if ppi and ppi > 250:
91 result['scale'] = ppi
C Shapiro5bf23a72020-04-24 11:40:17 -050092 return result
David Burger7fd1dbe2020-03-26 09:26:55 -060093
Andrew Lamb2413c982020-05-29 12:15:36 -060094
C Shapiro90fda252020-04-17 14:34:57 -050095def _BuildBluetooth(config, bluetooth_files):
96 bt_flags = config.sw_config.bluetooth_config.flags
97 # Convert to native map (from proto wrapper)
98 bt_flags_map = dict(bt_flags)
99 result = {}
100 if bt_flags_map:
101 result['flags'] = bt_flags_map
C Shapiro74da76e2020-05-04 13:02:20 -0500102 bt_comp = config.hw_design_config.hardware_features.bluetooth.component.usb
C Shapiro90fda252020-04-17 14:34:57 -0500103 if bt_comp.vendor_id:
104 bt_id = _BluetoothId(config.hw_design.name.lower(), bt_comp)
105 if bt_id in bluetooth_files:
106 result['config'] = bluetooth_files[bt_id]
107 return result
108
David Burger7fd1dbe2020-03-26 09:26:55 -0600109
110def _BuildFingerprint(hw_topology):
Andrew Lambc2c55462020-04-06 08:43:34 -0600111 if hw_topology.HasField('fingerprint'):
David Burger7fd1dbe2020-03-26 09:26:55 -0600112 fp = hw_topology.fingerprint.hardware_feature.fingerprint
David Burger92609a32020-04-23 10:38:50 -0600113 result = {}
114 if fp.location != topology_pb2.HardwareFeatures.Fingerprint.NOT_PRESENT:
115 location = fp.Location.DESCRIPTOR.values_by_number[fp.location].name
116 result['sensor-location'] = location.lower().replace('_', '-')
117 if fp.board:
118 result['board'] = fp.board
David Burger7fd1dbe2020-03-26 09:26:55 -0600119 return result
120
121
122def _FwBcsPath(payload):
123 if payload and payload.firmware_image_name:
Andrew Lamb2413c982020-05-29 12:15:36 -0600124 return 'bcs://%s.%d.%d.0.tbz2' % (payload.firmware_image_name,
125 payload.version.major,
126 payload.version.minor)
David Burger7fd1dbe2020-03-26 09:26:55 -0600127
128
129def _FwBuildTarget(payload):
130 if payload:
131 return payload.build_target_name
132
133
134def _BuildFirmware(config):
David Burgerb70b6762020-05-21 12:14:59 -0600135 """Returns firmware config, or None if no build targets."""
Andrew Lamb3da156d2020-04-16 16:00:56 -0600136 fw_payload_config = config.sw_config.firmware
137 fw_build_config = config.sw_config.firmware_build_config
138 main_ro = fw_payload_config.main_ro_payload
139 main_rw = fw_payload_config.main_rw_payload
140 ec_ro = fw_payload_config.ec_ro_payload
141 pd_ro = fw_payload_config.pd_ro_payload
David Burger7fd1dbe2020-03-26 09:26:55 -0600142
143 build_targets = {}
Andrew Lamb3da156d2020-04-16 16:00:56 -0600144
Andrew Lambf8954ee2020-04-21 10:24:40 -0600145 _Set(fw_build_config.build_targets.depthcharge, build_targets, 'depthcharge')
146 _Set(fw_build_config.build_targets.coreboot, build_targets, 'coreboot')
147 _Set(fw_build_config.build_targets.ec, build_targets, 'ec')
148 _Set(
149 list(fw_build_config.build_targets.ec_extras), build_targets, 'ec_extras')
150 _Set(fw_build_config.build_targets.libpayload, build_targets, 'libpayload')
David Burger7fd1dbe2020-03-26 09:26:55 -0600151
David Burgerb70b6762020-05-21 12:14:59 -0600152 if not build_targets:
153 return None
154
David Burger7fd1dbe2020-03-26 09:26:55 -0600155 result = {
156 'bcs-overlay': config.build_target.overlay_name,
157 'build-targets': build_targets,
David Burger7fd1dbe2020-03-26 09:26:55 -0600158 }
Andrew Lamb883fa042020-04-06 11:37:22 -0600159
160 _Set(main_ro.firmware_image_name.lower(), result, 'image-name')
161
Andrew Lamb883fa042020-04-06 11:37:22 -0600162 _Set(_FwBcsPath(main_ro), result, 'main-ro-image')
163 _Set(_FwBcsPath(main_rw), result, 'main-rw-image')
164 _Set(_FwBcsPath(ec_ro), result, 'ec-ro-image')
165 _Set(_FwBcsPath(pd_ro), result, 'pd-ro-image')
David Burger7fd1dbe2020-03-26 09:26:55 -0600166
Andrew Lambf39fbe82020-04-13 16:14:33 -0600167 _Set(
168 config.hw_design_config.hardware_features.fw_config.value,
169 result,
170 'firmware-config',
171 )
172
David Burger7fd1dbe2020-03-26 09:26:55 -0600173 return result
174
175
176def _BuildFwSigning(config):
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500177 if config.sw_config.firmware and config.device_signer_config:
David Burger68e0d142020-05-15 17:29:33 -0600178 hw_design = config.hw_design.name.lower()
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500179 return {
180 'key-id': config.device_signer_config.key_id,
C Shapiro10e9a612020-05-19 17:06:43 -0500181 # TODO(shapiroc): Need to fix for whitelabel.
182 # Whitelabel will collide on unique signature-id values.
David Burger68e0d142020-05-15 17:29:33 -0600183 'signature-id': hw_design,
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500184 }
185 return {}
David Burger7fd1dbe2020-03-26 09:26:55 -0600186
187
188def _File(source, destination):
Andrew Lamb2413c982020-05-29 12:15:36 -0600189 return {'destination': destination, 'source': source}
David Burger7fd1dbe2020-03-26 09:26:55 -0600190
191
192def _BuildAudio(config):
193 alsa_path = '/usr/share/alsa/ucm'
194 cras_path = '/etc/cras'
195 project_name = config.hw_design.name.lower()
David Burger43250662020-05-07 11:21:50 -0600196 program_name = config.program.name.lower()
Andrew Lamb7d536782020-04-07 10:23:55 -0600197 if not config.sw_config.HasField('audio_config'):
David Burger7fd1dbe2020-03-26 09:26:55 -0600198 return {}
199 audio = config.sw_config.audio_config
200 card = audio.card_name
David Burger599ff7b2020-04-06 16:29:31 -0600201 card_with_suffix = audio.card_name
202 if audio.ucm_suffix:
203 card_with_suffix += '.' + audio.ucm_suffix
David Burger7fd1dbe2020-03-26 09:26:55 -0600204 files = []
205 if audio.ucm_file:
Andrew Lamb2413c982020-05-29 12:15:36 -0600206 files.append(
207 _File(audio.ucm_file,
208 '%s/%s/HiFi.conf' % (alsa_path, card_with_suffix)))
David Burger7fd1dbe2020-03-26 09:26:55 -0600209 if audio.ucm_master_file:
Andrew Lamb2413c982020-05-29 12:15:36 -0600210 files.append(
211 _File(audio.ucm_master_file, '%s/%s/%s.conf' %
212 (alsa_path, card_with_suffix, card_with_suffix)))
David Burger7fd1dbe2020-03-26 09:26:55 -0600213 if audio.card_config_file:
Andrew Lamb2413c982020-05-29 12:15:36 -0600214 files.append(
215 _File(audio.card_config_file,
216 '%s/%s/%s' % (cras_path, project_name, card)))
David Burger7fd1dbe2020-03-26 09:26:55 -0600217 if audio.dsp_file:
218 files.append(
David Burger2e254902020-04-02 16:56:01 -0600219 _File(audio.dsp_file, '%s/%s/dsp.ini' % (cras_path, project_name)))
David Burgere1a37492020-05-06 09:29:24 -0600220 if audio.module_file:
221 files.append(
David Burger43250662020-05-07 11:21:50 -0600222 _File(audio.module_file, '/etc/modprobe.d/alsa-%s.conf' % program_name))
David Burgere1a37492020-05-06 09:29:24 -0600223 if audio.board_file:
224 files.append(
225 _File(audio.board_file, '%s/%s/board.ini' % (cras_path, project_name)))
David Burger599ff7b2020-04-06 16:29:31 -0600226
227 result = {
David Burger7fd1dbe2020-03-26 09:26:55 -0600228 'main': {
229 'cras-config-dir': project_name,
230 'files': files,
231 }
232 }
David Burger599ff7b2020-04-06 16:29:31 -0600233 if audio.ucm_suffix:
David Burger03cdcbd2020-04-13 13:54:48 -0600234 result['main']['ucm-suffix'] = audio.ucm_suffix
David Burger599ff7b2020-04-06 16:29:31 -0600235
236 return result
David Burger7fd1dbe2020-03-26 09:26:55 -0600237
238
David Burger8aa8fa32020-04-14 08:30:34 -0600239def _BuildCamera(hw_topology):
240 if hw_topology.HasField('camera'):
241 camera = hw_topology.camera.hardware_feature.camera
242 result = {}
243 if camera.count.value:
244 result['count'] = camera.count.value
245 return result
246
247
Andrew Lamb7806ce92020-04-07 10:22:17 -0600248def _BuildIdentity(hw_scan_config, program, brand_scan_config=None):
David Burger7fd1dbe2020-03-26 09:26:55 -0600249 identity = {}
250 _Set(hw_scan_config.firmware_sku, identity, 'sku-id')
251 _Set(hw_scan_config.smbios_name_match, identity, 'smbios-name-match')
Andrew Lamb7806ce92020-04-07 10:22:17 -0600252 # 'platform-name' is needed to support 'mosys platform name'. Clients should
253 # longer require platform name, but set it here for backwards compatibility.
254 _Set(program.name, identity, 'platform-name')
David Burger7fd1dbe2020-03-26 09:26:55 -0600255 # ARM architecture
256 _Set(hw_scan_config.device_tree_compatible_match, identity,
257 'device-tree-compatible-match')
258
259 if brand_scan_config:
260 _Set(brand_scan_config.whitelabel_tag, identity, 'whitelabel-tag')
261
262 return identity
263
264
265def _Lookup(id_value, id_map):
266 if id_value.value:
267 key = id_value.value
268 if key in id_map:
269 return id_map[id_value.value]
270 error = 'Failed to lookup %s with value: %s' % (
271 id_value.__class__.__name__.replace('Id', ''), key)
272 print(error)
273 print('Check the config contents provided:')
274 pp = pprint.PrettyPrinter(indent=4)
275 pp.pprint(id_map)
276 raise Exception(error)
277
278
C Shapiro2b6d5332020-05-06 17:51:35 -0500279def _BuildTouchFileConfig(config, project_name):
280 partners = dict([(x.id.value, x) for x in config.partners.value])
281 files = []
282 for comp in config.components:
C Shapiro4813be62020-05-13 17:31:58 -0500283 touch = comp.touchscreen
284 # Everything is the same for Touch screen/pad, except different fields
285 if comp.HasField('touchpad'):
286 touch = comp.touchpad
287 if touch.product_id:
C Shapiro2b6d5332020-05-06 17:51:35 -0500288 vendor = _Lookup(comp.manufacturer_id, partners)
289 if not vendor:
Andrew Lamb2413c982020-05-29 12:15:36 -0600290 raise Exception("Manufacturer must be set for touch device %s" %
291 comp.id.value)
C Shapiro2b6d5332020-05-06 17:51:35 -0500292
C Shapiro4813be62020-05-13 17:31:58 -0500293 product_id = touch.product_id
294 fw_version = touch.fw_version
C Shapiro2b6d5332020-05-06 17:51:35 -0500295
C Shapiro5c6fc212020-05-13 16:32:09 -0500296 touch_vendor = vendor.touch_vendor
297 sym_link = touch_vendor.fw_file_format.format(
Andrew Lamb2413c982020-05-29 12:15:36 -0600298 vendor_name=vendor.name,
299 vendor_id=touch_vendor.vendor_id,
300 product_id=product_id,
301 fw_version=fw_version,
302 product_series=touch.product_series)
C Shapiro2b6d5332020-05-06 17:51:35 -0500303
304 file_name = "%s_%s.bin" % (product_id, fw_version)
305 fw_file_path = os.path.join(TOUCH_PATH, vendor.name, file_name)
306
307 if not os.path.exists(fw_file_path):
Andrew Lamb2413c982020-05-29 12:15:36 -0600308 raise Exception("Touchscreen fw bin file doesn't exist at: %s" %
309 fw_file_path)
C Shapiro2b6d5332020-05-06 17:51:35 -0500310
311 files.append({
Andrew Lamb2413c982020-05-29 12:15:36 -0600312 "destination":
313 "/opt/google/touch/firmware/%s_%s" % (vendor.name, file_name),
314 "source":
315 os.path.join(project_name, fw_file_path),
316 "symlink":
317 os.path.join("/lib/firmware", sym_link),
C Shapiro2b6d5332020-05-06 17:51:35 -0500318 })
319
320 result = {}
321 _Set(files, result, 'files')
322 return result
323
324
325def _TransformBuildConfigs(config, config_files=ConfigFiles({}, {}, {}, None)):
David Burger7fd1dbe2020-03-26 09:26:55 -0600326 partners = dict([(x.id.value, x) for x in config.partners.value])
327 programs = dict([(x.id.value, x) for x in config.programs.value])
David Burger7fd1dbe2020-03-26 09:26:55 -0600328 sw_configs = list(config.software_configs)
329 brand_configs = dict([(x.brand_id.value, x) for x in config.brand_configs])
330
C Shapiroa0b766c2020-03-31 08:35:28 -0500331 if len(config.build_targets) != 1:
332 # Artifact of sharing the config_bundle for analysis and transforms.
333 # Integrated analysis of multiple programs/projects it the only time
334 # having multiple build targets would be valid.
335 raise Exception('Single build_target required for transform')
336
David Burger7fd1dbe2020-03-26 09:26:55 -0600337 results = {}
338 for hw_design in config.designs.value:
339 if config.device_brands.value:
Andrew Lamb2413c982020-05-29 12:15:36 -0600340 device_brands = [
341 x for x in config.device_brands.value
342 if x.design_id.value == hw_design.id.value
343 ]
David Burger7fd1dbe2020-03-26 09:26:55 -0600344 else:
345 device_brands = [device_brand_pb2.DeviceBrand()]
346
347 for device_brand in device_brands:
348 # Brand config can be empty since platform JSON config allows it
349 brand_config = brand_config_pb2.BrandConfig()
350 if device_brand.id.value in brand_configs:
351 brand_config = brand_configs[device_brand.id.value]
352
353 for hw_design_config in hw_design.configs:
354 design_id = hw_design_config.id.value
Andrew Lamb2413c982020-05-29 12:15:36 -0600355 sw_config_matches = [
356 x for x in sw_configs if x.design_config_id.value == design_id
357 ]
David Burger7fd1dbe2020-03-26 09:26:55 -0600358 if len(sw_config_matches) == 1:
359 sw_config = sw_config_matches[0]
360 elif len(sw_config_matches) > 1:
361 raise Exception('Multiple software configs found for: %s' % design_id)
362 else:
363 raise Exception('Software config is required for: %s' % design_id)
364
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500365 program = _Lookup(hw_design.program_id, programs)
C Shapiroadefd7c2020-05-19 16:37:21 -0500366 signer_configs_by_design = {}
367 signer_configs_by_brand = {}
368 for signer_config in program.device_signer_configs:
369 design_id = signer_config.design_id.value
370 brand_id = signer_config.brand_id.value
371 if design_id:
372 signer_configs_by_design[design_id] = signer_config
373 elif brand_id:
374 signer_configs_by_brand[brand_id] = signer_config
375 else:
376 raise Exception('No ID found for signer config: %s' % signer_config)
377
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500378 device_signer_config = None
C Shapiroadefd7c2020-05-19 16:37:21 -0500379 if signer_configs_by_design or signer_configs_by_brand:
380 design_id = hw_design.id.value
381 brand_id = device_brand.id.value
382 if design_id in signer_configs_by_design:
383 device_signer_config = signer_configs_by_design[design_id]
384 elif brand_id in signer_configs_by_brand:
385 device_signer_config = signer_configs_by_brand[brand_id]
386 else:
387 # Assume that if signer configs are set, every config is setup
Andrew Lamb2413c982020-05-29 12:15:36 -0600388 raise Exception('Signer config missing for design: %s, brand: %s' %
389 (design_id, brand_id))
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500390
C Shapiro90fda252020-04-17 14:34:57 -0500391 transformed_config = _TransformBuildConfig(
392 Config(
393 program=program,
394 hw_design=hw_design,
395 odm=_Lookup(hw_design.odm_id, partners),
396 hw_design_config=hw_design_config,
397 device_brand=device_brand,
398 device_signer_config=device_signer_config,
399 oem=_Lookup(device_brand.oem_id, partners),
400 sw_config=sw_config,
401 brand_config=brand_config,
Andrew Lamb2413c982020-05-29 12:15:36 -0600402 build_target=config.build_targets[0]), config_files)
David Burger7fd1dbe2020-03-26 09:26:55 -0600403
Andrew Lamb2413c982020-05-29 12:15:36 -0600404 config_json = json.dumps(
405 transformed_config,
406 sort_keys=True,
407 indent=2,
408 separators=(',', ': '))
David Burger7fd1dbe2020-03-26 09:26:55 -0600409
410 if config_json not in results:
411 results[config_json] = transformed_config
412
413 return list(results.values())
414
415
C Shapiro5bf23a72020-04-24 11:40:17 -0500416def _TransformBuildConfig(config, config_files):
David Burger7fd1dbe2020-03-26 09:26:55 -0600417 """Transforms Config instance into target platform JSON schema.
418
419 Args:
420 config: Config namedtuple
C Shapiro5bf23a72020-04-24 11:40:17 -0500421 config_files: Map to look up the generated config files.
David Burger7fd1dbe2020-03-26 09:26:55 -0600422
423 Returns:
424 Unique config payload based on the platform JSON schema.
425 """
426 result = {
Andrew Lamb2413c982020-05-29 12:15:36 -0600427 'identity':
428 _BuildIdentity(config.sw_config.id_scan_config, config.program,
429 config.brand_config.scan_config),
430 'name':
431 config.hw_design.name.lower(),
David Burger7fd1dbe2020-03-26 09:26:55 -0600432 }
433
C Shapiro5bf23a72020-04-24 11:40:17 -0500434 _Set(_BuildArc(config, config_files), result, 'arc')
David Burger7fd1dbe2020-03-26 09:26:55 -0600435 _Set(_BuildAudio(config), result, 'audio')
C Shapiro5bf23a72020-04-24 11:40:17 -0500436 _Set(_BuildBluetooth(config, config_files.bluetooth), result, 'bluetooth')
David Burger7fd1dbe2020-03-26 09:26:55 -0600437 _Set(config.device_brand.brand_code, result, 'brand-code')
Andrew Lamb2413c982020-05-29 12:15:36 -0600438 _Set(
439 _BuildCamera(config.hw_design_config.hardware_topology), result, 'camera')
David Burger7fd1dbe2020-03-26 09:26:55 -0600440 _Set(_BuildFirmware(config), result, 'firmware')
441 _Set(_BuildFwSigning(config), result, 'firmware-signing')
Andrew Lamb2413c982020-05-29 12:15:36 -0600442 _Set(
443 _BuildFingerprint(config.hw_design_config.hardware_topology), result,
444 'fingerprint')
David Burger7fd1dbe2020-03-26 09:26:55 -0600445 power_prefs = config.sw_config.power_config.preferences
446 power_prefs_map = dict(
Andrew Lamb2413c982020-05-29 12:15:36 -0600447 (x.replace('_', '-'), power_prefs[x]) for x in power_prefs)
David Burger7fd1dbe2020-03-26 09:26:55 -0600448 _Set(power_prefs_map, result, 'power')
C Shapiro6830e6c2020-04-29 13:29:56 -0500449 _Set(config_files.dptf_file, result, 'thermal')
C Shapiro2b6d5332020-05-06 17:51:35 -0500450 _Set(config_files.touch_fw, result, 'touch')
David Burger7fd1dbe2020-03-26 09:26:55 -0600451
452 return result
453
454
455def WriteOutput(configs, output=None):
456 """Writes a list of configs to platform JSON format.
457
458 Args:
459 configs: List of config dicts defined in cros_config_schema.yaml
460 output: Target file output (if None, prints to stdout)
461 """
Andrew Lamb2413c982020-05-29 12:15:36 -0600462 json_output = json.dumps({'chromeos': {
463 'configs': configs,
464 }},
465 sort_keys=True,
466 indent=2,
467 separators=(',', ': '))
David Burger7fd1dbe2020-03-26 09:26:55 -0600468 if output:
469 with open(output, 'w') as output_stream:
470 # Using print function adds proper trailing newline.
471 print(json_output, file=output_stream)
472 else:
473 print(json_output)
474
475
C Shapiro90fda252020-04-17 14:34:57 -0500476def _BluetoothId(project_name, bt_comp):
Andrew Lamb2413c982020-05-29 12:15:36 -0600477 return '_'.join(
478 [project_name, bt_comp.vendor_id, bt_comp.product_id, bt_comp.bcd_device])
C Shapiro90fda252020-04-17 14:34:57 -0500479
480
C Shapiro5bf23a72020-04-24 11:40:17 -0500481def _Feature(name, present):
482 attrib = {'name': name}
483 if present:
484 return etree.Element('feature', attrib=attrib)
485 else:
486 return etree.Element('unavailable-feature', attrib=attrib)
487
488
489def _AnyPresent(features):
Andrew Lamb2413c982020-05-29 12:15:36 -0600490 return topology_pb2.HardwareFeatures.PRESENT in features
C Shapiro5bf23a72020-04-24 11:40:17 -0500491
492
493def _ArcHardwareFeatureId(design_config):
494 return design_config.id.value.lower().replace(':', '_')
495
496
C Shapiroea33cff2020-05-11 13:32:05 -0500497def _WriteArcHardwareFeatureFile(output_dir, file_name, config_content):
David Burger77a1d312020-05-23 16:05:45 -0600498 output_dir += '/arc'
499 os.makedirs(output_dir, exist_ok=True)
500 output = '%s/%s' % (output_dir, file_name)
Andrew Lamb2413c982020-05-29 12:15:36 -0600501 file_content = minidom.parseString(config_content).toprettyxml(
502 indent=' ', encoding='utf-8')
C Shapiroea33cff2020-05-11 13:32:05 -0500503
504 with open(output, 'wb') as f:
505 f.write(file_content)
506
507
C Shapiro5c877992020-04-29 12:11:28 -0500508def WriteArcHardwareFeatureFiles(config, output_dir, build_root_dir):
C Shapiro5bf23a72020-04-24 11:40:17 -0500509 """Writes ARC hardware_feature.xml files for each config
510
511 Args:
512 config: Source ConfigBundle to process.
513 output_dir: Path to the generated output.
C Shapiro5c877992020-04-29 12:11:28 -0500514 build_root_path: Path to the config file from portage's perspective.
C Shapiro5bf23a72020-04-24 11:40:17 -0500515 Returns:
516 dict that maps the design_config_id onto the correct file.
517 """
C Shapiro5bf23a72020-04-24 11:40:17 -0500518 result = {}
C Shapiroea33cff2020-05-11 13:32:05 -0500519 configs_by_design = {}
C Shapiro5bf23a72020-04-24 11:40:17 -0500520 for hw_design in config.designs.value:
521 for design_config in hw_design.configs:
522 hw_features = design_config.hardware_features
523 multi_camera = hw_features.camera.count == 2
524 touchscreen = _AnyPresent([hw_features.screen.touch_support])
525 acc = hw_features.accelerometer
526 gyro = hw_features.gyroscope
527 compass = hw_features.magnetometer
528 ls = hw_features.light_sensor
529 root = etree.Element('permissions')
530 root.extend([
531 _Feature('android.hardware.camera', multi_camera),
532 _Feature('android.hardware.camera.autofocus', multi_camera),
533 _Feature('android.hardware.sensor.accelerometer',
Andrew Lamb2413c982020-05-29 12:15:36 -0600534 _AnyPresent([acc.lid_accelerometer,
535 acc.base_accelerometer])),
C Shapiro5bf23a72020-04-24 11:40:17 -0500536 _Feature('android.hardware.sensor.gyroscope',
Andrew Lamb2413c982020-05-29 12:15:36 -0600537 _AnyPresent([gyro.lid_gyroscope, gyro.base_gyroscope])),
538 _Feature(
539 'android.hardware.sensor.compass',
540 _AnyPresent([compass.lid_magnetometer,
541 compass.base_magnetometer])),
C Shapiro5bf23a72020-04-24 11:40:17 -0500542 _Feature('android.hardware.sensor.light',
Andrew Lamb2413c982020-05-29 12:15:36 -0600543 _AnyPresent([ls.lid_lightsensor, ls.base_lightsensor])),
C Shapiro5bf23a72020-04-24 11:40:17 -0500544 _Feature('android.hardware.touchscreen', touchscreen),
545 _Feature('android.hardware.touchscreen.multitouch', touchscreen),
Andrew Lamb2413c982020-05-29 12:15:36 -0600546 _Feature('android.hardware.touchscreen.multitouch.distinct',
547 touchscreen),
548 _Feature('android.hardware.touchscreen.multitouch.jazzhand',
549 touchscreen),
C Shapiro5bf23a72020-04-24 11:40:17 -0500550 ])
551
C Shapiroea33cff2020-05-11 13:32:05 -0500552 design_name = hw_design.name.lower()
C Shapiro5bf23a72020-04-24 11:40:17 -0500553
C Shapiroea33cff2020-05-11 13:32:05 -0500554 # Constructs the following map:
555 # design_name -> config -> design_configs
556 # This allows any of the following file naming schemes:
557 # - All configs within a design share config (design_name prefix only)
558 # - Nobody shares (full design_name and config id prefix needed)
559 #
560 # Having shared configs when possible makes code reviews easier around
561 # the configs and makes debugging easier on the platform side.
562 config_content = etree.tostring(root)
563 arc_configs = configs_by_design.get(design_name, {})
564 design_configs = arc_configs.get(config_content, [])
565 design_configs.append(design_config)
566 arc_configs[config_content] = design_configs
567 configs_by_design[design_name] = arc_configs
C Shapiro9a3ac8c2020-04-25 07:49:21 -0500568
C Shapiroea33cff2020-05-11 13:32:05 -0500569 for design_name, unique_configs in configs_by_design.items():
570 for file_content, design_configs in unique_configs.items():
Andrew Lamb2413c982020-05-29 12:15:36 -0600571 file_name = 'hardware_features_%s.xml' % design_name
572 if len(unique_configs) == 1:
573 _WriteArcHardwareFeatureFile(output_dir, file_name, file_content)
C Shapiro9a3ac8c2020-04-25 07:49:21 -0500574
Andrew Lamb2413c982020-05-29 12:15:36 -0600575 for design_config in design_configs:
576 feature_id = _ArcHardwareFeatureId(design_config)
577 if len(unique_configs) > 1:
578 file_name = 'hardware_features_%s.xml' % feature_id
579 _WriteArcHardwareFeatureFile(output_dir, file_name, file_content)
580 result[feature_id] = {
581 'build-path': '%s/arc/%s' % (build_root_dir, file_name),
582 'system-path': '/etc/%s' % file_name,
583 }
C Shapiro5bf23a72020-04-24 11:40:17 -0500584 return result
585
586
C Shapiro5c877992020-04-29 12:11:28 -0500587def WriteBluetoothConfigFiles(config, output_dir, build_root_path):
C Shapiro90fda252020-04-17 14:34:57 -0500588 """Writes bluetooth conf files for every unique bluetooth chip.
589
590 Args:
591 config: Source ConfigBundle to process.
592 output_dir: Path to the generated output.
C Shapiro5c877992020-04-29 12:11:28 -0500593 build_root_path: Path to the config file from portage's perspective.
C Shapiro90fda252020-04-17 14:34:57 -0500594 Returns:
595 dict that maps the bluetooth component id onto the file config.
596 """
David Burger77a1d312020-05-23 16:05:45 -0600597 output_dir += '/bluetooth'
C Shapiro90fda252020-04-17 14:34:57 -0500598 result = {}
599 for hw_design in config.designs.value:
600 project_name = hw_design.name.lower()
601 for design_config in hw_design.configs:
C Shapiro74da76e2020-05-04 13:02:20 -0500602 bt_comp = design_config.hardware_features.bluetooth.component.usb
C Shapiro90fda252020-04-17 14:34:57 -0500603 if bt_comp.vendor_id:
604 bt_id = _BluetoothId(project_name, bt_comp)
605 result[bt_id] = {
C Shapiro5c877992020-04-29 12:11:28 -0500606 'build-path': '%s/bluetooth/%s.conf' % (build_root_path, bt_id),
C Shapiro90fda252020-04-17 14:34:57 -0500607 'system-path': '/etc/bluetooth/%s/main.conf' % bt_id,
608 }
609 bt_content = '''[General]
Andrew Lamb2413c982020-05-29 12:15:36 -0600610DeviceID = bluetooth:%s:%s:%s''' % (bt_comp.vendor_id, bt_comp.product_id,
C Shapiro90fda252020-04-17 14:34:57 -0500611 bt_comp.bcd_device)
612
David Burger77a1d312020-05-23 16:05:45 -0600613 os.makedirs(output_dir, exist_ok=True)
614 output = '%s/%s.conf' % (output_dir, bt_id)
C Shapiro90fda252020-04-17 14:34:57 -0500615 with open(output, 'w') as output_stream:
616 # Using print function adds proper trailing newline.
617 print(bt_content, file=output_stream)
618 return result
619
620
David Burger7fd1dbe2020-03-26 09:26:55 -0600621def _ReadConfig(path):
David Burgerd4f32962020-05-02 12:07:40 -0600622 """Reads a ConfigBundle proto from a json pb file.
David Burgere6f76222020-04-27 11:08:01 -0600623
624 Args:
David Burgerd4f32962020-05-02 12:07:40 -0600625 path: Path to the file encoding the json pb proto.
David Burgere6f76222020-04-27 11:08:01 -0600626 """
627 config = config_bundle_pb2.ConfigBundle()
628 with open(path, 'r') as f:
629 return json_format.Parse(f.read(), config)
630
631
David Burger7fd1dbe2020-03-26 09:26:55 -0600632def _MergeConfigs(configs):
633 result = config_bundle_pb2.ConfigBundle()
634 for config in configs:
635 result.MergeFrom(config)
636
637 return result
638
639
Andrew Lamb2413c982020-05-29 12:15:36 -0600640def Main(project_configs, program_config, output):
David Burger7fd1dbe2020-03-26 09:26:55 -0600641 """Transforms source proto config into platform JSON.
642
643 Args:
644 project_configs: List of source project configs to transform.
645 program_config: Program config for the given set of projects.
646 output: Output file that will be generated by the transform.
647 """
Andrew Lamb2413c982020-05-29 12:15:36 -0600648 configs = _MergeConfigs([_ReadConfig(program_config)] +
649 [_ReadConfig(config) for config in project_configs])
C Shapiro5bf23a72020-04-24 11:40:17 -0500650 bluetooth_files = {}
651 arc_hw_feature_files = {}
C Shapiro2b6d5332020-05-06 17:51:35 -0500652 touch_fw = {}
C Shapiro6830e6c2020-04-29 13:29:56 -0500653 dptf_file = None
C Shapiro5bf23a72020-04-24 11:40:17 -0500654 output_dir = os.path.dirname(output)
C Shapiro5c877992020-04-29 12:11:28 -0500655 build_root_dir = output_dir
C Shapiro5c877992020-04-29 12:11:28 -0500656 if 'sw_build_config' in output_dir:
657 full_path = os.path.realpath(output)
Andrew Lamb2413c982020-05-29 12:15:36 -0600658 project_name = re.match(r'.*/(\w*)/sw_build_config/.*',
659 full_path).groups(1)[0]
C Shapiro5c877992020-04-29 12:11:28 -0500660 # Projects don't know about each other until they are integrated into the
661 # build system. When this happens, the files need to be able to co-exist
662 # without any collisions. This prefixes the project name (which is how
663 # portage maps in the project), so project files co-exist and can be
664 # installed together.
665 # This is necessary to allow projects to share files at the program level
666 # without having portage file installation collisions.
667 build_root_dir = os.path.join(project_name, output_dir)
C Shapiro6830e6c2020-04-29 13:29:56 -0500668
C Shapiro7356bd62020-05-02 05:21:33 -0500669 if os.path.exists(DPTF_PATH):
670 project_dptf_path = os.path.join(project_name, 'dptf.dv')
671 dptf_file = {
Andrew Lamb2413c982020-05-29 12:15:36 -0600672 'dptf-dv':
673 project_dptf_path,
674 'files': [
675 _File(
676 os.path.join(project_name, DPTF_PATH),
677 os.path.join('/etc/dptf', project_dptf_path))
678 ]
C Shapiro7356bd62020-05-02 05:21:33 -0500679 }
C Shapiro2b6d5332020-05-06 17:51:35 -0500680 if os.path.exists(TOUCH_PATH):
681 touch_fw = _BuildTouchFileConfig(configs, project_name)
Andrew Lamb2413c982020-05-29 12:15:36 -0600682 bluetooth_files = WriteBluetoothConfigFiles(configs, output_dir,
683 build_root_dir)
684 arc_hw_feature_files = WriteArcHardwareFeatureFiles(configs, output_dir,
685 build_root_dir)
C Shapiro5bf23a72020-04-24 11:40:17 -0500686 config_files = ConfigFiles(
687 bluetooth=bluetooth_files,
688 arc_hw_features=arc_hw_feature_files,
C Shapiro2b6d5332020-05-06 17:51:35 -0500689 touch_fw=touch_fw,
Andrew Lamb2413c982020-05-29 12:15:36 -0600690 dptf_file=dptf_file)
C Shapiro5bf23a72020-04-24 11:40:17 -0500691 WriteOutput(_TransformBuildConfigs(configs, config_files), output)
David Burger7fd1dbe2020-03-26 09:26:55 -0600692
693
694def main(argv=None):
695 """Main program which parses args and runs
696
697 Args:
698 argv: List of command line arguments, if None uses sys.argv.
699 """
700 if argv is None:
701 argv = sys.argv[1:]
702 opts = ParseArgs(argv)
703 Main(opts.project_configs, opts.program_config, opts.output)
704
705
706if __name__ == '__main__':
707 sys.exit(main(sys.argv[1:]))