cp210x.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  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 access to the EEPROM of Silabs CP210x devices
  6. The following class is available:
  7. class Cp210xProgrammer:
  8. Provides direct access to the CP210x, can be used to write single data
  9. directly or via an EEPROM image.
  10. """
  11. import usb
  12. from usb.util import CTRL_IN, CTRL_OUT, CTRL_TYPE_VENDOR
  13. __all__ = ['Cp210xProgrammer', 'Cp210xError']
  14. CP210x_CONFIG = 0xFF
  15. REG_VENDOR_ID = 0x3701
  16. REG_PRODUCT_ID = 0x3702
  17. REG_PRODUCT_STRING = 0x3703
  18. REG_SERIAL_NUMBER = 0x3704
  19. REG_CFG_ATTRIBUTES = 0x3705
  20. REG_MAX_POWER = 0x3706
  21. REG_VERSION = 0x3707
  22. REG_UNKNOWN = 0x3708
  23. REG_EEPROM = 0x3709
  24. REG_LOCK_VALUE = 0x370A
  25. REG_PART_NUMBER = 0x370B
  26. SIZE_EEPROM = 0x0400
  27. SIZE_PRODUCT_STRING = 255
  28. SIZE_SERIAL_NUMBER = 128
  29. SIZE_BAUDRATES = 32
  30. SIZE_BAUDRATE_CFG = 10
  31. SIZE_BAUDRATE_TABLE = SIZE_BAUDRATES * SIZE_BAUDRATE_CFG
  32. SIZE_VENDOR_STRING = 50
  33. # Buffer size limits
  34. # (see AN721: CP210x/CP211x Device Customization Guide)
  35. CP210x_MAX_PRODUCT_STRLEN = (SIZE_PRODUCT_STRING - 2) // 2
  36. CP210x_MAX_SERIAL_STRLEN = (SIZE_SERIAL_NUMBER - 2) // 2
  37. LCK_LOCKED = 0xF0
  38. LCK_UNLOCKED = 0xFF
  39. VID_SILABS = 0x10C4
  40. PID_CP210x = 0xEA60
  41. VALUES = [
  42. ('product_string', 'string'),
  43. ('serial_number', 'string'),
  44. ('vendor_id', 'id'),
  45. ('product_id', 'id'),
  46. ('version', 'version'),
  47. ('bus_powered', 'boolean'),
  48. ('max_power', 'int'),
  49. ('locked', 'boolean'),
  50. ('part_number', 'int'),
  51. ('vendor_string', 'string'),
  52. ('baudrate_table', 'list'),
  53. ]
  54. def to_div2(p):
  55. value = int(p / 2)
  56. if (value * 2) < p:
  57. value += 1
  58. return value
  59. def to_bcd(i):
  60. assert i >= 0 and i <= 99
  61. return (i // 10) << 4 | (i % 10)
  62. def to_bcd2( (i, j) ):
  63. return to_bcd(i) << 8 | to_bcd(j)
  64. def from_bcd(num):
  65. return num & 0x0F + (num >> 4) * 10
  66. def from_bcd2(data):
  67. return (from_bcd(data >> 8), from_bcd(data & 0xFF))
  68. def from_binary(data, le=True):
  69. value = 0
  70. if le:
  71. data = data[::-1]
  72. for byte in data:
  73. value = value << 8 | ord(byte)
  74. return value
  75. def to_binary(value, size=2, le=True):
  76. data = ''
  77. for i in range(size):
  78. data += chr(value & 0xFF)
  79. value >>= 8
  80. if le:
  81. return data
  82. else:
  83. return data[::-1]
  84. def parse_baudrate_cfg(data):
  85. return (from_binary(data[0:2], le=False),
  86. from_binary(data[2:4], le=False),
  87. from_binary(data[4:5]),
  88. from_binary(data[6:10]))
  89. def build_baudrate_cfg(baudgen, timer0reload, prescaler, baudrate):
  90. return (to_binary(baudgen, le=False) + to_binary(timer0reload, le=False) +
  91. to_binary(prescaler, 1) + '\x00' + to_binary(baudrate, 4))
  92. class Cp210xError(IOError):
  93. pass
  94. class DeviceLocked(Cp210xError):
  95. pass
  96. class Cp210xMatch(object):
  97. def __init__(self, patterns):
  98. self.patterns = patterns
  99. def __call__(self, dev):
  100. for pattern in self.patterns:
  101. for name, value in pattern.items():
  102. if getattr(dev, name) != value:
  103. return False
  104. return True
  105. return False
  106. class Cp210xProgrammer(object):
  107. """Program a Silabs CP2101, CP2102 or CP2103
  108. This module provides access to Silabs CP210x devices to set some USB
  109. descriptor fields and some USB descriptor strings.
  110. The following fields can be set:
  111. * Vendor ID
  112. * Product ID
  113. * Product String
  114. * Serial Number
  115. * Device Version
  116. * Bus Powered
  117. * max. Power consumption
  118. Either use PyUSB to find a device, and pass the usb.Device object
  119. to the constructor, or use Cp210xProgrammer.list_device() to list all
  120. devices matching certain pattern.
  121. """
  122. TIMEOUT = 300 #ms
  123. @classmethod
  124. def list_devices(self, patterns=[{ 'idVendor': VID_SILABS,
  125. 'idProduct': PID_CP210x }]):
  126. """Yields a list of devices matching certain patterns.
  127. param patterns: This must be a list of dictionaries. Each device
  128. in the usb tree is matched against all patterns in the list.
  129. All fields of the descriptors are compared with the corresponding
  130. values in the dictionary. A device matches the pattern only if all
  131. values match.
  132. For example:
  133. >> list(Cp210xProgrammer.list_device([{ 'idVendor': VID_SILABS,
  134. 'idProduct': PID_CP210x }]))
  135. [device(...)]
  136. """
  137. return usb.core.find(find_all=True,
  138. custom_match=Cp210xMatch(patterns))
  139. def __init__(self, usbdev):
  140. self.usbdev = usbdev
  141. self._locked = None
  142. try:
  143. self.has_kernel_driver = usbdev.is_kernel_driver_active(0)
  144. except NotImplementedError:
  145. self.has_kernel_driver = False
  146. if self.has_kernel_driver:
  147. cfg = usbdev.get_active_configuration()
  148. self.intf = cfg[(0,0)].bInterfaceNumber
  149. usbdev.detach_kernel_driver(self.intf)
  150. usbdev.set_configuration()
  151. def reset(self):
  152. """Force the USB stack to reset the device.
  153. Resets the device through an hard reset over the port to which the
  154. device is connected. After that happend the EEPROM content in the device
  155. is reread and the device's descriptors are the one written to it.
  156. """
  157. self.usbdev.reset()
  158. def __del__(self):
  159. if self.has_kernel_driver:
  160. self.usbdev.attach_kernel_driver(self.intf)
  161. def _set_config(self, value, index=0, data=None, request=CP210x_CONFIG):
  162. if self.get_locked():
  163. raise DeviceLocked()
  164. res = self.usbdev.ctrl_transfer(CTRL_OUT | CTRL_TYPE_VENDOR,
  165. request, value, index, data)
  166. if data is not None and res != len(data):
  167. raise Cp210xError("Short write (%d of %d bytes)"
  168. % (res, len(data)))
  169. def _set_config_string(self, value, content, max_desc_size):
  170. assert isinstance(content, basestring)
  171. encoded = content.encode('utf-16-le')
  172. desc_size = len(encoded) + 2
  173. assert desc_size <= max_desc_size
  174. self._set_config(value, data=chr(desc_size) + "\x03" + encoded)
  175. def _get_config(self, value, length, index=0, request=CP210x_CONFIG):
  176. res = self.usbdev.ctrl_transfer(CTRL_IN | CTRL_TYPE_VENDOR,
  177. request, value, index, length)
  178. return res.tostring()
  179. def _get_int8_config(self, value, index=0, request=CP210x_CONFIG):
  180. return ord(self._get_config(value, 1, index=index, request=request))
  181. def _get_int16_config(self, value, index=0, request=CP210x_CONFIG):
  182. data = self._get_config(value, 2, index=index, request=request)
  183. return ord(data[0]) << 8 | ord(data[1])
  184. def get_eeprom_content(self):
  185. """Get the entire EEPROM content as one big 1024-byte blob.
  186. """
  187. return self._get_config(REG_EEPROM, SIZE_EEPROM)
  188. def get_baudrate_content(self):
  189. """Get the baudrate table as binary data.
  190. """
  191. return self._get_config(REG_EEPROM, SIZE_BAUDRATE_TABLE)
  192. def get_baudrate_table(self):
  193. """Get the baudrate table.
  194. A list containing 4-tuples is returned.
  195. Each tuple contains the following data:
  196. * BaudGen: Value used to generate the baudrate.
  197. * Time0Reset: Value used to generate the usb timeout.
  198. * Prescaler: Used to down-scale the baudrate.
  199. * Baudrate: The baudrate which activates this entry.
  200. """
  201. data = self.get_baudrate_content()
  202. return [parse_baudrate_cfg(data[pos:pos+SIZE_BAUDRATE_CFG])
  203. for pos in range(0, SIZE_BAUDRATE_TABLE, SIZE_BAUDRATE_CFG)]
  204. def set_baudrate_table(self, baudrates):
  205. """Write the baudrate table.
  206. See get_baudrate_table() for the structure of the table.
  207. """
  208. assert len(baudrates) == SIZE_BAUDRATES
  209. self.set_baudrate_content(data=''.join(build_baudrate_cfg(*cfg)
  210. for cfg in baudrates))
  211. baudrate_table = property(get_baudrate_table, set_baudrate_table)
  212. def get_part_number(self):
  213. """Get the part number of the device.
  214. Returns: 1 for a CP2101
  215. 2 for a CP2102
  216. 3 for a CP2103
  217. """
  218. return self._get_int8_config(REG_PART_NUMBER)
  219. def get_locked(self):
  220. """Read the lock value of the device.
  221. When True is returnes no data can be written to the device.
  222. """
  223. if self._locked is None:
  224. self._locked = self._get_int8_config(REG_LOCK_VALUE) == LCK_LOCKED
  225. return self._locked
  226. def set_eeprom_content(self, content):
  227. """Write a 1024-byte blob to the EEPROM
  228. """
  229. assert len(content) == SIZE_EEPROM, ("EEPROM data must be %i bytes."
  230. % SIZE_EEPROM)
  231. assert isinstance(content, str), "EEPROM data must be string."
  232. self._set_config(REG_EEPROM, data=content)
  233. def set_product_id(self, pid):
  234. """Set the Product ID
  235. """
  236. assert pid > 0x0000 and pid < 0xFFFF
  237. self._set_config(REG_PRODUCT_ID, pid)
  238. def set_vendor_id(self, vid):
  239. """Set the Vendor ID
  240. """
  241. assert vid > 0x0000 and vid < 0xFFFF
  242. self._set_config(REG_VENDOR_ID, vid)
  243. def set_product_string(self, product_string):
  244. """Set the product string.
  245. The string will be encoded with UTF-16 and must not exceed
  246. CP210x_MAX_PRODUCT_STRLEN.
  247. For Unicode Plane 0 (BMP; code points 0-FFFF), this specifies
  248. the maximum length of the string in characters.
  249. """
  250. self._set_config_string(REG_PRODUCT_STRING, product_string,
  251. SIZE_PRODUCT_STRING)
  252. def set_serial_number(self, serial_number):
  253. """Set the serial number string.
  254. The string will be encoded with UTF-16 and must not exceed
  255. CP210x_MAX_SERIAL_STRLEN.
  256. For Unicode Plane 0 (BMP; code points 0-FFFF), this specifies
  257. the maximum length of the string in characters.
  258. """
  259. self._set_config_string(REG_SERIAL_NUMBER, serial_number,
  260. SIZE_SERIAL_NUMBER)
  261. def set_max_power(self, max_power):
  262. """Set maximum power consumption.
  263. """
  264. assert max_power >= 0 and max_power <= 500
  265. self._set_config(REG_MAX_POWER, to_div2(max_power))
  266. def set_bus_powered(self, bus_powered):
  267. """Set the bus-powered flag in the device descriptor.
  268. """
  269. if bus_powered:
  270. self._set_config(REG_CFG_ATTRIBUTES, 0xC0)
  271. else:
  272. self._set_config(REG_CFG_ATTRIBUTES, 0x80)
  273. def set_version(self, version):
  274. """Set the device version .
  275. """
  276. self._set_config(REG_VERSION, to_bcd2(version))
  277. def set_locked(self, locked):
  278. """Set the lock value of the device.
  279. When True is returned no data can be written to the device.
  280. """
  281. if locked:
  282. self._set_config(REG_LOCK_VALUE, LCK_LOCKED)
  283. else:
  284. self._set_config(REG_LOCK_VALUE, LCK_UNLOCKED)
  285. def set_values(self, values):
  286. for name, value in values.items():
  287. if name not in ['part_number', 'vendor_string']:
  288. getattr(self, "set_" + name) (value)