blob: 40e58f24aed1a940fb38841228d56a634343c029 [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
David Burger7fd1dbe2020-03-26 09:26:55 -060026Config = namedtuple('Config',
27 ['program',
28 'hw_design',
29 'odm',
30 'hw_design_config',
31 'device_brand',
C Shapiro2f0bb5d2020-04-14 10:07:47 -050032 'device_signer_config',
David Burger7fd1dbe2020-03-26 09:26:55 -060033 'oem',
34 'sw_config',
35 'brand_config',
36 'build_target'])
37
C Shapiro5bf23a72020-04-24 11:40:17 -050038ConfigFiles = namedtuple('ConfigFiles',
39 ['bluetooth',
C Shapiro6830e6c2020-04-29 13:29:56 -050040 'arc_hw_features',
C Shapiro2b6d5332020-05-06 17:51:35 -050041 'touch_fw',
C Shapiro6830e6c2020-04-29 13:29:56 -050042 'dptf_file'])
C Shapiro5bf23a72020-04-24 11:40:17 -050043
C Shapiro6830e6c2020-04-29 13:29:56 -050044DPTF_PATH = 'sw_build_config/platform/chromeos-config/thermal/dptf.dv'
C Shapiro2b6d5332020-05-06 17:51:35 -050045TOUCH_PATH = 'sw_build_config/platform/chromeos-config/touch'
David Burger7fd1dbe2020-03-26 09:26:55 -060046
47def ParseArgs(argv):
48 """Parse the available arguments.
49
50 Invalid arguments or -h cause this function to print a message and exit.
51
52 Args:
53 argv: List of string arguments (excluding program name / argv[0])
54
55 Returns:
56 argparse.Namespace object containing the attributes.
57 """
58 parser = argparse.ArgumentParser(
59 description='Converts source proto config into platform JSON config.')
60 parser.add_argument(
61 '-c',
62 '--project_configs',
63 nargs='+',
64 type=str,
65 help='Space delimited list of source protobinary project config files.')
66 parser.add_argument(
67 '-p',
68 '--program_config',
69 type=str,
70 help='Path to the source program-level protobinary file')
71 parser.add_argument(
72 '-o',
73 '--output',
74 type=str,
75 help='Output file that will be generated')
76 return parser.parse_args(argv)
77
78
79def _Set(field, target, target_name):
80 if field:
81 target[target_name] = field
82
83
C Shapiro5bf23a72020-04-24 11:40:17 -050084def _BuildArc(config, config_files):
David Burger7fd1dbe2020-03-26 09:26:55 -060085 if config.build_target.arc:
86 build_properties = {
87 'device': config.build_target.arc.device,
88 'first-api-level': config.build_target.arc.first_api_level,
89 'marketing-name': config.device_brand.brand_name,
90 'metrics-tag': config.hw_design.name.lower(),
Andrew Lambb47b7dc2020-04-07 10:20:32 -060091 'product': config.build_target.id.value,
David Burger7fd1dbe2020-03-26 09:26:55 -060092 }
93 if config.oem:
94 build_properties['oem'] = config.oem.name
C Shapiro5bf23a72020-04-24 11:40:17 -050095 result = {
96 'build-properties': build_properties
97 }
98 feature_id = _ArcHardwareFeatureId(config.hw_design_config)
99 if feature_id in config_files.arc_hw_features:
100 result['hardware-features'] = config_files.arc_hw_features[feature_id]
C Shapiroa3f202d2020-05-19 08:18:45 -0500101 topology = config.hw_design_config.hardware_topology
102 ppi = topology.screen.hardware_feature.screen.panel_properties.pixels_per_in
103 # Only set for high resolution displays
104 if ppi and ppi > 250:
105 result['scale'] = ppi
C Shapiro5bf23a72020-04-24 11:40:17 -0500106 return result
David Burger7fd1dbe2020-03-26 09:26:55 -0600107
C Shapiro90fda252020-04-17 14:34:57 -0500108def _BuildBluetooth(config, bluetooth_files):
109 bt_flags = config.sw_config.bluetooth_config.flags
110 # Convert to native map (from proto wrapper)
111 bt_flags_map = dict(bt_flags)
112 result = {}
113 if bt_flags_map:
114 result['flags'] = bt_flags_map
C Shapiro74da76e2020-05-04 13:02:20 -0500115 bt_comp = config.hw_design_config.hardware_features.bluetooth.component.usb
C Shapiro90fda252020-04-17 14:34:57 -0500116 if bt_comp.vendor_id:
117 bt_id = _BluetoothId(config.hw_design.name.lower(), bt_comp)
118 if bt_id in bluetooth_files:
119 result['config'] = bluetooth_files[bt_id]
120 return result
121
David Burger7fd1dbe2020-03-26 09:26:55 -0600122
123def _BuildFingerprint(hw_topology):
Andrew Lambc2c55462020-04-06 08:43:34 -0600124 if hw_topology.HasField('fingerprint'):
David Burger7fd1dbe2020-03-26 09:26:55 -0600125 fp = hw_topology.fingerprint.hardware_feature.fingerprint
David Burger92609a32020-04-23 10:38:50 -0600126 result = {}
127 if fp.location != topology_pb2.HardwareFeatures.Fingerprint.NOT_PRESENT:
128 location = fp.Location.DESCRIPTOR.values_by_number[fp.location].name
129 result['sensor-location'] = location.lower().replace('_', '-')
130 if fp.board:
131 result['board'] = fp.board
David Burger7fd1dbe2020-03-26 09:26:55 -0600132 return result
133
134
135def _FwBcsPath(payload):
136 if payload and payload.firmware_image_name:
137 return 'bcs://%s.%d.%d.0.tbz2' % (
138 payload.firmware_image_name,
139 payload.version.major,
140 payload.version.minor)
141
142
143def _FwBuildTarget(payload):
144 if payload:
145 return payload.build_target_name
146
147
148def _BuildFirmware(config):
Andrew Lamb3da156d2020-04-16 16:00:56 -0600149 fw_payload_config = config.sw_config.firmware
150 fw_build_config = config.sw_config.firmware_build_config
151 main_ro = fw_payload_config.main_ro_payload
152 main_rw = fw_payload_config.main_rw_payload
153 ec_ro = fw_payload_config.ec_ro_payload
154 pd_ro = fw_payload_config.pd_ro_payload
David Burger7fd1dbe2020-03-26 09:26:55 -0600155
156 build_targets = {}
Andrew Lamb3da156d2020-04-16 16:00:56 -0600157
Andrew Lambf8954ee2020-04-21 10:24:40 -0600158 _Set(fw_build_config.build_targets.depthcharge, build_targets, 'depthcharge')
159 _Set(fw_build_config.build_targets.coreboot, build_targets, 'coreboot')
160 _Set(fw_build_config.build_targets.ec, build_targets, 'ec')
161 _Set(
162 list(fw_build_config.build_targets.ec_extras), build_targets, 'ec_extras')
163 _Set(fw_build_config.build_targets.libpayload, build_targets, 'libpayload')
David Burger7fd1dbe2020-03-26 09:26:55 -0600164
165 result = {
166 'bcs-overlay': config.build_target.overlay_name,
167 'build-targets': build_targets,
David Burger7fd1dbe2020-03-26 09:26:55 -0600168 }
Andrew Lamb883fa042020-04-06 11:37:22 -0600169
170 _Set(main_ro.firmware_image_name.lower(), result, 'image-name')
171
Andrew Lamb883fa042020-04-06 11:37:22 -0600172 _Set(_FwBcsPath(main_ro), result, 'main-ro-image')
173 _Set(_FwBcsPath(main_rw), result, 'main-rw-image')
174 _Set(_FwBcsPath(ec_ro), result, 'ec-ro-image')
175 _Set(_FwBcsPath(pd_ro), result, 'pd-ro-image')
David Burger7fd1dbe2020-03-26 09:26:55 -0600176
Andrew Lambf39fbe82020-04-13 16:14:33 -0600177 _Set(
178 config.hw_design_config.hardware_features.fw_config.value,
179 result,
180 'firmware-config',
181 )
182
David Burger7fd1dbe2020-03-26 09:26:55 -0600183 return result
184
185
186def _BuildFwSigning(config):
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500187 if config.sw_config.firmware and config.device_signer_config:
David Burger68e0d142020-05-15 17:29:33 -0600188 program = config.program.id.value
189 hw_design = config.hw_design.name.lower()
David Burger4a344652020-05-15 19:45:26 -0600190 brand_code = config.device_brand.brand_code
191 if program == 'Zork' and brand_code == 'ZZCR':
192 # TODO(https://crbug.com/1070814): Hack!!!, Zork projects that do not have
193 # their own brand-code do not share signing keys. Thus this hack for now.
David Burgerf762dee2020-05-17 05:58:58 -0600194 # TODO(https://crbug.com/1083770): Also, Berknip signing keys not present
195 # in signing server, special case.
196 key_id = 'TREMBYLE' if hw_design == 'berknip' else hw_design.upper()
David Burger68e0d142020-05-15 17:29:33 -0600197 return {
David Burgerf762dee2020-05-17 05:58:58 -0600198 'key-id': key_id,
David Burger68e0d142020-05-15 17:29:33 -0600199 'signature-id': hw_design,
200 }
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500201 return {
202 'key-id': config.device_signer_config.key_id,
David Burger68e0d142020-05-15 17:29:33 -0600203 'signature-id': hw_design,
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500204 }
205 return {}
David Burger7fd1dbe2020-03-26 09:26:55 -0600206
207
208def _File(source, destination):
209 return {
210 'destination': destination,
211 'source': source
212 }
213
214
215def _BuildAudio(config):
216 alsa_path = '/usr/share/alsa/ucm'
217 cras_path = '/etc/cras'
218 project_name = config.hw_design.name.lower()
David Burger43250662020-05-07 11:21:50 -0600219 program_name = config.program.name.lower()
Andrew Lamb7d536782020-04-07 10:23:55 -0600220 if not config.sw_config.HasField('audio_config'):
David Burger7fd1dbe2020-03-26 09:26:55 -0600221 return {}
222 audio = config.sw_config.audio_config
223 card = audio.card_name
David Burger599ff7b2020-04-06 16:29:31 -0600224 card_with_suffix = audio.card_name
225 if audio.ucm_suffix:
226 card_with_suffix += '.' + audio.ucm_suffix
David Burger7fd1dbe2020-03-26 09:26:55 -0600227 files = []
228 if audio.ucm_file:
David Burger599ff7b2020-04-06 16:29:31 -0600229 files.append(_File(
230 audio.ucm_file,
231 '%s/%s/HiFi.conf' % (alsa_path, card_with_suffix)))
David Burger7fd1dbe2020-03-26 09:26:55 -0600232 if audio.ucm_master_file:
233 files.append(_File(
David Burger599ff7b2020-04-06 16:29:31 -0600234 audio.ucm_master_file,
235 '%s/%s/%s.conf' % (alsa_path, card_with_suffix, card_with_suffix)))
David Burger7fd1dbe2020-03-26 09:26:55 -0600236 if audio.card_config_file:
237 files.append(_File(
238 audio.card_config_file, '%s/%s/%s' % (cras_path, project_name, card)))
239 if audio.dsp_file:
240 files.append(
David Burger2e254902020-04-02 16:56:01 -0600241 _File(audio.dsp_file, '%s/%s/dsp.ini' % (cras_path, project_name)))
David Burgere1a37492020-05-06 09:29:24 -0600242 if audio.module_file:
243 files.append(
David Burger43250662020-05-07 11:21:50 -0600244 _File(audio.module_file, '/etc/modprobe.d/alsa-%s.conf' % program_name))
David Burgere1a37492020-05-06 09:29:24 -0600245 if audio.board_file:
246 files.append(
247 _File(audio.board_file, '%s/%s/board.ini' % (cras_path, project_name)))
David Burger599ff7b2020-04-06 16:29:31 -0600248
249 result = {
David Burger7fd1dbe2020-03-26 09:26:55 -0600250 'main': {
251 'cras-config-dir': project_name,
252 'files': files,
253 }
254 }
David Burger599ff7b2020-04-06 16:29:31 -0600255 if audio.ucm_suffix:
David Burger03cdcbd2020-04-13 13:54:48 -0600256 result['main']['ucm-suffix'] = audio.ucm_suffix
David Burger599ff7b2020-04-06 16:29:31 -0600257
258 return result
David Burger7fd1dbe2020-03-26 09:26:55 -0600259
260
David Burger8aa8fa32020-04-14 08:30:34 -0600261def _BuildCamera(hw_topology):
262 if hw_topology.HasField('camera'):
263 camera = hw_topology.camera.hardware_feature.camera
264 result = {}
265 if camera.count.value:
266 result['count'] = camera.count.value
267 return result
268
269
Andrew Lamb7806ce92020-04-07 10:22:17 -0600270def _BuildIdentity(hw_scan_config, program, brand_scan_config=None):
David Burger7fd1dbe2020-03-26 09:26:55 -0600271 identity = {}
272 _Set(hw_scan_config.firmware_sku, identity, 'sku-id')
273 _Set(hw_scan_config.smbios_name_match, identity, 'smbios-name-match')
Andrew Lamb7806ce92020-04-07 10:22:17 -0600274 # 'platform-name' is needed to support 'mosys platform name'. Clients should
275 # longer require platform name, but set it here for backwards compatibility.
276 _Set(program.name, identity, 'platform-name')
David Burger7fd1dbe2020-03-26 09:26:55 -0600277 # ARM architecture
278 _Set(hw_scan_config.device_tree_compatible_match, identity,
279 'device-tree-compatible-match')
280
281 if brand_scan_config:
282 _Set(brand_scan_config.whitelabel_tag, identity, 'whitelabel-tag')
283
284 return identity
285
286
287def _Lookup(id_value, id_map):
288 if id_value.value:
289 key = id_value.value
290 if key in id_map:
291 return id_map[id_value.value]
292 error = 'Failed to lookup %s with value: %s' % (
293 id_value.__class__.__name__.replace('Id', ''), key)
294 print(error)
295 print('Check the config contents provided:')
296 pp = pprint.PrettyPrinter(indent=4)
297 pp.pprint(id_map)
298 raise Exception(error)
299
300
C Shapiro2b6d5332020-05-06 17:51:35 -0500301def _BuildTouchFileConfig(config, project_name):
302 partners = dict([(x.id.value, x) for x in config.partners.value])
303 files = []
304 for comp in config.components:
C Shapiro4813be62020-05-13 17:31:58 -0500305 touch = comp.touchscreen
306 # Everything is the same for Touch screen/pad, except different fields
307 if comp.HasField('touchpad'):
308 touch = comp.touchpad
309 if touch.product_id:
C Shapiro2b6d5332020-05-06 17:51:35 -0500310 vendor = _Lookup(comp.manufacturer_id, partners)
311 if not vendor:
312 raise Exception(
C Shapiro4813be62020-05-13 17:31:58 -0500313 "Manufacturer must be set for touch device %s" % comp.id.value)
C Shapiro2b6d5332020-05-06 17:51:35 -0500314
C Shapiro4813be62020-05-13 17:31:58 -0500315 product_id = touch.product_id
316 fw_version = touch.fw_version
C Shapiro2b6d5332020-05-06 17:51:35 -0500317
C Shapiro5c6fc212020-05-13 16:32:09 -0500318 touch_vendor = vendor.touch_vendor
319 sym_link = touch_vendor.fw_file_format.format(
C Shapiro2b6d5332020-05-06 17:51:35 -0500320 vendor_name = vendor.name,
C Shapiro5c6fc212020-05-13 16:32:09 -0500321 vendor_id = touch_vendor.vendor_id,
C Shapiro2b6d5332020-05-06 17:51:35 -0500322 product_id = product_id,
323 fw_version = fw_version,
C Shapiro4813be62020-05-13 17:31:58 -0500324 product_series = touch.product_series
C Shapiro2b6d5332020-05-06 17:51:35 -0500325 )
326
327 file_name = "%s_%s.bin" % (product_id, fw_version)
328 fw_file_path = os.path.join(TOUCH_PATH, vendor.name, file_name)
329
330 if not os.path.exists(fw_file_path):
331 raise Exception(
332 "Touchscreen fw bin file doesn't exist at: %s" % fw_file_path)
333
334 files.append({
335 "destination": "/opt/google/touch/firmware/%s_%s" % (
336 vendor.name, file_name),
337 "source": os.path.join(project_name, fw_file_path),
338 "symlink": os.path.join("/lib/firmware", sym_link),
339 })
340
341 result = {}
342 _Set(files, result, 'files')
343 return result
344
345
346def _TransformBuildConfigs(config, config_files=ConfigFiles({}, {}, {}, None)):
David Burger7fd1dbe2020-03-26 09:26:55 -0600347 partners = dict([(x.id.value, x) for x in config.partners.value])
348 programs = dict([(x.id.value, x) for x in config.programs.value])
David Burger7fd1dbe2020-03-26 09:26:55 -0600349 sw_configs = list(config.software_configs)
350 brand_configs = dict([(x.brand_id.value, x) for x in config.brand_configs])
351
C Shapiroa0b766c2020-03-31 08:35:28 -0500352 if len(config.build_targets) != 1:
353 # Artifact of sharing the config_bundle for analysis and transforms.
354 # Integrated analysis of multiple programs/projects it the only time
355 # having multiple build targets would be valid.
356 raise Exception('Single build_target required for transform')
357
David Burger7fd1dbe2020-03-26 09:26:55 -0600358 results = {}
359 for hw_design in config.designs.value:
360 if config.device_brands.value:
361 device_brands = [x for x in config.device_brands.value
362 if x.design_id.value == hw_design.id.value]
363 else:
364 device_brands = [device_brand_pb2.DeviceBrand()]
365
366 for device_brand in device_brands:
367 # Brand config can be empty since platform JSON config allows it
368 brand_config = brand_config_pb2.BrandConfig()
369 if device_brand.id.value in brand_configs:
370 brand_config = brand_configs[device_brand.id.value]
371
372 for hw_design_config in hw_design.configs:
373 design_id = hw_design_config.id.value
374 sw_config_matches = [x for x in sw_configs
375 if x.design_config_id.value == design_id]
376 if len(sw_config_matches) == 1:
377 sw_config = sw_config_matches[0]
378 elif len(sw_config_matches) > 1:
379 raise Exception('Multiple software configs found for: %s' % design_id)
380 else:
381 raise Exception('Software config is required for: %s' % design_id)
382
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500383 program = _Lookup(hw_design.program_id, programs)
C Shapiroadefd7c2020-05-19 16:37:21 -0500384 signer_configs_by_design = {}
385 signer_configs_by_brand = {}
386 for signer_config in program.device_signer_configs:
387 design_id = signer_config.design_id.value
388 brand_id = signer_config.brand_id.value
389 if design_id:
390 signer_configs_by_design[design_id] = signer_config
391 elif brand_id:
392 signer_configs_by_brand[brand_id] = signer_config
393 else:
394 raise Exception('No ID found for signer config: %s' % signer_config)
395
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500396 device_signer_config = None
C Shapiroadefd7c2020-05-19 16:37:21 -0500397 if signer_configs_by_design or signer_configs_by_brand:
398 design_id = hw_design.id.value
399 brand_id = device_brand.id.value
400 if design_id in signer_configs_by_design:
401 device_signer_config = signer_configs_by_design[design_id]
402 elif brand_id in signer_configs_by_brand:
403 device_signer_config = signer_configs_by_brand[brand_id]
404 else:
405 # Assume that if signer configs are set, every config is setup
406 raise Exception(
407 'Signer config missing for design: %s, brand: %s' % (
408 design_id, brand_id))
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500409
C Shapiro90fda252020-04-17 14:34:57 -0500410 transformed_config = _TransformBuildConfig(
411 Config(
412 program=program,
413 hw_design=hw_design,
414 odm=_Lookup(hw_design.odm_id, partners),
415 hw_design_config=hw_design_config,
416 device_brand=device_brand,
417 device_signer_config=device_signer_config,
418 oem=_Lookup(device_brand.oem_id, partners),
419 sw_config=sw_config,
420 brand_config=brand_config,
421 build_target=config.build_targets[0]),
C Shapiro5bf23a72020-04-24 11:40:17 -0500422 config_files)
David Burger7fd1dbe2020-03-26 09:26:55 -0600423
424 config_json = json.dumps(transformed_config,
425 sort_keys=True,
426 indent=2,
427 separators=(',', ': '))
428
429 if config_json not in results:
430 results[config_json] = transformed_config
431
432 return list(results.values())
433
434
C Shapiro5bf23a72020-04-24 11:40:17 -0500435def _TransformBuildConfig(config, config_files):
David Burger7fd1dbe2020-03-26 09:26:55 -0600436 """Transforms Config instance into target platform JSON schema.
437
438 Args:
439 config: Config namedtuple
C Shapiro5bf23a72020-04-24 11:40:17 -0500440 config_files: Map to look up the generated config files.
David Burger7fd1dbe2020-03-26 09:26:55 -0600441
442 Returns:
443 Unique config payload based on the platform JSON schema.
444 """
445 result = {
446 'identity': _BuildIdentity(
447 config.sw_config.id_scan_config,
Andrew Lamb7806ce92020-04-07 10:22:17 -0600448 config.program,
David Burger7fd1dbe2020-03-26 09:26:55 -0600449 config.brand_config.scan_config),
450 'name': config.hw_design.name.lower(),
451 }
452
C Shapiro5bf23a72020-04-24 11:40:17 -0500453 _Set(_BuildArc(config, config_files), result, 'arc')
David Burger7fd1dbe2020-03-26 09:26:55 -0600454 _Set(_BuildAudio(config), result, 'audio')
C Shapiro5bf23a72020-04-24 11:40:17 -0500455 _Set(_BuildBluetooth(config, config_files.bluetooth), result, 'bluetooth')
David Burger7fd1dbe2020-03-26 09:26:55 -0600456 _Set(config.device_brand.brand_code, result, 'brand-code')
David Burger8aa8fa32020-04-14 08:30:34 -0600457 _Set(_BuildCamera(
458 config.hw_design_config.hardware_topology), result, 'camera')
David Burger7fd1dbe2020-03-26 09:26:55 -0600459 _Set(_BuildFirmware(config), result, 'firmware')
460 _Set(_BuildFwSigning(config), result, 'firmware-signing')
461 _Set(_BuildFingerprint(
462 config.hw_design_config.hardware_topology), result, 'fingerprint')
463 power_prefs = config.sw_config.power_config.preferences
464 power_prefs_map = dict(
465 (x.replace('_', '-'),
466 power_prefs[x]) for x in power_prefs)
467 _Set(power_prefs_map, result, 'power')
C Shapiro6830e6c2020-04-29 13:29:56 -0500468 _Set(config_files.dptf_file, result, 'thermal')
C Shapiro2b6d5332020-05-06 17:51:35 -0500469 _Set(config_files.touch_fw, result, 'touch')
David Burger7fd1dbe2020-03-26 09:26:55 -0600470
471 return result
472
473
474def WriteOutput(configs, output=None):
475 """Writes a list of configs to platform JSON format.
476
477 Args:
478 configs: List of config dicts defined in cros_config_schema.yaml
479 output: Target file output (if None, prints to stdout)
480 """
481 json_output = json.dumps(
482 {'chromeos': {
483 'configs': configs,
484 }},
485 sort_keys=True,
486 indent=2,
487 separators=(',', ': '))
488 if output:
489 with open(output, 'w') as output_stream:
490 # Using print function adds proper trailing newline.
491 print(json_output, file=output_stream)
492 else:
493 print(json_output)
494
495
C Shapiro90fda252020-04-17 14:34:57 -0500496def _BluetoothId(project_name, bt_comp):
497 return '_'.join([project_name,
498 bt_comp.vendor_id,
499 bt_comp.product_id,
500 bt_comp.bcd_device])
501
502
C Shapiro5bf23a72020-04-24 11:40:17 -0500503def _Feature(name, present):
504 attrib = {'name': name}
505 if present:
506 return etree.Element('feature', attrib=attrib)
507 else:
508 return etree.Element('unavailable-feature', attrib=attrib)
509
510
511def _AnyPresent(features):
512 return topology_pb2.HardwareFeatures.PRESENT in features;
513
514
515def _ArcHardwareFeatureId(design_config):
516 return design_config.id.value.lower().replace(':', '_')
517
518
C Shapiroea33cff2020-05-11 13:32:05 -0500519def _WriteArcHardwareFeatureFile(output_dir, file_name, config_content):
520 output = '%s/arc/%s' % (output_dir, file_name)
521 file_content = minidom.parseString(
522 config_content).toprettyxml(indent=' ', encoding='utf-8')
523
524 with open(output, 'wb') as f:
525 f.write(file_content)
526
527
C Shapiro5c877992020-04-29 12:11:28 -0500528def WriteArcHardwareFeatureFiles(config, output_dir, build_root_dir):
C Shapiro5bf23a72020-04-24 11:40:17 -0500529 """Writes ARC hardware_feature.xml files for each config
530
531 Args:
532 config: Source ConfigBundle to process.
533 output_dir: Path to the generated output.
C Shapiro5c877992020-04-29 12:11:28 -0500534 build_root_path: Path to the config file from portage's perspective.
C Shapiro5bf23a72020-04-24 11:40:17 -0500535 Returns:
536 dict that maps the design_config_id onto the correct file.
537 """
C Shapiro5bf23a72020-04-24 11:40:17 -0500538 result = {}
C Shapiroea33cff2020-05-11 13:32:05 -0500539 configs_by_design = {}
C Shapiro5bf23a72020-04-24 11:40:17 -0500540 for hw_design in config.designs.value:
541 for design_config in hw_design.configs:
542 hw_features = design_config.hardware_features
543 multi_camera = hw_features.camera.count == 2
544 touchscreen = _AnyPresent([hw_features.screen.touch_support])
545 acc = hw_features.accelerometer
546 gyro = hw_features.gyroscope
547 compass = hw_features.magnetometer
548 ls = hw_features.light_sensor
549 root = etree.Element('permissions')
550 root.extend([
551 _Feature('android.hardware.camera', multi_camera),
552 _Feature('android.hardware.camera.autofocus', multi_camera),
553 _Feature('android.hardware.sensor.accelerometer',
554 _AnyPresent(
555 [acc.lid_accelerometer, acc.base_accelerometer])),
556 _Feature('android.hardware.sensor.gyroscope',
557 _AnyPresent(
558 [gyro.lid_gyroscope, gyro.base_gyroscope])),
559 _Feature('android.hardware.sensor.compass',
560 _AnyPresent(
561 [compass.lid_magnetometer, compass.base_magnetometer])),
562 _Feature('android.hardware.sensor.light',
563 _AnyPresent(
564 [ls.lid_lightsensor, ls.base_lightsensor])),
565 _Feature('android.hardware.touchscreen', touchscreen),
566 _Feature('android.hardware.touchscreen.multitouch', touchscreen),
567 _Feature(
568 'android.hardware.touchscreen.multitouch.distinct', touchscreen),
569 _Feature(
570 'android.hardware.touchscreen.multitouch.jazzhand', touchscreen),
571 ])
572
C Shapiroea33cff2020-05-11 13:32:05 -0500573 design_name = hw_design.name.lower()
C Shapiro5bf23a72020-04-24 11:40:17 -0500574
C Shapiroea33cff2020-05-11 13:32:05 -0500575 # Constructs the following map:
576 # design_name -> config -> design_configs
577 # This allows any of the following file naming schemes:
578 # - All configs within a design share config (design_name prefix only)
579 # - Nobody shares (full design_name and config id prefix needed)
580 #
581 # Having shared configs when possible makes code reviews easier around
582 # the configs and makes debugging easier on the platform side.
583 config_content = etree.tostring(root)
584 arc_configs = configs_by_design.get(design_name, {})
585 design_configs = arc_configs.get(config_content, [])
586 design_configs.append(design_config)
587 arc_configs[config_content] = design_configs
588 configs_by_design[design_name] = arc_configs
C Shapiro9a3ac8c2020-04-25 07:49:21 -0500589
C Shapiroea33cff2020-05-11 13:32:05 -0500590 for design_name, unique_configs in configs_by_design.items():
591 for file_content, design_configs in unique_configs.items():
592 file_name = 'hardware_features_%s.xml' % design_name
593 if len(unique_configs) == 1:
594 _WriteArcHardwareFeatureFile(output_dir, file_name, file_content)
C Shapiro9a3ac8c2020-04-25 07:49:21 -0500595
C Shapiroea33cff2020-05-11 13:32:05 -0500596 for design_config in design_configs:
597 feature_id = _ArcHardwareFeatureId(design_config)
598 if len(unique_configs) > 1:
599 file_name = 'hardware_features_%s.xml' % feature_id
600 _WriteArcHardwareFeatureFile(output_dir, file_name, file_content)
601 result[feature_id] = {
602 'build-path': '%s/arc/%s' % (build_root_dir, file_name),
603 'system-path': '/etc/%s' % file_name,
604 }
C Shapiro5bf23a72020-04-24 11:40:17 -0500605 return result
606
607
C Shapiro5c877992020-04-29 12:11:28 -0500608def WriteBluetoothConfigFiles(config, output_dir, build_root_path):
C Shapiro90fda252020-04-17 14:34:57 -0500609 """Writes bluetooth conf files for every unique bluetooth chip.
610
611 Args:
612 config: Source ConfigBundle to process.
613 output_dir: Path to the generated output.
C Shapiro5c877992020-04-29 12:11:28 -0500614 build_root_path: Path to the config file from portage's perspective.
C Shapiro90fda252020-04-17 14:34:57 -0500615 Returns:
616 dict that maps the bluetooth component id onto the file config.
617 """
C Shapiro90fda252020-04-17 14:34:57 -0500618 result = {}
619 for hw_design in config.designs.value:
620 project_name = hw_design.name.lower()
621 for design_config in hw_design.configs:
C Shapiro74da76e2020-05-04 13:02:20 -0500622 bt_comp = design_config.hardware_features.bluetooth.component.usb
C Shapiro90fda252020-04-17 14:34:57 -0500623 if bt_comp.vendor_id:
624 bt_id = _BluetoothId(project_name, bt_comp)
625 result[bt_id] = {
C Shapiro5c877992020-04-29 12:11:28 -0500626 'build-path': '%s/bluetooth/%s.conf' % (build_root_path, bt_id),
C Shapiro90fda252020-04-17 14:34:57 -0500627 'system-path': '/etc/bluetooth/%s/main.conf' % bt_id,
628 }
629 bt_content = '''[General]
630DeviceID = bluetooth:%s:%s:%s''' % (bt_comp.vendor_id,
631 bt_comp.product_id,
632 bt_comp.bcd_device)
633
634 output = '%s/bluetooth/%s.conf' % (output_dir, bt_id)
635 with open(output, 'w') as output_stream:
636 # Using print function adds proper trailing newline.
637 print(bt_content, file=output_stream)
638 return result
639
640
David Burger7fd1dbe2020-03-26 09:26:55 -0600641def _ReadConfig(path):
David Burgerd4f32962020-05-02 12:07:40 -0600642 """Reads a ConfigBundle proto from a json pb file.
David Burgere6f76222020-04-27 11:08:01 -0600643
644 Args:
David Burgerd4f32962020-05-02 12:07:40 -0600645 path: Path to the file encoding the json pb proto.
David Burgere6f76222020-04-27 11:08:01 -0600646 """
647 config = config_bundle_pb2.ConfigBundle()
648 with open(path, 'r') as f:
649 return json_format.Parse(f.read(), config)
650
651
David Burger7fd1dbe2020-03-26 09:26:55 -0600652def _MergeConfigs(configs):
653 result = config_bundle_pb2.ConfigBundle()
654 for config in configs:
655 result.MergeFrom(config)
656
657 return result
658
659
660def Main(project_configs,
661 program_config,
662 output):
663 """Transforms source proto config into platform JSON.
664
665 Args:
666 project_configs: List of source project configs to transform.
667 program_config: Program config for the given set of projects.
668 output: Output file that will be generated by the transform.
669 """
C Shapiro90fda252020-04-17 14:34:57 -0500670 configs =_MergeConfigs(
671 [_ReadConfig(program_config)] +
672 [_ReadConfig(config) for config in project_configs])
C Shapiro5bf23a72020-04-24 11:40:17 -0500673 bluetooth_files = {}
674 arc_hw_feature_files = {}
C Shapiro2b6d5332020-05-06 17:51:35 -0500675 touch_fw = {}
C Shapiro6830e6c2020-04-29 13:29:56 -0500676 dptf_file = None
C Shapiro5bf23a72020-04-24 11:40:17 -0500677 output_dir = os.path.dirname(output)
C Shapiro5c877992020-04-29 12:11:28 -0500678 build_root_dir = output_dir
C Shapiro5c877992020-04-29 12:11:28 -0500679 if 'sw_build_config' in output_dir:
680 full_path = os.path.realpath(output)
C Shapiro6438fb32020-05-01 16:43:49 -0500681 project_name = re.match(
682 r'.*/(\w*)/sw_build_config/.*', full_path).groups(1)[0]
C Shapiro5c877992020-04-29 12:11:28 -0500683 # Projects don't know about each other until they are integrated into the
684 # build system. When this happens, the files need to be able to co-exist
685 # without any collisions. This prefixes the project name (which is how
686 # portage maps in the project), so project files co-exist and can be
687 # installed together.
688 # This is necessary to allow projects to share files at the program level
689 # without having portage file installation collisions.
690 build_root_dir = os.path.join(project_name, output_dir)
C Shapiro6830e6c2020-04-29 13:29:56 -0500691
C Shapiro7356bd62020-05-02 05:21:33 -0500692 if os.path.exists(DPTF_PATH):
693 project_dptf_path = os.path.join(project_name, 'dptf.dv')
694 dptf_file = {
695 'dptf-dv': project_dptf_path,
696 'files': [_File(os.path.join(project_name, DPTF_PATH),
697 os.path.join('/etc/dptf', project_dptf_path))]
698 }
C Shapiro2b6d5332020-05-06 17:51:35 -0500699 if os.path.exists(TOUCH_PATH):
700 touch_fw = _BuildTouchFileConfig(configs, project_name)
C Shapiro5bf23a72020-04-24 11:40:17 -0500701 if os.path.exists(os.path.join(output_dir, 'bluetooth')):
C Shapiro5c877992020-04-29 12:11:28 -0500702 bluetooth_files = WriteBluetoothConfigFiles(
703 configs, output_dir, build_root_dir)
C Shapiro5bf23a72020-04-24 11:40:17 -0500704 if os.path.exists(os.path.join(output_dir, 'arc')):
705 arc_hw_feature_files = WriteArcHardwareFeatureFiles(
C Shapiro5c877992020-04-29 12:11:28 -0500706 configs, output_dir, build_root_dir)
C Shapiro5bf23a72020-04-24 11:40:17 -0500707 config_files = ConfigFiles(
708 bluetooth=bluetooth_files,
709 arc_hw_features=arc_hw_feature_files,
C Shapiro2b6d5332020-05-06 17:51:35 -0500710 touch_fw=touch_fw,
C Shapiro6830e6c2020-04-29 13:29:56 -0500711 dptf_file=dptf_file
C Shapiro5bf23a72020-04-24 11:40:17 -0500712 )
713 WriteOutput(_TransformBuildConfigs(configs, config_files), output)
David Burger7fd1dbe2020-03-26 09:26:55 -0600714
715
716def main(argv=None):
717 """Main program which parses args and runs
718
719 Args:
720 argv: List of command line arguments, if None uses sys.argv.
721 """
722 if argv is None:
723 argv = sys.argv[1:]
724 opts = ParseArgs(argv)
725 Main(opts.project_configs, opts.program_config, opts.output)
726
727
728if __name__ == '__main__':
729 sys.exit(main(sys.argv[1:]))