Skip to content

Commit 9a31961

Browse files
authored
Bugfix for checksum calculations
1 parent 2ea0f8b commit 9a31961

File tree

6 files changed

+63
-29
lines changed

6 files changed

+63
-29
lines changed

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html),
77
as of version 2.1.1.
88

9+
## [3.0.1] - 2022-05-11
10+
### Fixed
11+
- Fixed checksum calculation on Linux.
12+
913
## [3.0.0] - 2022-04-09
1014
### Added
1115
- Added support for python 3.10.
@@ -70,7 +74,8 @@ as of version 2.1.1.
7074
- Added a changelog.
7175

7276

73-
[Unreleased]: https://github.com/newAM/monitorcontrol/compare/3.0.0...HEAD
77+
[Unreleased]: https://github.com/newAM/monitorcontrol/compare/3.0.1...HEAD
78+
[3.0.1]: https://github.com/newAM/monitorcontrol/compare/3.0.0...3.0.1
7479
[3.0.0]: https://github.com/newAM/monitorcontrol/compare/2.5.1...3.0.0
7580
[2.5.1]: https://github.com/newAM/monitorcontrol/compare/2.5.0...2.5.1
7681
[2.5.0]: https://github.com/newAM/monitorcontrol/compare/2.4.2...2.5.0

flake.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

monitorcontrol/vcp/vcp_linux.py

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ class LinuxVCP(VCP):
3838

3939
# addresses
4040
DDCCI_ADDR = 0x37 # DDC-CI command address on the I2C bus
41-
HOST_ADDRESS = 0x50 # virtual I2C slave address of the host
41+
HOST_ADDRESS = 0x51 # virtual I2C slave address of the host
4242
I2C_SLAVE = 0x0703 # I2C bus slave address
4343

4444
GET_VCP_RESULT_CODES = {
@@ -110,9 +110,12 @@ def set_vcp_feature(self, code: int, value: int):
110110
# add headers and footers
111111
data.insert(0, (len(data) | self.PROTOCOL_FLAG))
112112
data.insert(0, self.HOST_ADDRESS)
113-
data.append(self.get_checksum(data))
113+
data.append(
114+
self.get_checksum(bytearray([self.DDCCI_ADDR << 1]) + data)
115+
)
114116

115117
# write data
118+
self.logger.debug("data=" + " ".join([f"{x:02X}" for x in data]))
116119
self.write_bytes(data)
117120

118121
# store time of last set VCP
@@ -141,24 +144,26 @@ def get_vcp_feature(self, code: int) -> Tuple[int, int]:
141144
# add headers and footers
142145
data.insert(0, (len(data) | self.PROTOCOL_FLAG))
143146
data.insert(0, self.HOST_ADDRESS)
144-
data.append(self.get_checksum(data))
145-
self.logger.debug(f"data={data}")
147+
data.append(
148+
self.get_checksum(bytearray([self.DDCCI_ADDR << 1]) + data)
149+
)
146150

147151
# write data
152+
self.logger.debug("data=" + " ".join([f"{x:02X}" for x in data]))
148153
self.write_bytes(data)
149154

150155
time.sleep(self.GET_VCP_TIMEOUT)
151156

152157
# read the data
153158
header = self.read_bytes(self.GET_VCP_HEADER_LENGTH)
154-
self.logger.debug(f"header={header}")
155-
source, length = struct.unpack("BB", header)
159+
self.logger.debug("header=" + " ".join([f"{x:02X}" for x in header]))
160+
source, length = struct.unpack("=BB", header)
156161
length &= ~self.PROTOCOL_FLAG # clear protocol flag
157162
payload = self.read_bytes(length + 1)
158-
self.logger.debug(f"payload={payload}")
163+
self.logger.debug("payload=" + " ".join([f"{x:02X}" for x in payload]))
159164

160165
# check checksum
161-
payload, checksum = struct.unpack(f"{length}sB", payload)
166+
payload, checksum = struct.unpack(f"={length}sB", payload)
162167
calculated_checksum = self.get_checksum(header + payload)
163168
checksum_xor = checksum ^ calculated_checksum
164169
if checksum_xor:
@@ -245,11 +250,15 @@ def get_vcp_capabilities(self):
245250

246251
# read the data
247252
header = self.read_bytes(self.GET_VCP_HEADER_LENGTH)
248-
self.logger.debug(f"response header={header}")
253+
self.logger.debug(
254+
"header=" + " ".join([f"{x:02X}" for x in header])
255+
)
249256
source, length = struct.unpack("BB", header)
250257
length &= ~self.PROTOCOL_FLAG # clear protocol flag
251258
payload = self.read_bytes(length + 1)
252-
self.logger.debug(f"payload={payload}")
259+
self.logger.debug(
260+
"payload=" + " ".join([f"{x:02X}" for x in payload])
261+
)
253262

254263
# check if length is valid
255264
if length < 3 or length > 35:
@@ -298,19 +307,19 @@ def get_vcp_capabilities(self):
298307

299308
return caps_str
300309

301-
def get_checksum(self, data: List, prime: bool = False) -> int:
310+
@staticmethod
311+
def get_checksum(data: bytearray) -> int:
302312
"""
303313
Computes the checksum for a set of data, with the option to
304314
use the virtual host address (per the DDC-CI specification).
305315
306316
Args:
307-
data: data array to transmit
308-
prime: compute checksum using the 0x50 virtual host address
317+
data: Data array to transmit.
309318
310319
Returns:
311-
checksum for the data
320+
Checksum for the data.
312321
"""
313-
checksum = self.HOST_ADDRESS
322+
checksum = 0x00
314323
for data_byte in data:
315324
checksum ^= data_byte
316325
return checksum

poetry.lock

Lines changed: 9 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ target-version = ["py36", "py37", "py38"]
55
[tool.poetry]
66
name = "monitorcontrol"
77
description = "Monitor controls using MCCS over DDC-CI."
8-
version = "3.0.0"
8+
version = "3.0.1"
99
authors = ["Alex Martens <alex@thinglab.org>"]
1010
license = "MIT"
1111
readme = "README.rst"

tests/test_linux_vcp.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import pytest
2+
from monitorcontrol.vcp.vcp_linux import LinuxVCP
3+
4+
5+
@pytest.mark.parametrize(
6+
"data, checksum",
7+
[
8+
(bytearray([0x6E, 0x51, 0x82, 0x01, 0x10]), 0xAC),
9+
(bytearray([0xF0, 0xF1, 0x81, 0xB1]), 0x31),
10+
(bytearray([0x6E, 0xF1, 0x81, 0xB1]), 0xAF),
11+
],
12+
)
13+
def test_get_checksum(data: bytearray, checksum: int):
14+
computed = LinuxVCP.get_checksum(data)
15+
xor = checksum ^ computed
16+
assert computed == checksum, (
17+
f"computed=0x{computed:02X} 0b{computed:08b} "
18+
f"checksum=0x{checksum:02X} 0b{checksum:08b} "
19+
f"xor=0x{xor:02X} 0b{xor:08b}"
20+
)

0 commit comments

Comments
 (0)