manifest.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. # Copyright (c) 2015, Nordic Semiconductor
  2. # All rights reserved.
  3. #
  4. # Redistribution and use in source and binary forms, with or without
  5. # modification, are permitted provided that the following conditions are met:
  6. #
  7. # * Redistributions of source code must retain the above copyright notice, this
  8. # list of conditions and the following disclaimer.
  9. #
  10. # * Redistributions in binary form must reproduce the above copyright notice,
  11. # this list of conditions and the following disclaimer in the documentation
  12. # and/or other materials provided with the distribution.
  13. #
  14. # * Neither the name of Nordic Semiconductor ASA nor the names of its
  15. # contributors may be used to endorse or promote products derived from
  16. # this software without specific prior written permission.
  17. #
  18. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  19. # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  20. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  22. # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  23. # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  24. # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  25. # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  26. # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  27. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. # Python libraries
  29. import json
  30. import binascii
  31. import os
  32. # Nordic libraries
  33. from nordicsemi.exceptions import NotImplementedException
  34. from nordicsemi.dfu.init_packet import PacketField
  35. from nordicsemi.dfu.model import HexType, FirmwareKeys
  36. class ManifestGenerator(object):
  37. def __init__(self, dfu_version, firmwares_data):
  38. """
  39. The Manifest Generator constructor. Needs a data structure to generate a manifest from.
  40. :type float dfu_version: The dfu version number to state in manifest
  41. :type dict firmwares_data: The firmwares data structure describing the Nordic DFU package
  42. """
  43. self.dfu_version = dfu_version
  44. self.firmwares_data = firmwares_data
  45. self.manifest = None
  46. def generate_manifest(self):
  47. self.manifest = Manifest()
  48. self.manifest.dfu_version = self.dfu_version
  49. for key in self.firmwares_data:
  50. firmware_dict = self.firmwares_data[key]
  51. if key == HexType.SD_BL:
  52. _firmware = SoftdeviceBootloaderFirmware()
  53. _firmware.bl_size = firmware_dict[FirmwareKeys.BL_SIZE]
  54. _firmware.sd_size = firmware_dict[FirmwareKeys.SD_SIZE]
  55. else:
  56. _firmware = Firmware()
  57. # Strip path, add only filename
  58. _firmware.bin_file = os.path.basename(firmware_dict[FirmwareKeys.BIN_FILENAME])
  59. _firmware.dat_file = os.path.basename(firmware_dict[FirmwareKeys.DAT_FILENAME])
  60. init_packet_data = InitPacketData()
  61. for init_packet_data_key in firmware_dict[FirmwareKeys.INIT_PACKET_DATA]:
  62. field = firmware_dict[FirmwareKeys.INIT_PACKET_DATA][init_packet_data_key]
  63. if init_packet_data_key == PacketField.APP_VERSION:
  64. init_packet_data.application_version = field
  65. elif init_packet_data_key == PacketField.DEVICE_TYPE:
  66. init_packet_data.device_type = field
  67. elif init_packet_data_key == PacketField.DEVICE_REVISION:
  68. init_packet_data.device_revision = field
  69. elif init_packet_data_key == PacketField.REQUIRED_SOFTDEVICES_ARRAY:
  70. init_packet_data.softdevice_req = field
  71. elif init_packet_data_key == PacketField.NORDIC_PROPRIETARY_OPT_DATA_EXT_PACKET_ID:
  72. init_packet_data.ext_packet_id = field
  73. elif init_packet_data_key == PacketField.NORDIC_PROPRIETARY_OPT_DATA_FIRMWARE_LENGTH:
  74. init_packet_data.firmware_length = field
  75. elif init_packet_data_key == PacketField.NORDIC_PROPRIETARY_OPT_DATA_FIRMWARE_HASH:
  76. init_packet_data.firmware_hash = binascii.hexlify(field)
  77. elif init_packet_data_key == PacketField.NORDIC_PROPRIETARY_OPT_DATA_FIRMWARE_CRC16:
  78. init_packet_data.firmware_crc16 = field
  79. elif init_packet_data_key == PacketField.NORDIC_PROPRIETARY_OPT_DATA_INIT_PACKET_ECDS:
  80. init_packet_data.init_packet_ecds = binascii.hexlify(field)
  81. # mesh specific:
  82. elif init_packet_data_key == PacketField.NORDIC_PROPRIETARY_OPT_DATA_MESH_TYPE:
  83. pass #handled below
  84. elif init_packet_data_key == PacketField.NORDIC_PROPRIETARY_OPT_DATA_MESH_COMPANY_ID:
  85. init_packet_data.company_id = field
  86. elif init_packet_data_key == PacketField.NORDIC_PROPRIETARY_OPT_DATA_MESH_APPLICATION_ID:
  87. init_packet_data.application_id = field
  88. elif init_packet_data_key == PacketField.NORDIC_PROPRIETARY_OPT_DATA_MESH_BOOTLOADER_ID:
  89. init_packet_data.bootloader_id = field
  90. elif init_packet_data_key == PacketField.NORDIC_PROPRIETARY_OPT_DATA_MESH_START_ADDR:
  91. init_packet_data.start_addr = field
  92. elif init_packet_data_key == PacketField.NORDIC_PROPRIETARY_OPT_DATA_IS_MESH:
  93. pass #redundant in this context
  94. else:
  95. raise NotImplementedException(
  96. "Support for init packet data type {0} not implemented yet.".format(init_packet_data_key))
  97. _firmware.init_packet_data = init_packet_data
  98. if key == HexType.APPLICATION:
  99. self.manifest.application = _firmware
  100. elif key == HexType.BOOTLOADER:
  101. self.manifest.bootloader = _firmware
  102. elif key == HexType.SOFTDEVICE:
  103. self.manifest.softdevice = _firmware
  104. elif key == HexType.SD_BL:
  105. self.manifest.softdevice_bootloader = _firmware
  106. else:
  107. raise NotImplementedException("Support for firmware type {0} not implemented yet.".format(key))
  108. return self.to_json()
  109. def to_json(self):
  110. def remove_none_entries(d):
  111. if not isinstance(d, dict):
  112. return d
  113. return dict((k, remove_none_entries(v)) for k, v in d.iteritems() if v is not None)
  114. return json.dumps({'manifest': self.manifest},
  115. default=lambda o: remove_none_entries(o.__dict__),
  116. sort_keys=True, indent=4,
  117. separators=(',', ': '))
  118. class InitPacketData(object):
  119. def __init__(self,
  120. device_type=None,
  121. device_revision=None,
  122. company_id=None,
  123. application_id=None,
  124. application_version=None,
  125. bootloader_id=None,
  126. softdevice_req=None,
  127. ext_packet_id=None,
  128. firmware_length=None,
  129. firmware_hash=None,
  130. firmware_crc16=None,
  131. init_packet_ecds=None,
  132. start_addr=None
  133. ):
  134. """
  135. The InitPacketData data model.
  136. :param int device_type: device type
  137. :param int device_revision: device revision
  138. :param int company_id: company ID for mesh application
  139. :param int application_id: application ID for mesh application
  140. :param int application_version: application version
  141. :param int bootloader_id: bootloader ID for mesh
  142. :param list softdevice_req: softdevice requirements
  143. :param int ext_packet_id: packet extension id
  144. :param int firmware_length: firmware length
  145. :param str firmware_hash: firmware hash
  146. :param int firmware_crc16: firmware CRC-16 calculated value
  147. :param str init_packet_ecds: Init packet signature
  148. :param int start_addr: start address for mesh
  149. :return: InitPacketData
  150. """
  151. self.device_type = device_type
  152. self.device_revision = device_revision
  153. self.company_id = company_id
  154. self.application_id = application_id
  155. self.application_version = application_version
  156. self.bootloader_id = bootloader_id
  157. self.softdevice_req = softdevice_req
  158. self.ext_packet_id = ext_packet_id
  159. self.firmware_length = firmware_length
  160. self.firmware_hash = firmware_hash
  161. self.firmware_crc16 = firmware_crc16
  162. self.init_packet_ecds = init_packet_ecds
  163. self.start_addr = start_addr
  164. class Firmware(object):
  165. def __init__(self,
  166. bin_file=None,
  167. dat_file=None,
  168. init_packet_data=None):
  169. """
  170. The firmware datamodel
  171. :param str bin_file: Firmware binary file
  172. :param str dat_file: Firmware .dat file (init packet for Nordic DFU)
  173. :param dict init_packet_data: Initial packet data
  174. :return:
  175. """
  176. self.dat_file = dat_file
  177. self.bin_file = bin_file
  178. if init_packet_data:
  179. self.init_packet_data = InitPacketData(**init_packet_data)
  180. class SoftdeviceBootloaderFirmware(Firmware):
  181. def __init__(self,
  182. bin_file=None,
  183. dat_file=None,
  184. init_packet_data=None,
  185. sd_size=None,
  186. bl_size=None):
  187. """
  188. The SoftdeviceBootloaderFirmware data model
  189. :param str bin_file: Firmware binary file
  190. :param str dat_file: Firmware .dat file (init packet for Nordic DFU)
  191. :param int sd_size: The softdevice size
  192. :param int bl_size: The bootloader size
  193. :return: SoftdeviceBootloaderFirmware
  194. """
  195. super(SoftdeviceBootloaderFirmware, self).__init__(
  196. bin_file,
  197. dat_file,
  198. init_packet_data)
  199. self.sd_size = sd_size
  200. self.bl_size = bl_size
  201. class Manifest:
  202. def __init__(self,
  203. application=None,
  204. bootloader=None,
  205. softdevice=None,
  206. softdevice_bootloader=None,
  207. dfu_version=None):
  208. """
  209. The Manifest data model.
  210. :param dict application: Application firmware in package
  211. :param dict bootloader: Bootloader firmware in package
  212. :param dict softdevice: Softdevice firmware in package
  213. :param dict softdevice_bootloader: Combined softdevice and bootloader firmware in package
  214. :return: Manifest
  215. """
  216. self.softdevice_bootloader = \
  217. SoftdeviceBootloaderFirmware(**softdevice_bootloader) if softdevice_bootloader else None
  218. self.softdevice = Firmware(**softdevice) if softdevice else None
  219. self.bootloader = Firmware(**bootloader) if bootloader else None
  220. self.application = Firmware(**application) if application else None
  221. self.dfu_version = dfu_version
  222. @staticmethod
  223. def from_json(data):
  224. """
  225. Parses a manifest according to Nordic DFU package specification.
  226. :param str data: The manifest in string format
  227. :return: Manifest
  228. """
  229. kwargs = json.loads(data)
  230. return Manifest(**kwargs['manifest'])