Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / scripts / flashing / esp32_firmware_utils.py
1 #!/usr/bin/env python3
2 # Copyright (c) 2020 Project CHIP Authors
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 """Flash an ESP32 device.
16
17 This is layered so that a caller can perform individual operations
18 through an `Flasher` instance, or operations according to a command line.
19 For `Flasher`, see the class documentation. For the parse_command()
20 interface or standalone execution:
21
22 usage: esp32_firmware_utils.py [-h] [--verbose] [--erase] [--application FILE]
23                                [--verify_application] [--reset] [--skip_reset]
24                                [--esptool FILE] [--parttool FILE]
25                                [--sdkconfig FILE] [--chip CHIP] [--port PORT]
26                                [--baud BAUD] [--before ACTION]
27                                [--after ACTION] [--flash_mode MODE]
28                                [--flash_freq FREQ] [--flash_size SIZE]
29                                [--compress] [--bootloader FILE]
30                                [--bootloader_offset OFFSET] [--partition FILE]
31                                [--partition_offset OFFSET]
32                                [--application_offset OFFSET]
33
34 Flash ESP32 device
35
36 optional arguments:
37   -h, --help            show this help message and exit
38
39 configuration:
40   --verbose, -v         Report more verbosely
41   --esptool FILE        File name of the esptool executable
42   --parttool FILE       File name of the parttool executable
43   --sdkconfig FILE      File containing option defaults
44   --chip CHIP           Target chip type
45   --port PORT           Serial port device
46   --baud BAUD           Serial port baud rate
47   --before ACTION       What to do before connecting
48   --after ACTION        What to do when finished
49   --flash_mode MODE, --flash-mode MODE
50                         Flash mode
51   --flash_freq FREQ, --flash-freq FREQ
52                         Flash frequency
53   --flash_size SIZE, --flash-size SIZE
54                         Flash size
55   --compress, -z        Compress data in transfer
56   --bootloader FILE     Bootloader image
57   --bootloader_offset OFFSET, --bootloader-offset OFFSET
58                         Bootloader offset
59   --partition FILE      Partition table image
60   --partition_offset OFFSET, --partition-offset OFFSET
61                         Partition table offset
62   --application_offset OFFSET, --application-offset OFFSET
63                         Application offset
64
65 operations:
66   --erase               Erase device
67   --application FILE    Flash an image
68   --verify_application, --verify-application
69                         Verify the image after flashing
70   --reset               Reset device after flashing
71   --skip_reset, --skip-reset
72                         Do not reset device after flashing
73 """
74
75 import os
76 import sys
77
78 import firmware_utils
79
80 # Additional options that can be use to configure an `Flasher`
81 # object (as dictionary keys) and/or passed as command line options.
82 ESP32_OPTIONS = {
83     # Configuration options define properties used in flashing operations.
84     'configuration': {
85         # Tool configuration options.
86         'esptool': {
87             'help': 'File name of the esptool executable',
88             'default': None,
89             'argparse': {
90                 'metavar': 'FILE',
91             },
92             'command': [
93                 {'option': 'esptool'},
94                 {'optional': 'port'},
95                 {'optional': 'baud'},
96                 {'optional': 'before'},
97                 {'optional': 'after'},
98                 ()
99             ],
100             'verify': ['{esptool}', 'version'],
101             'error':
102                 """\
103                 Unable to execute {esptool}.
104
105                 Please ensure that this tool is installed and
106                 that $IDF_PATH is set. See the ESP32 example
107                 README for installation instructions.
108                 """,
109         },
110         'parttool': {
111             'help': 'File name of the parttool executable',
112             'default': None,
113             'argparse': {
114                 'metavar': 'FILE'
115             },
116             'command': [
117                 {'option': 'parttool'},
118                 {'optional': 'port'},
119                 {'optional': 'baud'},
120                 {
121                     'option': 'partition',
122                     'result': ['--partition-table-file', '{partition}'],
123                     'expand': True
124                 },
125                 ()
126             ],
127             'verify': ['{parttool}', '--quiet'],
128             'error':
129                 """\
130                 Unable to execute {parttool}.
131
132                 Please ensure that this tool is installed and
133                 that $IDF_PATH is set. See the ESP32 example
134                 README for installation instructions.
135                 """,
136         },
137         'sdkconfig': {
138             'help': 'File containing option defaults',
139             'default': None,
140             'argparse': {
141                 'metavar': 'FILE'
142             },
143         },
144
145         # Device configuration options.
146         'chip': {
147             'help': 'Target chip type',
148             'default': 'esp32',
149             'argparse': {
150                 'metavar': 'CHIP'
151             },
152             'sdkconfig': 'CONFIG_IDF_TARGET',
153         },
154         'port': {
155             'help': 'Serial port device',
156             'default': None,
157             'argparse': {
158                 'metavar': 'PORT',
159             },
160             'sdkconfig': 'CONFIG_ESPTOOLPY_PORT',
161         },
162         'baud': {
163             'help': 'Serial port baud rate',
164             'default': None,
165             'argparse': {
166                 'metavar': 'BAUD',
167             },
168             'sdkconfig': 'CONFIG_ESPTOOLPY_BAUD',
169         },
170         'before': {
171             'help': 'What to do before connecting',
172             'default': None,
173             'argparse': {
174                 'metavar': 'ACTION',
175             },
176             'sdkconfig': 'CONFIG_ESPTOOLPY_BEFORE',
177         },
178         'after': {
179             'help': 'What to do when finished',
180             'default': None,
181             'argparse': {
182                 'metavar': 'ACTION',
183             },
184             'sdkconfig': 'CONFIG_ESPTOOLPY_AFTER',
185         },
186         'flash_mode': {
187             'help': 'Flash mode',
188             'default': None,
189             'argparse': {
190                 'metavar': 'MODE',
191             },
192             'sdkconfig': 'CONFIG_ESPTOOLPY_FLASHMODE',
193         },
194         'flash_freq': {
195             'help': 'Flash frequency',
196             'default': None,
197             'argparse': {
198                 'metavar': 'FREQ',
199             },
200             'sdkconfig': 'CONFIG_ESPTOOLPY_FLASHFREQ',
201         },
202         'flash_size': {
203             'help': 'Flash size',
204             'default': None,
205             'argparse': {
206                 'metavar': 'SIZE',
207             },
208             'sdkconfig': 'CONFIG_ESPTOOLPY_FLASHSIZE',
209         },
210         'compress': {
211             'help': 'Compress data in transfer',
212             'default': None,
213             'alias': ['-z'],
214             'argparse': {
215                 'action': 'store_true'
216             },
217             'sdkconfig': 'CONFIG_ESPTOOLPY_COMPRESSED',
218         },
219
220         # Flashing things.
221         'bootloader': {
222             'help': 'Bootloader image',
223             'default': None,
224             'argparse': {
225                 'metavar': 'FILE'
226             },
227         },
228         'bootloader_offset': {
229             'help': 'Bootloader offset',
230             'default': '0x1000',
231             'argparse': {
232                 'metavar': 'OFFSET'
233             },
234         },
235         'partition': {
236             'help': 'Partition table image',
237             'default': None,
238             'argparse': {
239                 'metavar': 'FILE'
240             },
241         },
242         'partition_offset': {
243             'help': 'Partition table offset',
244             'default': None,
245             'argparse': {
246                 'metavar': 'OFFSET'
247             },
248             'sdkconfig': 'CONFIG_PARTITION_TABLE_OFFSET',
249         },
250         'application_offset': {
251             'help': 'Application offset',
252             'default': None,
253             'argparse': {
254                 'metavar': 'OFFSET'
255             },
256         },
257     },
258 }
259
260
261 def namespace_defaults(dst, src):
262     for key, value in src.items():
263         if key not in dst or getattr(dst, key) is None:
264             setattr(dst, key, value)
265
266
267 class Flasher(firmware_utils.Flasher):
268     """Manage esp32 flashing."""
269
270     def __init__(self, **options):
271         super().__init__(platform='ESP32', module=__name__, **options)
272         self.define_options(ESP32_OPTIONS)
273
274     def _postprocess_argv(self):
275         if self.option.sdkconfig:
276             namespace_defaults(self.option,
277                                self.read_sdkconfig(self.option.sdkconfig))
278 #       idf = os.environ.get('IDF_PATH')
279 #       if idf:
280 #           if self.option.esptool is None:
281 #               self.option.esptool = os.path.join(
282 #                   idf,
283 #                   'components/esptool_py/esptool/esptool.py')
284 #           if self.option.parttool is None:
285 #               self.option.parttool = os.path.join(
286 #                   idf,
287 #                   'components/partition_table/parttool.py')
288
289     def read_sdkconfig(self, filename):
290         """Given an ESP32 sdkconfig file, read it for values of options
291            not otherwise set.
292         """
293         config_map = {}
294         for key, info in vars(self.info).items():
295             config_key = info.get('sdkconfig')
296             if config_key:
297                 config_map[config_key] = key
298         result = {}
299         with open(filename) as f:
300             for line in f:
301                 k, eq, v = line.strip().partition('=')
302                 if eq == '=' and k in config_map:
303                     result[config_map[k]] = v.strip('"')
304         return result
305
306     IDF_PATH_TOOLS = {
307         'esptool': 'components/esptool_py/esptool/esptool.py',
308         'parttool': 'components/partition_table/parttool.py',
309     }
310
311     def locate_tool(self, tool):
312         if tool in self.IDF_PATH_TOOLS:
313             idf_path = os.environ.get('IDF_PATH')
314             if idf_path:
315                 return os.path.join(idf_path, self.IDF_PATH_TOOLS[tool])
316         return super().locate_tool(tool)
317
318     # Common command line arguments for esptool flashing subcommands.
319     FLASH_ARGUMENTS = [
320         {'optional': 'flash_mode'},
321         {'optional': 'flash_freq'},
322         {'optional': 'flash_size'},
323         {
324             'match': '{compress}',
325             'test': [(True, '-z'), ('y', '-z')],
326             'default': '-u'
327         },
328     ]
329
330     def erase(self):
331         """Perform `commander device masserase`."""
332         return self.run_tool('esptool', ['erase_flash'], {}, 'Erase device')
333
334     def verify(self, image, address=0):
335         """Verify image(s)."""
336         if not isinstance(image, list):
337             image = [address, image]
338         return self.run_tool(
339             'esptool',
340             ['verify_flash', self.FLASH_ARGUMENTS, image],
341             name='Verify',
342             pass_message='Verified',
343             fail_message='Not verified',
344             fail_level=2)
345
346     def flash(self, image, address=0):
347         """Flash image(s)."""
348         if not isinstance(image, list):
349             image = [address, image]
350         return self.run_tool(
351             'esptool',
352             ['write_flash', self.FLASH_ARGUMENTS, image],
353             name='Flash')
354
355     PARTITION_INFO = {
356         'phy': ['--partition-type', 'data', '--partition-subtype', 'phy'],
357         'application': ['--partition-boot-default'],
358         'ota': ['--partition-type', 'data', '--partition-subtype', 'ota'],
359         'factory': [
360             '--partition-type', 'app', '--partition-subtype', 'factory'
361         ],
362     }
363
364     def get_partition_info(self, item, info, options=None):
365         """Run parttool to get partition information."""
366         return self.run_tool(
367             'parttool',
368             ['get_partition_info', self.PARTITION_INFO[item], '--info', info],
369             options or {},
370             capture_output=True).stdout.strip()
371
372     def make_wrapper(self, argv):
373         self.parser.add_argument(
374             '--use-parttool',
375             metavar='FILENAME',
376             help='partition tool to configure flashing script')
377         self.parser.add_argument(
378             '--use-partition-file',
379             metavar='FILENAME',
380             help='partition file to configure flashing script')
381         self.parser.add_argument(
382             '--use-sdkconfig',
383             metavar='FILENAME',
384             help='sdkconfig to configure flashing script')
385         super().make_wrapper(argv)
386
387     def _platform_wrapper_args(self, args):
388         if args.use_sdkconfig:
389             # Include values from sdkconfig so that it isn't needed at
390             # flashing time.
391             namespace_defaults(args, self.read_sdkconfig(args.use_sdkconfig))
392         parttool = args.use_parttool or args.parttool
393         partfile = args.use_partition_file or args.partition
394         if parttool and partfile:
395             # Get unspecified offsets from the partition file now,
396             # so that parttool isn't needed at flashing time.
397             if args.application and args.application_offset is None:
398                 args.application_offset = self.get_partition_info(
399                     'application', 'offset',
400                     {'parttool': parttool, 'partition': partfile})
401
402     def actions(self):
403         """Perform actions on the device according to self.option."""
404         self.log(3, 'Options:', self.option)
405
406         if self.option.erase:
407             if self.erase().err:
408                 return self
409
410         bootloader = self.optional_file(self.option.bootloader)
411         application = self.optional_file(self.option.application)
412         partition = self.optional_file(self.option.partition)
413
414         if bootloader or application or partition:
415             # Collect the flashable items.
416             flash = []
417             if bootloader:
418                 flash += [self.option.bootloader_offset, bootloader]
419             if application:
420                 offset = self.option.application_offset
421                 if offset is None:
422                     offset = self.get_partition_info('application', 'offset')
423                 flash += [offset, application]
424             if partition:
425                 flash += [self.option.partition_offset, partition]
426
427             # esptool.py doesn't have an independent reset command, so we add
428             # an `--after` option to the final operation, which may be either
429             # flash or verify.
430             if self.option.after is None:
431                 if self.option.reset or self.option.reset is None:
432                     self.option.after = 'hard_reset'
433                 else:
434                     self.option.after = 'no_reset'
435             if self.option.verify_application:
436                 verify_after = self.option.after
437                 self.option.after = 'no_reset'
438
439             if self.flash(flash).err:
440                 return self
441             if self.option.verify_application:
442                 self.option.after = verify_after
443                 if self.verify(flash).err:
444                     return self
445
446         return self
447
448 ### Mobly integration
449 class ESP32Platform:
450   def __init__(self, flasher_args):
451       self.flasher = Flasher(**flasher_args)
452
453   def flash(self):
454       self.flasher.flash_command([os.getcwd()])
455
456 def verify_platform_args(platform_args):
457     required_args = [
458         'application',
459         'parttool',
460         'port',
461         'baud',
462         'before',
463         'after',
464         'flash_mode',
465         'flash_freq',
466         'flash_size',
467         'compress',
468         'bootloader',
469         'partition',
470         'partition_offset',
471         'application_offset',
472     ]
473     difference = set(required_args) - set(platform_args)
474     if difference:
475         raise ValueError("Required arguments missing: %s" % difference)
476
477 def create_platform(platform_args):
478     verify_platform_args(platform_args[0])
479     return ESP32Platform(platform_args[0])
480
481 ### End of Mobly integration
482
483 if __name__ == '__main__':
484     sys.exit(Flasher().flash_command(sys.argv))