Skip to content

Commit 05de864

Browse files
committed
python: correct i2c detection logic
The absence of I2C functionality does not imply SMBus ability. Instead we check for required SMBus functionality and find the bus with the most SPD EEPROMs. On my system the old code hits bus 3 and stops. The new code finds bus 6 with 0x50 and bus 1 with both 0x50 and 0x51, and correctly selects bus 1.
1 parent 5c49fd3 commit 05de864

File tree

1 file changed

+64
-21
lines changed

1 file changed

+64
-21
lines changed

spd-eeprom.py

Lines changed: 64 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,17 @@
2929
sys.exit("This operating system is not supported.")
3030

3131
# To determine what functionality is present
32-
I2C_FUNC_I2C = 0x00000001
32+
I2C_FUNC_SMBUS_QUICK = 0x00010000
33+
I2C_FUNC_SMBUS_READ_BYTE = 0x00020000
34+
I2C_FUNC_SMBUS_WRITE_BYTE = 0x00040000
35+
I2C_FUNC_SMBUS_READ_BYTE_DATA = 0x00080000
36+
I2C_FUNC_SMBUS_WRITE_BYTE_DATA = 0x00100000
37+
I2C_FUNC_SMBUS_READ_WORD_DATA = 0x00200000
38+
I2C_FUNC_SMBUS_WRITE_WORD_DATA = 0x00400000
39+
40+
REQUIRED_FUNC = I2C_FUNC_SMBUS_READ_BYTE | I2C_FUNC_SMBUS_WRITE_BYTE | \
41+
I2C_FUNC_SMBUS_READ_BYTE_DATA | I2C_FUNC_SMBUS_WRITE_BYTE_DATA | \
42+
I2C_FUNC_SMBUS_QUICK
3343

3444
# Data for SMBus Messages
3545
class i2c_smbus_data(ctypes.Union):
@@ -105,6 +115,10 @@ def print_usage():
105115
print(" -w write data to SPD EEPROM (input from file)")
106116
print(" -d DIMM DIMM slot index (0 - 7)")
107117
print(" -f FILE input or output file path")
118+
print()
119+
print("Environment Variables:")
120+
print(" SPD_EEPROM_SMBUS specify the SMBus index to use (e.g. 1 for /dev/i2c-1)")
121+
print()
108122

109123
def spd_set_page(fd, page):
110124
try:
@@ -213,48 +227,75 @@ def smbus_probe(dimm_slot = None):
213227
except Exception:
214228
pass
215229

216-
smbus_idx = ""
230+
fd = None
231+
smbus_idx = None
217232

218233
files = os.listdir("/dev")
219234
files = list(filter(lambda x: x.startswith("i2c-"), files))
220235

221-
for entry in files:
222-
fd = os.open("/dev/" + entry, os.O_RDWR)
223-
if not i2c_smbus_get_funcs(fd) & I2C_FUNC_I2C:
224-
smbus_idx = entry[4:]
225-
break
226-
227-
if smbus_idx.isdigit():
228-
smbus_idx = int(smbus_idx)
236+
if os.getenv("SPD_EEPROM_SMBUS") is not None:
237+
fd, smbus_idx, n = try_smbus("i2c-" + os.getenv("SPD_EEPROM_SMBUS"), dimm_slot)
238+
if fd is None:
239+
sys.exit("Could not access SMBus %s." % os.getenv("SPD_EEPROM_SMBUS"))
240+
else:
241+
return fd, smbus_idx
229242
else:
230-
sys.exit("No SMBus adapter found.")
231-
232-
if dimm_slot == None:
233-
print("Probing for SPD EEPROM on SMBus %d" % smbus_idx)
243+
rets = [try_smbus(x, dimm_slot) for x in files]
244+
rets = list(filter(lambda x: x[0] is not None, rets))
245+
rets = sorted(rets, key=lambda x: x[2], reverse=True)
246+
if len(rets) == 0:
247+
sys.exit("No supported SMBus found.")
248+
else:
249+
fd, smbus_idx = rets[0][0], rets[0][1]
250+
print("Selecting /dev/i2c-%d by EEPROM count" % smbus_idx)
251+
return fd, smbus_idx
252+
253+
254+
def try_smbus(file: str, dimm_slot = None):
255+
fd = os.open("/dev/" + file, os.O_RDWR)
256+
smbus_name: str = file[4:]
257+
if i2c_smbus_get_funcs(fd) & REQUIRED_FUNC != REQUIRED_FUNC:
258+
print("I2C adapter %s does not support required SMBus functions." % file)
234259
print()
260+
os.close(fd)
261+
return None, None, None
235262

263+
if smbus_name.isdigit():
264+
smbus_idx: int = int(smbus_name)
265+
else:
266+
print("Invalid SMBus index: %s" % smbus_name)
267+
return None, None, None
268+
269+
print("Probing for SPD EEPROM on SMBus %d" % smbus_idx)
270+
if dimm_slot is None:
236271
eeprom = 0
237272
ee1004 = spd_set_page(fd, 0)
238273

239274
for slot in range(0, 8):
240275
try:
241276
i2c_smbus_read_byte(fd, 0x50 + slot)
242-
243277
print("DIMM slot %d: %s SPD EEPROM" % (slot, "512 Byte EE1004" if ee1004 else "256 Byte AT24"))
244-
245278
eeprom += 1
246279
except IOError:
247280
pass
248281

249282
if eeprom == 0:
250-
print("No SPD EEPROM detected.")
283+
print("No SPD EEPROM detected on SMBus %d." % smbus_idx)
284+
print()
285+
os.close(fd)
286+
return None, None, None
251287
else:
252288
try:
253289
i2c_smbus_read_byte(fd, 0x50 + dimm_slot)
290+
eeprom = 1
254291
except IOError:
255-
sys.exit("DIMM slot %d is empty." % dimm_slot)
292+
print("DIMM slot %d is empty." % dimm_slot)
293+
os.close(fd)
294+
print()
295+
return None, None, None
256296

257-
return fd, smbus_idx
297+
print()
298+
return fd, smbus_idx, eeprom
258299

259300
def main():
260301
if os.getuid():
@@ -266,7 +307,7 @@ def main():
266307
sys.exit(print_usage())
267308

268309
op_code = 0
269-
dimm_slot = ""
310+
dimm_slot = None
270311
file_path = ""
271312

272313
for opt, arg in opts:
@@ -282,7 +323,9 @@ def main():
282323
file_path = arg
283324

284325
if op_code == 1 and len(opts) == 1:
285-
smbus_probe()
326+
_, smbus_idx = smbus_probe()
327+
print("To save time for next time, you can specify the SMBus index" \
328+
" by: `export SPD_EEPROM_SMBUS=%d`." % smbus_idx)
286329
elif op_code != 0 \
287330
and len(opts) == 3 \
288331
and len(file_path) != 0 \

0 commit comments

Comments
 (0)