blob: 1d2bdf3096cbaa89c153789a6cbe40f4f0f445cb [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
Ren-Pei Zengce869dd2020-08-18 01:29:37 +08008# pylint: disable=too-many-lines
9
David Burger7fd1dbe2020-03-26 09:26:55 -060010import argparse
11import json
12import pprint
C Shapiro90fda252020-04-17 14:34:57 -050013import os
David Burger7fd1dbe2020-03-26 09:26:55 -060014import sys
C Shapiro90fda252020-04-17 14:34:57 -050015import re
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
Ren-Pei Zengacf03be2020-11-11 13:56:51 +080022from lxml import etree
Andrew Lambcd33f702020-06-11 10:45:16 -060023
Prathmesh Prabhu72f8a002020-04-10 09:57:53 -070024from chromiumos.config.api import device_brand_pb2
David Burger92609a32020-04-23 10:38:50 -060025from chromiumos.config.api import topology_pb2
C Shapiro5bf23a72020-04-24 11:40:17 -050026from chromiumos.config.payload import config_bundle_pb2
Prathmesh Prabhu72f8a002020-04-10 09:57:53 -070027from chromiumos.config.api.software import brand_config_pb2
David Burger7fd1dbe2020-03-26 09:26:55 -060028
Andrew Lamb2413c982020-05-29 12:15:36 -060029Config = namedtuple('Config', [
30 'program', 'hw_design', 'odm', 'hw_design_config', 'device_brand',
31 'device_signer_config', 'oem', 'sw_config', 'brand_config', 'build_target'
32])
David Burger7fd1dbe2020-03-26 09:26:55 -060033
Ren-Pei Zengacf03be2020-11-11 13:56:51 +080034ConfigFiles = namedtuple('ConfigFiles', [
35 'arc_hw_features', 'arc_media_profiles', 'touch_fw', 'dptf_map',
36 'camera_map', 'wifi_sar_map'
37])
David Burger8ee9b4d2020-06-16 17:40:21 -060038
39CAMERA_CONFIG_DEST_PATH_TEMPLATE = '/etc/camera/camera_config_{}.json'
40CAMERA_CONFIG_SOURCE_PATH_TEMPLATE = (
41 'sw_build_config/platform/chromeos-config/camera/camera_config_{}.json')
C Shapiro5bf23a72020-04-24 11:40:17 -050042
David Burger52c9d322020-06-09 07:16:18 -060043DPTF_PATH = 'sw_build_config/platform/chromeos-config/thermal'
44DPTF_FILE = 'dptf.dv'
David Burger2f0d9522020-07-30 10:52:28 -060045
C Shapiro2b6d5332020-05-06 17:51:35 -050046TOUCH_PATH = 'sw_build_config/platform/chromeos-config/touch'
Andrew Lamb6c42efc2020-06-16 10:40:43 -060047WALLPAPER_BASE_PATH = '/usr/share/chromeos-assets/wallpaper'
David Burger7fd1dbe2020-03-26 09:26:55 -060048
Ren-Pei Zengacf03be2020-11-11 13:56:51 +080049XML_DECLARATION = b'<?xml version="1.0" encoding="utf-8"?>\n'
50
Andrew Lamb2413c982020-05-29 12:15:36 -060051
Andrew Lambcd33f702020-06-11 10:45:16 -060052def parse_args(argv):
David Burger7fd1dbe2020-03-26 09:26:55 -060053 """Parse the available arguments.
54
55 Invalid arguments or -h cause this function to print a message and exit.
56
57 Args:
58 argv: List of string arguments (excluding program name / argv[0])
59
60 Returns:
61 argparse.Namespace object containing the attributes.
62 """
63 parser = argparse.ArgumentParser(
64 description='Converts source proto config into platform JSON config.')
65 parser.add_argument(
66 '-c',
67 '--project_configs',
68 nargs='+',
69 type=str,
70 help='Space delimited list of source protobinary project config files.')
71 parser.add_argument(
72 '-p',
73 '--program_config',
74 type=str,
75 help='Path to the source program-level protobinary file')
76 parser.add_argument(
Andrew Lamb2413c982020-05-29 12:15:36 -060077 '-o', '--output', type=str, help='Output file that will be generated')
David Burger7fd1dbe2020-03-26 09:26:55 -060078 return parser.parse_args(argv)
79
80
David Burger8ee9b4d2020-06-16 17:40:21 -060081def _upsert(field, target, target_name):
82 """Updates or inserts `field` within `target`.
83
84 If `target_name` already exists within `target` an update is performed,
85 otherwise, an insert is performed.
86 """
Sam McNally9a873f72020-06-05 19:47:22 +100087 if field or field == 0:
David Burger8ee9b4d2020-06-16 17:40:21 -060088 if target_name in target:
89 target[target_name].update(field)
90 else:
91 target[target_name] = field
David Burger7fd1dbe2020-03-26 09:26:55 -060092
93
Andrew Lambcd33f702020-06-11 10:45:16 -060094def _build_arc(config, config_files):
95 if not config.build_target.arc:
96 return None
97
98 build_properties = {
99 'device': config.build_target.arc.device,
100 'first-api-level': config.build_target.arc.first_api_level,
101 'marketing-name': config.device_brand.brand_name,
102 'metrics-tag': config.hw_design.name.lower(),
103 'product': config.build_target.id.value,
104 }
105 if config.oem:
106 build_properties['oem'] = config.oem.name
107 result = {'build-properties': build_properties}
Ren-Pei Zengc6ba15c2020-10-17 00:51:25 +0800108 config_id = _get_formatted_config_id(config.hw_design_config)
109 if config_id in config_files.arc_hw_features:
110 result['hardware-features'] = config_files.arc_hw_features[config_id]
Ren-Pei Zengacf03be2020-11-11 13:56:51 +0800111 if config_id in config_files.arc_media_profiles:
112 result['media-profiles'] = config_files.arc_media_profiles[config_id]
Andrew Lambcd33f702020-06-11 10:45:16 -0600113 topology = config.hw_design_config.hardware_topology
114 ppi = topology.screen.hardware_feature.screen.panel_properties.pixels_per_in
115 # Only set for high resolution displays
116 if ppi and ppi > 250:
117 result['scale'] = ppi
David Burger2f0d9522020-07-30 10:52:28 -0600118
Andrew Lambcd33f702020-06-11 10:45:16 -0600119 return result
David Burger7fd1dbe2020-03-26 09:26:55 -0600120
Andrew Lamb2413c982020-05-29 12:15:36 -0600121
Andrew Lamb319cc922020-06-15 10:45:46 -0600122def _build_ash_flags(config: Config) -> List[str]:
123 """Returns a list of Ash flags for config.
124
125 Ash is the window manager and system UI for ChromeOS, see
126 https://chromium.googlesource.com/chromium/src/+/refs/heads/master/ash/.
127 """
128 # A map from flag name -> value. Value may be None for boolean flags.
129 flags = {}
130
131 hw_features = config.hw_design_config.hardware_features
132 if hw_features.stylus.stylus == topology_pb2.HardwareFeatures.Stylus.INTERNAL:
Andrew Lamb2e641e22020-06-15 12:30:41 -0600133 flags['has-internal-stylus'] = None
Andrew Lamb319cc922020-06-15 10:45:46 -0600134
Andrew Lamb2e641e22020-06-15 12:30:41 -0600135 fp_loc = hw_features.fingerprint.location
136 if fp_loc and fp_loc != topology_pb2.HardwareFeatures.Fingerprint.NOT_PRESENT:
137 loc_name = topology_pb2.HardwareFeatures.Fingerprint.Location.Name(fp_loc)
138 flags['fingerprint-sensor-location'] = loc_name.lower().replace('_', '-')
139
Andrew Lamb6c42efc2020-06-16 10:40:43 -0600140 wallpaper = config.brand_config.wallpaper
141 # If a wallpaper is set, the 'default-wallpaper-is-oem' flag needs to be set.
142 # If a wallpaper is not set, the 'default_[large|small].jpg' wallpapers
143 # should still be set.
144 if wallpaper:
145 flags['default-wallpaper-is-oem'] = None
146 else:
147 wallpaper = 'default'
148
149 for size in ('small', 'large'):
150 flags[f'default-wallpaper-{size}'] = (
151 f'{WALLPAPER_BASE_PATH}/{wallpaper}_{size}.jpg')
152
153 # For each size, also install 'guest' and 'child' wallpapers.
154 for wallpaper_type in ('guest', 'child'):
155 flags[f'{wallpaper_type}-wallpaper-{size}'] = (
156 f'{WALLPAPER_BASE_PATH}/{wallpaper_type}_{size}.jpg')
157
C Shapiro2e97aad2020-11-16 11:58:10 -0600158 regulatory_label = config.brand_config.regulatory_label
159 if regulatory_label:
160 flags['regulatory-label-dir'] = (regulatory_label)
161
Andrew Lamb72d41362020-06-17 09:19:02 -0600162 flags['arc-build-properties'] = json_format.MessageToDict(
163 config.build_target.arc)
164
Andrew Lamb90b168c2020-06-22 10:42:30 -0600165 power_button = hw_features.power_button
166 if power_button.edge:
167 flags['ash-power-button-position'] = json.dumps({
168 'edge':
169 topology_pb2.HardwareFeatures.Button.Edge.Name(power_button.edge
170 ).lower(),
171 # Starlark sometimes represents float literals strangely, e.g. changing
172 # 0.9 to 0.899999. Round to two digits here.
173 'position':
174 round(power_button.position, 2)
175 })
176
177 volume_button = hw_features.volume_button
178 if volume_button.edge:
179 flags['ash-side-volume-button-position'] = json.dumps({
180 'region':
181 topology_pb2.HardwareFeatures.Button.Region.Name(
182 volume_button.region).lower(),
183 'edge':
184 topology_pb2.HardwareFeatures.Button.Edge.Name(volume_button.edge
185 ).lower(),
186 })
187
Andrew Lamb2e641e22020-06-15 12:30:41 -0600188 return sorted([f'--{k}={v}' if v else f'--{k}' for k, v in flags.items()])
Andrew Lamb319cc922020-06-15 10:45:46 -0600189
190
191def _build_ui(config: Config) -> dict:
192 """Builds the 'ui' property from cros_config_schema."""
193 return {'extra-ash-flags': _build_ash_flags(config)}
194
195
David Burger07af5242020-08-11 11:08:25 -0600196def _build_bluetooth(config):
C Shapiro90fda252020-04-17 14:34:57 -0500197 bt_flags = config.sw_config.bluetooth_config.flags
198 # Convert to native map (from proto wrapper)
199 bt_flags_map = dict(bt_flags)
200 result = {}
201 if bt_flags_map:
202 result['flags'] = bt_flags_map
C Shapiro90fda252020-04-17 14:34:57 -0500203 return result
204
David Burger7fd1dbe2020-03-26 09:26:55 -0600205
David Burgerceeb68a2020-09-03 11:31:10 -0600206def _build_ath10k_config(ath10k_config):
207 """Builds the wifi configuration for the ath10k driver.
208
209 Args:
210 ath10k_config: Ath10kConfig config.
211
212 Returns:
213 wifi configuration for the ath10k driver.
214 """
David Burgerec753912020-08-10 12:59:11 -0600215 result = {}
David Burgerec753912020-08-10 12:59:11 -0600216
David Burgerceeb68a2020-09-03 11:31:10 -0600217 def power_chain(power):
218 return {
219 'limit-2g': power.limit_2g,
220 'limit-5g': power.limit_5g,
221 }
David Burgerec753912020-08-10 12:59:11 -0600222
David Burgerceeb68a2020-09-03 11:31:10 -0600223 result['tablet-mode-power-table-ath10k'] = power_chain(
224 ath10k_config.tablet_mode_power_table)
225 result['non-tablet-mode-power-table-ath10k'] = power_chain(
226 ath10k_config.non_tablet_mode_power_table)
David Burgerec753912020-08-10 12:59:11 -0600227 return result
228
229
David Burgerceeb68a2020-09-03 11:31:10 -0600230def _build_rtw88_config(rtw88_config):
231 """Builds the wifi configuration for the rtw88 driver.
232
233 Args:
234 rtw88_config: Rtw88Config config.
235
236 Returns:
237 wifi configuration for the rtw88 driver.
238 """
239 result = {}
240
241 def power_chain(power):
242 return {
243 'limit-2g': power.limit_2g,
244 'limit-5g-1': power.limit_5g_1,
245 'limit-5g-3': power.limit_5g_3,
246 'limit-5g-4': power.limit_5g_4,
247 }
248
249 result['tablet-mode-power-table-rtw'] = power_chain(
250 rtw88_config.tablet_mode_power_table)
251 result['non-tablet-mode-power-table-rtw'] = power_chain(
252 rtw88_config.non_tablet_mode_power_table)
253
254 def offsets(offset):
255 return {
256 'offset-2g': offset.offset_2g,
257 'offset-5g': offset.offset_5g,
258 }
259
260 result['geo-offsets-fcc'] = offsets(rtw88_config.offset_fcc)
261 result['geo-offsets-eu'] = offsets(rtw88_config.offset_eu)
262 result['geo-offsets-rest-of-world'] = offsets(rtw88_config.offset_other)
263 return result
264
265
266def _build_intel_config(config, config_files):
267 """Builds the wifi configuration for the intel driver.
268
269 Args:
270 config: Config namedtuple
271 config_files: Map to look up the generated config files.
272
273 Returns:
274 wifi configuration for the intel driver.
275 """
276 design_name = config.hw_design.name.lower()
277 return config_files.wifi_sar_map.get(design_name)
278
279
280def _build_wifi(config, config_files):
281 """Builds the wifi configuration.
282
283 Args:
284 config: Config namedtuple
285 config_files: Map to look up the generated config files.
286
287 Returns:
288 wifi configuration.
289 """
290 config_field = config.sw_config.wifi_config.WhichOneof('wifi_config')
291 if config_field == 'ath10k_config':
292 return _build_ath10k_config(config.sw_config.wifi_config.ath10k_config)
293 if config_field == 'rtw88_config':
294 return _build_rtw88_config(config.sw_config.wifi_config.rtw88_config)
295 if config_field == 'intel_config':
296 return _build_intel_config(config, config_files)
297 return {}
298
299
Andrew Lambcd33f702020-06-11 10:45:16 -0600300def _build_fingerprint(hw_topology):
301 if not hw_topology.HasField('fingerprint'):
302 return None
303
304 fp = hw_topology.fingerprint.hardware_feature.fingerprint
305 result = {}
306 if fp.location != topology_pb2.HardwareFeatures.Fingerprint.NOT_PRESENT:
307 location = fp.Location.DESCRIPTOR.values_by_number[fp.location].name
308 result['sensor-location'] = location.lower().replace('_', '-')
309 if fp.board:
310 result['board'] = fp.board
Tom Hughesdfc35402020-06-29 16:02:09 -0700311 if fp.ro_version:
312 result['ro-version'] = fp.ro_version
313
Andrew Lambcd33f702020-06-11 10:45:16 -0600314 return result
David Burger7fd1dbe2020-03-26 09:26:55 -0600315
316
Trent Beginf067ccb2020-08-12 12:33:53 -0600317def _build_hardware_properties(hw_topology):
318 if not hw_topology.HasField('form_factor'):
319 return None
320
321 form_factor = hw_topology.form_factor.hardware_feature.form_factor.form_factor
322 result = {}
323 if form_factor in [
324 topology_pb2.HardwareFeatures.FormFactor.CHROMEBIT,
325 topology_pb2.HardwareFeatures.FormFactor.CHROMEBASE,
326 topology_pb2.HardwareFeatures.FormFactor.CHROMEBOX
327 ]:
328 result['psu-type'] = "AC_only"
329 else:
330 result['psu-type'] = "battery"
331
332 result['has-backlight'] = form_factor not in [
333 topology_pb2.HardwareFeatures.FormFactor.CHROMEBIT,
334 topology_pb2.HardwareFeatures.FormFactor.CHROMEBOX
335 ]
336
337 return result
338
339
Andrew Lambcd33f702020-06-11 10:45:16 -0600340def _fw_bcs_path(payload):
David Burger7fd1dbe2020-03-26 09:26:55 -0600341 if payload and payload.firmware_image_name:
Andrew Lamb2413c982020-05-29 12:15:36 -0600342 return 'bcs://%s.%d.%d.0.tbz2' % (payload.firmware_image_name,
343 payload.version.major,
344 payload.version.minor)
David Burger7fd1dbe2020-03-26 09:26:55 -0600345
Andrew Lambcd33f702020-06-11 10:45:16 -0600346 return None
David Burger7fd1dbe2020-03-26 09:26:55 -0600347
Andrew Lambcd33f702020-06-11 10:45:16 -0600348
349def _fw_build_target(payload):
David Burger7fd1dbe2020-03-26 09:26:55 -0600350 if payload:
351 return payload.build_target_name
352
Andrew Lambcd33f702020-06-11 10:45:16 -0600353 return None
David Burger7fd1dbe2020-03-26 09:26:55 -0600354
Andrew Lambcd33f702020-06-11 10:45:16 -0600355
356def _build_firmware(config):
David Burgerb70b6762020-05-21 12:14:59 -0600357 """Returns firmware config, or None if no build targets."""
Andrew Lamb3da156d2020-04-16 16:00:56 -0600358 fw_payload_config = config.sw_config.firmware
359 fw_build_config = config.sw_config.firmware_build_config
360 main_ro = fw_payload_config.main_ro_payload
361 main_rw = fw_payload_config.main_rw_payload
362 ec_ro = fw_payload_config.ec_ro_payload
363 pd_ro = fw_payload_config.pd_ro_payload
David Burger7fd1dbe2020-03-26 09:26:55 -0600364
365 build_targets = {}
Andrew Lamb3da156d2020-04-16 16:00:56 -0600366
David Burger8ee9b4d2020-06-16 17:40:21 -0600367 _upsert(fw_build_config.build_targets.depthcharge, build_targets,
368 'depthcharge')
369 _upsert(fw_build_config.build_targets.coreboot, build_targets, 'coreboot')
370 _upsert(fw_build_config.build_targets.ec, build_targets, 'ec')
371 _upsert(
Andrew Lambf8954ee2020-04-21 10:24:40 -0600372 list(fw_build_config.build_targets.ec_extras), build_targets, 'ec_extras')
David Burger8ee9b4d2020-06-16 17:40:21 -0600373 _upsert(fw_build_config.build_targets.libpayload, build_targets, 'libpayload')
David Burger7fd1dbe2020-03-26 09:26:55 -0600374
David Burgerb70b6762020-05-21 12:14:59 -0600375 if not build_targets:
376 return None
377
David Burger7fd1dbe2020-03-26 09:26:55 -0600378 result = {
379 'bcs-overlay': config.build_target.overlay_name,
380 'build-targets': build_targets,
David Burger7fd1dbe2020-03-26 09:26:55 -0600381 }
Andrew Lamb883fa042020-04-06 11:37:22 -0600382
David Burger8ee9b4d2020-06-16 17:40:21 -0600383 _upsert(main_ro.firmware_image_name.lower(), result, 'image-name')
Andrew Lamb883fa042020-04-06 11:37:22 -0600384
David Burger8ee9b4d2020-06-16 17:40:21 -0600385 _upsert(_fw_bcs_path(main_ro), result, 'main-ro-image')
386 _upsert(_fw_bcs_path(main_rw), result, 'main-rw-image')
387 _upsert(_fw_bcs_path(ec_ro), result, 'ec-ro-image')
388 _upsert(_fw_bcs_path(pd_ro), result, 'pd-ro-image')
David Burger7fd1dbe2020-03-26 09:26:55 -0600389
David Burger8ee9b4d2020-06-16 17:40:21 -0600390 _upsert(
Andrew Lambf39fbe82020-04-13 16:14:33 -0600391 config.hw_design_config.hardware_features.fw_config.value,
392 result,
393 'firmware-config',
394 )
395
David Burger7fd1dbe2020-03-26 09:26:55 -0600396 return result
397
398
Sam McNallyfd14e4b2020-09-12 10:26:35 +1000399def _build_fw_signing(config, whitelabel):
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500400 if config.sw_config.firmware and config.device_signer_config:
David Burger68e0d142020-05-15 17:29:33 -0600401 hw_design = config.hw_design.name.lower()
Sam McNally2fc807f2020-07-16 18:13:53 +1000402 brand_scan_config = config.brand_config.scan_config
403 if brand_scan_config and brand_scan_config.whitelabel_tag:
404 signature_id = '%s-%s' % (hw_design, brand_scan_config.whitelabel_tag)
405 else:
406 signature_id = hw_design
407
Sam McNallyfd14e4b2020-09-12 10:26:35 +1000408 result = {
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500409 'key-id': config.device_signer_config.key_id,
Sam McNally2fc807f2020-07-16 18:13:53 +1000410 'signature-id': signature_id,
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500411 }
Sam McNallyfd14e4b2020-09-12 10:26:35 +1000412 if whitelabel:
413 result['sig-id-in-customization-id'] = True
414 return result
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500415 return {}
David Burger7fd1dbe2020-03-26 09:26:55 -0600416
417
Andrew Lambcd33f702020-06-11 10:45:16 -0600418def _file(source, destination):
Andrew Lamb2413c982020-05-29 12:15:36 -0600419 return {'destination': destination, 'source': source}
David Burger7fd1dbe2020-03-26 09:26:55 -0600420
421
David Burger40dfe3a2020-06-18 17:09:13 -0600422def _file_v2(build_path, system_path):
423 return {'build-path': build_path, 'system-path': system_path}
424
425
Andrew Lambcd33f702020-06-11 10:45:16 -0600426def _build_audio(config):
David Burger178f3ef2020-06-26 12:11:57 -0600427 if not config.sw_config.audio_configs:
428 return {}
David Burger7fd1dbe2020-03-26 09:26:55 -0600429 alsa_path = '/usr/share/alsa/ucm'
430 cras_path = '/etc/cras'
431 project_name = config.hw_design.name.lower()
David Burger43250662020-05-07 11:21:50 -0600432 program_name = config.program.name.lower()
David Burger7fd1dbe2020-03-26 09:26:55 -0600433 files = []
David Burger178f3ef2020-06-26 12:11:57 -0600434 ucm_suffix = None
435 for audio in config.sw_config.audio_configs:
436 card = audio.card_name
437 card_with_suffix = audio.card_name
438 if audio.ucm_suffix:
439 # TODO: last ucm_suffix wins.
440 ucm_suffix = audio.ucm_suffix
441 card_with_suffix += '.' + audio.ucm_suffix
442 if audio.ucm_file:
443 files.append(
444 _file(audio.ucm_file,
445 '%s/%s/HiFi.conf' % (alsa_path, card_with_suffix)))
446 if audio.ucm_master_file:
447 files.append(
448 _file(
449 audio.ucm_master_file, '%s/%s/%s.conf' %
Andrew Lamb2413c982020-05-29 12:15:36 -0600450 (alsa_path, card_with_suffix, card_with_suffix)))
David Burger178f3ef2020-06-26 12:11:57 -0600451 if audio.card_config_file:
452 files.append(
453 _file(audio.card_config_file,
454 '%s/%s/%s' % (cras_path, project_name, card)))
455 if audio.dsp_file:
456 files.append(
457 _file(audio.dsp_file, '%s/%s/dsp.ini' % (cras_path, project_name)))
458 if audio.module_file:
459 files.append(
460 _file(audio.module_file,
461 '/etc/modprobe.d/alsa-%s.conf' % program_name))
462 if audio.board_file:
463 files.append(
464 _file(audio.board_file,
465 '%s/%s/board.ini' % (cras_path, project_name)))
David Burger599ff7b2020-04-06 16:29:31 -0600466
467 result = {
David Burger7fd1dbe2020-03-26 09:26:55 -0600468 'main': {
469 'cras-config-dir': project_name,
470 'files': files,
471 }
472 }
David Burger178f3ef2020-06-26 12:11:57 -0600473
474 if ucm_suffix:
475 result['main']['ucm-suffix'] = ucm_suffix
David Burger599ff7b2020-04-06 16:29:31 -0600476
477 return result
David Burger7fd1dbe2020-03-26 09:26:55 -0600478
479
Andrew Lambcd33f702020-06-11 10:45:16 -0600480def _build_camera(hw_topology):
Ren-Pei Zeng573d6332020-11-12 14:55:22 +0800481 camera_pb = topology_pb2.HardwareFeatures.Camera
482 camera = hw_topology.camera.hardware_feature.camera
483 result = {'count': len(camera.devices)}
484 if camera.devices:
485 result['devices'] = []
486 for device in camera.devices:
487 interface = {
488 camera_pb.INTERFACE_USB: 'usb',
489 camera_pb.INTERFACE_MIPI: 'mipi',
490 }[device.interface]
491 facing = {
492 camera_pb.FACING_FRONT: 'front',
493 camera_pb.FACING_BACK: 'back',
494 }[device.facing]
495 orientation = {
496 camera_pb.ORIENTATION_0: 0,
497 camera_pb.ORIENTATION_90: 90,
498 camera_pb.ORIENTATION_180: 180,
499 camera_pb.ORIENTATION_270: 270,
500 }[device.orientation]
501 flags = {
502 'support-1080p':
503 bool(device.flags & camera_pb.FLAGS_SUPPORT_1080P),
504 'support-autofocus':
505 bool(device.flags & camera_pb.FLAGS_SUPPORT_AUTOFOCUS),
506 }
507 result['devices'].append({
508 'interface': interface,
509 'facing': facing,
510 'orientation': orientation,
511 'flags': flags,
512 'ids': list(device.ids),
513 })
514 return result
David Burger8aa8fa32020-04-14 08:30:34 -0600515
Andrew Lambcd33f702020-06-11 10:45:16 -0600516
517def _build_identity(hw_scan_config, program, brand_scan_config=None):
David Burger7fd1dbe2020-03-26 09:26:55 -0600518 identity = {}
David Burger8ee9b4d2020-06-16 17:40:21 -0600519 _upsert(hw_scan_config.firmware_sku, identity, 'sku-id')
520 _upsert(hw_scan_config.smbios_name_match, identity, 'smbios-name-match')
Andrew Lamb7806ce92020-04-07 10:22:17 -0600521 # 'platform-name' is needed to support 'mosys platform name'. Clients should
Sean McAllister0b757772020-11-13 12:22:36 -0700522 # no longer require platform name, but set it here for backwards compatibility.
523 if program.mosys_platform_name:
524 _upsert(program.mosys_platform_name, identity, 'platform-name')
525 else:
526 _upsert(program.name, identity, 'platform-name')
527
David Burger7fd1dbe2020-03-26 09:26:55 -0600528 # ARM architecture
David Burger8ee9b4d2020-06-16 17:40:21 -0600529 _upsert(hw_scan_config.device_tree_compatible_match, identity,
530 'device-tree-compatible-match')
David Burger7fd1dbe2020-03-26 09:26:55 -0600531
532 if brand_scan_config:
David Burger8ee9b4d2020-06-16 17:40:21 -0600533 _upsert(brand_scan_config.whitelabel_tag, identity, 'whitelabel-tag')
David Burger7fd1dbe2020-03-26 09:26:55 -0600534
535 return identity
536
537
Andrew Lambcd33f702020-06-11 10:45:16 -0600538def _lookup(id_value, id_map):
539 if not id_value.value:
540 return None
541
542 key = id_value.value
543 if key in id_map:
544 return id_map[id_value.value]
545 error = 'Failed to lookup %s with value: %s' % (
546 id_value.__class__.__name__.replace('Id', ''), key)
547 print(error)
548 print('Check the config contents provided:')
549 printer = pprint.PrettyPrinter(indent=4)
550 printer.pprint(id_map)
551 raise Exception(error)
David Burger7fd1dbe2020-03-26 09:26:55 -0600552
553
Andrew Lambcd33f702020-06-11 10:45:16 -0600554def _build_touch_file_config(config, project_name):
Sean McAllistereaf10b72020-08-03 13:41:06 -0600555 partners = {x.id.value: x for x in config.partner_list}
C Shapiro2b6d5332020-05-06 17:51:35 -0500556 files = []
557 for comp in config.components:
C Shapiro4813be62020-05-13 17:31:58 -0500558 touch = comp.touchscreen
559 # Everything is the same for Touch screen/pad, except different fields
560 if comp.HasField('touchpad'):
561 touch = comp.touchpad
562 if touch.product_id:
Andrew Lambcd33f702020-06-11 10:45:16 -0600563 vendor = _lookup(comp.manufacturer_id, partners)
C Shapiro2b6d5332020-05-06 17:51:35 -0500564 if not vendor:
Andrew Lamb2413c982020-05-29 12:15:36 -0600565 raise Exception("Manufacturer must be set for touch device %s" %
566 comp.id.value)
C Shapiro2b6d5332020-05-06 17:51:35 -0500567
C Shapiro4813be62020-05-13 17:31:58 -0500568 product_id = touch.product_id
569 fw_version = touch.fw_version
C Shapiro2b6d5332020-05-06 17:51:35 -0500570
C Shapiro2b6d5332020-05-06 17:51:35 -0500571 file_name = "%s_%s.bin" % (product_id, fw_version)
572 fw_file_path = os.path.join(TOUCH_PATH, vendor.name, file_name)
573
574 if not os.path.exists(fw_file_path):
Andrew Lamb2413c982020-05-29 12:15:36 -0600575 raise Exception("Touchscreen fw bin file doesn't exist at: %s" %
576 fw_file_path)
C Shapiro2b6d5332020-05-06 17:51:35 -0500577
C Shapiro303cece2020-07-22 07:15:21 -0500578 touch_vendor = vendor.touch_vendor
579 sym_link = touch_vendor.symlink_file_format.format(
580 vendor_name=vendor.name,
581 vendor_id=touch_vendor.vendor_id,
582 product_id=product_id,
583 fw_version=fw_version,
584 product_series=touch.product_series)
585
586 dest = "%s_%s" % (vendor.name, file_name)
587 if touch_vendor.destination_file_format:
588 dest = touch_vendor.destination_file_format.format(
589 vendor_name=vendor.name,
590 vendor_id=touch_vendor.vendor_id,
591 product_id=product_id,
592 fw_version=fw_version,
593 product_series=touch.product_series)
594
C Shapiro2b6d5332020-05-06 17:51:35 -0500595 files.append({
C Shapiro303cece2020-07-22 07:15:21 -0500596 "destination": os.path.join("/opt/google/touch/firmware", dest),
YH Lin9160fc52020-07-22 16:35:28 -0700597 "source": os.path.join(project_name, fw_file_path),
598 "symlink": os.path.join("/lib/firmware", sym_link),
C Shapiro2b6d5332020-05-06 17:51:35 -0500599 })
600
601 result = {}
David Burger8ee9b4d2020-06-16 17:40:21 -0600602 _upsert(files, result, 'files')
C Shapiro2b6d5332020-05-06 17:51:35 -0500603 return result
604
605
David Burgerceeb68a2020-09-03 11:31:10 -0600606def _sw_config(sw_configs, design_config_id):
607 """Returns the correct software config for `design_config_id`.
608
609 Returns the correct software config match for `design_config_id`. If no such
610 config or multiple such configs are found an exception is raised.
611 """
612 sw_config_matches = [
613 x for x in sw_configs if x.design_config_id.value == design_config_id
614 ]
615 if len(sw_config_matches) == 1:
616 return sw_config_matches[0]
617 if len(sw_config_matches) > 1:
618 raise ValueError('Multiple software configs found for: %s' %
619 design_config_id)
620 raise ValueError('Software config is required for: %s' % design_config_id)
621
622
Sam McNallyfd14e4b2020-09-12 10:26:35 +1000623def _is_whitelabel(brand_configs, device_brands):
624 for device_brand in device_brands:
625 if device_brand.id.value in brand_configs:
626 brand_scan_config = brand_configs[device_brand.id.value].scan_config
627 if brand_scan_config and brand_scan_config.whitelabel_tag:
628 return True
629 return False
630
631
David Burgerceeb68a2020-09-03 11:31:10 -0600632def _transform_build_configs(config,
Ren-Pei Zengacf03be2020-11-11 13:56:51 +0800633 config_files=ConfigFiles({}, {}, {}, {}, {}, {})):
Andrew Lambcd33f702020-06-11 10:45:16 -0600634 # pylint: disable=too-many-locals,too-many-branches
Sean McAllistereaf10b72020-08-03 13:41:06 -0600635 partners = {x.id.value: x for x in config.partner_list}
Sean McAllisterf38d1e92020-08-03 13:57:53 -0600636 programs = {x.id.value: x for x in config.program_list}
David Burger7fd1dbe2020-03-26 09:26:55 -0600637 sw_configs = list(config.software_configs)
Andrew Lambcd33f702020-06-11 10:45:16 -0600638 brand_configs = {x.brand_id.value: x for x in config.brand_configs}
David Burger7fd1dbe2020-03-26 09:26:55 -0600639
C Shapiroa0b766c2020-03-31 08:35:28 -0500640 if len(config.build_targets) != 1:
641 # Artifact of sharing the config_bundle for analysis and transforms.
642 # Integrated analysis of multiple programs/projects it the only time
643 # having multiple build targets would be valid.
644 raise Exception('Single build_target required for transform')
645
David Burger7fd1dbe2020-03-26 09:26:55 -0600646 results = {}
Sean McAllisterf66887b2020-08-03 14:00:51 -0600647 for hw_design in config.design_list:
Sean McAllister6cbb0ec2020-08-03 14:03:37 -0600648 if config.device_brand_list:
Andrew Lamb2413c982020-05-29 12:15:36 -0600649 device_brands = [
Sean McAllister6cbb0ec2020-08-03 14:03:37 -0600650 x for x in config.device_brand_list
Andrew Lamb2413c982020-05-29 12:15:36 -0600651 if x.design_id.value == hw_design.id.value
652 ]
David Burger7fd1dbe2020-03-26 09:26:55 -0600653 else:
654 device_brands = [device_brand_pb2.DeviceBrand()]
655
Sam McNallyfd14e4b2020-09-12 10:26:35 +1000656 whitelabel = _is_whitelabel(brand_configs, device_brands)
657
David Burger7fd1dbe2020-03-26 09:26:55 -0600658 for device_brand in device_brands:
659 # Brand config can be empty since platform JSON config allows it
660 brand_config = brand_config_pb2.BrandConfig()
661 if device_brand.id.value in brand_configs:
662 brand_config = brand_configs[device_brand.id.value]
663
664 for hw_design_config in hw_design.configs:
David Burgerceeb68a2020-09-03 11:31:10 -0600665 sw_config = _sw_config(sw_configs, hw_design_config.id.value)
Andrew Lambcd33f702020-06-11 10:45:16 -0600666 program = _lookup(hw_design.program_id, programs)
C Shapiroadefd7c2020-05-19 16:37:21 -0500667 signer_configs_by_design = {}
668 signer_configs_by_brand = {}
669 for signer_config in program.device_signer_configs:
670 design_id = signer_config.design_id.value
671 brand_id = signer_config.brand_id.value
672 if design_id:
673 signer_configs_by_design[design_id] = signer_config
674 elif brand_id:
675 signer_configs_by_brand[brand_id] = signer_config
676 else:
677 raise Exception('No ID found for signer config: %s' % signer_config)
678
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500679 device_signer_config = None
C Shapiroadefd7c2020-05-19 16:37:21 -0500680 if signer_configs_by_design or signer_configs_by_brand:
681 design_id = hw_design.id.value
682 brand_id = device_brand.id.value
683 if design_id in signer_configs_by_design:
684 device_signer_config = signer_configs_by_design[design_id]
685 elif brand_id in signer_configs_by_brand:
686 device_signer_config = signer_configs_by_brand[brand_id]
687 else:
688 # Assume that if signer configs are set, every config is setup
Andrew Lamb2413c982020-05-29 12:15:36 -0600689 raise Exception('Signer config missing for design: %s, brand: %s' %
690 (design_id, brand_id))
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500691
Andrew Lambcd33f702020-06-11 10:45:16 -0600692 transformed_config = _transform_build_config(
C Shapiro90fda252020-04-17 14:34:57 -0500693 Config(
694 program=program,
695 hw_design=hw_design,
Andrew Lambcd33f702020-06-11 10:45:16 -0600696 odm=_lookup(hw_design.odm_id, partners),
C Shapiro90fda252020-04-17 14:34:57 -0500697 hw_design_config=hw_design_config,
698 device_brand=device_brand,
699 device_signer_config=device_signer_config,
Andrew Lambcd33f702020-06-11 10:45:16 -0600700 oem=_lookup(device_brand.oem_id, partners),
C Shapiro90fda252020-04-17 14:34:57 -0500701 sw_config=sw_config,
702 brand_config=brand_config,
Sam McNallyfd14e4b2020-09-12 10:26:35 +1000703 build_target=config.build_targets[0]), config_files, whitelabel)
David Burger7fd1dbe2020-03-26 09:26:55 -0600704
Andrew Lamb2413c982020-05-29 12:15:36 -0600705 config_json = json.dumps(
706 transformed_config,
707 sort_keys=True,
708 indent=2,
709 separators=(',', ': '))
David Burger7fd1dbe2020-03-26 09:26:55 -0600710
711 if config_json not in results:
712 results[config_json] = transformed_config
713
714 return list(results.values())
715
716
Sam McNallyfd14e4b2020-09-12 10:26:35 +1000717def _transform_build_config(config, config_files, whitelabel):
David Burger7fd1dbe2020-03-26 09:26:55 -0600718 """Transforms Config instance into target platform JSON schema.
719
720 Args:
721 config: Config namedtuple
C Shapiro5bf23a72020-04-24 11:40:17 -0500722 config_files: Map to look up the generated config files.
Sam McNallyfd14e4b2020-09-12 10:26:35 +1000723 whitelabel: Whether the config is for a whitelabel design
David Burger7fd1dbe2020-03-26 09:26:55 -0600724
725 Returns:
726 Unique config payload based on the platform JSON schema.
727 """
728 result = {
Andrew Lamb2413c982020-05-29 12:15:36 -0600729 'identity':
Andrew Lambcd33f702020-06-11 10:45:16 -0600730 _build_identity(config.sw_config.id_scan_config, config.program,
731 config.brand_config.scan_config),
Andrew Lamb2413c982020-05-29 12:15:36 -0600732 'name':
733 config.hw_design.name.lower(),
David Burger7fd1dbe2020-03-26 09:26:55 -0600734 }
735
David Burger8ee9b4d2020-06-16 17:40:21 -0600736 _upsert(_build_arc(config, config_files), result, 'arc')
737 _upsert(_build_audio(config), result, 'audio')
David Burger07af5242020-08-11 11:08:25 -0600738 _upsert(_build_bluetooth(config), result, 'bluetooth')
David Burgerceeb68a2020-09-03 11:31:10 -0600739 _upsert(_build_wifi(config, config_files), result, 'wifi')
Andrew Lambca279902020-08-06 10:13:42 -0600740 _upsert(config.brand_config.wallpaper, result, 'wallpaper')
C Shapiro2e97aad2020-11-16 11:58:10 -0600741 _upsert(config.brand_config.regulatory_label, result, 'regulatory-label')
David Burger8ee9b4d2020-06-16 17:40:21 -0600742 _upsert(config.device_brand.brand_code, result, 'brand-code')
743 _upsert(
Andrew Lambcd33f702020-06-11 10:45:16 -0600744 _build_camera(config.hw_design_config.hardware_topology), result,
745 'camera')
David Burger8ee9b4d2020-06-16 17:40:21 -0600746 _upsert(_build_firmware(config), result, 'firmware')
Sam McNallyfd14e4b2020-09-12 10:26:35 +1000747 _upsert(_build_fw_signing(config, whitelabel), result, 'firmware-signing')
David Burger8ee9b4d2020-06-16 17:40:21 -0600748 _upsert(
Andrew Lambcd33f702020-06-11 10:45:16 -0600749 _build_fingerprint(config.hw_design_config.hardware_topology), result,
Andrew Lamb2413c982020-05-29 12:15:36 -0600750 'fingerprint')
Andrew Lamb0d236ab2020-06-30 12:30:20 -0600751 _upsert(_build_ui(config), result, 'ui')
David Burger7fd1dbe2020-03-26 09:26:55 -0600752 power_prefs = config.sw_config.power_config.preferences
753 power_prefs_map = dict(
Andrew Lamb2413c982020-05-29 12:15:36 -0600754 (x.replace('_', '-'), power_prefs[x]) for x in power_prefs)
David Burger8ee9b4d2020-06-16 17:40:21 -0600755 _upsert(power_prefs_map, result, 'power')
756 if config_files.camera_map:
757 camera_file = config_files.camera_map.get(config.hw_design.name, {})
758 _upsert(camera_file, result, 'camera')
David Burger52c9d322020-06-09 07:16:18 -0600759 if config_files.dptf_map:
760 # Prefer design specific if found, if not fall back to project wide config
761 # mapped under the empty string.
762 if config_files.dptf_map.get(config.hw_design.name):
763 dptf_file = config_files.dptf_map[config.hw_design.name]
764 else:
765 dptf_file = config_files.dptf_map.get('')
David Burger8ee9b4d2020-06-16 17:40:21 -0600766 _upsert(dptf_file, result, 'thermal')
767 _upsert(config_files.touch_fw, result, 'touch')
Trent Beginf067ccb2020-08-12 12:33:53 -0600768 _upsert(
769 _build_hardware_properties(config.hw_design_config.hardware_topology),
770 result, 'hardware-properties')
David Burger7fd1dbe2020-03-26 09:26:55 -0600771
772 return result
773
774
Andrew Lambcd33f702020-06-11 10:45:16 -0600775def write_output(configs, output=None):
David Burger7fd1dbe2020-03-26 09:26:55 -0600776 """Writes a list of configs to platform JSON format.
777
778 Args:
779 configs: List of config dicts defined in cros_config_schema.yaml
780 output: Target file output (if None, prints to stdout)
781 """
Andrew Lamb2413c982020-05-29 12:15:36 -0600782 json_output = json.dumps({'chromeos': {
783 'configs': configs,
784 }},
785 sort_keys=True,
786 indent=2,
787 separators=(',', ': '))
David Burger7fd1dbe2020-03-26 09:26:55 -0600788 if output:
789 with open(output, 'w') as output_stream:
790 # Using print function adds proper trailing newline.
791 print(json_output, file=output_stream)
792 else:
793 print(json_output)
794
795
Andrew Lambcd33f702020-06-11 10:45:16 -0600796def _feature(name, present):
C Shapiro5bf23a72020-04-24 11:40:17 -0500797 attrib = {'name': name}
798 if present:
799 return etree.Element('feature', attrib=attrib)
Andrew Lambcd33f702020-06-11 10:45:16 -0600800
801 return etree.Element('unavailable-feature', attrib=attrib)
C Shapiro5bf23a72020-04-24 11:40:17 -0500802
803
Andrew Lambcd33f702020-06-11 10:45:16 -0600804def _any_present(features):
Andrew Lamb2413c982020-05-29 12:15:36 -0600805 return topology_pb2.HardwareFeatures.PRESENT in features
C Shapiro5bf23a72020-04-24 11:40:17 -0500806
807
Ren-Pei Zengc6ba15c2020-10-17 00:51:25 +0800808def _get_formatted_config_id(design_config):
C Shapiro5bf23a72020-04-24 11:40:17 -0500809 return design_config.id.value.lower().replace(':', '_')
810
811
Ren-Pei Zengc6ba15c2020-10-17 00:51:25 +0800812def _write_file(output_dir, file_name, file_content):
David Burger77a1d312020-05-23 16:05:45 -0600813 os.makedirs(output_dir, exist_ok=True)
Ren-Pei Zengc6ba15c2020-10-17 00:51:25 +0800814 output = '{}/{}'.format(output_dir, file_name)
C Shapiroea33cff2020-05-11 13:32:05 -0500815 with open(output, 'wb') as f:
816 f.write(file_content)
817
818
Ren-Pei Zengf22b5382020-09-02 13:31:22 +0800819def _get_arc_camera_features(camera):
820 """Gets camera related features for ARC hardware_features.xml from camera
821 topology. Check
822 https://developer.android.com/reference/android/content/pm/PackageManager#FEATURE_CAMERA
823 and CTS android.app.cts.SystemFeaturesTest#testCameraFeatures for the correct
824 settings.
825
826 Args:
827 camera: A HardwareFeatures.Camera proto message.
828 Returns:
829 list of camera related ARC features as XML elements.
830 """
831 camera_pb = topology_pb2.HardwareFeatures.Camera
832
Ren-Pei Zeng9b5682d2020-10-14 17:37:30 +0800833 count = len(camera.devices)
834 has_front_camera = any(
835 (d.facing == camera_pb.FACING_FRONT for d in camera.devices))
836 has_back_camera = any(
837 (d.facing == camera_pb.FACING_BACK for d in camera.devices))
838 has_autofocus_back_camera = any((d.facing == camera_pb.FACING_BACK and
839 d.flags & camera_pb.FLAGS_SUPPORT_AUTOFOCUS
840 for d in camera.devices))
841 # Assumes MIPI cameras support FULL-level.
842 # TODO(kamesan): Setting this in project configs when there's an exception.
843 has_level_full_camera = any(
844 (d.interface == camera_pb.INTERFACE_MIPI for d in camera.devices))
Ren-Pei Zengf22b5382020-09-02 13:31:22 +0800845
Ren-Pei Zengf22b5382020-09-02 13:31:22 +0800846 return [
847 _feature('android.hardware.camera', has_back_camera),
848 _feature('android.hardware.camera.any', count > 0),
849 _feature('android.hardware.camera.autofocus', has_autofocus_back_camera),
850 _feature('android.hardware.camera.capability.manual_post_processing',
851 has_level_full_camera),
852 _feature('android.hardware.camera.capability.manual_sensor',
853 has_level_full_camera),
854 _feature('android.hardware.camera.front', has_front_camera),
855 _feature('android.hardware.camera.level.full', has_level_full_camera),
856 ]
857
858
Ren-Pei Zengc6ba15c2020-10-17 00:51:25 +0800859def _generate_arc_hardware_features(hw_features):
860 """Generates ARC hardware_features.xml file content.
C Shapiro5bf23a72020-04-24 11:40:17 -0500861
862 Args:
Ren-Pei Zengc6ba15c2020-10-17 00:51:25 +0800863 hw_features: HardwareFeatures proto message.
C Shapiro5bf23a72020-04-24 11:40:17 -0500864 Returns:
Ren-Pei Zengc6ba15c2020-10-17 00:51:25 +0800865 bytes of the hardware_features.xml content.
C Shapiro5bf23a72020-04-24 11:40:17 -0500866 """
Ren-Pei Zengc6ba15c2020-10-17 00:51:25 +0800867 touchscreen = _any_present([hw_features.screen.touch_support])
868 acc = hw_features.accelerometer
869 gyro = hw_features.gyroscope
870 compass = hw_features.magnetometer
871 light_sensor = hw_features.light_sensor
872 root = etree.Element('permissions')
873 root.extend(
874 _get_arc_camera_features(hw_features.camera) + [
875 _feature(
876 'android.hardware.sensor.accelerometer',
877 _any_present([acc.lid_accelerometer, acc.base_accelerometer])),
878 _feature('android.hardware.sensor.gyroscope',
879 _any_present([gyro.lid_gyroscope, gyro.base_gyroscope])),
880 _feature(
881 'android.hardware.sensor.compass',
882 _any_present(
883 [compass.lid_magnetometer, compass.base_magnetometer])),
884 _feature(
885 'android.hardware.sensor.light',
886 _any_present([
887 light_sensor.lid_lightsensor, light_sensor.base_lightsensor
888 ])),
889 _feature('android.hardware.touchscreen', touchscreen),
890 _feature('android.hardware.touchscreen.multitouch', touchscreen),
891 _feature('android.hardware.touchscreen.multitouch.distinct',
892 touchscreen),
893 _feature('android.hardware.touchscreen.multitouch.jazzhand',
894 touchscreen),
895 ])
Ren-Pei Zengacf03be2020-11-11 13:56:51 +0800896 return XML_DECLARATION + etree.tostring(root, pretty_print=True)
897
898
899def _generate_arc_media_profiles(hw_features, sw_config):
900 """Generates ARC media_profiles.xml file content.
901
902 Args:
903 hw_features: HardwareFeatures proto message.
904 sw_config: SoftwareConfig proto message.
905 Returns:
906 bytes of the media_profiles.xml content, or None if |sw_config| disables the
907 generation.
908 """
909
910 def _gen_camcorder_profiles(camera_id, resolutions):
911 elem = etree.Element(
912 'CamcorderProfiles', attrib={'cameraId': str(camera_id)})
913 for width, height in resolutions:
914 elem.extend([
915 _gen_encoder_profile(width, height, False),
916 _gen_encoder_profile(width, height, True),
917 ])
918 elem.extend([
919 etree.Element('ImageEncoding', attrib={'quality': '90'}),
920 etree.Element('ImageEncoding', attrib={'quality': '80'}),
921 etree.Element('ImageEncoding', attrib={'quality': '70'}),
922 etree.Element('ImageDecoding', attrib={'memCap': '20000000'}),
923 ])
924 return elem
925
926 def _gen_encoder_profile(width, height, timelapse):
927 elem = etree.Element(
928 'EncoderProfile',
929 attrib={
930 'quality': ('timelapse' if timelapse else '') + str(height) + 'p',
931 'fileFormat': 'mp4',
932 'duration': '60',
933 })
934 elem.append(
935 etree.Element(
936 'Video',
937 attrib={
938 'codec': 'h264',
939 'bitRate': '8000000',
940 'width': str(width),
941 'height': str(height),
942 'frameRate': '30',
943 }))
944 elem.append(
945 etree.Element(
946 'Audio',
947 attrib={
948 'codec': 'aac',
949 'bitRate': '96000',
950 'sampleRate': '44100',
951 'channels': '1',
952 }))
953 return elem
954
955 def _gen_video_encoder_cap(name, min_bit_rate, max_bit_rate):
956 return etree.Element(
957 'VideoEncoderCap',
958 attrib={
959 'name': name,
960 'enabled': 'true',
961 'minBitRate': str(min_bit_rate),
962 'maxBitRate': str(max_bit_rate),
963 'minFrameWidth': '320',
964 'maxFrameWidth': '1920',
965 'minFrameHeight': '240',
966 'maxFrameHeight': '1080',
967 'minFrameRate': '15',
968 'maxFrameRate': '30',
969 })
970
971 def _gen_audio_encoder_cap(name, min_bit_rate, max_bit_rate, min_sample_rate,
972 max_sample_rate):
973 return etree.Element(
974 'AudioEncoderCap',
975 attrib={
976 'name': name,
977 'enabled': 'true',
978 'minBitRate': str(min_bit_rate),
979 'maxBitRate': str(max_bit_rate),
980 'minSampleRate': str(min_sample_rate),
981 'maxSampleRate': str(max_sample_rate),
982 'minChannels': '1',
983 'maxChannels': '1',
984 })
985
986 camera_config = sw_config.camera_config
987 if not camera_config.generate_media_profiles:
988 return None
989
990 camera_pb = topology_pb2.HardwareFeatures.Camera
991 root = etree.Element('MediaSettings')
992 camera_id = 0
993 for facing in [camera_pb.FACING_BACK, camera_pb.FACING_FRONT]:
994 camera_device = next(
995 (d for d in hw_features.camera.devices if d.facing == facing), None)
996 if camera_device is None:
997 continue
998 if camera_config.camcorder_resolutions:
999 resolutions = [
1000 (r.width, r.height) for r in camera_config.camcorder_resolutions
1001 ]
1002 else:
1003 resolutions = [(1280, 720)]
1004 if camera_device.flags & camera_pb.FLAGS_SUPPORT_1080P:
1005 resolutions.append((1920, 1080))
1006 root.append(_gen_camcorder_profiles(camera_id, resolutions))
1007 camera_id += 1
1008
1009 root.extend([
1010 etree.Element('EncoderOutputFileFormat', attrib={'name': '3gp'}),
1011 etree.Element('EncoderOutputFileFormat', attrib={'name': 'mp4'}),
1012 _gen_video_encoder_cap('h264', 64000, 17000000),
1013 _gen_video_encoder_cap('h263', 64000, 1000000),
1014 _gen_video_encoder_cap('m4v', 64000, 2000000),
1015 _gen_audio_encoder_cap('aac', 758, 288000, 8000, 48000),
1016 _gen_audio_encoder_cap('heaac', 8000, 64000, 16000, 48000),
1017 _gen_audio_encoder_cap('aaceld', 16000, 192000, 16000, 48000),
1018 _gen_audio_encoder_cap('amrwb', 6600, 23050, 16000, 16000),
1019 _gen_audio_encoder_cap('amrnb', 5525, 12200, 8000, 8000),
1020 etree.Element(
1021 'VideoDecoderCap', attrib={
1022 'name': 'wmv',
1023 'enabled': 'false'
1024 }),
1025 etree.Element(
1026 'AudioDecoderCap', attrib={
1027 'name': 'wma',
1028 'enabled': 'false'
1029 }),
1030 ])
1031 return XML_DECLARATION + etree.tostring(root, pretty_print=True)
Ren-Pei Zengc6ba15c2020-10-17 00:51:25 +08001032
1033
1034def _write_files_by_design_config(configs, output_dir, build_dir, system_dir,
1035 file_name_template, generate_file_content):
1036 """Writes generated files for each design config.
1037
1038 Args:
1039 configs: Source ConfigBundle to process.
1040 output_dir: Path to the generated output.
1041 build_dir: Path to the config file from portage's perspective.
1042 system_dir: Path to the config file in the target device.
1043 file_name_template: Template string of the config file name including one
1044 format()-style replacement field for the config id, e.g. 'config_{}.xml'.
1045 generate_file_content: Function to generate config file content from
Ren-Pei Zengacf03be2020-11-11 13:56:51 +08001046 HardwareFeatures and SoftwareConfig proto.
Ren-Pei Zengc6ba15c2020-10-17 00:51:25 +08001047 Returns:
1048 dict that maps the formatted config id to the correct file.
1049 """
1050 # pylint: disable=too-many-arguments,too-many-locals
C Shapiro5bf23a72020-04-24 11:40:17 -05001051 result = {}
C Shapiroea33cff2020-05-11 13:32:05 -05001052 configs_by_design = {}
Ren-Pei Zengc6ba15c2020-10-17 00:51:25 +08001053 for hw_design in configs.design_list:
C Shapiro5bf23a72020-04-24 11:40:17 -05001054 for design_config in hw_design.configs:
Ren-Pei Zengacf03be2020-11-11 13:56:51 +08001055 sw_config = _sw_config(configs.software_configs, design_config.id.value)
1056 config_content = generate_file_content(design_config.hardware_features,
1057 sw_config)
1058 if not config_content:
1059 continue
C Shapiroea33cff2020-05-11 13:32:05 -05001060 design_name = hw_design.name.lower()
C Shapiro5bf23a72020-04-24 11:40:17 -05001061
C Shapiroea33cff2020-05-11 13:32:05 -05001062 # Constructs the following map:
1063 # design_name -> config -> design_configs
1064 # This allows any of the following file naming schemes:
1065 # - All configs within a design share config (design_name prefix only)
1066 # - Nobody shares (full design_name and config id prefix needed)
1067 #
1068 # Having shared configs when possible makes code reviews easier around
1069 # the configs and makes debugging easier on the platform side.
C Shapiroea33cff2020-05-11 13:32:05 -05001070 arc_configs = configs_by_design.get(design_name, {})
1071 design_configs = arc_configs.get(config_content, [])
1072 design_configs.append(design_config)
1073 arc_configs[config_content] = design_configs
1074 configs_by_design[design_name] = arc_configs
C Shapiro9a3ac8c2020-04-25 07:49:21 -05001075
C Shapiroea33cff2020-05-11 13:32:05 -05001076 for design_name, unique_configs in configs_by_design.items():
1077 for file_content, design_configs in unique_configs.items():
Ren-Pei Zengc6ba15c2020-10-17 00:51:25 +08001078 file_name = file_name_template.format(design_name)
Andrew Lamb2413c982020-05-29 12:15:36 -06001079 if len(unique_configs) == 1:
Ren-Pei Zengc6ba15c2020-10-17 00:51:25 +08001080 _write_file(output_dir, file_name, file_content)
C Shapiro9a3ac8c2020-04-25 07:49:21 -05001081
Andrew Lamb2413c982020-05-29 12:15:36 -06001082 for design_config in design_configs:
Ren-Pei Zengc6ba15c2020-10-17 00:51:25 +08001083 config_id = _get_formatted_config_id(design_config)
Andrew Lamb2413c982020-05-29 12:15:36 -06001084 if len(unique_configs) > 1:
Ren-Pei Zengc6ba15c2020-10-17 00:51:25 +08001085 file_name = file_name_template.format(config_id)
1086 _write_file(output_dir, file_name, file_content)
1087 result[config_id] = _file_v2('{}/{}'.format(build_dir, file_name),
1088 '{}/{}'.format(system_dir, file_name))
C Shapiro5bf23a72020-04-24 11:40:17 -05001089 return result
1090
1091
Ren-Pei Zengc6ba15c2020-10-17 00:51:25 +08001092def _write_arc_hardware_feature_files(configs, output_root_dir, build_root_dir):
Ren-Pei Zengacf03be2020-11-11 13:56:51 +08001093 return _write_files_by_design_config(
1094 configs, output_root_dir + '/arc', build_root_dir + '/arc', '/etc',
1095 'hardware_features_{}.xml',
1096 lambda hw_features, _: _generate_arc_hardware_features(hw_features))
1097
1098
1099def _write_arc_media_profile_files(configs, output_root_dir, build_root_dir):
Ren-Pei Zengc6ba15c2020-10-17 00:51:25 +08001100 return _write_files_by_design_config(configs, output_root_dir + '/arc',
1101 build_root_dir + '/arc', '/etc',
Ren-Pei Zengacf03be2020-11-11 13:56:51 +08001102 'media_profiles_{}.xml',
1103 _generate_arc_media_profiles)
Ren-Pei Zengc6ba15c2020-10-17 00:51:25 +08001104
1105
Andrew Lambcd33f702020-06-11 10:45:16 -06001106def _read_config(path):
David Burgerd4f32962020-05-02 12:07:40 -06001107 """Reads a ConfigBundle proto from a json pb file.
David Burgere6f76222020-04-27 11:08:01 -06001108
1109 Args:
David Burgerd4f32962020-05-02 12:07:40 -06001110 path: Path to the file encoding the json pb proto.
David Burgere6f76222020-04-27 11:08:01 -06001111 """
1112 config = config_bundle_pb2.ConfigBundle()
1113 with open(path, 'r') as f:
1114 return json_format.Parse(f.read(), config)
1115
1116
Andrew Lambcd33f702020-06-11 10:45:16 -06001117def _merge_configs(configs):
David Burger7fd1dbe2020-03-26 09:26:55 -06001118 result = config_bundle_pb2.ConfigBundle()
1119 for config in configs:
1120 result.MergeFrom(config)
1121
1122 return result
1123
1124
David Burger1ba78a22020-06-18 18:42:47 -06001125def _camera_map(configs, project_name):
David Burger8ee9b4d2020-06-16 17:40:21 -06001126 """Produces a camera config map for the given configs.
1127
1128 Produces a map that maps from the design name to the camera config for that
1129 design.
1130
1131 Args:
1132 configs: Source ConfigBundle to process.
David Burger1ba78a22020-06-18 18:42:47 -06001133 project_name: Name of project processing for.
David Burger8ee9b4d2020-06-16 17:40:21 -06001134
1135 Returns:
1136 map from design name to camera config.
1137 """
1138 result = {}
Sean McAllisterf66887b2020-08-03 14:00:51 -06001139 for design in configs.design_list:
David Burger8ee9b4d2020-06-16 17:40:21 -06001140 design_name = design.name
David Burger0d9e8462020-06-19 14:12:37 -06001141 config_path = CAMERA_CONFIG_SOURCE_PATH_TEMPLATE.format(design_name.lower())
David Burger8ee9b4d2020-06-16 17:40:21 -06001142 if os.path.exists(config_path):
David Burger0d9e8462020-06-19 14:12:37 -06001143 destination = CAMERA_CONFIG_DEST_PATH_TEMPLATE.format(design_name.lower())
David Burger8ee9b4d2020-06-16 17:40:21 -06001144 result[design_name] = {
David Burger1ba78a22020-06-18 18:42:47 -06001145 'config-file':
1146 _file_v2(os.path.join(project_name, config_path), destination),
David Burger8ee9b4d2020-06-16 17:40:21 -06001147 }
1148 return result
1149
1150
David Burger52c9d322020-06-09 07:16:18 -06001151def _dptf_map(configs, project_name):
1152 """Produces a dptf map for the given configs.
1153
1154 Produces a map that maps from design name to the dptf file config for that
1155 design. It looks for the dptf files at:
David Burger2f0d9522020-07-30 10:52:28 -06001156 DPTF_PATH + '/' + DPTF_FILE
David Burger52c9d322020-06-09 07:16:18 -06001157 for a project wide config, that it maps under the empty string, and at:
David Burger2f0d9522020-07-30 10:52:28 -06001158 DPTF_PATH + '/' + design_name + '/' + DPTF_FILE
David Burger52c9d322020-06-09 07:16:18 -06001159 for design specific configs that it maps under the design name.
1160
1161 Args:
1162 configs: Source ConfigBundle to process.
1163 project_name: Name of project processing for.
1164
1165 Returns:
David Burger8ee9b4d2020-06-16 17:40:21 -06001166 map from design name or empty string (project wide), to dptf config.
David Burger52c9d322020-06-09 07:16:18 -06001167 """
1168 result = {}
David Burger52c9d322020-06-09 07:16:18 -06001169 # Looking at top level for project wide, and then for each design name
1170 # for design specific.
Sean McAllisterf66887b2020-08-03 14:00:51 -06001171 dirs = [""] + [d.name for d in configs.design_list]
David Burger52c9d322020-06-09 07:16:18 -06001172 for directory in dirs:
David Burgera2252762020-07-09 15:09:49 -06001173 design = directory.lower()
1174 if os.path.exists(os.path.join(DPTF_PATH, design, DPTF_FILE)):
David Burger2f0d9522020-07-30 10:52:28 -06001175 project_dptf_path = os.path.join(project_name, design, DPTF_FILE)
David Burger52c9d322020-06-09 07:16:18 -06001176 dptf_file = {
1177 'dptf-dv':
1178 project_dptf_path,
1179 'files': [
1180 _file(
David Burgera2252762020-07-09 15:09:49 -06001181 os.path.join(project_name, DPTF_PATH, design, DPTF_FILE),
David Burger52c9d322020-06-09 07:16:18 -06001182 os.path.join('/etc/dptf', project_dptf_path))
1183 ]
1184 }
1185 result[directory] = dptf_file
1186 return result
1187
1188
David Burgerceeb68a2020-09-03 11:31:10 -06001189def _wifi_sar_map(configs, project_name, output_dir, build_root_dir):
1190 """Constructs a map from design name to wifi sar config for that design.
1191
1192 Constructs a map from design name to the wifi sar config for that design.
1193 In the process a wifi sar hex file is generated that the config points at.
1194 This mapping is only made for the intel wifi where the generated file is
1195 provided when building coreboot.
1196
1197 Args:
1198 configs: Source ConfigBundle to process.
1199 project_name: Name of project processing for.
1200 output_dir: Path to the generated output.
Ren-Pei Zengc6ba15c2020-10-17 00:51:25 +08001201 build_root_dir: Path to the config file from portage's perspective.
David Burgerceeb68a2020-09-03 11:31:10 -06001202
1203 Returns:
1204 dict that maps the design name onto the wifi config for that design.
1205 """
1206 # pylint: disable=too-many-locals
1207 result = {}
1208 programs = {p.id.value: p for p in configs.program_list}
1209 sw_configs = list(configs.software_configs)
1210 for hw_design in configs.design_list:
1211 for hw_design_config in hw_design.configs:
1212 sw_config = _sw_config(sw_configs, hw_design_config.id.value)
1213 if sw_config.wifi_config.HasField('intel_config'):
1214 sar_file_content = _create_intel_sar_file_content(
1215 sw_config.wifi_config.intel_config)
1216 design_name = hw_design.name.lower()
1217 program = _lookup(hw_design.program_id, programs)
1218 wifi_sar_id = _extract_fw_config_value(hw_design_config, program,
1219 'Intel wifi sar id')
1220 output_path = os.path.join(output_dir, 'wifi')
1221 os.makedirs(output_path, exist_ok=True)
1222 filename = 'wifi_sar_{}.hex'.format(wifi_sar_id)
1223 output_path = os.path.join(output_path, filename)
1224 build_path = os.path.join(build_root_dir, 'wifi', filename)
1225 if os.path.exists(output_path):
1226 with open(output_path, 'r') as f:
1227 if f.read() != sar_file_content:
1228 raise Exception(
1229 'Project {} has conflicting wifi sar file content under '
1230 'wifi sar id {}.'.format(project_name, wifi_sar_id))
1231 else:
1232 with open(output_path, 'w') as f:
1233 f.write(sar_file_content)
1234 system_path = '/firmware/cbfs-rw-raw/{}/{}'.format(
1235 project_name, filename)
David Burger89e7cae2020-09-15 17:04:12 -06001236 result[design_name] = {'sar-file': _file_v2(build_path, system_path)}
David Burgerceeb68a2020-09-03 11:31:10 -06001237 return result
1238
1239
1240def _extract_fw_config_value(hw_design_config, program, name):
1241 """Extracts the firwmare config value with the given name.
1242
1243 Args:
1244 hw_design_config: Design extracting value from.
1245 program: Program the `hw_design_config` belongs to.
1246 name: Name of firmware config segment to extract.
1247
1248 Returns: the extracted value or raises a ValueError if no firmware
1249 configuration segment with `name` is found.
1250 """
1251 fw_config = hw_design_config.hardware_features.fw_config.value
1252 for fcs in program.firmware_configuration_segments:
1253 if fcs.name == name:
1254 value = fw_config & fcs.mask
1255 lsb_bit_set = (~fcs.mask + 1) & fcs.mask
1256 return value // lsb_bit_set
1257 raise ValueError(
1258 'No firmware configuration segment with name {} found'.format(name))
1259
1260
1261def _create_intel_sar_file_content(intel_config):
1262 """Creates and returns the intel sar file content for the given config.
1263
1264 Creates and returns the sar file content that is used with intel drivers
1265 only.
1266
1267 Args:
1268 intel_config: IntelConfig config.
1269
1270 Returns:
1271 sar file content for the given config, see:
1272 https://chromeos.google.com/partner/dlm/docs/connectivity/wifidyntxpower.html
1273 """
1274
1275 def to_hex(val):
1276 if val > 255 or val < 0:
1277 raise Exception('Sar file value %s out of range' % val)
1278 return '{0:0{1}X}'.format(val, 2)
1279
1280 def power_table(tpc):
1281 return (to_hex(tpc.limit_2g) + to_hex(tpc.limit_5g_1) +
1282 to_hex(tpc.limit_5g_2) + to_hex(tpc.limit_5g_3) +
1283 to_hex(tpc.limit_5g_4))
1284
1285 def wgds_value(wgds):
1286 return to_hex(wgds)
1287
1288 def offset_table(offsets):
1289 return (to_hex(offsets.max_2g) + to_hex(offsets.offset_2g_a) +
1290 to_hex(offsets.offset_2g_b) + to_hex(offsets.max_5g) +
1291 to_hex(offsets.offset_5g_a) + to_hex(offsets.offset_5g_b))
1292
1293 # See https://chromeos.google.com/partner/dlm/docs/connectivity/wifidyntxpower.html
1294 return (power_table(intel_config.tablet_mode_power_table_a) +
1295 power_table(intel_config.tablet_mode_power_table_b) +
1296 power_table(intel_config.non_tablet_mode_power_table_a) +
1297 power_table(intel_config.non_tablet_mode_power_table_b) +
1298 '00000000000000000000' + '00000000000000000000' +
1299 wgds_value(intel_config.wgds_version) +
1300 offset_table(intel_config.offset_fcc) +
1301 offset_table(intel_config.offset_eu) +
1302 offset_table(intel_config.offset_other))
1303
1304
Andrew Lambcd33f702020-06-11 10:45:16 -06001305def Main(project_configs, program_config, output): # pylint: disable=invalid-name
David Burger7fd1dbe2020-03-26 09:26:55 -06001306 """Transforms source proto config into platform JSON.
1307
1308 Args:
1309 project_configs: List of source project configs to transform.
1310 program_config: Program config for the given set of projects.
1311 output: Output file that will be generated by the transform.
1312 """
Andrew Lambcd33f702020-06-11 10:45:16 -06001313 configs = _merge_configs([_read_config(program_config)] +
1314 [_read_config(config) for config in project_configs])
C Shapiro2b6d5332020-05-06 17:51:35 -05001315 touch_fw = {}
David Burger8ee9b4d2020-06-16 17:40:21 -06001316 camera_map = {}
David Burgerceeb68a2020-09-03 11:31:10 -06001317 dptf_map = {}
1318 wifi_sar_map = {}
C Shapiro5bf23a72020-04-24 11:40:17 -05001319 output_dir = os.path.dirname(output)
C Shapiro5c877992020-04-29 12:11:28 -05001320 build_root_dir = output_dir
C Shapiro5c877992020-04-29 12:11:28 -05001321 if 'sw_build_config' in output_dir:
1322 full_path = os.path.realpath(output)
Andrew Lamb6b607732020-08-24 15:52:46 -06001323 project_name = re.match(r'.*/(\w*)/(public_)?sw_build_config/.*',
Andrew Lamb2413c982020-05-29 12:15:36 -06001324 full_path).groups(1)[0]
C Shapiro5c877992020-04-29 12:11:28 -05001325 # Projects don't know about each other until they are integrated into the
1326 # build system. When this happens, the files need to be able to co-exist
1327 # without any collisions. This prefixes the project name (which is how
1328 # portage maps in the project), so project files co-exist and can be
1329 # installed together.
1330 # This is necessary to allow projects to share files at the program level
1331 # without having portage file installation collisions.
1332 build_root_dir = os.path.join(project_name, output_dir)
C Shapiro6830e6c2020-04-29 13:29:56 -05001333
David Burger1ba78a22020-06-18 18:42:47 -06001334 camera_map = _camera_map(configs, project_name)
David Burger52c9d322020-06-09 07:16:18 -06001335 dptf_map = _dptf_map(configs, project_name)
David Burgerceeb68a2020-09-03 11:31:10 -06001336 wifi_sar_map = _wifi_sar_map(configs, project_name, output_dir,
1337 build_root_dir)
David Burger52c9d322020-06-09 07:16:18 -06001338
C Shapiro2b6d5332020-05-06 17:51:35 -05001339 if os.path.exists(TOUCH_PATH):
Andrew Lambcd33f702020-06-11 10:45:16 -06001340 touch_fw = _build_touch_file_config(configs, project_name)
Andrew Lambcd33f702020-06-11 10:45:16 -06001341 arc_hw_feature_files = _write_arc_hardware_feature_files(
1342 configs, output_dir, build_root_dir)
Ren-Pei Zengacf03be2020-11-11 13:56:51 +08001343 arc_media_profile_files = _write_arc_media_profile_files(
1344 configs, output_dir, build_root_dir)
C Shapiro5bf23a72020-04-24 11:40:17 -05001345 config_files = ConfigFiles(
C Shapiro5bf23a72020-04-24 11:40:17 -05001346 arc_hw_features=arc_hw_feature_files,
Ren-Pei Zengacf03be2020-11-11 13:56:51 +08001347 arc_media_profiles=arc_media_profile_files,
C Shapiro2b6d5332020-05-06 17:51:35 -05001348 touch_fw=touch_fw,
David Burger8ee9b4d2020-06-16 17:40:21 -06001349 dptf_map=dptf_map,
David Burgerceeb68a2020-09-03 11:31:10 -06001350 camera_map=camera_map,
1351 wifi_sar_map=wifi_sar_map)
Andrew Lambcd33f702020-06-11 10:45:16 -06001352 write_output(_transform_build_configs(configs, config_files), output)
David Burger7fd1dbe2020-03-26 09:26:55 -06001353
1354
1355def main(argv=None):
1356 """Main program which parses args and runs
1357
1358 Args:
1359 argv: List of command line arguments, if None uses sys.argv.
1360 """
1361 if argv is None:
1362 argv = sys.argv[1:]
Andrew Lambcd33f702020-06-11 10:45:16 -06001363 opts = parse_args(argv)
David Burger7fd1dbe2020-03-26 09:26:55 -06001364 Main(opts.project_configs, opts.program_config, opts.output)
1365
1366
1367if __name__ == '__main__':
1368 sys.exit(main(sys.argv[1:]))