Skip to content

Commit 95ecdef

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 95ecdef

File tree

1 file changed

+65
-21
lines changed

1 file changed

+65
-21
lines changed

spd-eeprom.py

Lines changed: 65 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
# See the License for the specific language governing permissions and
1717
# limitations under the License.
1818

19+
from importlib.resources import files
1920
import os
2021
import sys
2122
import time
@@ -29,7 +30,17 @@
2930
sys.exit("This operating system is not supported.")
3031

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

3445
# Data for SMBus Messages
3546
class i2c_smbus_data(ctypes.Union):
@@ -105,6 +116,10 @@ def print_usage():
105116
print(" -w write data to SPD EEPROM (input from file)")
106117
print(" -d DIMM DIMM slot index (0 - 7)")
107118
print(" -f FILE input or output file path")
119+
print()
120+
print("Environment Variables:")
121+
print(" SPD_EEPROM_SMBUS specify the SMBus index to use (e.g. 1 for /dev/i2c-1)")
122+
print()
108123

109124
def spd_set_page(fd, page):
110125
try:
@@ -213,48 +228,75 @@ def smbus_probe(dimm_slot = None):
213228
except Exception:
214229
pass
215230

216-
smbus_idx = ""
231+
fd = None
232+
smbus_idx = None
217233

218234
files = os.listdir("/dev")
219235
files = list(filter(lambda x: x.startswith("i2c-"), files))
220236

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)
237+
if os.getenv("SPD_EEPROM_SMBUS") is not None:
238+
fd, smbus_idx, n = try_smbus("i2c-" + os.getenv("SPD_EEPROM_SMBUS"), dimm_slot)
239+
if fd is None:
240+
sys.exit("Could not access SMBus %s." % os.getenv("SPD_EEPROM_SMBUS"))
241+
else:
242+
return fd, smbus_idx
229243
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)
244+
rets = [try_smbus(x, dimm_slot) for x in files]
245+
rets = list(filter(lambda x: x[0] is not None, rets))
246+
rets = sorted(rets, key=lambda x: x[2], reverse=True)
247+
if len(rets) == 0:
248+
sys.exit("No supported SMBus found.")
249+
else:
250+
fd, smbus_idx = rets[0][0], rets[0][1]
251+
print("Selecting /dev/i2c-%d by EEPROM count" % smbus_idx)
252+
return fd, smbus_idx
253+
254+
255+
def try_smbus(file: str, dimm_slot = None):
256+
fd = os.open("/dev/" + file, os.O_RDWR)
257+
smbus_name: str = file[4:]
258+
if i2c_smbus_get_funcs(fd) & REQUIRED_FUNC != REQUIRED_FUNC:
259+
print("I2C adapter %s does not support required SMBus functions." % file)
234260
print()
261+
os.close(fd)
262+
return None, None, None
235263

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

239275
for slot in range(0, 8):
240276
try:
241277
i2c_smbus_read_byte(fd, 0x50 + slot)
242-
243278
print("DIMM slot %d: %s SPD EEPROM" % (slot, "512 Byte EE1004" if ee1004 else "256 Byte AT24"))
244-
245279
eeprom += 1
246280
except IOError:
247281
pass
248282

249283
if eeprom == 0:
250-
print("No SPD EEPROM detected.")
284+
print("No SPD EEPROM detected on SMBus %d." % smbus_idx)
285+
print()
286+
os.close(fd)
287+
return None, None, None
251288
else:
252289
try:
253290
i2c_smbus_read_byte(fd, 0x50 + dimm_slot)
291+
eeprom = 1
254292
except IOError:
255-
sys.exit("DIMM slot %d is empty." % dimm_slot)
293+
print("DIMM slot %d is empty." % dimm_slot)
294+
os.close(fd)
295+
print()
296+
return None, None, None
256297

257-
return fd, smbus_idx
298+
print()
299+
return fd, smbus_idx, eeprom
258300

259301
def main():
260302
if os.getuid():
@@ -266,7 +308,7 @@ def main():
266308
sys.exit(print_usage())
267309

268310
op_code = 0
269-
dimm_slot = ""
311+
dimm_slot = None
270312
file_path = ""
271313

272314
for opt, arg in opts:
@@ -282,7 +324,9 @@ def main():
282324
file_path = arg
283325

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

0 commit comments

Comments
 (0)