init_packet.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  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. from enum import Enum
  29. from nordicsemi.exceptions import *
  30. from nordicsemi.dfu.model import HexType
  31. from exceptions import KeyError
  32. import struct
  33. INIT_PACKET_USES_CRC16 = 0
  34. INIT_PACKET_USES_HASH = 1
  35. INIT_PACKET_EXT_USES_ECDS = 2
  36. class PacketField(Enum):
  37. DEVICE_TYPE = 1
  38. DEVICE_REVISION = 2
  39. APP_VERSION = 3
  40. REQUIRED_SOFTDEVICES_ARRAY = 4
  41. OPT_DATA = 5
  42. NORDIC_PROPRIETARY_OPT_DATA_EXT_PACKET_ID = 6
  43. NORDIC_PROPRIETARY_OPT_DATA_FIRMWARE_LENGTH = 7
  44. NORDIC_PROPRIETARY_OPT_DATA_FIRMWARE_HASH = 8
  45. NORDIC_PROPRIETARY_OPT_DATA_FIRMWARE_CRC16 = 9
  46. NORDIC_PROPRIETARY_OPT_DATA_INIT_PACKET_ECDS = 10
  47. NORDIC_PROPRIETARY_OPT_DATA_IS_MESH = 11
  48. NORDIC_PROPRIETARY_OPT_DATA_MESH_START_ADDR = 12
  49. NORDIC_PROPRIETARY_OPT_DATA_MESH_TYPE = 13
  50. NORDIC_PROPRIETARY_OPT_DATA_MESH_COMPANY_ID = 14
  51. NORDIC_PROPRIETARY_OPT_DATA_MESH_APPLICATION_ID = 15
  52. NORDIC_PROPRIETARY_OPT_DATA_MESH_BOOTLOADER_ID = 16
  53. class Packet(object):
  54. """
  55. Class that implements the INIT packet format.
  56. http://developer.nordicsemi.com/nRF51_SDK/doc/7.1.0/s110/html/a00065.html
  57. """
  58. UNSIGNED_SHORT = "H"
  59. UNSIGNED_INT = "I"
  60. UNSIGNED_CHAR = "B"
  61. CHAR_ARRAY = "s"
  62. def __init__(self, init_packet_fields):
  63. """
  64. :param init_packet_fields: Dictionary with packet fields
  65. """
  66. self.init_packet_fields = init_packet_fields
  67. def generate_packet(self):
  68. """
  69. Generates a binary packet from provided init_packet_fields provided in constructor.
  70. This version includes the extended data
  71. :return str: Returns a string representing the init_packet (in binary)
  72. """
  73. # Create struct format string based on keys that are
  74. # present in self.init_packet_fields
  75. format_string = self.__generate_struct_format_string()
  76. args = []
  77. for key in sorted(self.init_packet_fields.keys(), key=lambda x: x.value):
  78. # Add length to fields that required that
  79. if key in [PacketField.REQUIRED_SOFTDEVICES_ARRAY,
  80. PacketField.OPT_DATA]:
  81. args.append(len(self.init_packet_fields[key]))
  82. args.extend(self.init_packet_fields[key])
  83. else:
  84. args.append(self.init_packet_fields[key])
  85. return struct.pack(format_string, *args)
  86. def __generate_struct_format_string(self):
  87. format_string = "<" # Use little endian format with standard sizes for python,
  88. # see https://docs.python.org/2/library/struct.html
  89. for key in sorted(self.init_packet_fields.keys(), key=lambda x: x.value):
  90. if key in [PacketField.DEVICE_TYPE,
  91. PacketField.DEVICE_REVISION,
  92. ]:
  93. format_string += Packet.UNSIGNED_SHORT
  94. elif key in [PacketField.APP_VERSION]:
  95. format_string += Packet.UNSIGNED_INT
  96. elif key in [PacketField.REQUIRED_SOFTDEVICES_ARRAY]:
  97. array_elements = self.init_packet_fields[key]
  98. format_string += Packet.UNSIGNED_SHORT # Add length field to format packet
  99. for _ in range(len(array_elements)):
  100. format_string += Packet.UNSIGNED_SHORT
  101. elif key in [PacketField.OPT_DATA]:
  102. format_string += Packet.UNSIGNED_SHORT # Add length field to optional data
  103. format_string += "{0}{1}".format(len(self.init_packet_fields[key]), Packet.CHAR_ARRAY)
  104. elif key in [PacketField.NORDIC_PROPRIETARY_OPT_DATA_EXT_PACKET_ID]:
  105. format_string += Packet.UNSIGNED_INT # Add the extended packet id field
  106. elif key == PacketField.NORDIC_PROPRIETARY_OPT_DATA_FIRMWARE_LENGTH:
  107. format_string += Packet.UNSIGNED_INT # Add the firmware length field
  108. elif key == PacketField.NORDIC_PROPRIETARY_OPT_DATA_FIRMWARE_HASH:
  109. format_string += "32{0}".format(Packet.CHAR_ARRAY) # SHA-256 requires 32 bytes
  110. elif key == PacketField.NORDIC_PROPRIETARY_OPT_DATA_FIRMWARE_CRC16:
  111. format_string += Packet.UNSIGNED_SHORT
  112. elif key == PacketField.NORDIC_PROPRIETARY_OPT_DATA_INIT_PACKET_ECDS:
  113. format_string += "64{0}".format(Packet.CHAR_ARRAY) # ECDS based on P-256 using SHA-256 requires 64 bytes
  114. return format_string
  115. class PacketMesh(object):
  116. """
  117. Class that implements the INIT packet for the mesh.
  118. """
  119. UNSIGNED_SHORT = "H"
  120. UNSIGNED_INT = "I"
  121. UNSIGNED_CHAR = "B"
  122. CHAR_ARRAY = "s"
  123. def __init__(self, init_packet_fields):
  124. """
  125. :param init_packet_fields: Dictionary with packet fields
  126. """
  127. self.init_packet_fields = init_packet_fields
  128. def generate_packet(self):
  129. """
  130. Generates a binary packet from provided init_packet_fields provided in constructor.
  131. :return str: Returns a string representing the init_packet (in binary)
  132. """
  133. try:
  134. packet_elems = [self.init_packet_fields[PacketField.NORDIC_PROPRIETARY_OPT_DATA_MESH_TYPE],
  135. self.init_packet_fields[PacketField.NORDIC_PROPRIETARY_OPT_DATA_MESH_START_ADDR],
  136. self.init_packet_fields[PacketField.NORDIC_PROPRIETARY_OPT_DATA_FIRMWARE_LENGTH]]
  137. format_string = "<BII"
  138. if PacketField.NORDIC_PROPRIETARY_OPT_DATA_INIT_PACKET_ECDS in self.init_packet_fields.keys():
  139. format_string += "B64s"
  140. packet_elems.append(64)
  141. packet_elems.append(self.init_packet_fields[PacketField.NORDIC_PROPRIETARY_OPT_DATA_INIT_PACKET_ECDS])
  142. else:
  143. format_string += "B"
  144. packet_elems.append(0)
  145. dfu_type = self.init_packet_fields[PacketField.NORDIC_PROPRIETARY_OPT_DATA_MESH_TYPE]
  146. if dfu_type is HexType.SOFTDEVICE:
  147. format_string += "H"
  148. if (self.init_packet_fields[PacketField.REQUIRED_SOFTDEVICES_ARRAY] and
  149. len(self.init_packet_fields[PacketField.REQUIRED_SOFTDEVICES_ARRAY])):
  150. packet_elems.append(self.init_packet_fields[PacketField.REQUIRED_SOFTDEVICES_ARRAY][0])
  151. else:
  152. packet_elems.append(0xFFFF) # no SD required
  153. elif dfu_type is HexType.BOOTLOADER:
  154. format_string += "H"
  155. packet_elems.append(self.init_packet_fields[PacketField.NORDIC_PROPRIETARY_OPT_DATA_MESH_BOOTLOADER_ID])
  156. elif dfu_type is HexType.APPLICATION:
  157. format_string += "IHI"
  158. packet_elems.append(self.init_packet_fields[PacketField.NORDIC_PROPRIETARY_OPT_DATA_MESH_COMPANY_ID])
  159. packet_elems.append(self.init_packet_fields[PacketField.NORDIC_PROPRIETARY_OPT_DATA_MESH_APPLICATION_ID])
  160. packet_elems.append(self.init_packet_fields[PacketField.APP_VERSION])
  161. except KeyError, e:
  162. raise NordicSemiException("A field required for generating a mesh package was omitted: {0}".format(e.message))
  163. return struct.pack(format_string, *packet_elems)