Skip to content
View in the app

A better way to browse. Learn more.

Unraid

A full-screen app on your home screen with push notifications, badges and more.

To install this app on iOS and iPadOS
  1. Tap the Share icon in Safari
  2. Scroll the menu and tap Add to Home Screen.
  3. Tap Add in the top-right corner.
To install this app on Android
  1. Tap the 3-dot menu (⋮) in the top-right corner of the browser.
  2. Tap Add to Home screen or Install app.
  3. Confirm by tapping Install.

salsify

Members
  • Joined

  • Last visited

  1. Thanks for the feedback @blucobalt! I set up a 92 mm fan below mine with https://makerworld.com/en/models/393956-single-92mm-fan-pcie-mount#profileId-295072 It's not perfectly aligned with the LSI's heatsink but it helped the temperature go from ~ 90 °C to ~ 50 °C.
  2. salsify changed their profile photo
  3. Hello, If it helps porting to a plugin or the base OS, the following script reads controller temperatures directly via ioctl without needing lsiutil or storcli. It talks to the device node created by the mpt3sas kernel module. I could only test with mpt3sas (9305-16i) as I don't have hardware using the older mpt/mpt2 drivers. Created with the help of generative AI, use at your own risk! #!/usr/bin/env python3 """Read LSI SAS IOC temperature directly via ioctl (no lsiutil needed).""" import ctypes import fcntl import os import struct import sys # MPI constants MPI_FUNCTION_CONFIG = 0x04 MPI_CONFIG_ACTION_PAGE_HEADER = 0x00 MPI_CONFIG_ACTION_PAGE_READ_CURRENT = 0x01 MPI_CONFIG_PAGETYPE_IO_UNIT = 0x00 IO_UNIT_PAGE_NUMBER = 7 # Temperature unit constants TEMP_NOT_PRESENT = 0x00 TEMP_FAHRENHEIT = 0x01 TEMP_CELSIUS = 0x02 # ioctl numbers: _IOC(_IOC_READ|_IOC_WRITE, magic, nr, size) # mptctl uses magic 'm' (0x6D), mpt2/3 use 'L' (0x4C), both nr=20 # sizeof(struct mpt_ioctl_command) = 72 on 64-bit (same for mpt2/mpt3 variants) STRUCT_SIZE = 72 DEVICES = [ ("/dev/mpt3ctl", ord("L"), 20), ("/dev/mpt2ctl", ord("L"), 20), ("/dev/mptctl", ord("m"), 20), ] def _ioctl_number(magic, nr): """Compute ioctl request number: _IOWR(magic, nr, struct_size).""" # _IOC(direction=3, type, nr, size) on Linux: # direction bits [31:30], size [29:16], type [15:8], nr [7:0] return (3 << 30) | (STRUCT_SIZE << 16) | (magic << 8) | nr def _build_config_msg(action, page_length=0, page_number=IO_UNIT_PAGE_NUMBER, page_type=MPI_CONFIG_PAGETYPE_IO_UNIT): """Build a Config request message (28 bytes, no SGE).""" # Config_t layout (28 bytes): # Action(1) AliasIndex(1) ChainOffset(1) Function(1) # ExtPageLength(2) ExtPageType(1) MsgFlags(1) # MsgContext(4) # Reserved2(8) # Header: PageVersion(1) PageLength(1) PageNumber(1) PageType(1) # PageAddress(4) msg = struct.pack( "<BBBB HBB I 8s BBBB I", action, # Action 0, # AliasIndex 0, # ChainOffset MPI_FUNCTION_CONFIG, # Function 0, # ExtPageLength 0, # ExtPageType 0, # MsgFlags 0, # MsgContext b"\x00" * 8, # Reserved2 0, # Header.PageVersion page_length, # Header.PageLength (in dwords) page_number, # Header.PageNumber page_type, # Header.PageType 0, # PageAddress ) return msg def _do_config_ioctl(fd, ioctl_nr, iocnum, action, page_length=0, data_in_size=0): """Issue an MPTCOMMAND config ioctl. Returns (reply_bytes, data_in_bytes).""" mf = _build_config_msg(action, page_length=page_length) reply_buf = ctypes.create_string_buffer(128) data_in_buf = ctypes.create_string_buffer(data_in_size) if data_in_size else None # Build mpt_ioctl_command struct (manual packing for 64-bit): # hdr.iocnum(4) hdr.port(4) hdr.maxDataSize(4) timeout(4) # replyFrameBufPtr(8) dataInBufPtr(8) dataOutBufPtr(8) senseDataPtr(8) # maxReplyBytes(4) dataInSize(4) dataOutSize(4) maxSenseBytes(4) # dataSgeOffset(4) mf[28] data_in_ptr = ctypes.addressof(data_in_buf) if data_in_buf else 0 cmd = struct.pack( "<IIII QQQQ IIIII", iocnum, # hdr.iocnum 0, # hdr.port 0, # hdr.maxDataSize 10, # timeout (seconds) ctypes.addressof(reply_buf), # replyFrameBufPtr data_in_ptr, # dataInBufPtr 0, # dataOutBufPtr (NULL - no writes!) 0, # senseDataPtr 128, # maxReplyBytes data_in_size, # dataInSize 0, # dataOutSize (0 - no writes!) 0, # maxSenseBytes 7, # dataSgeOffset (dwords into mf where SGE goes) ) cmd += mf cmd_buf = ctypes.create_string_buffer(cmd, len(cmd)) try: fcntl.ioctl(fd, ioctl_nr, cmd_buf) except OSError as e: return None, None reply_bytes = bytes(reply_buf) data_in_bytes = bytes(data_in_buf) if data_in_buf else b"" return reply_bytes, data_in_bytes def read_ioc_temperature(fd, ioctl_nr, iocnum): """Read IO Unit Page 7 and return temperature in Celsius, or None.""" # Step 1: Get page header to learn page length reply, _ = _do_config_ioctl(fd, ioctl_nr, iocnum, MPI_CONFIG_ACTION_PAGE_HEADER) if reply is None: return None # ConfigReply: IOCStatus at offset 0x0E, Header at offset 0x14 ioc_status = struct.unpack_from("<H", reply, 0x0E)[0] & 0x7FFF if ioc_status != 0: return None # Header: PageVersion(1) PageLength(1) PageNumber(1) PageType(1) page_length = reply[0x15] # in 32-bit dwords if page_length == 0: return None # Step 2: Read the actual page data data_in_size = page_length * 4 reply, data = _do_config_ioctl( fd, ioctl_nr, iocnum, MPI_CONFIG_ACTION_PAGE_READ_CURRENT, page_length=page_length, data_in_size=data_in_size, ) if reply is None or data is None: return None ioc_status = struct.unpack_from("<H", reply, 0x0E)[0] & 0x7FFF if ioc_status != 0: return None # IO Unit Page 7: IOCTemperature at offset 0x10 (U16), IOCTemperatureUnits at 0x12 (U8) if len(data) < 0x13: return None raw_temp = struct.unpack_from("<H", data, 0x10)[0] units = data[0x12] if units == TEMP_NOT_PRESENT: return None elif units == TEMP_FAHRENHEIT: celsius = (raw_temp - 32) * 5 // 9 elif units == TEMP_CELSIUS: celsius = raw_temp else: celsius = raw_temp # unknown units, return raw return celsius def main(): if os.geteuid() != 0: print("Error: must run as root", file=sys.stderr) sys.exit(1) found_any = False for dev_path, magic, nr in DEVICES: if not os.path.exists(dev_path): continue ioctl_nr = _ioctl_number(magic, nr) try: fd = os.open(dev_path, os.O_RDWR) except OSError: continue for iocnum in range(16): temp = read_ioc_temperature(fd, ioctl_nr, iocnum) if temp is not None: print(f"ioc{iocnum}: {temp}C") found_any = True os.close(fd) if not found_any: print("No LSI IOC temperature sensors found", file=sys.stderr) sys.exit(1) if __name__ == "__main__": main()

Account

Navigation

Search

Search

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.