blob: db622956f5b4b237cc50f3149d4b24d315032c2c [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
Andrew Lamb319cc922020-06-15 10:45:46 -060017from typing import List
18
David Burger7fd1dbe2020-03-26 09:26:55 -060019from collections import namedtuple
20
Andrew Lambcd33f702020-06-11 10:45:16 -060021from google.protobuf import json_format
22
Prathmesh Prabhu72f8a002020-04-10 09:57:53 -070023from chromiumos.config.api import device_brand_pb2
David Burger92609a32020-04-23 10:38:50 -060024from chromiumos.config.api import topology_pb2
C Shapiro5bf23a72020-04-24 11:40:17 -050025from chromiumos.config.payload import config_bundle_pb2
Prathmesh Prabhu72f8a002020-04-10 09:57:53 -070026from chromiumos.config.api.software import brand_config_pb2
David Burger7fd1dbe2020-03-26 09:26:55 -060027
Andrew Lamb2413c982020-05-29 12:15:36 -060028Config = namedtuple('Config', [
29 'program', 'hw_design', 'odm', 'hw_design_config', 'device_brand',
30 'device_signer_config', 'oem', 'sw_config', 'brand_config', 'build_target'
31])
David Burger7fd1dbe2020-03-26 09:26:55 -060032
Andrew Lamb2413c982020-05-29 12:15:36 -060033ConfigFiles = namedtuple(
David Burger52c9d322020-06-09 07:16:18 -060034 'ConfigFiles', ['bluetooth', 'arc_hw_features', 'touch_fw', 'dptf_map'])
C Shapiro5bf23a72020-04-24 11:40:17 -050035
David Burger52c9d322020-06-09 07:16:18 -060036DPTF_PATH = 'sw_build_config/platform/chromeos-config/thermal'
37DPTF_FILE = 'dptf.dv'
C Shapiro2b6d5332020-05-06 17:51:35 -050038TOUCH_PATH = 'sw_build_config/platform/chromeos-config/touch'
Andrew Lamb6c42efc2020-06-16 10:40:43 -060039WALLPAPER_BASE_PATH = '/usr/share/chromeos-assets/wallpaper'
David Burger7fd1dbe2020-03-26 09:26:55 -060040
Andrew Lamb2413c982020-05-29 12:15:36 -060041
Andrew Lambcd33f702020-06-11 10:45:16 -060042def parse_args(argv):
David Burger7fd1dbe2020-03-26 09:26:55 -060043 """Parse the available arguments.
44
45 Invalid arguments or -h cause this function to print a message and exit.
46
47 Args:
48 argv: List of string arguments (excluding program name / argv[0])
49
50 Returns:
51 argparse.Namespace object containing the attributes.
52 """
53 parser = argparse.ArgumentParser(
54 description='Converts source proto config into platform JSON config.')
55 parser.add_argument(
56 '-c',
57 '--project_configs',
58 nargs='+',
59 type=str,
60 help='Space delimited list of source protobinary project config files.')
61 parser.add_argument(
62 '-p',
63 '--program_config',
64 type=str,
65 help='Path to the source program-level protobinary file')
66 parser.add_argument(
Andrew Lamb2413c982020-05-29 12:15:36 -060067 '-o', '--output', type=str, help='Output file that will be generated')
David Burger7fd1dbe2020-03-26 09:26:55 -060068 return parser.parse_args(argv)
69
70
Andrew Lambcd33f702020-06-11 10:45:16 -060071def _set(field, target, target_name):
Sam McNally9a873f72020-06-05 19:47:22 +100072 if field or field == 0:
David Burger7fd1dbe2020-03-26 09:26:55 -060073 target[target_name] = field
74
75
Andrew Lambcd33f702020-06-11 10:45:16 -060076def _build_arc(config, config_files):
77 if not config.build_target.arc:
78 return None
79
80 build_properties = {
81 'device': config.build_target.arc.device,
82 'first-api-level': config.build_target.arc.first_api_level,
83 'marketing-name': config.device_brand.brand_name,
84 'metrics-tag': config.hw_design.name.lower(),
85 'product': config.build_target.id.value,
86 }
87 if config.oem:
88 build_properties['oem'] = config.oem.name
89 result = {'build-properties': build_properties}
90 feature_id = _arc_hardware_feature_id(config.hw_design_config)
91 if feature_id in config_files.arc_hw_features:
92 result['hardware-features'] = config_files.arc_hw_features[feature_id]
93 topology = config.hw_design_config.hardware_topology
94 ppi = topology.screen.hardware_feature.screen.panel_properties.pixels_per_in
95 # Only set for high resolution displays
96 if ppi and ppi > 250:
97 result['scale'] = ppi
98 return result
David Burger7fd1dbe2020-03-26 09:26:55 -060099
Andrew Lamb2413c982020-05-29 12:15:36 -0600100
Andrew Lamb319cc922020-06-15 10:45:46 -0600101def _build_ash_flags(config: Config) -> List[str]:
102 """Returns a list of Ash flags for config.
103
104 Ash is the window manager and system UI for ChromeOS, see
105 https://chromium.googlesource.com/chromium/src/+/refs/heads/master/ash/.
106 """
107 # A map from flag name -> value. Value may be None for boolean flags.
108 flags = {}
109
110 hw_features = config.hw_design_config.hardware_features
111 if hw_features.stylus.stylus == topology_pb2.HardwareFeatures.Stylus.INTERNAL:
Andrew Lamb2e641e22020-06-15 12:30:41 -0600112 flags['has-internal-stylus'] = None
Andrew Lamb319cc922020-06-15 10:45:46 -0600113
Andrew Lamb2e641e22020-06-15 12:30:41 -0600114 fp_loc = hw_features.fingerprint.location
115 if fp_loc and fp_loc != topology_pb2.HardwareFeatures.Fingerprint.NOT_PRESENT:
116 loc_name = topology_pb2.HardwareFeatures.Fingerprint.Location.Name(fp_loc)
117 flags['fingerprint-sensor-location'] = loc_name.lower().replace('_', '-')
118
Andrew Lamb6c42efc2020-06-16 10:40:43 -0600119 wallpaper = config.brand_config.wallpaper
120 # If a wallpaper is set, the 'default-wallpaper-is-oem' flag needs to be set.
121 # If a wallpaper is not set, the 'default_[large|small].jpg' wallpapers
122 # should still be set.
123 if wallpaper:
124 flags['default-wallpaper-is-oem'] = None
125 else:
126 wallpaper = 'default'
127
128 for size in ('small', 'large'):
129 flags[f'default-wallpaper-{size}'] = (
130 f'{WALLPAPER_BASE_PATH}/{wallpaper}_{size}.jpg')
131
132 # For each size, also install 'guest' and 'child' wallpapers.
133 for wallpaper_type in ('guest', 'child'):
134 flags[f'{wallpaper_type}-wallpaper-{size}'] = (
135 f'{WALLPAPER_BASE_PATH}/{wallpaper_type}_{size}.jpg')
136
Andrew Lamb2e641e22020-06-15 12:30:41 -0600137 return sorted([f'--{k}={v}' if v else f'--{k}' for k, v in flags.items()])
Andrew Lamb319cc922020-06-15 10:45:46 -0600138
139
140def _build_ui(config: Config) -> dict:
141 """Builds the 'ui' property from cros_config_schema."""
142 return {'extra-ash-flags': _build_ash_flags(config)}
143
144
Andrew Lambcd33f702020-06-11 10:45:16 -0600145def _build_bluetooth(config, bluetooth_files):
C Shapiro90fda252020-04-17 14:34:57 -0500146 bt_flags = config.sw_config.bluetooth_config.flags
147 # Convert to native map (from proto wrapper)
148 bt_flags_map = dict(bt_flags)
149 result = {}
150 if bt_flags_map:
151 result['flags'] = bt_flags_map
C Shapiro74da76e2020-05-04 13:02:20 -0500152 bt_comp = config.hw_design_config.hardware_features.bluetooth.component.usb
C Shapiro90fda252020-04-17 14:34:57 -0500153 if bt_comp.vendor_id:
Andrew Lambcd33f702020-06-11 10:45:16 -0600154 bt_id = _bluetooth_id(config.hw_design.name.lower(), bt_comp)
C Shapiro90fda252020-04-17 14:34:57 -0500155 if bt_id in bluetooth_files:
156 result['config'] = bluetooth_files[bt_id]
157 return result
158
David Burger7fd1dbe2020-03-26 09:26:55 -0600159
Andrew Lambcd33f702020-06-11 10:45:16 -0600160def _build_fingerprint(hw_topology):
161 if not hw_topology.HasField('fingerprint'):
162 return None
163
164 fp = hw_topology.fingerprint.hardware_feature.fingerprint
165 result = {}
166 if fp.location != topology_pb2.HardwareFeatures.Fingerprint.NOT_PRESENT:
167 location = fp.Location.DESCRIPTOR.values_by_number[fp.location].name
168 result['sensor-location'] = location.lower().replace('_', '-')
169 if fp.board:
170 result['board'] = fp.board
171 return result
David Burger7fd1dbe2020-03-26 09:26:55 -0600172
173
Andrew Lambcd33f702020-06-11 10:45:16 -0600174def _fw_bcs_path(payload):
David Burger7fd1dbe2020-03-26 09:26:55 -0600175 if payload and payload.firmware_image_name:
Andrew Lamb2413c982020-05-29 12:15:36 -0600176 return 'bcs://%s.%d.%d.0.tbz2' % (payload.firmware_image_name,
177 payload.version.major,
178 payload.version.minor)
David Burger7fd1dbe2020-03-26 09:26:55 -0600179
Andrew Lambcd33f702020-06-11 10:45:16 -0600180 return None
David Burger7fd1dbe2020-03-26 09:26:55 -0600181
Andrew Lambcd33f702020-06-11 10:45:16 -0600182
183def _fw_build_target(payload):
David Burger7fd1dbe2020-03-26 09:26:55 -0600184 if payload:
185 return payload.build_target_name
186
Andrew Lambcd33f702020-06-11 10:45:16 -0600187 return None
David Burger7fd1dbe2020-03-26 09:26:55 -0600188
Andrew Lambcd33f702020-06-11 10:45:16 -0600189
190def _build_firmware(config):
David Burgerb70b6762020-05-21 12:14:59 -0600191 """Returns firmware config, or None if no build targets."""
Andrew Lamb3da156d2020-04-16 16:00:56 -0600192 fw_payload_config = config.sw_config.firmware
193 fw_build_config = config.sw_config.firmware_build_config
194 main_ro = fw_payload_config.main_ro_payload
195 main_rw = fw_payload_config.main_rw_payload
196 ec_ro = fw_payload_config.ec_ro_payload
197 pd_ro = fw_payload_config.pd_ro_payload
David Burger7fd1dbe2020-03-26 09:26:55 -0600198
199 build_targets = {}
Andrew Lamb3da156d2020-04-16 16:00:56 -0600200
Andrew Lambcd33f702020-06-11 10:45:16 -0600201 _set(fw_build_config.build_targets.depthcharge, build_targets, 'depthcharge')
202 _set(fw_build_config.build_targets.coreboot, build_targets, 'coreboot')
203 _set(fw_build_config.build_targets.ec, build_targets, 'ec')
204 _set(
Andrew Lambf8954ee2020-04-21 10:24:40 -0600205 list(fw_build_config.build_targets.ec_extras), build_targets, 'ec_extras')
Andrew Lambcd33f702020-06-11 10:45:16 -0600206 _set(fw_build_config.build_targets.libpayload, build_targets, 'libpayload')
David Burger7fd1dbe2020-03-26 09:26:55 -0600207
David Burgerb70b6762020-05-21 12:14:59 -0600208 if not build_targets:
209 return None
210
David Burger7fd1dbe2020-03-26 09:26:55 -0600211 result = {
212 'bcs-overlay': config.build_target.overlay_name,
213 'build-targets': build_targets,
David Burger7fd1dbe2020-03-26 09:26:55 -0600214 }
Andrew Lamb883fa042020-04-06 11:37:22 -0600215
Andrew Lambcd33f702020-06-11 10:45:16 -0600216 _set(main_ro.firmware_image_name.lower(), result, 'image-name')
Andrew Lamb883fa042020-04-06 11:37:22 -0600217
Andrew Lambcd33f702020-06-11 10:45:16 -0600218 _set(_fw_bcs_path(main_ro), result, 'main-ro-image')
219 _set(_fw_bcs_path(main_rw), result, 'main-rw-image')
220 _set(_fw_bcs_path(ec_ro), result, 'ec-ro-image')
221 _set(_fw_bcs_path(pd_ro), result, 'pd-ro-image')
David Burger7fd1dbe2020-03-26 09:26:55 -0600222
Andrew Lambcd33f702020-06-11 10:45:16 -0600223 _set(
Andrew Lambf39fbe82020-04-13 16:14:33 -0600224 config.hw_design_config.hardware_features.fw_config.value,
225 result,
226 'firmware-config',
227 )
228
David Burger7fd1dbe2020-03-26 09:26:55 -0600229 return result
230
231
Andrew Lambcd33f702020-06-11 10:45:16 -0600232def _build_fw_signing(config):
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500233 if config.sw_config.firmware and config.device_signer_config:
David Burger68e0d142020-05-15 17:29:33 -0600234 hw_design = config.hw_design.name.lower()
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500235 return {
236 'key-id': config.device_signer_config.key_id,
C Shapiro10e9a612020-05-19 17:06:43 -0500237 # TODO(shapiroc): Need to fix for whitelabel.
238 # Whitelabel will collide on unique signature-id values.
David Burger68e0d142020-05-15 17:29:33 -0600239 'signature-id': hw_design,
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500240 }
241 return {}
David Burger7fd1dbe2020-03-26 09:26:55 -0600242
243
Andrew Lambcd33f702020-06-11 10:45:16 -0600244def _file(source, destination):
Andrew Lamb2413c982020-05-29 12:15:36 -0600245 return {'destination': destination, 'source': source}
David Burger7fd1dbe2020-03-26 09:26:55 -0600246
247
Andrew Lambcd33f702020-06-11 10:45:16 -0600248def _build_audio(config):
David Burger7fd1dbe2020-03-26 09:26:55 -0600249 alsa_path = '/usr/share/alsa/ucm'
250 cras_path = '/etc/cras'
251 project_name = config.hw_design.name.lower()
David Burger43250662020-05-07 11:21:50 -0600252 program_name = config.program.name.lower()
Andrew Lamb7d536782020-04-07 10:23:55 -0600253 if not config.sw_config.HasField('audio_config'):
David Burger7fd1dbe2020-03-26 09:26:55 -0600254 return {}
255 audio = config.sw_config.audio_config
256 card = audio.card_name
David Burger599ff7b2020-04-06 16:29:31 -0600257 card_with_suffix = audio.card_name
258 if audio.ucm_suffix:
259 card_with_suffix += '.' + audio.ucm_suffix
David Burger7fd1dbe2020-03-26 09:26:55 -0600260 files = []
261 if audio.ucm_file:
Andrew Lamb2413c982020-05-29 12:15:36 -0600262 files.append(
Andrew Lambcd33f702020-06-11 10:45:16 -0600263 _file(audio.ucm_file,
Andrew Lamb2413c982020-05-29 12:15:36 -0600264 '%s/%s/HiFi.conf' % (alsa_path, card_with_suffix)))
David Burger7fd1dbe2020-03-26 09:26:55 -0600265 if audio.ucm_master_file:
Andrew Lamb2413c982020-05-29 12:15:36 -0600266 files.append(
Andrew Lambcd33f702020-06-11 10:45:16 -0600267 _file(audio.ucm_master_file, '%s/%s/%s.conf' %
Andrew Lamb2413c982020-05-29 12:15:36 -0600268 (alsa_path, card_with_suffix, card_with_suffix)))
David Burger7fd1dbe2020-03-26 09:26:55 -0600269 if audio.card_config_file:
Andrew Lamb2413c982020-05-29 12:15:36 -0600270 files.append(
Andrew Lambcd33f702020-06-11 10:45:16 -0600271 _file(audio.card_config_file,
Andrew Lamb2413c982020-05-29 12:15:36 -0600272 '%s/%s/%s' % (cras_path, project_name, card)))
David Burger7fd1dbe2020-03-26 09:26:55 -0600273 if audio.dsp_file:
274 files.append(
Andrew Lambcd33f702020-06-11 10:45:16 -0600275 _file(audio.dsp_file, '%s/%s/dsp.ini' % (cras_path, project_name)))
David Burgere1a37492020-05-06 09:29:24 -0600276 if audio.module_file:
277 files.append(
Andrew Lambcd33f702020-06-11 10:45:16 -0600278 _file(audio.module_file, '/etc/modprobe.d/alsa-%s.conf' % program_name))
David Burgere1a37492020-05-06 09:29:24 -0600279 if audio.board_file:
280 files.append(
Andrew Lambcd33f702020-06-11 10:45:16 -0600281 _file(audio.board_file, '%s/%s/board.ini' % (cras_path, project_name)))
David Burger599ff7b2020-04-06 16:29:31 -0600282
283 result = {
David Burger7fd1dbe2020-03-26 09:26:55 -0600284 'main': {
285 'cras-config-dir': project_name,
286 'files': files,
287 }
288 }
David Burger599ff7b2020-04-06 16:29:31 -0600289 if audio.ucm_suffix:
David Burger03cdcbd2020-04-13 13:54:48 -0600290 result['main']['ucm-suffix'] = audio.ucm_suffix
David Burger599ff7b2020-04-06 16:29:31 -0600291
292 return result
David Burger7fd1dbe2020-03-26 09:26:55 -0600293
294
Andrew Lambcd33f702020-06-11 10:45:16 -0600295def _build_camera(hw_topology):
David Burger8aa8fa32020-04-14 08:30:34 -0600296 if hw_topology.HasField('camera'):
297 camera = hw_topology.camera.hardware_feature.camera
298 result = {}
299 if camera.count.value:
300 result['count'] = camera.count.value
301 return result
302
Andrew Lambcd33f702020-06-11 10:45:16 -0600303 return None
David Burger8aa8fa32020-04-14 08:30:34 -0600304
Andrew Lambcd33f702020-06-11 10:45:16 -0600305
306def _build_identity(hw_scan_config, program, brand_scan_config=None):
David Burger7fd1dbe2020-03-26 09:26:55 -0600307 identity = {}
Andrew Lambcd33f702020-06-11 10:45:16 -0600308 _set(hw_scan_config.firmware_sku, identity, 'sku-id')
309 _set(hw_scan_config.smbios_name_match, identity, 'smbios-name-match')
Andrew Lamb7806ce92020-04-07 10:22:17 -0600310 # 'platform-name' is needed to support 'mosys platform name'. Clients should
311 # longer require platform name, but set it here for backwards compatibility.
Andrew Lambcd33f702020-06-11 10:45:16 -0600312 _set(program.name, identity, 'platform-name')
David Burger7fd1dbe2020-03-26 09:26:55 -0600313 # ARM architecture
Andrew Lambcd33f702020-06-11 10:45:16 -0600314 _set(hw_scan_config.device_tree_compatible_match, identity,
David Burger7fd1dbe2020-03-26 09:26:55 -0600315 'device-tree-compatible-match')
316
317 if brand_scan_config:
Andrew Lambcd33f702020-06-11 10:45:16 -0600318 _set(brand_scan_config.whitelabel_tag, identity, 'whitelabel-tag')
David Burger7fd1dbe2020-03-26 09:26:55 -0600319
320 return identity
321
322
Andrew Lambcd33f702020-06-11 10:45:16 -0600323def _lookup(id_value, id_map):
324 if not id_value.value:
325 return None
326
327 key = id_value.value
328 if key in id_map:
329 return id_map[id_value.value]
330 error = 'Failed to lookup %s with value: %s' % (
331 id_value.__class__.__name__.replace('Id', ''), key)
332 print(error)
333 print('Check the config contents provided:')
334 printer = pprint.PrettyPrinter(indent=4)
335 printer.pprint(id_map)
336 raise Exception(error)
David Burger7fd1dbe2020-03-26 09:26:55 -0600337
338
Andrew Lambcd33f702020-06-11 10:45:16 -0600339def _build_touch_file_config(config, project_name):
340 partners = {x.id.value: x for x in config.partners.value}
C Shapiro2b6d5332020-05-06 17:51:35 -0500341 files = []
342 for comp in config.components:
C Shapiro4813be62020-05-13 17:31:58 -0500343 touch = comp.touchscreen
344 # Everything is the same for Touch screen/pad, except different fields
345 if comp.HasField('touchpad'):
346 touch = comp.touchpad
347 if touch.product_id:
Andrew Lambcd33f702020-06-11 10:45:16 -0600348 vendor = _lookup(comp.manufacturer_id, partners)
C Shapiro2b6d5332020-05-06 17:51:35 -0500349 if not vendor:
Andrew Lamb2413c982020-05-29 12:15:36 -0600350 raise Exception("Manufacturer must be set for touch device %s" %
351 comp.id.value)
C Shapiro2b6d5332020-05-06 17:51:35 -0500352
C Shapiro4813be62020-05-13 17:31:58 -0500353 product_id = touch.product_id
354 fw_version = touch.fw_version
C Shapiro2b6d5332020-05-06 17:51:35 -0500355
C Shapiro5c6fc212020-05-13 16:32:09 -0500356 touch_vendor = vendor.touch_vendor
357 sym_link = touch_vendor.fw_file_format.format(
Andrew Lamb2413c982020-05-29 12:15:36 -0600358 vendor_name=vendor.name,
359 vendor_id=touch_vendor.vendor_id,
360 product_id=product_id,
361 fw_version=fw_version,
362 product_series=touch.product_series)
C Shapiro2b6d5332020-05-06 17:51:35 -0500363
364 file_name = "%s_%s.bin" % (product_id, fw_version)
365 fw_file_path = os.path.join(TOUCH_PATH, vendor.name, file_name)
366
367 if not os.path.exists(fw_file_path):
Andrew Lamb2413c982020-05-29 12:15:36 -0600368 raise Exception("Touchscreen fw bin file doesn't exist at: %s" %
369 fw_file_path)
C Shapiro2b6d5332020-05-06 17:51:35 -0500370
371 files.append({
Andrew Lamb2413c982020-05-29 12:15:36 -0600372 "destination":
373 "/opt/google/touch/firmware/%s_%s" % (vendor.name, file_name),
374 "source":
375 os.path.join(project_name, fw_file_path),
376 "symlink":
377 os.path.join("/lib/firmware", sym_link),
C Shapiro2b6d5332020-05-06 17:51:35 -0500378 })
379
380 result = {}
Andrew Lambcd33f702020-06-11 10:45:16 -0600381 _set(files, result, 'files')
C Shapiro2b6d5332020-05-06 17:51:35 -0500382 return result
383
384
Andrew Lambcd33f702020-06-11 10:45:16 -0600385def _transform_build_configs(config, config_files=ConfigFiles({}, {}, {},
386 None)):
387 # pylint: disable=too-many-locals,too-many-branches
388 partners = {x.id.value: x for x in config.partners.value}
389 programs = {x.id.value: x for x in config.programs.value}
David Burger7fd1dbe2020-03-26 09:26:55 -0600390 sw_configs = list(config.software_configs)
Andrew Lambcd33f702020-06-11 10:45:16 -0600391 brand_configs = {x.brand_id.value: x for x in config.brand_configs}
David Burger7fd1dbe2020-03-26 09:26:55 -0600392
C Shapiroa0b766c2020-03-31 08:35:28 -0500393 if len(config.build_targets) != 1:
394 # Artifact of sharing the config_bundle for analysis and transforms.
395 # Integrated analysis of multiple programs/projects it the only time
396 # having multiple build targets would be valid.
397 raise Exception('Single build_target required for transform')
398
David Burger7fd1dbe2020-03-26 09:26:55 -0600399 results = {}
400 for hw_design in config.designs.value:
401 if config.device_brands.value:
Andrew Lamb2413c982020-05-29 12:15:36 -0600402 device_brands = [
403 x for x in config.device_brands.value
404 if x.design_id.value == hw_design.id.value
405 ]
David Burger7fd1dbe2020-03-26 09:26:55 -0600406 else:
407 device_brands = [device_brand_pb2.DeviceBrand()]
408
409 for device_brand in device_brands:
410 # Brand config can be empty since platform JSON config allows it
411 brand_config = brand_config_pb2.BrandConfig()
412 if device_brand.id.value in brand_configs:
413 brand_config = brand_configs[device_brand.id.value]
414
415 for hw_design_config in hw_design.configs:
416 design_id = hw_design_config.id.value
Andrew Lamb2413c982020-05-29 12:15:36 -0600417 sw_config_matches = [
418 x for x in sw_configs if x.design_config_id.value == design_id
419 ]
David Burger7fd1dbe2020-03-26 09:26:55 -0600420 if len(sw_config_matches) == 1:
421 sw_config = sw_config_matches[0]
422 elif len(sw_config_matches) > 1:
423 raise Exception('Multiple software configs found for: %s' % design_id)
424 else:
425 raise Exception('Software config is required for: %s' % design_id)
426
Andrew Lambcd33f702020-06-11 10:45:16 -0600427 program = _lookup(hw_design.program_id, programs)
C Shapiroadefd7c2020-05-19 16:37:21 -0500428 signer_configs_by_design = {}
429 signer_configs_by_brand = {}
430 for signer_config in program.device_signer_configs:
431 design_id = signer_config.design_id.value
432 brand_id = signer_config.brand_id.value
433 if design_id:
434 signer_configs_by_design[design_id] = signer_config
435 elif brand_id:
436 signer_configs_by_brand[brand_id] = signer_config
437 else:
438 raise Exception('No ID found for signer config: %s' % signer_config)
439
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500440 device_signer_config = None
C Shapiroadefd7c2020-05-19 16:37:21 -0500441 if signer_configs_by_design or signer_configs_by_brand:
442 design_id = hw_design.id.value
443 brand_id = device_brand.id.value
444 if design_id in signer_configs_by_design:
445 device_signer_config = signer_configs_by_design[design_id]
446 elif brand_id in signer_configs_by_brand:
447 device_signer_config = signer_configs_by_brand[brand_id]
448 else:
449 # Assume that if signer configs are set, every config is setup
Andrew Lamb2413c982020-05-29 12:15:36 -0600450 raise Exception('Signer config missing for design: %s, brand: %s' %
451 (design_id, brand_id))
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500452
Andrew Lambcd33f702020-06-11 10:45:16 -0600453 transformed_config = _transform_build_config(
C Shapiro90fda252020-04-17 14:34:57 -0500454 Config(
455 program=program,
456 hw_design=hw_design,
Andrew Lambcd33f702020-06-11 10:45:16 -0600457 odm=_lookup(hw_design.odm_id, partners),
C Shapiro90fda252020-04-17 14:34:57 -0500458 hw_design_config=hw_design_config,
459 device_brand=device_brand,
460 device_signer_config=device_signer_config,
Andrew Lambcd33f702020-06-11 10:45:16 -0600461 oem=_lookup(device_brand.oem_id, partners),
C Shapiro90fda252020-04-17 14:34:57 -0500462 sw_config=sw_config,
463 brand_config=brand_config,
Andrew Lamb2413c982020-05-29 12:15:36 -0600464 build_target=config.build_targets[0]), config_files)
David Burger7fd1dbe2020-03-26 09:26:55 -0600465
Andrew Lamb2413c982020-05-29 12:15:36 -0600466 config_json = json.dumps(
467 transformed_config,
468 sort_keys=True,
469 indent=2,
470 separators=(',', ': '))
David Burger7fd1dbe2020-03-26 09:26:55 -0600471
472 if config_json not in results:
473 results[config_json] = transformed_config
474
475 return list(results.values())
476
477
Andrew Lambcd33f702020-06-11 10:45:16 -0600478def _transform_build_config(config, config_files):
David Burger7fd1dbe2020-03-26 09:26:55 -0600479 """Transforms Config instance into target platform JSON schema.
480
481 Args:
482 config: Config namedtuple
C Shapiro5bf23a72020-04-24 11:40:17 -0500483 config_files: Map to look up the generated config files.
David Burger7fd1dbe2020-03-26 09:26:55 -0600484
485 Returns:
486 Unique config payload based on the platform JSON schema.
487 """
488 result = {
Andrew Lamb2413c982020-05-29 12:15:36 -0600489 'identity':
Andrew Lambcd33f702020-06-11 10:45:16 -0600490 _build_identity(config.sw_config.id_scan_config, config.program,
491 config.brand_config.scan_config),
Andrew Lamb2413c982020-05-29 12:15:36 -0600492 'name':
493 config.hw_design.name.lower(),
David Burger7fd1dbe2020-03-26 09:26:55 -0600494 }
495
Andrew Lambcd33f702020-06-11 10:45:16 -0600496 _set(_build_arc(config, config_files), result, 'arc')
497 _set(_build_audio(config), result, 'audio')
498 _set(_build_bluetooth(config, config_files.bluetooth), result, 'bluetooth')
499 _set(config.device_brand.brand_code, result, 'brand-code')
500 _set(
501 _build_camera(config.hw_design_config.hardware_topology), result,
502 'camera')
503 _set(_build_firmware(config), result, 'firmware')
504 _set(_build_fw_signing(config), result, 'firmware-signing')
505 _set(
506 _build_fingerprint(config.hw_design_config.hardware_topology), result,
Andrew Lamb2413c982020-05-29 12:15:36 -0600507 'fingerprint')
Andrew Lamb319cc922020-06-15 10:45:46 -0600508
509 # TODO(crbug.com/1093837): Enable _build_ui for real programs once ready.
510 if config.program.id.value == "FAKE_PROGRAM":
511 _set(_build_ui(config), result, 'ui')
David Burger7fd1dbe2020-03-26 09:26:55 -0600512 power_prefs = config.sw_config.power_config.preferences
513 power_prefs_map = dict(
Andrew Lamb2413c982020-05-29 12:15:36 -0600514 (x.replace('_', '-'), power_prefs[x]) for x in power_prefs)
Andrew Lambcd33f702020-06-11 10:45:16 -0600515 _set(power_prefs_map, result, 'power')
David Burger52c9d322020-06-09 07:16:18 -0600516 if config_files.dptf_map:
517 # Prefer design specific if found, if not fall back to project wide config
518 # mapped under the empty string.
519 if config_files.dptf_map.get(config.hw_design.name):
520 dptf_file = config_files.dptf_map[config.hw_design.name]
521 else:
522 dptf_file = config_files.dptf_map.get('')
523 _set(dptf_file, result, 'thermal')
Andrew Lambcd33f702020-06-11 10:45:16 -0600524 _set(config_files.touch_fw, result, 'touch')
David Burger7fd1dbe2020-03-26 09:26:55 -0600525
526 return result
527
528
Andrew Lambcd33f702020-06-11 10:45:16 -0600529def write_output(configs, output=None):
David Burger7fd1dbe2020-03-26 09:26:55 -0600530 """Writes a list of configs to platform JSON format.
531
532 Args:
533 configs: List of config dicts defined in cros_config_schema.yaml
534 output: Target file output (if None, prints to stdout)
535 """
Andrew Lamb2413c982020-05-29 12:15:36 -0600536 json_output = json.dumps({'chromeos': {
537 'configs': configs,
538 }},
539 sort_keys=True,
540 indent=2,
541 separators=(',', ': '))
David Burger7fd1dbe2020-03-26 09:26:55 -0600542 if output:
543 with open(output, 'w') as output_stream:
544 # Using print function adds proper trailing newline.
545 print(json_output, file=output_stream)
546 else:
547 print(json_output)
548
549
Andrew Lambcd33f702020-06-11 10:45:16 -0600550def _bluetooth_id(project_name, bt_comp):
Andrew Lamb2413c982020-05-29 12:15:36 -0600551 return '_'.join(
552 [project_name, bt_comp.vendor_id, bt_comp.product_id, bt_comp.bcd_device])
C Shapiro90fda252020-04-17 14:34:57 -0500553
554
Andrew Lambcd33f702020-06-11 10:45:16 -0600555def _feature(name, present):
C Shapiro5bf23a72020-04-24 11:40:17 -0500556 attrib = {'name': name}
557 if present:
558 return etree.Element('feature', attrib=attrib)
Andrew Lambcd33f702020-06-11 10:45:16 -0600559
560 return etree.Element('unavailable-feature', attrib=attrib)
C Shapiro5bf23a72020-04-24 11:40:17 -0500561
562
Andrew Lambcd33f702020-06-11 10:45:16 -0600563def _any_present(features):
Andrew Lamb2413c982020-05-29 12:15:36 -0600564 return topology_pb2.HardwareFeatures.PRESENT in features
C Shapiro5bf23a72020-04-24 11:40:17 -0500565
566
Andrew Lambcd33f702020-06-11 10:45:16 -0600567def _arc_hardware_feature_id(design_config):
C Shapiro5bf23a72020-04-24 11:40:17 -0500568 return design_config.id.value.lower().replace(':', '_')
569
570
Andrew Lambcd33f702020-06-11 10:45:16 -0600571def _write_arc_hardware_feature_file(output_dir, file_name, config_content):
David Burger77a1d312020-05-23 16:05:45 -0600572 output_dir += '/arc'
573 os.makedirs(output_dir, exist_ok=True)
574 output = '%s/%s' % (output_dir, file_name)
Andrew Lamb2413c982020-05-29 12:15:36 -0600575 file_content = minidom.parseString(config_content).toprettyxml(
576 indent=' ', encoding='utf-8')
C Shapiroea33cff2020-05-11 13:32:05 -0500577
578 with open(output, 'wb') as f:
579 f.write(file_content)
580
581
Andrew Lambcd33f702020-06-11 10:45:16 -0600582def _write_arc_hardware_feature_files(config, output_dir, build_root_dir):
C Shapiro5bf23a72020-04-24 11:40:17 -0500583 """Writes ARC hardware_feature.xml files for each config
584
585 Args:
586 config: Source ConfigBundle to process.
587 output_dir: Path to the generated output.
C Shapiro5c877992020-04-29 12:11:28 -0500588 build_root_path: Path to the config file from portage's perspective.
C Shapiro5bf23a72020-04-24 11:40:17 -0500589 Returns:
590 dict that maps the design_config_id onto the correct file.
591 """
Andrew Lambcd33f702020-06-11 10:45:16 -0600592 # pylint: disable=too-many-locals
C Shapiro5bf23a72020-04-24 11:40:17 -0500593 result = {}
C Shapiroea33cff2020-05-11 13:32:05 -0500594 configs_by_design = {}
C Shapiro5bf23a72020-04-24 11:40:17 -0500595 for hw_design in config.designs.value:
596 for design_config in hw_design.configs:
597 hw_features = design_config.hardware_features
598 multi_camera = hw_features.camera.count == 2
Andrew Lambcd33f702020-06-11 10:45:16 -0600599 touchscreen = _any_present([hw_features.screen.touch_support])
C Shapiro5bf23a72020-04-24 11:40:17 -0500600 acc = hw_features.accelerometer
601 gyro = hw_features.gyroscope
602 compass = hw_features.magnetometer
Andrew Lambcd33f702020-06-11 10:45:16 -0600603 light_sensor = hw_features.light_sensor
C Shapiro5bf23a72020-04-24 11:40:17 -0500604 root = etree.Element('permissions')
605 root.extend([
Andrew Lambcd33f702020-06-11 10:45:16 -0600606 _feature('android.hardware.camera', multi_camera),
607 _feature('android.hardware.camera.autofocus', multi_camera),
608 _feature(
609 'android.hardware.sensor.accelerometer',
610 _any_present([acc.lid_accelerometer, acc.base_accelerometer])),
611 _feature('android.hardware.sensor.gyroscope',
612 _any_present([gyro.lid_gyroscope, gyro.base_gyroscope])),
613 _feature(
Andrew Lamb2413c982020-05-29 12:15:36 -0600614 'android.hardware.sensor.compass',
Andrew Lambcd33f702020-06-11 10:45:16 -0600615 _any_present(
616 [compass.lid_magnetometer, compass.base_magnetometer])),
617 _feature(
618 'android.hardware.sensor.light',
619 _any_present(
620 [light_sensor.lid_lightsensor,
621 light_sensor.base_lightsensor])),
622 _feature('android.hardware.touchscreen', touchscreen),
623 _feature('android.hardware.touchscreen.multitouch', touchscreen),
624 _feature('android.hardware.touchscreen.multitouch.distinct',
Andrew Lamb2413c982020-05-29 12:15:36 -0600625 touchscreen),
Andrew Lambcd33f702020-06-11 10:45:16 -0600626 _feature('android.hardware.touchscreen.multitouch.jazzhand',
Andrew Lamb2413c982020-05-29 12:15:36 -0600627 touchscreen),
C Shapiro5bf23a72020-04-24 11:40:17 -0500628 ])
629
C Shapiroea33cff2020-05-11 13:32:05 -0500630 design_name = hw_design.name.lower()
C Shapiro5bf23a72020-04-24 11:40:17 -0500631
C Shapiroea33cff2020-05-11 13:32:05 -0500632 # Constructs the following map:
633 # design_name -> config -> design_configs
634 # This allows any of the following file naming schemes:
635 # - All configs within a design share config (design_name prefix only)
636 # - Nobody shares (full design_name and config id prefix needed)
637 #
638 # Having shared configs when possible makes code reviews easier around
639 # the configs and makes debugging easier on the platform side.
640 config_content = etree.tostring(root)
641 arc_configs = configs_by_design.get(design_name, {})
642 design_configs = arc_configs.get(config_content, [])
643 design_configs.append(design_config)
644 arc_configs[config_content] = design_configs
645 configs_by_design[design_name] = arc_configs
C Shapiro9a3ac8c2020-04-25 07:49:21 -0500646
C Shapiroea33cff2020-05-11 13:32:05 -0500647 for design_name, unique_configs in configs_by_design.items():
648 for file_content, design_configs in unique_configs.items():
Andrew Lamb2413c982020-05-29 12:15:36 -0600649 file_name = 'hardware_features_%s.xml' % design_name
650 if len(unique_configs) == 1:
Andrew Lambcd33f702020-06-11 10:45:16 -0600651 _write_arc_hardware_feature_file(output_dir, file_name, file_content)
C Shapiro9a3ac8c2020-04-25 07:49:21 -0500652
Andrew Lamb2413c982020-05-29 12:15:36 -0600653 for design_config in design_configs:
Andrew Lambcd33f702020-06-11 10:45:16 -0600654 feature_id = _arc_hardware_feature_id(design_config)
Andrew Lamb2413c982020-05-29 12:15:36 -0600655 if len(unique_configs) > 1:
656 file_name = 'hardware_features_%s.xml' % feature_id
Andrew Lambcd33f702020-06-11 10:45:16 -0600657 _write_arc_hardware_feature_file(output_dir, file_name, file_content)
Andrew Lamb2413c982020-05-29 12:15:36 -0600658 result[feature_id] = {
659 'build-path': '%s/arc/%s' % (build_root_dir, file_name),
660 'system-path': '/etc/%s' % file_name,
661 }
C Shapiro5bf23a72020-04-24 11:40:17 -0500662 return result
663
664
Andrew Lambcd33f702020-06-11 10:45:16 -0600665def _write_bluetooth_config_files(config, output_dir, build_root_path):
C Shapiro90fda252020-04-17 14:34:57 -0500666 """Writes bluetooth conf files for every unique bluetooth chip.
667
668 Args:
669 config: Source ConfigBundle to process.
670 output_dir: Path to the generated output.
C Shapiro5c877992020-04-29 12:11:28 -0500671 build_root_path: Path to the config file from portage's perspective.
C Shapiro90fda252020-04-17 14:34:57 -0500672 Returns:
673 dict that maps the bluetooth component id onto the file config.
674 """
David Burger77a1d312020-05-23 16:05:45 -0600675 output_dir += '/bluetooth'
C Shapiro90fda252020-04-17 14:34:57 -0500676 result = {}
677 for hw_design in config.designs.value:
678 project_name = hw_design.name.lower()
679 for design_config in hw_design.configs:
C Shapiro74da76e2020-05-04 13:02:20 -0500680 bt_comp = design_config.hardware_features.bluetooth.component.usb
C Shapiro90fda252020-04-17 14:34:57 -0500681 if bt_comp.vendor_id:
Andrew Lambcd33f702020-06-11 10:45:16 -0600682 bt_id = _bluetooth_id(project_name, bt_comp)
C Shapiro90fda252020-04-17 14:34:57 -0500683 result[bt_id] = {
C Shapiro5c877992020-04-29 12:11:28 -0500684 'build-path': '%s/bluetooth/%s.conf' % (build_root_path, bt_id),
C Shapiro90fda252020-04-17 14:34:57 -0500685 'system-path': '/etc/bluetooth/%s/main.conf' % bt_id,
686 }
687 bt_content = '''[General]
Andrew Lamb2413c982020-05-29 12:15:36 -0600688DeviceID = bluetooth:%s:%s:%s''' % (bt_comp.vendor_id, bt_comp.product_id,
C Shapiro90fda252020-04-17 14:34:57 -0500689 bt_comp.bcd_device)
690
David Burger77a1d312020-05-23 16:05:45 -0600691 os.makedirs(output_dir, exist_ok=True)
692 output = '%s/%s.conf' % (output_dir, bt_id)
C Shapiro90fda252020-04-17 14:34:57 -0500693 with open(output, 'w') as output_stream:
694 # Using print function adds proper trailing newline.
695 print(bt_content, file=output_stream)
696 return result
697
698
Andrew Lambcd33f702020-06-11 10:45:16 -0600699def _read_config(path):
David Burgerd4f32962020-05-02 12:07:40 -0600700 """Reads a ConfigBundle proto from a json pb file.
David Burgere6f76222020-04-27 11:08:01 -0600701
702 Args:
David Burgerd4f32962020-05-02 12:07:40 -0600703 path: Path to the file encoding the json pb proto.
David Burgere6f76222020-04-27 11:08:01 -0600704 """
705 config = config_bundle_pb2.ConfigBundle()
706 with open(path, 'r') as f:
707 return json_format.Parse(f.read(), config)
708
709
Andrew Lambcd33f702020-06-11 10:45:16 -0600710def _merge_configs(configs):
David Burger7fd1dbe2020-03-26 09:26:55 -0600711 result = config_bundle_pb2.ConfigBundle()
712 for config in configs:
713 result.MergeFrom(config)
714
715 return result
716
717
David Burger52c9d322020-06-09 07:16:18 -0600718def _dptf_map(configs, project_name):
719 """Produces a dptf map for the given configs.
720
721 Produces a map that maps from design name to the dptf file config for that
722 design. It looks for the dptf files at:
723 DPTF_PATH + DPTF_FILE
724 for a project wide config, that it maps under the empty string, and at:
725 DPTF_PATH + design_name + DPTF_FILE
726 for design specific configs that it maps under the design name.
727
728 Args:
729 configs: Source ConfigBundle to process.
730 project_name: Name of project processing for.
731
732 Returns:
733 map from design name or empty string (project wide), to dptf config
734 """
735 result = {}
736 project_dptf_path = os.path.join(project_name, 'dptf.dv')
737 # Looking at top level for project wide, and then for each design name
738 # for design specific.
739 dirs = [""] + [d.name for d in configs.designs.value]
740 for directory in dirs:
741 if os.path.exists(os.path.join(DPTF_PATH, directory, DPTF_FILE)):
742 dptf_file = {
743 'dptf-dv':
744 project_dptf_path,
745 'files': [
746 _file(
747 os.path.join(project_name, DPTF_PATH, directory, DPTF_FILE),
748 os.path.join('/etc/dptf', project_dptf_path))
749 ]
750 }
751 result[directory] = dptf_file
752 return result
753
754
Andrew Lambcd33f702020-06-11 10:45:16 -0600755def Main(project_configs, program_config, output): # pylint: disable=invalid-name
David Burger7fd1dbe2020-03-26 09:26:55 -0600756 """Transforms source proto config into platform JSON.
757
758 Args:
759 project_configs: List of source project configs to transform.
760 program_config: Program config for the given set of projects.
761 output: Output file that will be generated by the transform.
762 """
Andrew Lambcd33f702020-06-11 10:45:16 -0600763 configs = _merge_configs([_read_config(program_config)] +
764 [_read_config(config) for config in project_configs])
C Shapiro5bf23a72020-04-24 11:40:17 -0500765 bluetooth_files = {}
766 arc_hw_feature_files = {}
C Shapiro2b6d5332020-05-06 17:51:35 -0500767 touch_fw = {}
David Burger52c9d322020-06-09 07:16:18 -0600768 dptf_map = {}
C Shapiro5bf23a72020-04-24 11:40:17 -0500769 output_dir = os.path.dirname(output)
C Shapiro5c877992020-04-29 12:11:28 -0500770 build_root_dir = output_dir
C Shapiro5c877992020-04-29 12:11:28 -0500771 if 'sw_build_config' in output_dir:
772 full_path = os.path.realpath(output)
Andrew Lamb2413c982020-05-29 12:15:36 -0600773 project_name = re.match(r'.*/(\w*)/sw_build_config/.*',
774 full_path).groups(1)[0]
C Shapiro5c877992020-04-29 12:11:28 -0500775 # Projects don't know about each other until they are integrated into the
776 # build system. When this happens, the files need to be able to co-exist
777 # without any collisions. This prefixes the project name (which is how
778 # portage maps in the project), so project files co-exist and can be
779 # installed together.
780 # This is necessary to allow projects to share files at the program level
781 # without having portage file installation collisions.
782 build_root_dir = os.path.join(project_name, output_dir)
C Shapiro6830e6c2020-04-29 13:29:56 -0500783
David Burger52c9d322020-06-09 07:16:18 -0600784 dptf_map = _dptf_map(configs, project_name)
785
C Shapiro2b6d5332020-05-06 17:51:35 -0500786 if os.path.exists(TOUCH_PATH):
Andrew Lambcd33f702020-06-11 10:45:16 -0600787 touch_fw = _build_touch_file_config(configs, project_name)
788 bluetooth_files = _write_bluetooth_config_files(configs, output_dir,
789 build_root_dir)
790 arc_hw_feature_files = _write_arc_hardware_feature_files(
791 configs, output_dir, build_root_dir)
C Shapiro5bf23a72020-04-24 11:40:17 -0500792 config_files = ConfigFiles(
793 bluetooth=bluetooth_files,
794 arc_hw_features=arc_hw_feature_files,
C Shapiro2b6d5332020-05-06 17:51:35 -0500795 touch_fw=touch_fw,
David Burger52c9d322020-06-09 07:16:18 -0600796 dptf_map=dptf_map)
Andrew Lambcd33f702020-06-11 10:45:16 -0600797 write_output(_transform_build_configs(configs, config_files), output)
David Burger7fd1dbe2020-03-26 09:26:55 -0600798
799
800def main(argv=None):
801 """Main program which parses args and runs
802
803 Args:
804 argv: List of command line arguments, if None uses sys.argv.
805 """
806 if argv is None:
807 argv = sys.argv[1:]
Andrew Lambcd33f702020-06-11 10:45:16 -0600808 opts = parse_args(argv)
David Burger7fd1dbe2020-03-26 09:26:55 -0600809 Main(opts.project_configs, opts.program_config, opts.output)
810
811
812if __name__ == '__main__':
813 sys.exit(main(sys.argv[1:]))