eeprom.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. # -*- coding: utf-8 -*-
  2. # Copyright (c) 2007 Johannes Hölzl <johannes.hoelzl@gmx.de>
  3. #
  4. # This library is covered by the GNU LGPL, read LICENSE for details.
  5. """Provides logical view of the EEPROM of Silabs CP210x devices
  6. The following class is available:
  7. class EEPROM:
  8. Can be used to read or write a hex file containing the EEPROM content
  9. of a CP210x. Also provides access to single fields in the EEPROM.
  10. """
  11. import cp210x
  12. from cp210x import from_binary, to_binary, VALUES
  13. __all__ = ['EEPROM', 'HexFileError']
  14. POS_BAUDRATE_TABLE = 0x0000
  15. POS_PART_NUMBER = 0x01FF
  16. POS_STRING_DESC_0 = 0x0200
  17. POS_PRODUCT_STRING = 0x0208
  18. POS_SERIAL_NUMBER = 0x0307
  19. POS_VENDOR_ID = 0x0390
  20. POS_PRODUCT_ID = 0x0392
  21. POS_VERSION = 0x0394
  22. POS_CFG_ATTRIBUTES = 0x03A1
  23. POS_MAX_POWER = 0x03A2
  24. POS_VENDOR_STRING = 0x03C3
  25. POS_LOCK_VALUE = 0x03FF
  26. class HexFileError(StandardError):
  27. pass
  28. def checksum(line):
  29. return sum(ord(c) for c in line) & 0xFF
  30. def _int_value(position, size, read=lambda x:x, write=lambda x:x):
  31. def get(self):
  32. return read(from_binary(self.get(position, size)))
  33. def set(self, value):
  34. self.set(position, to_binary(write(value), size))
  35. return property(get, set)
  36. def _str_value(position, max_desc_size):
  37. def get(self):
  38. desc_size = from_binary(self.get(position, 1))
  39. assert desc_size <= max_desc_size and desc_size >= 2, "desc_size: %d, max: %d" % (desc_size, max_desc_size)
  40. assert self.get(position + 1, 1) == '\x03', "Missing 0x03 at %04X" % (position + 1)
  41. return self.get(position + 2, desc_size - 2).decode('utf-16-le')
  42. def set(self, value):
  43. encoded = value.encode('utf-16-le')
  44. desc_size = len(encoded) + 2
  45. assert desc_size <= max_desc_size
  46. self.set(position, chr(desc_size) + '\x03' + encoded)
  47. return property(get, set)
  48. class EEPROM(object):
  49. START_ADDRESS = 0x3600
  50. def __init__(self, content=None):
  51. if isinstance(content, str) or content is None:
  52. assert content is None or len(content) == cp210x.SIZE_EEPROM
  53. self.content = content
  54. elif isinstance(content, cp210x.Cp210xProgrammer):
  55. self.content = content.get_eeprom_content()
  56. else:
  57. self.parse_hex_file(content.read())
  58. def write_to_cp210x(self, cp210xDevice):
  59. cp210xDevice.set_eeprom_content(self.content)
  60. def parse_hex_file(self, hex_content):
  61. self.content = ''
  62. address = self.START_ADDRESS
  63. for tag in hex_content.split('\n'):
  64. if not tag.startswith(':'):
  65. raise HexFileError("Line doesn't start with ':'")
  66. try:
  67. content = tag[1:].decode('hex')
  68. except TypeError:
  69. raise HexFileError("Hex data expected")
  70. if len(content) < 5:
  71. raise HexFileError("Line too short")
  72. if checksum(content) != 0:
  73. raise HexFileError("Checksum error")
  74. size = from_binary(content[0])
  75. tag_address = from_binary(content[1:3], le=False)
  76. tag_type = from_binary(content[3:4])
  77. line = content[4:-1]
  78. if tag_type == 0x00:
  79. if tag_address != address:
  80. raise HexFileError("Expected address %04X but found %04X"
  81. % (address, tag_address))
  82. self.content += line
  83. address += len(line)
  84. elif tag_type == 0x01:
  85. if size != 0 or len(line) != 0:
  86. raise HexFileError("Defect end tag")
  87. break
  88. else:
  89. raise HexFileError("Unknown tag type %02X" % tag_type)
  90. def build_hex_file(self):
  91. for tag_start in range(0, len(self.content), 0x10):
  92. line = self.content[tag_start:tag_start+0x10]
  93. address = self.START_ADDRESS + tag_start
  94. tag = (to_binary(len(line), 1) +
  95. to_binary(address, le=False) +
  96. '\x00' +
  97. line)
  98. cs = checksum(tag)
  99. if cs == 0:
  100. tag += '\x00'
  101. else:
  102. tag += chr(0x100 - cs)
  103. yield ":%s\n" % tag.encode('hex')
  104. yield ":00000001FF\n"
  105. def write_hex_file(self, f):
  106. if isinstance(f, str):
  107. f = file(f, 'wb')
  108. do_close = True
  109. else:
  110. do_close = False
  111. for line in self.build_hex_file():
  112. f.write(line)
  113. if do_close:
  114. f.close()
  115. def read_hex_file(self, f):
  116. if isinstance(f, str):
  117. f = file(f, 'rb')
  118. do_close = True
  119. else:
  120. do_close = False
  121. self.parse_hex_file(f.read())
  122. if do_close:
  123. f.close()
  124. def get(self, pos, length):
  125. return self.content[pos:pos+length]
  126. def set(self, pos, data):
  127. self.content = (self.content[:pos] +
  128. data +
  129. self.content[pos + len(data):])
  130. def _get_baudrate_table(self):
  131. dat = self.get(POS_BAUDRATE_TABLE, cp210x.SIZE_BAUDRATE_TABLE)
  132. return [cp210x.parse_baudrate_cfg(dat[pos:pos+cp210x.SIZE_BAUDRATE_CFG])
  133. for pos in range(0, cp210x.SIZE_BAUDRATE_TABLE,
  134. cp210x.SIZE_BAUDRATE_CFG)]
  135. def _set_baudrate_table(self, baudrates):
  136. assert len(baudrates) == cp210x.SIZE_BAUDRATES
  137. self.set(POS_BAUDRATE_TABLE,
  138. ''.join(cp210x.build_baudrate_cfg(*cfg) for cfg in baudrates))
  139. baudrate_table = property(_get_baudrate_table, _set_baudrate_table)
  140. product_string = _str_value(POS_PRODUCT_STRING, cp210x.SIZE_PRODUCT_STRING)
  141. serial_number = _str_value(POS_SERIAL_NUMBER, cp210x.SIZE_SERIAL_NUMBER)
  142. part_number = _int_value(POS_PART_NUMBER, 1)
  143. vendor_id = _int_value(POS_VENDOR_ID, 2)
  144. product_id = _int_value(POS_PRODUCT_ID, 2)
  145. version = _int_value(POS_VERSION, 2,
  146. cp210x.from_bcd2, cp210x.to_bcd2)
  147. bus_powered = _int_value(POS_CFG_ATTRIBUTES, 1,
  148. lambda a: bool(a & 0x40),
  149. lambda a: 0xC0 if a else 0x80)
  150. max_power = _int_value(POS_MAX_POWER, 1, lambda p: p*2, cp210x.to_div2)
  151. vendor_string = _str_value(POS_VENDOR_STRING, cp210x.SIZE_VENDOR_STRING)
  152. locked = _int_value(POS_LOCK_VALUE, 1,
  153. lambda l: l == cp210x.LCK_LOCKED,
  154. lambda b: cp210x.LCK_LOCKED if b
  155. else cp210x.LCK_UNLOCKED)
  156. def get_values(self):
  157. return dict((name, getattr(self, name)) for name, type in VALUES)
  158. def set_values(self, values):
  159. for name, value in values.items():
  160. setattr(self, name, value)