blob: a9980b94e2f9bdc355a3aa13149019ae80d27794 [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
C Shapiro5bf23a72020-04-24 11:40:17 -050016import xml.etree.ElementTree as etree
C Shapiro9a3ac8c2020-04-25 07:49:21 -050017import xml.dom.minidom as minidom
David Burger7fd1dbe2020-03-26 09:26:55 -060018
Andrew Lamb319cc922020-06-15 10:45:46 -060019from typing import List
20
David Burger7fd1dbe2020-03-26 09:26:55 -060021from collections import namedtuple
22
Andrew Lambcd33f702020-06-11 10:45:16 -060023from google.protobuf import json_format
24
Prathmesh Prabhu72f8a002020-04-10 09:57:53 -070025from chromiumos.config.api import device_brand_pb2
David Burger92609a32020-04-23 10:38:50 -060026from chromiumos.config.api import topology_pb2
C Shapiro5bf23a72020-04-24 11:40:17 -050027from chromiumos.config.payload import config_bundle_pb2
Prathmesh Prabhu72f8a002020-04-10 09:57:53 -070028from chromiumos.config.api.software import brand_config_pb2
David Burger7fd1dbe2020-03-26 09:26:55 -060029
Andrew Lamb2413c982020-05-29 12:15:36 -060030Config = namedtuple('Config', [
31 'program', 'hw_design', 'odm', 'hw_design_config', 'device_brand',
32 'device_signer_config', 'oem', 'sw_config', 'brand_config', 'build_target'
33])
David Burger7fd1dbe2020-03-26 09:26:55 -060034
David Burger07af5242020-08-11 11:08:25 -060035ConfigFiles = namedtuple(
David Burgerceeb68a2020-09-03 11:31:10 -060036 'ConfigFiles',
37 ['arc_hw_features', 'touch_fw', 'dptf_map', 'camera_map', 'wifi_sar_map'])
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
Andrew Lamb2413c982020-05-29 12:15:36 -060049
Andrew Lambcd33f702020-06-11 10:45:16 -060050def parse_args(argv):
David Burger7fd1dbe2020-03-26 09:26:55 -060051 """Parse the available arguments.
52
53 Invalid arguments or -h cause this function to print a message and exit.
54
55 Args:
56 argv: List of string arguments (excluding program name / argv[0])
57
58 Returns:
59 argparse.Namespace object containing the attributes.
60 """
61 parser = argparse.ArgumentParser(
62 description='Converts source proto config into platform JSON config.')
63 parser.add_argument(
64 '-c',
65 '--project_configs',
66 nargs='+',
67 type=str,
68 help='Space delimited list of source protobinary project config files.')
69 parser.add_argument(
70 '-p',
71 '--program_config',
72 type=str,
73 help='Path to the source program-level protobinary file')
74 parser.add_argument(
Andrew Lamb2413c982020-05-29 12:15:36 -060075 '-o', '--output', type=str, help='Output file that will be generated')
David Burger7fd1dbe2020-03-26 09:26:55 -060076 return parser.parse_args(argv)
77
78
David Burger8ee9b4d2020-06-16 17:40:21 -060079def _upsert(field, target, target_name):
80 """Updates or inserts `field` within `target`.
81
82 If `target_name` already exists within `target` an update is performed,
83 otherwise, an insert is performed.
84 """
Sam McNally9a873f72020-06-05 19:47:22 +100085 if field or field == 0:
David Burger8ee9b4d2020-06-16 17:40:21 -060086 if target_name in target:
87 target[target_name].update(field)
88 else:
89 target[target_name] = field
David Burger7fd1dbe2020-03-26 09:26:55 -060090
91
Andrew Lambcd33f702020-06-11 10:45:16 -060092def _build_arc(config, config_files):
93 if not config.build_target.arc:
94 return None
95
96 build_properties = {
97 'device': config.build_target.arc.device,
98 'first-api-level': config.build_target.arc.first_api_level,
99 'marketing-name': config.device_brand.brand_name,
100 'metrics-tag': config.hw_design.name.lower(),
101 'product': config.build_target.id.value,
102 }
103 if config.oem:
104 build_properties['oem'] = config.oem.name
105 result = {'build-properties': build_properties}
106 feature_id = _arc_hardware_feature_id(config.hw_design_config)
107 if feature_id in config_files.arc_hw_features:
108 result['hardware-features'] = config_files.arc_hw_features[feature_id]
109 topology = config.hw_design_config.hardware_topology
110 ppi = topology.screen.hardware_feature.screen.panel_properties.pixels_per_in
111 # Only set for high resolution displays
112 if ppi and ppi > 250:
113 result['scale'] = ppi
David Burger2f0d9522020-07-30 10:52:28 -0600114
Andrew Lambcd33f702020-06-11 10:45:16 -0600115 return result
David Burger7fd1dbe2020-03-26 09:26:55 -0600116
Andrew Lamb2413c982020-05-29 12:15:36 -0600117
Andrew Lamb319cc922020-06-15 10:45:46 -0600118def _build_ash_flags(config: Config) -> List[str]:
119 """Returns a list of Ash flags for config.
120
121 Ash is the window manager and system UI for ChromeOS, see
122 https://chromium.googlesource.com/chromium/src/+/refs/heads/master/ash/.
123 """
124 # A map from flag name -> value. Value may be None for boolean flags.
125 flags = {}
126
127 hw_features = config.hw_design_config.hardware_features
128 if hw_features.stylus.stylus == topology_pb2.HardwareFeatures.Stylus.INTERNAL:
Andrew Lamb2e641e22020-06-15 12:30:41 -0600129 flags['has-internal-stylus'] = None
Andrew Lamb319cc922020-06-15 10:45:46 -0600130
Andrew Lamb2e641e22020-06-15 12:30:41 -0600131 fp_loc = hw_features.fingerprint.location
132 if fp_loc and fp_loc != topology_pb2.HardwareFeatures.Fingerprint.NOT_PRESENT:
133 loc_name = topology_pb2.HardwareFeatures.Fingerprint.Location.Name(fp_loc)
134 flags['fingerprint-sensor-location'] = loc_name.lower().replace('_', '-')
135
Andrew Lamb6c42efc2020-06-16 10:40:43 -0600136 wallpaper = config.brand_config.wallpaper
137 # If a wallpaper is set, the 'default-wallpaper-is-oem' flag needs to be set.
138 # If a wallpaper is not set, the 'default_[large|small].jpg' wallpapers
139 # should still be set.
140 if wallpaper:
141 flags['default-wallpaper-is-oem'] = None
142 else:
143 wallpaper = 'default'
144
145 for size in ('small', 'large'):
146 flags[f'default-wallpaper-{size}'] = (
147 f'{WALLPAPER_BASE_PATH}/{wallpaper}_{size}.jpg')
148
149 # For each size, also install 'guest' and 'child' wallpapers.
150 for wallpaper_type in ('guest', 'child'):
151 flags[f'{wallpaper_type}-wallpaper-{size}'] = (
152 f'{WALLPAPER_BASE_PATH}/{wallpaper_type}_{size}.jpg')
153
Andrew Lamb72d41362020-06-17 09:19:02 -0600154 flags['arc-build-properties'] = json_format.MessageToDict(
155 config.build_target.arc)
156
Andrew Lamb90b168c2020-06-22 10:42:30 -0600157 power_button = hw_features.power_button
158 if power_button.edge:
159 flags['ash-power-button-position'] = json.dumps({
160 'edge':
161 topology_pb2.HardwareFeatures.Button.Edge.Name(power_button.edge
162 ).lower(),
163 # Starlark sometimes represents float literals strangely, e.g. changing
164 # 0.9 to 0.899999. Round to two digits here.
165 'position':
166 round(power_button.position, 2)
167 })
168
169 volume_button = hw_features.volume_button
170 if volume_button.edge:
171 flags['ash-side-volume-button-position'] = json.dumps({
172 'region':
173 topology_pb2.HardwareFeatures.Button.Region.Name(
174 volume_button.region).lower(),
175 'edge':
176 topology_pb2.HardwareFeatures.Button.Edge.Name(volume_button.edge
177 ).lower(),
178 })
179
Andrew Lamb2e641e22020-06-15 12:30:41 -0600180 return sorted([f'--{k}={v}' if v else f'--{k}' for k, v in flags.items()])
Andrew Lamb319cc922020-06-15 10:45:46 -0600181
182
183def _build_ui(config: Config) -> dict:
184 """Builds the 'ui' property from cros_config_schema."""
185 return {'extra-ash-flags': _build_ash_flags(config)}
186
187
David Burger07af5242020-08-11 11:08:25 -0600188def _build_bluetooth(config):
C Shapiro90fda252020-04-17 14:34:57 -0500189 bt_flags = config.sw_config.bluetooth_config.flags
190 # Convert to native map (from proto wrapper)
191 bt_flags_map = dict(bt_flags)
192 result = {}
193 if bt_flags_map:
194 result['flags'] = bt_flags_map
C Shapiro90fda252020-04-17 14:34:57 -0500195 return result
196
David Burger7fd1dbe2020-03-26 09:26:55 -0600197
David Burgerceeb68a2020-09-03 11:31:10 -0600198def _build_ath10k_config(ath10k_config):
199 """Builds the wifi configuration for the ath10k driver.
200
201 Args:
202 ath10k_config: Ath10kConfig config.
203
204 Returns:
205 wifi configuration for the ath10k driver.
206 """
David Burgerec753912020-08-10 12:59:11 -0600207 result = {}
David Burgerec753912020-08-10 12:59:11 -0600208
David Burgerceeb68a2020-09-03 11:31:10 -0600209 def power_chain(power):
210 return {
211 'limit-2g': power.limit_2g,
212 'limit-5g': power.limit_5g,
213 }
David Burgerec753912020-08-10 12:59:11 -0600214
David Burgerceeb68a2020-09-03 11:31:10 -0600215 result['tablet-mode-power-table-ath10k'] = power_chain(
216 ath10k_config.tablet_mode_power_table)
217 result['non-tablet-mode-power-table-ath10k'] = power_chain(
218 ath10k_config.non_tablet_mode_power_table)
David Burgerec753912020-08-10 12:59:11 -0600219 return result
220
221
David Burgerceeb68a2020-09-03 11:31:10 -0600222def _build_rtw88_config(rtw88_config):
223 """Builds the wifi configuration for the rtw88 driver.
224
225 Args:
226 rtw88_config: Rtw88Config config.
227
228 Returns:
229 wifi configuration for the rtw88 driver.
230 """
231 result = {}
232
233 def power_chain(power):
234 return {
235 'limit-2g': power.limit_2g,
236 'limit-5g-1': power.limit_5g_1,
237 'limit-5g-3': power.limit_5g_3,
238 'limit-5g-4': power.limit_5g_4,
239 }
240
241 result['tablet-mode-power-table-rtw'] = power_chain(
242 rtw88_config.tablet_mode_power_table)
243 result['non-tablet-mode-power-table-rtw'] = power_chain(
244 rtw88_config.non_tablet_mode_power_table)
245
246 def offsets(offset):
247 return {
248 'offset-2g': offset.offset_2g,
249 'offset-5g': offset.offset_5g,
250 }
251
252 result['geo-offsets-fcc'] = offsets(rtw88_config.offset_fcc)
253 result['geo-offsets-eu'] = offsets(rtw88_config.offset_eu)
254 result['geo-offsets-rest-of-world'] = offsets(rtw88_config.offset_other)
255 return result
256
257
258def _build_intel_config(config, config_files):
259 """Builds the wifi configuration for the intel driver.
260
261 Args:
262 config: Config namedtuple
263 config_files: Map to look up the generated config files.
264
265 Returns:
266 wifi configuration for the intel driver.
267 """
268 design_name = config.hw_design.name.lower()
269 return config_files.wifi_sar_map.get(design_name)
270
271
272def _build_wifi(config, config_files):
273 """Builds the wifi configuration.
274
275 Args:
276 config: Config namedtuple
277 config_files: Map to look up the generated config files.
278
279 Returns:
280 wifi configuration.
281 """
282 config_field = config.sw_config.wifi_config.WhichOneof('wifi_config')
283 if config_field == 'ath10k_config':
284 return _build_ath10k_config(config.sw_config.wifi_config.ath10k_config)
285 if config_field == 'rtw88_config':
286 return _build_rtw88_config(config.sw_config.wifi_config.rtw88_config)
287 if config_field == 'intel_config':
288 return _build_intel_config(config, config_files)
289 return {}
290
291
Andrew Lambcd33f702020-06-11 10:45:16 -0600292def _build_fingerprint(hw_topology):
293 if not hw_topology.HasField('fingerprint'):
294 return None
295
296 fp = hw_topology.fingerprint.hardware_feature.fingerprint
297 result = {}
298 if fp.location != topology_pb2.HardwareFeatures.Fingerprint.NOT_PRESENT:
299 location = fp.Location.DESCRIPTOR.values_by_number[fp.location].name
300 result['sensor-location'] = location.lower().replace('_', '-')
301 if fp.board:
302 result['board'] = fp.board
Tom Hughesdfc35402020-06-29 16:02:09 -0700303 if fp.ro_version:
304 result['ro-version'] = fp.ro_version
305
Andrew Lambcd33f702020-06-11 10:45:16 -0600306 return result
David Burger7fd1dbe2020-03-26 09:26:55 -0600307
308
Trent Beginf067ccb2020-08-12 12:33:53 -0600309def _build_hardware_properties(hw_topology):
310 if not hw_topology.HasField('form_factor'):
311 return None
312
313 form_factor = hw_topology.form_factor.hardware_feature.form_factor.form_factor
314 result = {}
315 if form_factor in [
316 topology_pb2.HardwareFeatures.FormFactor.CHROMEBIT,
317 topology_pb2.HardwareFeatures.FormFactor.CHROMEBASE,
318 topology_pb2.HardwareFeatures.FormFactor.CHROMEBOX
319 ]:
320 result['psu-type'] = "AC_only"
321 else:
322 result['psu-type'] = "battery"
323
324 result['has-backlight'] = form_factor not in [
325 topology_pb2.HardwareFeatures.FormFactor.CHROMEBIT,
326 topology_pb2.HardwareFeatures.FormFactor.CHROMEBOX
327 ]
328
329 return result
330
331
Andrew Lambcd33f702020-06-11 10:45:16 -0600332def _fw_bcs_path(payload):
David Burger7fd1dbe2020-03-26 09:26:55 -0600333 if payload and payload.firmware_image_name:
Andrew Lamb2413c982020-05-29 12:15:36 -0600334 return 'bcs://%s.%d.%d.0.tbz2' % (payload.firmware_image_name,
335 payload.version.major,
336 payload.version.minor)
David Burger7fd1dbe2020-03-26 09:26:55 -0600337
Andrew Lambcd33f702020-06-11 10:45:16 -0600338 return None
David Burger7fd1dbe2020-03-26 09:26:55 -0600339
Andrew Lambcd33f702020-06-11 10:45:16 -0600340
341def _fw_build_target(payload):
David Burger7fd1dbe2020-03-26 09:26:55 -0600342 if payload:
343 return payload.build_target_name
344
Andrew Lambcd33f702020-06-11 10:45:16 -0600345 return None
David Burger7fd1dbe2020-03-26 09:26:55 -0600346
Andrew Lambcd33f702020-06-11 10:45:16 -0600347
348def _build_firmware(config):
David Burgerb70b6762020-05-21 12:14:59 -0600349 """Returns firmware config, or None if no build targets."""
Andrew Lamb3da156d2020-04-16 16:00:56 -0600350 fw_payload_config = config.sw_config.firmware
351 fw_build_config = config.sw_config.firmware_build_config
352 main_ro = fw_payload_config.main_ro_payload
353 main_rw = fw_payload_config.main_rw_payload
354 ec_ro = fw_payload_config.ec_ro_payload
355 pd_ro = fw_payload_config.pd_ro_payload
David Burger7fd1dbe2020-03-26 09:26:55 -0600356
357 build_targets = {}
Andrew Lamb3da156d2020-04-16 16:00:56 -0600358
David Burger8ee9b4d2020-06-16 17:40:21 -0600359 _upsert(fw_build_config.build_targets.depthcharge, build_targets,
360 'depthcharge')
361 _upsert(fw_build_config.build_targets.coreboot, build_targets, 'coreboot')
362 _upsert(fw_build_config.build_targets.ec, build_targets, 'ec')
363 _upsert(
Andrew Lambf8954ee2020-04-21 10:24:40 -0600364 list(fw_build_config.build_targets.ec_extras), build_targets, 'ec_extras')
David Burger8ee9b4d2020-06-16 17:40:21 -0600365 _upsert(fw_build_config.build_targets.libpayload, build_targets, 'libpayload')
David Burger7fd1dbe2020-03-26 09:26:55 -0600366
David Burgerb70b6762020-05-21 12:14:59 -0600367 if not build_targets:
368 return None
369
David Burger7fd1dbe2020-03-26 09:26:55 -0600370 result = {
371 'bcs-overlay': config.build_target.overlay_name,
372 'build-targets': build_targets,
David Burger7fd1dbe2020-03-26 09:26:55 -0600373 }
Andrew Lamb883fa042020-04-06 11:37:22 -0600374
David Burger8ee9b4d2020-06-16 17:40:21 -0600375 _upsert(main_ro.firmware_image_name.lower(), result, 'image-name')
Andrew Lamb883fa042020-04-06 11:37:22 -0600376
David Burger8ee9b4d2020-06-16 17:40:21 -0600377 _upsert(_fw_bcs_path(main_ro), result, 'main-ro-image')
378 _upsert(_fw_bcs_path(main_rw), result, 'main-rw-image')
379 _upsert(_fw_bcs_path(ec_ro), result, 'ec-ro-image')
380 _upsert(_fw_bcs_path(pd_ro), result, 'pd-ro-image')
David Burger7fd1dbe2020-03-26 09:26:55 -0600381
David Burger8ee9b4d2020-06-16 17:40:21 -0600382 _upsert(
Andrew Lambf39fbe82020-04-13 16:14:33 -0600383 config.hw_design_config.hardware_features.fw_config.value,
384 result,
385 'firmware-config',
386 )
387
David Burger7fd1dbe2020-03-26 09:26:55 -0600388 return result
389
390
Andrew Lambcd33f702020-06-11 10:45:16 -0600391def _build_fw_signing(config):
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500392 if config.sw_config.firmware and config.device_signer_config:
David Burger68e0d142020-05-15 17:29:33 -0600393 hw_design = config.hw_design.name.lower()
Sam McNally2fc807f2020-07-16 18:13:53 +1000394 brand_scan_config = config.brand_config.scan_config
395 if brand_scan_config and brand_scan_config.whitelabel_tag:
396 signature_id = '%s-%s' % (hw_design, brand_scan_config.whitelabel_tag)
397 else:
398 signature_id = hw_design
399
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500400 return {
401 'key-id': config.device_signer_config.key_id,
Sam McNally2fc807f2020-07-16 18:13:53 +1000402 'signature-id': signature_id,
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500403 }
404 return {}
David Burger7fd1dbe2020-03-26 09:26:55 -0600405
406
Andrew Lambcd33f702020-06-11 10:45:16 -0600407def _file(source, destination):
Andrew Lamb2413c982020-05-29 12:15:36 -0600408 return {'destination': destination, 'source': source}
David Burger7fd1dbe2020-03-26 09:26:55 -0600409
410
David Burger40dfe3a2020-06-18 17:09:13 -0600411def _file_v2(build_path, system_path):
412 return {'build-path': build_path, 'system-path': system_path}
413
414
Andrew Lambcd33f702020-06-11 10:45:16 -0600415def _build_audio(config):
David Burger178f3ef2020-06-26 12:11:57 -0600416 if not config.sw_config.audio_configs:
417 return {}
David Burger7fd1dbe2020-03-26 09:26:55 -0600418 alsa_path = '/usr/share/alsa/ucm'
419 cras_path = '/etc/cras'
420 project_name = config.hw_design.name.lower()
David Burger43250662020-05-07 11:21:50 -0600421 program_name = config.program.name.lower()
David Burger7fd1dbe2020-03-26 09:26:55 -0600422 files = []
David Burger178f3ef2020-06-26 12:11:57 -0600423 ucm_suffix = None
424 for audio in config.sw_config.audio_configs:
425 card = audio.card_name
426 card_with_suffix = audio.card_name
427 if audio.ucm_suffix:
428 # TODO: last ucm_suffix wins.
429 ucm_suffix = audio.ucm_suffix
430 card_with_suffix += '.' + audio.ucm_suffix
431 if audio.ucm_file:
432 files.append(
433 _file(audio.ucm_file,
434 '%s/%s/HiFi.conf' % (alsa_path, card_with_suffix)))
435 if audio.ucm_master_file:
436 files.append(
437 _file(
438 audio.ucm_master_file, '%s/%s/%s.conf' %
Andrew Lamb2413c982020-05-29 12:15:36 -0600439 (alsa_path, card_with_suffix, card_with_suffix)))
David Burger178f3ef2020-06-26 12:11:57 -0600440 if audio.card_config_file:
441 files.append(
442 _file(audio.card_config_file,
443 '%s/%s/%s' % (cras_path, project_name, card)))
444 if audio.dsp_file:
445 files.append(
446 _file(audio.dsp_file, '%s/%s/dsp.ini' % (cras_path, project_name)))
447 if audio.module_file:
448 files.append(
449 _file(audio.module_file,
450 '/etc/modprobe.d/alsa-%s.conf' % program_name))
451 if audio.board_file:
452 files.append(
453 _file(audio.board_file,
454 '%s/%s/board.ini' % (cras_path, project_name)))
David Burger599ff7b2020-04-06 16:29:31 -0600455
456 result = {
David Burger7fd1dbe2020-03-26 09:26:55 -0600457 'main': {
458 'cras-config-dir': project_name,
459 'files': files,
460 }
461 }
David Burger178f3ef2020-06-26 12:11:57 -0600462
463 if ucm_suffix:
464 result['main']['ucm-suffix'] = ucm_suffix
David Burger599ff7b2020-04-06 16:29:31 -0600465
466 return result
David Burger7fd1dbe2020-03-26 09:26:55 -0600467
468
Andrew Lambcd33f702020-06-11 10:45:16 -0600469def _build_camera(hw_topology):
David Burger8aa8fa32020-04-14 08:30:34 -0600470 if hw_topology.HasField('camera'):
Ren-Pei Zengce869dd2020-08-18 01:29:37 +0800471 camera_pb = topology_pb2.HardwareFeatures.Camera
David Burger8aa8fa32020-04-14 08:30:34 -0600472 camera = hw_topology.camera.hardware_feature.camera
473 result = {}
474 if camera.count.value:
475 result['count'] = camera.count.value
Ren-Pei Zengce869dd2020-08-18 01:29:37 +0800476 if camera.devices:
477 result['devices'] = []
478 for device in camera.devices:
479 interface = {
480 camera_pb.INTERFACE_USB: 'usb',
481 camera_pb.INTERFACE_MIPI: 'mipi',
482 }[device.interface]
483 facing = {
484 camera_pb.FACING_FRONT: 'front',
485 camera_pb.FACING_BACK: 'back',
486 }[device.facing]
487 orientation = {
488 camera_pb.ORIENTATION_0: 0,
489 camera_pb.ORIENTATION_90: 90,
490 camera_pb.ORIENTATION_180: 180,
491 camera_pb.ORIENTATION_270: 270,
492 }[device.orientation]
493 result['devices'].append({
494 'id': device.id,
495 'interface': interface,
496 'facing': facing,
497 'orientation': orientation,
498 })
David Burger8aa8fa32020-04-14 08:30:34 -0600499 return result
500
Andrew Lambcd33f702020-06-11 10:45:16 -0600501 return None
David Burger8aa8fa32020-04-14 08:30:34 -0600502
Andrew Lambcd33f702020-06-11 10:45:16 -0600503
504def _build_identity(hw_scan_config, program, brand_scan_config=None):
David Burger7fd1dbe2020-03-26 09:26:55 -0600505 identity = {}
David Burger8ee9b4d2020-06-16 17:40:21 -0600506 _upsert(hw_scan_config.firmware_sku, identity, 'sku-id')
507 _upsert(hw_scan_config.smbios_name_match, identity, 'smbios-name-match')
Andrew Lamb7806ce92020-04-07 10:22:17 -0600508 # 'platform-name' is needed to support 'mosys platform name'. Clients should
509 # longer require platform name, but set it here for backwards compatibility.
David Burger8ee9b4d2020-06-16 17:40:21 -0600510 _upsert(program.name, identity, 'platform-name')
David Burger7fd1dbe2020-03-26 09:26:55 -0600511 # ARM architecture
David Burger8ee9b4d2020-06-16 17:40:21 -0600512 _upsert(hw_scan_config.device_tree_compatible_match, identity,
513 'device-tree-compatible-match')
David Burger7fd1dbe2020-03-26 09:26:55 -0600514
515 if brand_scan_config:
David Burger8ee9b4d2020-06-16 17:40:21 -0600516 _upsert(brand_scan_config.whitelabel_tag, identity, 'whitelabel-tag')
David Burger7fd1dbe2020-03-26 09:26:55 -0600517
518 return identity
519
520
Andrew Lambcd33f702020-06-11 10:45:16 -0600521def _lookup(id_value, id_map):
522 if not id_value.value:
523 return None
524
525 key = id_value.value
526 if key in id_map:
527 return id_map[id_value.value]
528 error = 'Failed to lookup %s with value: %s' % (
529 id_value.__class__.__name__.replace('Id', ''), key)
530 print(error)
531 print('Check the config contents provided:')
532 printer = pprint.PrettyPrinter(indent=4)
533 printer.pprint(id_map)
534 raise Exception(error)
David Burger7fd1dbe2020-03-26 09:26:55 -0600535
536
Andrew Lambcd33f702020-06-11 10:45:16 -0600537def _build_touch_file_config(config, project_name):
Sean McAllistereaf10b72020-08-03 13:41:06 -0600538 partners = {x.id.value: x for x in config.partner_list}
C Shapiro2b6d5332020-05-06 17:51:35 -0500539 files = []
540 for comp in config.components:
C Shapiro4813be62020-05-13 17:31:58 -0500541 touch = comp.touchscreen
542 # Everything is the same for Touch screen/pad, except different fields
543 if comp.HasField('touchpad'):
544 touch = comp.touchpad
545 if touch.product_id:
Andrew Lambcd33f702020-06-11 10:45:16 -0600546 vendor = _lookup(comp.manufacturer_id, partners)
C Shapiro2b6d5332020-05-06 17:51:35 -0500547 if not vendor:
Andrew Lamb2413c982020-05-29 12:15:36 -0600548 raise Exception("Manufacturer must be set for touch device %s" %
549 comp.id.value)
C Shapiro2b6d5332020-05-06 17:51:35 -0500550
C Shapiro4813be62020-05-13 17:31:58 -0500551 product_id = touch.product_id
552 fw_version = touch.fw_version
C Shapiro2b6d5332020-05-06 17:51:35 -0500553
C Shapiro2b6d5332020-05-06 17:51:35 -0500554 file_name = "%s_%s.bin" % (product_id, fw_version)
555 fw_file_path = os.path.join(TOUCH_PATH, vendor.name, file_name)
556
557 if not os.path.exists(fw_file_path):
Andrew Lamb2413c982020-05-29 12:15:36 -0600558 raise Exception("Touchscreen fw bin file doesn't exist at: %s" %
559 fw_file_path)
C Shapiro2b6d5332020-05-06 17:51:35 -0500560
C Shapiro303cece2020-07-22 07:15:21 -0500561 touch_vendor = vendor.touch_vendor
562 sym_link = touch_vendor.symlink_file_format.format(
563 vendor_name=vendor.name,
564 vendor_id=touch_vendor.vendor_id,
565 product_id=product_id,
566 fw_version=fw_version,
567 product_series=touch.product_series)
568
569 dest = "%s_%s" % (vendor.name, file_name)
570 if touch_vendor.destination_file_format:
571 dest = touch_vendor.destination_file_format.format(
572 vendor_name=vendor.name,
573 vendor_id=touch_vendor.vendor_id,
574 product_id=product_id,
575 fw_version=fw_version,
576 product_series=touch.product_series)
577
C Shapiro2b6d5332020-05-06 17:51:35 -0500578 files.append({
C Shapiro303cece2020-07-22 07:15:21 -0500579 "destination": os.path.join("/opt/google/touch/firmware", dest),
YH Lin9160fc52020-07-22 16:35:28 -0700580 "source": os.path.join(project_name, fw_file_path),
581 "symlink": os.path.join("/lib/firmware", sym_link),
C Shapiro2b6d5332020-05-06 17:51:35 -0500582 })
583
584 result = {}
David Burger8ee9b4d2020-06-16 17:40:21 -0600585 _upsert(files, result, 'files')
C Shapiro2b6d5332020-05-06 17:51:35 -0500586 return result
587
588
David Burgerceeb68a2020-09-03 11:31:10 -0600589def _sw_config(sw_configs, design_config_id):
590 """Returns the correct software config for `design_config_id`.
591
592 Returns the correct software config match for `design_config_id`. If no such
593 config or multiple such configs are found an exception is raised.
594 """
595 sw_config_matches = [
596 x for x in sw_configs if x.design_config_id.value == design_config_id
597 ]
598 if len(sw_config_matches) == 1:
599 return sw_config_matches[0]
600 if len(sw_config_matches) > 1:
601 raise ValueError('Multiple software configs found for: %s' %
602 design_config_id)
603 raise ValueError('Software config is required for: %s' % design_config_id)
604
605
606def _transform_build_configs(config,
607 config_files=ConfigFiles({}, {}, {}, {}, {})):
Andrew Lambcd33f702020-06-11 10:45:16 -0600608 # pylint: disable=too-many-locals,too-many-branches
Sean McAllistereaf10b72020-08-03 13:41:06 -0600609 partners = {x.id.value: x for x in config.partner_list}
Sean McAllisterf38d1e92020-08-03 13:57:53 -0600610 programs = {x.id.value: x for x in config.program_list}
David Burger7fd1dbe2020-03-26 09:26:55 -0600611 sw_configs = list(config.software_configs)
Andrew Lambcd33f702020-06-11 10:45:16 -0600612 brand_configs = {x.brand_id.value: x for x in config.brand_configs}
David Burger7fd1dbe2020-03-26 09:26:55 -0600613
C Shapiroa0b766c2020-03-31 08:35:28 -0500614 if len(config.build_targets) != 1:
615 # Artifact of sharing the config_bundle for analysis and transforms.
616 # Integrated analysis of multiple programs/projects it the only time
617 # having multiple build targets would be valid.
618 raise Exception('Single build_target required for transform')
619
David Burger7fd1dbe2020-03-26 09:26:55 -0600620 results = {}
Sean McAllisterf66887b2020-08-03 14:00:51 -0600621 for hw_design in config.design_list:
Sean McAllister6cbb0ec2020-08-03 14:03:37 -0600622 if config.device_brand_list:
Andrew Lamb2413c982020-05-29 12:15:36 -0600623 device_brands = [
Sean McAllister6cbb0ec2020-08-03 14:03:37 -0600624 x for x in config.device_brand_list
Andrew Lamb2413c982020-05-29 12:15:36 -0600625 if x.design_id.value == hw_design.id.value
626 ]
David Burger7fd1dbe2020-03-26 09:26:55 -0600627 else:
628 device_brands = [device_brand_pb2.DeviceBrand()]
629
630 for device_brand in device_brands:
631 # Brand config can be empty since platform JSON config allows it
632 brand_config = brand_config_pb2.BrandConfig()
633 if device_brand.id.value in brand_configs:
634 brand_config = brand_configs[device_brand.id.value]
635
636 for hw_design_config in hw_design.configs:
David Burgerceeb68a2020-09-03 11:31:10 -0600637 sw_config = _sw_config(sw_configs, hw_design_config.id.value)
Andrew Lambcd33f702020-06-11 10:45:16 -0600638 program = _lookup(hw_design.program_id, programs)
C Shapiroadefd7c2020-05-19 16:37:21 -0500639 signer_configs_by_design = {}
640 signer_configs_by_brand = {}
641 for signer_config in program.device_signer_configs:
642 design_id = signer_config.design_id.value
643 brand_id = signer_config.brand_id.value
644 if design_id:
645 signer_configs_by_design[design_id] = signer_config
646 elif brand_id:
647 signer_configs_by_brand[brand_id] = signer_config
648 else:
649 raise Exception('No ID found for signer config: %s' % signer_config)
650
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500651 device_signer_config = None
C Shapiroadefd7c2020-05-19 16:37:21 -0500652 if signer_configs_by_design or signer_configs_by_brand:
653 design_id = hw_design.id.value
654 brand_id = device_brand.id.value
655 if design_id in signer_configs_by_design:
656 device_signer_config = signer_configs_by_design[design_id]
657 elif brand_id in signer_configs_by_brand:
658 device_signer_config = signer_configs_by_brand[brand_id]
659 else:
660 # Assume that if signer configs are set, every config is setup
Andrew Lamb2413c982020-05-29 12:15:36 -0600661 raise Exception('Signer config missing for design: %s, brand: %s' %
662 (design_id, brand_id))
C Shapiro2f0bb5d2020-04-14 10:07:47 -0500663
Andrew Lambcd33f702020-06-11 10:45:16 -0600664 transformed_config = _transform_build_config(
C Shapiro90fda252020-04-17 14:34:57 -0500665 Config(
666 program=program,
667 hw_design=hw_design,
Andrew Lambcd33f702020-06-11 10:45:16 -0600668 odm=_lookup(hw_design.odm_id, partners),
C Shapiro90fda252020-04-17 14:34:57 -0500669 hw_design_config=hw_design_config,
670 device_brand=device_brand,
671 device_signer_config=device_signer_config,
Andrew Lambcd33f702020-06-11 10:45:16 -0600672 oem=_lookup(device_brand.oem_id, partners),
C Shapiro90fda252020-04-17 14:34:57 -0500673 sw_config=sw_config,
674 brand_config=brand_config,
Andrew Lamb2413c982020-05-29 12:15:36 -0600675 build_target=config.build_targets[0]), config_files)
David Burger7fd1dbe2020-03-26 09:26:55 -0600676
Andrew Lamb2413c982020-05-29 12:15:36 -0600677 config_json = json.dumps(
678 transformed_config,
679 sort_keys=True,
680 indent=2,
681 separators=(',', ': '))
David Burger7fd1dbe2020-03-26 09:26:55 -0600682
683 if config_json not in results:
684 results[config_json] = transformed_config
685
686 return list(results.values())
687
688
Andrew Lambcd33f702020-06-11 10:45:16 -0600689def _transform_build_config(config, config_files):
David Burger7fd1dbe2020-03-26 09:26:55 -0600690 """Transforms Config instance into target platform JSON schema.
691
692 Args:
693 config: Config namedtuple
C Shapiro5bf23a72020-04-24 11:40:17 -0500694 config_files: Map to look up the generated config files.
David Burger7fd1dbe2020-03-26 09:26:55 -0600695
696 Returns:
697 Unique config payload based on the platform JSON schema.
698 """
699 result = {
Andrew Lamb2413c982020-05-29 12:15:36 -0600700 'identity':
Andrew Lambcd33f702020-06-11 10:45:16 -0600701 _build_identity(config.sw_config.id_scan_config, config.program,
702 config.brand_config.scan_config),
Andrew Lamb2413c982020-05-29 12:15:36 -0600703 'name':
704 config.hw_design.name.lower(),
David Burger7fd1dbe2020-03-26 09:26:55 -0600705 }
706
David Burger8ee9b4d2020-06-16 17:40:21 -0600707 _upsert(_build_arc(config, config_files), result, 'arc')
708 _upsert(_build_audio(config), result, 'audio')
David Burger07af5242020-08-11 11:08:25 -0600709 _upsert(_build_bluetooth(config), result, 'bluetooth')
David Burgerceeb68a2020-09-03 11:31:10 -0600710 _upsert(_build_wifi(config, config_files), result, 'wifi')
Andrew Lambca279902020-08-06 10:13:42 -0600711 _upsert(config.brand_config.wallpaper, result, 'wallpaper')
David Burger8ee9b4d2020-06-16 17:40:21 -0600712 _upsert(config.device_brand.brand_code, result, 'brand-code')
713 _upsert(
Andrew Lambcd33f702020-06-11 10:45:16 -0600714 _build_camera(config.hw_design_config.hardware_topology), result,
715 'camera')
David Burger8ee9b4d2020-06-16 17:40:21 -0600716 _upsert(_build_firmware(config), result, 'firmware')
717 _upsert(_build_fw_signing(config), result, 'firmware-signing')
718 _upsert(
Andrew Lambcd33f702020-06-11 10:45:16 -0600719 _build_fingerprint(config.hw_design_config.hardware_topology), result,
Andrew Lamb2413c982020-05-29 12:15:36 -0600720 'fingerprint')
Andrew Lamb0d236ab2020-06-30 12:30:20 -0600721 _upsert(_build_ui(config), result, 'ui')
David Burger7fd1dbe2020-03-26 09:26:55 -0600722 power_prefs = config.sw_config.power_config.preferences
723 power_prefs_map = dict(
Andrew Lamb2413c982020-05-29 12:15:36 -0600724 (x.replace('_', '-'), power_prefs[x]) for x in power_prefs)
David Burger8ee9b4d2020-06-16 17:40:21 -0600725 _upsert(power_prefs_map, result, 'power')
726 if config_files.camera_map:
727 camera_file = config_files.camera_map.get(config.hw_design.name, {})
728 _upsert(camera_file, result, 'camera')
David Burger52c9d322020-06-09 07:16:18 -0600729 if config_files.dptf_map:
730 # Prefer design specific if found, if not fall back to project wide config
731 # mapped under the empty string.
732 if config_files.dptf_map.get(config.hw_design.name):
733 dptf_file = config_files.dptf_map[config.hw_design.name]
734 else:
735 dptf_file = config_files.dptf_map.get('')
David Burger8ee9b4d2020-06-16 17:40:21 -0600736 _upsert(dptf_file, result, 'thermal')
737 _upsert(config_files.touch_fw, result, 'touch')
Trent Beginf067ccb2020-08-12 12:33:53 -0600738 _upsert(
739 _build_hardware_properties(config.hw_design_config.hardware_topology),
740 result, 'hardware-properties')
David Burger7fd1dbe2020-03-26 09:26:55 -0600741
742 return result
743
744
Andrew Lambcd33f702020-06-11 10:45:16 -0600745def write_output(configs, output=None):
David Burger7fd1dbe2020-03-26 09:26:55 -0600746 """Writes a list of configs to platform JSON format.
747
748 Args:
749 configs: List of config dicts defined in cros_config_schema.yaml
750 output: Target file output (if None, prints to stdout)
751 """
Andrew Lamb2413c982020-05-29 12:15:36 -0600752 json_output = json.dumps({'chromeos': {
753 'configs': configs,
754 }},
755 sort_keys=True,
756 indent=2,
757 separators=(',', ': '))
David Burger7fd1dbe2020-03-26 09:26:55 -0600758 if output:
759 with open(output, 'w') as output_stream:
760 # Using print function adds proper trailing newline.
761 print(json_output, file=output_stream)
762 else:
763 print(json_output)
764
765
Andrew Lambcd33f702020-06-11 10:45:16 -0600766def _feature(name, present):
C Shapiro5bf23a72020-04-24 11:40:17 -0500767 attrib = {'name': name}
768 if present:
769 return etree.Element('feature', attrib=attrib)
Andrew Lambcd33f702020-06-11 10:45:16 -0600770
771 return etree.Element('unavailable-feature', attrib=attrib)
C Shapiro5bf23a72020-04-24 11:40:17 -0500772
773
Andrew Lambcd33f702020-06-11 10:45:16 -0600774def _any_present(features):
Andrew Lamb2413c982020-05-29 12:15:36 -0600775 return topology_pb2.HardwareFeatures.PRESENT in features
C Shapiro5bf23a72020-04-24 11:40:17 -0500776
777
Andrew Lambcd33f702020-06-11 10:45:16 -0600778def _arc_hardware_feature_id(design_config):
C Shapiro5bf23a72020-04-24 11:40:17 -0500779 return design_config.id.value.lower().replace(':', '_')
780
781
Andrew Lambcd33f702020-06-11 10:45:16 -0600782def _write_arc_hardware_feature_file(output_dir, file_name, config_content):
David Burger77a1d312020-05-23 16:05:45 -0600783 output_dir += '/arc'
784 os.makedirs(output_dir, exist_ok=True)
785 output = '%s/%s' % (output_dir, file_name)
Andrew Lamb2413c982020-05-29 12:15:36 -0600786 file_content = minidom.parseString(config_content).toprettyxml(
787 indent=' ', encoding='utf-8')
C Shapiroea33cff2020-05-11 13:32:05 -0500788
789 with open(output, 'wb') as f:
790 f.write(file_content)
791
792
Ren-Pei Zengf22b5382020-09-02 13:31:22 +0800793def _get_arc_camera_features(camera):
794 """Gets camera related features for ARC hardware_features.xml from camera
795 topology. Check
796 https://developer.android.com/reference/android/content/pm/PackageManager#FEATURE_CAMERA
797 and CTS android.app.cts.SystemFeaturesTest#testCameraFeatures for the correct
798 settings.
799
800 Args:
801 camera: A HardwareFeatures.Camera proto message.
802 Returns:
803 list of camera related ARC features as XML elements.
804 """
805 camera_pb = topology_pb2.HardwareFeatures.Camera
806
807 if len(camera.devices) > 0:
808 count = len(camera.devices)
809 has_front_camera = any(
810 (d.facing == camera_pb.FACING_FRONT for d in camera.devices))
811 has_back_camera = any(
812 (d.facing == camera_pb.FACING_BACK for d in camera.devices))
813 # Assumes MIPI cameras support FULL-level.
814 # TODO(kamesan): Setting this in project configs when there's an exception.
815 has_level_full_camera = any(
816 (d.interface == camera_pb.INTERFACE_MIPI for d in camera.devices))
817 else:
818 # Fallback to use the old proto definition.
819 count = camera.count.value
820 has_front_camera = count > 0
821 has_back_camera = count > 1
822 has_level_full_camera = False
823
824 # Assumes the back camera supports autofocus.
825 # TODO(kamesan): Setting this in project configs when there's an exception.
826 has_autofocus_back_camera = has_back_camera
827
828 return [
829 _feature('android.hardware.camera', has_back_camera),
830 _feature('android.hardware.camera.any', count > 0),
831 _feature('android.hardware.camera.autofocus', has_autofocus_back_camera),
832 _feature('android.hardware.camera.capability.manual_post_processing',
833 has_level_full_camera),
834 _feature('android.hardware.camera.capability.manual_sensor',
835 has_level_full_camera),
836 _feature('android.hardware.camera.front', has_front_camera),
837 _feature('android.hardware.camera.level.full', has_level_full_camera),
838 ]
839
840
Andrew Lambcd33f702020-06-11 10:45:16 -0600841def _write_arc_hardware_feature_files(config, output_dir, build_root_dir):
C Shapiro5bf23a72020-04-24 11:40:17 -0500842 """Writes ARC hardware_feature.xml files for each config
843
844 Args:
845 config: Source ConfigBundle to process.
846 output_dir: Path to the generated output.
C Shapiro5c877992020-04-29 12:11:28 -0500847 build_root_path: Path to the config file from portage's perspective.
C Shapiro5bf23a72020-04-24 11:40:17 -0500848 Returns:
849 dict that maps the design_config_id onto the correct file.
850 """
Andrew Lambcd33f702020-06-11 10:45:16 -0600851 # pylint: disable=too-many-locals
C Shapiro5bf23a72020-04-24 11:40:17 -0500852 result = {}
C Shapiroea33cff2020-05-11 13:32:05 -0500853 configs_by_design = {}
Sean McAllisterf66887b2020-08-03 14:00:51 -0600854 for hw_design in config.design_list:
C Shapiro5bf23a72020-04-24 11:40:17 -0500855 for design_config in hw_design.configs:
856 hw_features = design_config.hardware_features
Andrew Lambcd33f702020-06-11 10:45:16 -0600857 touchscreen = _any_present([hw_features.screen.touch_support])
C Shapiro5bf23a72020-04-24 11:40:17 -0500858 acc = hw_features.accelerometer
859 gyro = hw_features.gyroscope
860 compass = hw_features.magnetometer
Andrew Lambcd33f702020-06-11 10:45:16 -0600861 light_sensor = hw_features.light_sensor
C Shapiro5bf23a72020-04-24 11:40:17 -0500862 root = etree.Element('permissions')
Ren-Pei Zengf22b5382020-09-02 13:31:22 +0800863 root.extend(
864 _get_arc_camera_features(hw_features.camera) + [
865 _feature(
866 'android.hardware.sensor.accelerometer',
867 _any_present([acc.lid_accelerometer, acc.base_accelerometer
868 ])),
869 _feature('android.hardware.sensor.gyroscope',
870 _any_present([gyro.lid_gyroscope, gyro.base_gyroscope])),
871 _feature(
872 'android.hardware.sensor.compass',
873 _any_present(
874 [compass.lid_magnetometer, compass.base_magnetometer])),
875 _feature(
876 'android.hardware.sensor.light',
877 _any_present([
878 light_sensor.lid_lightsensor,
879 light_sensor.base_lightsensor
880 ])),
881 _feature('android.hardware.touchscreen', touchscreen),
882 _feature('android.hardware.touchscreen.multitouch', touchscreen),
883 _feature('android.hardware.touchscreen.multitouch.distinct',
884 touchscreen),
885 _feature('android.hardware.touchscreen.multitouch.jazzhand',
886 touchscreen),
887 ])
C Shapiro5bf23a72020-04-24 11:40:17 -0500888
C Shapiroea33cff2020-05-11 13:32:05 -0500889 design_name = hw_design.name.lower()
C Shapiro5bf23a72020-04-24 11:40:17 -0500890
C Shapiroea33cff2020-05-11 13:32:05 -0500891 # Constructs the following map:
892 # design_name -> config -> design_configs
893 # This allows any of the following file naming schemes:
894 # - All configs within a design share config (design_name prefix only)
895 # - Nobody shares (full design_name and config id prefix needed)
896 #
897 # Having shared configs when possible makes code reviews easier around
898 # the configs and makes debugging easier on the platform side.
899 config_content = etree.tostring(root)
900 arc_configs = configs_by_design.get(design_name, {})
901 design_configs = arc_configs.get(config_content, [])
902 design_configs.append(design_config)
903 arc_configs[config_content] = design_configs
904 configs_by_design[design_name] = arc_configs
C Shapiro9a3ac8c2020-04-25 07:49:21 -0500905
C Shapiroea33cff2020-05-11 13:32:05 -0500906 for design_name, unique_configs in configs_by_design.items():
907 for file_content, design_configs in unique_configs.items():
Andrew Lamb2413c982020-05-29 12:15:36 -0600908 file_name = 'hardware_features_%s.xml' % design_name
909 if len(unique_configs) == 1:
Andrew Lambcd33f702020-06-11 10:45:16 -0600910 _write_arc_hardware_feature_file(output_dir, file_name, file_content)
C Shapiro9a3ac8c2020-04-25 07:49:21 -0500911
Andrew Lamb2413c982020-05-29 12:15:36 -0600912 for design_config in design_configs:
Andrew Lambcd33f702020-06-11 10:45:16 -0600913 feature_id = _arc_hardware_feature_id(design_config)
Andrew Lamb2413c982020-05-29 12:15:36 -0600914 if len(unique_configs) > 1:
915 file_name = 'hardware_features_%s.xml' % feature_id
Andrew Lambcd33f702020-06-11 10:45:16 -0600916 _write_arc_hardware_feature_file(output_dir, file_name, file_content)
David Burger40dfe3a2020-06-18 17:09:13 -0600917 result[feature_id] = _file_v2('%s/arc/%s' % (build_root_dir, file_name),
918 '/etc/%s' % file_name)
C Shapiro5bf23a72020-04-24 11:40:17 -0500919 return result
920
921
Andrew Lambcd33f702020-06-11 10:45:16 -0600922def _read_config(path):
David Burgerd4f32962020-05-02 12:07:40 -0600923 """Reads a ConfigBundle proto from a json pb file.
David Burgere6f76222020-04-27 11:08:01 -0600924
925 Args:
David Burgerd4f32962020-05-02 12:07:40 -0600926 path: Path to the file encoding the json pb proto.
David Burgere6f76222020-04-27 11:08:01 -0600927 """
928 config = config_bundle_pb2.ConfigBundle()
929 with open(path, 'r') as f:
930 return json_format.Parse(f.read(), config)
931
932
Andrew Lambcd33f702020-06-11 10:45:16 -0600933def _merge_configs(configs):
David Burger7fd1dbe2020-03-26 09:26:55 -0600934 result = config_bundle_pb2.ConfigBundle()
935 for config in configs:
936 result.MergeFrom(config)
937
938 return result
939
940
David Burger1ba78a22020-06-18 18:42:47 -0600941def _camera_map(configs, project_name):
David Burger8ee9b4d2020-06-16 17:40:21 -0600942 """Produces a camera config map for the given configs.
943
944 Produces a map that maps from the design name to the camera config for that
945 design.
946
947 Args:
948 configs: Source ConfigBundle to process.
David Burger1ba78a22020-06-18 18:42:47 -0600949 project_name: Name of project processing for.
David Burger8ee9b4d2020-06-16 17:40:21 -0600950
951 Returns:
952 map from design name to camera config.
953 """
954 result = {}
Sean McAllisterf66887b2020-08-03 14:00:51 -0600955 for design in configs.design_list:
David Burger8ee9b4d2020-06-16 17:40:21 -0600956 design_name = design.name
David Burger0d9e8462020-06-19 14:12:37 -0600957 config_path = CAMERA_CONFIG_SOURCE_PATH_TEMPLATE.format(design_name.lower())
David Burger8ee9b4d2020-06-16 17:40:21 -0600958 if os.path.exists(config_path):
David Burger0d9e8462020-06-19 14:12:37 -0600959 destination = CAMERA_CONFIG_DEST_PATH_TEMPLATE.format(design_name.lower())
David Burger8ee9b4d2020-06-16 17:40:21 -0600960 result[design_name] = {
David Burger1ba78a22020-06-18 18:42:47 -0600961 'config-path':
962 destination,
963 'config-file':
964 _file_v2(os.path.join(project_name, config_path), destination),
David Burger8ee9b4d2020-06-16 17:40:21 -0600965 }
966 return result
967
968
David Burger52c9d322020-06-09 07:16:18 -0600969def _dptf_map(configs, project_name):
970 """Produces a dptf map for the given configs.
971
972 Produces a map that maps from design name to the dptf file config for that
973 design. It looks for the dptf files at:
David Burger2f0d9522020-07-30 10:52:28 -0600974 DPTF_PATH + '/' + DPTF_FILE
David Burger52c9d322020-06-09 07:16:18 -0600975 for a project wide config, that it maps under the empty string, and at:
David Burger2f0d9522020-07-30 10:52:28 -0600976 DPTF_PATH + '/' + design_name + '/' + DPTF_FILE
David Burger52c9d322020-06-09 07:16:18 -0600977 for design specific configs that it maps under the design name.
978
979 Args:
980 configs: Source ConfigBundle to process.
981 project_name: Name of project processing for.
982
983 Returns:
David Burger8ee9b4d2020-06-16 17:40:21 -0600984 map from design name or empty string (project wide), to dptf config.
David Burger52c9d322020-06-09 07:16:18 -0600985 """
986 result = {}
David Burger52c9d322020-06-09 07:16:18 -0600987 # Looking at top level for project wide, and then for each design name
988 # for design specific.
Sean McAllisterf66887b2020-08-03 14:00:51 -0600989 dirs = [""] + [d.name for d in configs.design_list]
David Burger52c9d322020-06-09 07:16:18 -0600990 for directory in dirs:
David Burgera2252762020-07-09 15:09:49 -0600991 design = directory.lower()
992 if os.path.exists(os.path.join(DPTF_PATH, design, DPTF_FILE)):
David Burger2f0d9522020-07-30 10:52:28 -0600993 project_dptf_path = os.path.join(project_name, design, DPTF_FILE)
David Burger52c9d322020-06-09 07:16:18 -0600994 dptf_file = {
995 'dptf-dv':
996 project_dptf_path,
997 'files': [
998 _file(
David Burgera2252762020-07-09 15:09:49 -0600999 os.path.join(project_name, DPTF_PATH, design, DPTF_FILE),
David Burger52c9d322020-06-09 07:16:18 -06001000 os.path.join('/etc/dptf', project_dptf_path))
1001 ]
1002 }
1003 result[directory] = dptf_file
1004 return result
1005
1006
David Burgerceeb68a2020-09-03 11:31:10 -06001007def _wifi_sar_map(configs, project_name, output_dir, build_root_dir):
1008 """Constructs a map from design name to wifi sar config for that design.
1009
1010 Constructs a map from design name to the wifi sar config for that design.
1011 In the process a wifi sar hex file is generated that the config points at.
1012 This mapping is only made for the intel wifi where the generated file is
1013 provided when building coreboot.
1014
1015 Args:
1016 configs: Source ConfigBundle to process.
1017 project_name: Name of project processing for.
1018 output_dir: Path to the generated output.
1019 build_root_path: Path to the config file from portage's perspective.
1020
1021 Returns:
1022 dict that maps the design name onto the wifi config for that design.
1023 """
1024 # pylint: disable=too-many-locals
1025 result = {}
1026 programs = {p.id.value: p for p in configs.program_list}
1027 sw_configs = list(configs.software_configs)
1028 for hw_design in configs.design_list:
1029 for hw_design_config in hw_design.configs:
1030 sw_config = _sw_config(sw_configs, hw_design_config.id.value)
1031 if sw_config.wifi_config.HasField('intel_config'):
1032 sar_file_content = _create_intel_sar_file_content(
1033 sw_config.wifi_config.intel_config)
1034 design_name = hw_design.name.lower()
1035 program = _lookup(hw_design.program_id, programs)
1036 wifi_sar_id = _extract_fw_config_value(hw_design_config, program,
1037 'Intel wifi sar id')
1038 output_path = os.path.join(output_dir, 'wifi')
1039 os.makedirs(output_path, exist_ok=True)
1040 filename = 'wifi_sar_{}.hex'.format(wifi_sar_id)
1041 output_path = os.path.join(output_path, filename)
1042 build_path = os.path.join(build_root_dir, 'wifi', filename)
1043 if os.path.exists(output_path):
1044 with open(output_path, 'r') as f:
1045 if f.read() != sar_file_content:
1046 raise Exception(
1047 'Project {} has conflicting wifi sar file content under '
1048 'wifi sar id {}.'.format(project_name, wifi_sar_id))
1049 else:
1050 with open(output_path, 'w') as f:
1051 f.write(sar_file_content)
1052 system_path = '/firmware/cbfs-rw-raw/{}/{}'.format(
1053 project_name, filename)
1054 result[design_name] = _file_v2(build_path, system_path)
1055 return result
1056
1057
1058def _extract_fw_config_value(hw_design_config, program, name):
1059 """Extracts the firwmare config value with the given name.
1060
1061 Args:
1062 hw_design_config: Design extracting value from.
1063 program: Program the `hw_design_config` belongs to.
1064 name: Name of firmware config segment to extract.
1065
1066 Returns: the extracted value or raises a ValueError if no firmware
1067 configuration segment with `name` is found.
1068 """
1069 fw_config = hw_design_config.hardware_features.fw_config.value
1070 for fcs in program.firmware_configuration_segments:
1071 if fcs.name == name:
1072 value = fw_config & fcs.mask
1073 lsb_bit_set = (~fcs.mask + 1) & fcs.mask
1074 return value // lsb_bit_set
1075 raise ValueError(
1076 'No firmware configuration segment with name {} found'.format(name))
1077
1078
1079def _create_intel_sar_file_content(intel_config):
1080 """Creates and returns the intel sar file content for the given config.
1081
1082 Creates and returns the sar file content that is used with intel drivers
1083 only.
1084
1085 Args:
1086 intel_config: IntelConfig config.
1087
1088 Returns:
1089 sar file content for the given config, see:
1090 https://chromeos.google.com/partner/dlm/docs/connectivity/wifidyntxpower.html
1091 """
1092
1093 def to_hex(val):
1094 if val > 255 or val < 0:
1095 raise Exception('Sar file value %s out of range' % val)
1096 return '{0:0{1}X}'.format(val, 2)
1097
1098 def power_table(tpc):
1099 return (to_hex(tpc.limit_2g) + to_hex(tpc.limit_5g_1) +
1100 to_hex(tpc.limit_5g_2) + to_hex(tpc.limit_5g_3) +
1101 to_hex(tpc.limit_5g_4))
1102
1103 def wgds_value(wgds):
1104 return to_hex(wgds)
1105
1106 def offset_table(offsets):
1107 return (to_hex(offsets.max_2g) + to_hex(offsets.offset_2g_a) +
1108 to_hex(offsets.offset_2g_b) + to_hex(offsets.max_5g) +
1109 to_hex(offsets.offset_5g_a) + to_hex(offsets.offset_5g_b))
1110
1111 # See https://chromeos.google.com/partner/dlm/docs/connectivity/wifidyntxpower.html
1112 return (power_table(intel_config.tablet_mode_power_table_a) +
1113 power_table(intel_config.tablet_mode_power_table_b) +
1114 power_table(intel_config.non_tablet_mode_power_table_a) +
1115 power_table(intel_config.non_tablet_mode_power_table_b) +
1116 '00000000000000000000' + '00000000000000000000' +
1117 wgds_value(intel_config.wgds_version) +
1118 offset_table(intel_config.offset_fcc) +
1119 offset_table(intel_config.offset_eu) +
1120 offset_table(intel_config.offset_other))
1121
1122
Andrew Lambcd33f702020-06-11 10:45:16 -06001123def Main(project_configs, program_config, output): # pylint: disable=invalid-name
David Burger7fd1dbe2020-03-26 09:26:55 -06001124 """Transforms source proto config into platform JSON.
1125
1126 Args:
1127 project_configs: List of source project configs to transform.
1128 program_config: Program config for the given set of projects.
1129 output: Output file that will be generated by the transform.
1130 """
Andrew Lambcd33f702020-06-11 10:45:16 -06001131 configs = _merge_configs([_read_config(program_config)] +
1132 [_read_config(config) for config in project_configs])
C Shapiro5bf23a72020-04-24 11:40:17 -05001133 arc_hw_feature_files = {}
C Shapiro2b6d5332020-05-06 17:51:35 -05001134 touch_fw = {}
David Burger8ee9b4d2020-06-16 17:40:21 -06001135 camera_map = {}
David Burgerceeb68a2020-09-03 11:31:10 -06001136 dptf_map = {}
1137 wifi_sar_map = {}
C Shapiro5bf23a72020-04-24 11:40:17 -05001138 output_dir = os.path.dirname(output)
C Shapiro5c877992020-04-29 12:11:28 -05001139 build_root_dir = output_dir
C Shapiro5c877992020-04-29 12:11:28 -05001140 if 'sw_build_config' in output_dir:
1141 full_path = os.path.realpath(output)
Andrew Lamb6b607732020-08-24 15:52:46 -06001142 project_name = re.match(r'.*/(\w*)/(public_)?sw_build_config/.*',
Andrew Lamb2413c982020-05-29 12:15:36 -06001143 full_path).groups(1)[0]
C Shapiro5c877992020-04-29 12:11:28 -05001144 # Projects don't know about each other until they are integrated into the
1145 # build system. When this happens, the files need to be able to co-exist
1146 # without any collisions. This prefixes the project name (which is how
1147 # portage maps in the project), so project files co-exist and can be
1148 # installed together.
1149 # This is necessary to allow projects to share files at the program level
1150 # without having portage file installation collisions.
1151 build_root_dir = os.path.join(project_name, output_dir)
C Shapiro6830e6c2020-04-29 13:29:56 -05001152
David Burger1ba78a22020-06-18 18:42:47 -06001153 camera_map = _camera_map(configs, project_name)
David Burger52c9d322020-06-09 07:16:18 -06001154 dptf_map = _dptf_map(configs, project_name)
David Burgerceeb68a2020-09-03 11:31:10 -06001155 wifi_sar_map = _wifi_sar_map(configs, project_name, output_dir,
1156 build_root_dir)
David Burger52c9d322020-06-09 07:16:18 -06001157
C Shapiro2b6d5332020-05-06 17:51:35 -05001158 if os.path.exists(TOUCH_PATH):
Andrew Lambcd33f702020-06-11 10:45:16 -06001159 touch_fw = _build_touch_file_config(configs, project_name)
Andrew Lambcd33f702020-06-11 10:45:16 -06001160 arc_hw_feature_files = _write_arc_hardware_feature_files(
1161 configs, output_dir, build_root_dir)
C Shapiro5bf23a72020-04-24 11:40:17 -05001162 config_files = ConfigFiles(
C Shapiro5bf23a72020-04-24 11:40:17 -05001163 arc_hw_features=arc_hw_feature_files,
C Shapiro2b6d5332020-05-06 17:51:35 -05001164 touch_fw=touch_fw,
David Burger8ee9b4d2020-06-16 17:40:21 -06001165 dptf_map=dptf_map,
David Burgerceeb68a2020-09-03 11:31:10 -06001166 camera_map=camera_map,
1167 wifi_sar_map=wifi_sar_map)
Andrew Lambcd33f702020-06-11 10:45:16 -06001168 write_output(_transform_build_configs(configs, config_files), output)
David Burger7fd1dbe2020-03-26 09:26:55 -06001169
1170
1171def main(argv=None):
1172 """Main program which parses args and runs
1173
1174 Args:
1175 argv: List of command line arguments, if None uses sys.argv.
1176 """
1177 if argv is None:
1178 argv = sys.argv[1:]
Andrew Lambcd33f702020-06-11 10:45:16 -06001179 opts = parse_args(argv)
David Burger7fd1dbe2020-03-26 09:26:55 -06001180 Main(opts.project_configs, opts.program_config, opts.output)
1181
1182
1183if __name__ == '__main__':
1184 sys.exit(main(sys.argv[1:]))