Index: AUTHORS
===================================================================
--- AUTHORS	(版本 5613)
+++ AUTHORS	(工作副本)
@@ -46,6 +46,7 @@
 Hank Wu                 <hank@areca.com.tw>
 Shengfeng Zhou          <linux@highpoint-tech.com>
 Richard Zybert          <richard.zybert@zybert.co.uk>
+Hong Xu                 <babyxong@88.com>
 
 The first smartmontools code was derived from the smartsuite package,
 written by Michael Cornwell and Andre Hedrick.
Index: ChangeLog
===================================================================
--- ChangeLog	(版本 5613)
+++ ChangeLog	(工作副本)
@@ -1,5 +1,8 @@
 $Id$
 
+2024-07-17  Hong Xu  <babyxong@88.com>
+	Add PS3StorRAID (PS3Stor RAID Controller) support on Linux.
+
 2024-05-08  Christian Franke  <franke@computer.org>
 
 	smartctl.cpp, smartd.cpp: Fix segfault on missing option argument (#1830).
Index: configure.ac
===================================================================
--- configure.ac	(版本 5613)
+++ configure.ac	(工作副本)
@@ -627,7 +627,7 @@
 os_nvme_devicescan_changed=no
 case "${host}" in
   *-*-linux*)
-    os_deps='os_linux.o cciss.o dev_areca.o'
+    os_deps='os_linux.o cciss.o dev_areca.o ps3stor.o'
     os_dnsdomainname="'dnsdomainname' 'hostname -d'"
     os_nisdomainname="'nisdomainname' 'hostname -y' 'domainname'"
     os_man_filter=Linux
Index: Makefile.am
===================================================================
--- Makefile.am	(版本 5613)
+++ Makefile.am	(工作副本)
@@ -132,7 +132,9 @@
         dev_legacy.cpp \
         linux_nvme_ioctl.h \
         megaraid.h \
-        sssraid.h
+        sssraid.h \
+        ps3stor.cpp \
+        ps3stor.h 
 
 if OS_WIN32_MINGW
 
@@ -206,7 +208,9 @@
         freebsd_nvme_ioctl.h \
         netbsd_nvme_ioctl.h \
         megaraid.h \
-        sssraid.h
+        sssraid.h \
+        ps3stor.cpp \
+        ps3stor.h 
 
 if OS_POSIX
 
Index: os_linux.cpp
===================================================================
--- os_linux.cpp	(版本 5613)
+++ os_linux.cpp	(工作副本)
@@ -29,6 +29,9 @@
  *  Kernel compatibility By:    Andre Hedrick <andre@suse.com>
  *  Non-Copyright (C) 2000      Andre Hedrick <andre@suse.com>
  *
+ * Original PS3stor code:
+ *  Copyright (C) 2021-2024 Hong Xu <babyxong@88.com>
+ *
  * Other ars of this file are derived from code that was
  *
  * Copyright (C) 1999-2000 Michael Cornwell <cornwell@acm.org>
@@ -84,6 +87,8 @@
 // "include/uapi/linux/nvme_ioctl.h" from Linux kernel sources
 #include "linux_nvme_ioctl.h" // nvme_passthru_cmd, NVME_IOCTL_ADMIN_CMD
 
+#include "ps3stor.h"
+#include <map>
 #ifndef ENOTSUP
 #define ENOTSUP ENOSYS
 #endif
@@ -1523,6 +1528,270 @@
 }
 
 /////////////////////////////////////////////////////////////////////////////
+/// PS3 STOR support
+class linux_smart_interface;
+class linux_ps3stor_channel;
+class linux_ps3stor_device
+: public /* implements */ scsi_device,
+  public /* extends */ linux_smart_device
+{
+public:
+
+  linux_ps3stor_device(smart_interface *intf, const char *name,
+    unsigned int tgt);
+
+  virtual ~linux_ps3stor_device();
+
+  virtual smart_device * autodetect_open() override;
+
+  virtual bool is_open() const override;
+  virtual bool open() override;
+  virtual bool close() override;
+
+  virtual bool scsi_pass_through(scsi_cmnd_io *iop) override;
+
+private:
+  unsigned int  m_did;
+  bool          m_open_flag;
+  encl_id_t     m_eid;
+  slot_id_t     m_sid;
+  unsigned int  m_host;
+  bool scsi_cmd(scsi_cmnd_io *iop);
+  bool get_pd_position(encl_id_t &eid, slot_id_t &sid);
+};
+
+linux_ps3stor_device::linux_ps3stor_device(smart_interface *intf,
+  const char *dev_name, unsigned int tgt)
+ : smart_device(intf, dev_name, "ps3stor", "ps3stor"),
+   linux_smart_device(O_RDWR | O_NONBLOCK),
+   m_did(tgt),
+   m_open_flag(false),
+   m_eid(PS3STOR_INVALID_ENCL_ID),
+   m_sid(PS3STOR_INVALID_SLOT_ID),
+   m_host(0)
+{
+  set_info().info_name = strprintf("%s [ps3stor_disk_%02d]", dev_name, m_did);
+  set_info().dev_type = strprintf("ps3stor,%d", tgt);
+}
+
+linux_ps3stor_device::~linux_ps3stor_device()
+{
+  if (m_open_flag) {
+    m_open_flag = false;
+  }
+  set_fd(-1); // for ~linux_smart_device
+}
+
+smart_device * linux_ps3stor_device::autodetect_open()
+{
+  int report = scsi_debugmode;
+
+  // Open device
+  if (!open())
+    return this;
+
+  // The code below is based on smartd.cpp:SCSIFilterKnown()
+  if (strcmp(get_req_type(), "ps3stor"))
+    return this;
+
+  // Get INQUIRY
+  unsigned char req_buff[64] = {0, };
+  int req_len = 36;
+  if (scsiStdInquiry(this, req_buff, req_len)) {
+      close();
+      set_err(EIO, "INQUIRY failed");
+      return this;
+  }
+
+  int avail_len = req_buff[4] + 5;
+  int len = (avail_len < req_len ? avail_len : req_len);
+  if (len < 36)
+      return this;
+
+  if (report)
+    pout("Got ps3stor inquiry.. %s\n", req_buff+8);
+
+  // Use INQUIRY to detect type
+  {
+    // SAT?
+    ata_device * newdev = smi()->autodetect_sat_device(this, req_buff, len);
+    if (newdev) // NOTE: 'this' is now owned by '*newdev'
+      return newdev;
+  }
+
+  return this;
+}
+
+bool linux_ps3stor_device::is_open() const
+{
+    return m_open_flag;
+}
+
+bool linux_ps3stor_device::open()
+{
+  if(!m_open_flag) {
+  
+    if (sscanf(get_dev_name(), "/dev/bus/%u", &m_host) == 0) {
+      if (!linux_smart_device::open())
+        return false;
+      /* Get device HBA */
+      struct sg_scsi_id sgid;
+      if (ioctl(get_fd(), SG_GET_SCSI_ID, &sgid) == 0) {
+        m_host = sgid.host_no;
+      }
+      else if (ioctl(get_fd(), SCSI_IOCTL_GET_BUS_NUMBER, &m_host) != 0) {
+        int err = errno;
+        linux_smart_device::close();
+        return set_err(err, "can't get bus number");
+      } // we don't need this device anymore
+      linux_smart_device::close();
+    }
+    m_open_flag = true;
+  }
+  return true;
+}
+
+bool linux_ps3stor_device::close()
+{
+  m_open_flag = false;
+  return true;
+}
+
+bool linux_ps3stor_device::scsi_pass_through(scsi_cmnd_io *iop)
+{
+  int report = scsi_debugmode;
+
+  if (report > 0) {
+        int k, j;
+        const unsigned char * ucp = iop->cmnd;
+        const char * np;
+        char buff[256];
+        const int sz = (int)sizeof(buff);
+
+        np = scsi_get_opcode_name(ucp);
+        j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
+        for (k = 0; k < (int)iop->cmnd_len; ++k)
+            j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
+        if ((report > 1) &&
+            (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
+            int trunc = (iop->dxfer_len > 256) ? 1 : 0;
+
+            snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n  Outgoing "
+                     "data, len=%d%s:\n", (int)iop->dxfer_len,
+                     (trunc ? " [only first 256 bytes shown]" : ""));
+            dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
+        }
+        else
+            snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
+        pout("%s", buff);
+  }
+
+  if (iop->cmnd[0] == 0x00)
+    return true;
+
+  return scsi_cmd(iop);
+}
+
+bool linux_ps3stor_device::scsi_cmd(scsi_cmnd_io *iop)
+{
+ // Controller rejects Test Unit Ready
+  if (iop->cmnd[0] == 0x00)
+    return true;
+
+  if (iop->cmnd[0] == SAT_ATA_PASSTHROUGH_12 || iop->cmnd[0] == SAT_ATA_PASSTHROUGH_16) {
+    // Controller does not return ATA output registers in SAT sense data
+    if (iop->cmnd[2] & (1 << 5)) // chk_cond
+      return set_err(ENOSYS, "ATA return descriptor not supported by controller firmware");
+  }
+  // SMART WRITE LOG SECTOR causing media errors
+  if ((iop->cmnd[0] == SAT_ATA_PASSTHROUGH_16 // SAT16 WRITE LOG
+      && iop->cmnd[14] == ATA_SMART_CMD && iop->cmnd[3]==0 && iop->cmnd[4] == ATA_SMART_WRITE_LOG_SECTOR) ||
+      (iop->cmnd[0] == SAT_ATA_PASSTHROUGH_12 // SAT12 WRITE LOG
+       && iop->cmnd[9] == ATA_SMART_CMD && iop->cmnd[3] == ATA_SMART_WRITE_LOG_SECTOR))
+  {
+    if(!failuretest_permissive)
+       return set_err(ENOSYS, "SMART WRITE LOG SECTOR may cause problems, try with -T permissive to force");
+  }
+
+  if(iop->cmnd_len > PS3STOR_SCSI_MAX_CDB_LENGTH) {
+    return set_err(ENOSYS, "ps3stor cannot support scsi cdb longer than %d", PS3STOR_SCSI_MAX_CDB_LENGTH);
+  }
+
+  //get enclid slotid
+  encl_id_t eid = 0;
+  slot_id_t sid = 0;
+  if(!get_pd_position(eid, sid)) {
+    pout("linux_ps3stor_device::scsi_cmd: get_pd_position of device %u failed.\n", m_did);
+    return set_err(EIO, "linux_ps3stor_device::scsi_cmd: get_pd_position of device %u failed.", m_did);
+  }
+
+  // ps3stor_scsi_req
+
+  ps3stor_scsi_req_t  scsireq = {};
+  scsireq.reserved = 1;
+  switch (iop->dxfer_dir) {
+  case DXFER_NONE:
+    scsireq.cmddir = PS3STOR_SCSI_CMD_DIR_NONE;
+    break;
+  case DXFER_FROM_DEVICE:
+    scsireq.cmddir = PS3STOR_SCSI_CMD_DIR_TO_HOST;
+    break;
+  case DXFER_TO_DEVICE:
+    scsireq.cmddir = PS3STOR_SCSI_CMD_DIR_FROM_HOST;
+    break;
+  default:
+    pout("scsi_cmd: bad dxfer_dir\n");
+    return set_err(EINVAL, "scsi_cmd: bad dxfer_dir\n");
+  } 
+  scsireq.checklen = 0;
+  memcpy(scsireq.cdb, iop->cmnd, iop->cmnd_len);
+
+
+  // ps3stor_scsi_rsp
+  struct ps3stor_scsi_rsp scsirsp = {};
+
+  if(PS3STOR_ERRNO_SUCCESS != ps3chn()->pd_scsi_passthrough(m_host, eid, sid, scsireq, scsirsp, iop->dxferp, iop->dxfer_len)) {
+    return -1;
+  }
+
+  if(scsirsp.entry.status != 0) {
+    if(scsirsp.entry.status == 12) {
+      return set_err(EIO, "linux_ps3stor_device::scsi_cmd: Device %u does not exist\n", m_did);
+    } else {
+        uint8_t scsi_status = scsirsp.entry.status;
+        //ignore underrun
+        if(scsi_status != PS3STOR_SCSI_STATUS_UNDERRUN) {
+          //free(scsi_passthru);
+          return set_err((errno ? errno : EIO), "linux_ps3stor_device::scsi_cmd result: %u.%u = %d/%hhu",
+                      m_host, m_did, errno, scsi_status
+                );
+        }
+    }
+  }
+  iop->scsi_status = scsirsp.entry.status;
+  memcpy(iop->sensep, scsirsp.entry.sensebuf, PS3STOR_MIN(iop->max_sense_len, sizeof(scsirsp.entry.sensebuf)));
+  return true;
+}
+
+bool linux_ps3stor_device::get_pd_position(encl_id_t &eid, slot_id_t &sid)
+{
+  ps3stor_pd_baseinfo_t baseinfo = {};
+  //1.get enclId and slotId from ioc for the first time
+  if(m_eid == PS3STOR_INVALID_ENCL_ID && m_sid == PS3STOR_INVALID_SLOT_ID) {
+    if(PS3STOR_ERRNO_SUCCESS != ps3chn()->pd_get_baseinfo_by_devid(m_host, m_did, baseinfo)) {
+      return false;
+    }    
+    m_eid = baseinfo.enclid;
+    m_sid = baseinfo.slotid;
+  }
+  //2.copy value from member
+  eid = m_eid;
+  sid = m_sid;
+
+  return true;
+}
+
+/////////////////////////////////////////////////////////////////////////////
 /// CCISS RAID support
 
 #ifdef HAVE_LINUX_CCISS_IOCTL_H
@@ -2863,6 +3132,11 @@
   bool get_dev_sssraid(smart_device_list & devlist);
   int sssraid_pd_add_list(int bus_no, smart_device_list & devlist);
   int sssraid_pdlist_cmd(int bus_no, uint16_t start_idx, void *buf, size_t bufsize, uint8_t *statusp);
+
+  bool get_dev_ps3stor(smart_device_list & devlist);
+  int ps3stor_pd_add_list(int bus_no, smart_device_list & devlist);
+  int ps3stor_pdlist_cmd(int bus_no, std::vector<uint16_t> &devidlist);
+
 };
 
 std::string linux_smart_interface::get_os_version_str()
@@ -3137,6 +3411,9 @@
     get_dev_megasas(devlist);
     // get device list from the sssraid device
     get_dev_sssraid(devlist);
+
+    // get device list from the ps3stor device
+    get_dev_ps3stor(devlist);
   }
 
   if (type_nvme) {
@@ -3323,6 +3600,101 @@
   return (0);
 }
 
+// getting devices from ps3 and ps3stor, if available
+bool linux_smart_interface::get_dev_ps3stor(smart_device_list &devlist)
+{
+  // init ps3stor and get controller list , return false on err or not found
+  if(!ps3stor_init()) {
+    return false;
+  }
+  // try to add device for each controller
+  std::vector<unsigned> hostlist;
+  ps3chn()->get_host_list(hostlist);
+  for(uint16_t i = 0; i < hostlist.size(); i++)
+  {
+    ps3stor_pd_add_list(hostlist.at(i), devlist);
+  }
+  return true;
+}
+
+int linux_smart_interface::ps3stor_pd_add_list(int bus_no, smart_device_list &devlist)
+{
+  // get pd device id list
+  std::vector<uint16_t> devid_list;
+  ps3stor_pdlist_cmd(bus_no, devid_list);
+
+  // add all SCSI devices
+  for (unsigned i = 0; i < devid_list.size(); i++) {
+    char line[128];
+    snprintf(line, sizeof(line) - 1, "/dev/bus/%d", bus_no);
+    smart_device * dev = new linux_ps3stor_device(this, line, devid_list.at(i));
+    devlist.push_back(dev);
+  }
+  return (0);  
+}
+
+int linux_smart_interface::ps3stor_pdlist_cmd(int bus_no, std::vector<uint16_t> &devidlist)
+{
+  // get enclist and get devlsit for each encl
+  uint8_t enclcount = 0;
+  struct ps3stor_encl_list *encllist = NULL; 
+
+  //1.get encl count
+  if(PS3STOR_ERRNO_SUCCESS != ps3chn()->get_enclcount(bus_no, enclcount))
+  {
+    return -1;
+  } 
+
+  //2.get encl list
+  if(enclcount > 0)
+  {
+    size_t listsize = sizeof(struct ps3stor_encl_list) + enclcount * sizeof(uint8_t);
+    encllist = (struct ps3stor_encl_list*)malloc(listsize) ;
+    if(PS3STOR_ERRNO_SUCCESS != ps3chn()->get_encllist(bus_no, encllist, listsize)) {
+      free(encllist);
+      encllist = NULL;
+      return -1;
+    }
+  }
+
+  //3.get pd list for each encl
+  if(encllist != NULL && encllist->count > 0) {
+    enclcount = PS3STOR_MIN(enclcount, encllist->count);
+    for(uint8_t i = 0; i < enclcount; i++){
+      uint16_t devcount = 0;
+      uint8_t eid = encllist->idlist[i];
+      if(PS3STOR_ERRNO_SUCCESS != ps3chn()->pd_get_devcount_by_encl(bus_no, eid, devcount)) {
+        return -1;
+      }
+      if(devcount > 0)
+      {
+        size_t listsize = sizeof(uint16_t) * devcount;
+        uint16_t *devlist = (uint16_t*)malloc(listsize);
+        memset(devlist, 0, listsize);
+        if(PS3STOR_ERRNO_SUCCESS != ps3chn()->pd_get_devlist_by_encl(bus_no, eid, devlist, listsize)) {
+          free(devlist);
+          devlist = NULL;
+          return -1;
+        }
+        for(int j = 0; j < devcount; j++)
+        {
+          devidlist.push_back(devlist[j]);
+        }         
+        free(devlist);
+        devlist = NULL;
+      }
+
+    }
+  }
+  
+  if(encllist != NULL) {
+    free(encllist);
+    encllist = NULL;
+  }
+
+  return 0;
+}
+
 int
 linux_smart_interface::sssraid_pd_add_list(int bus_no, smart_device_list & devlist)
 {
@@ -3613,12 +3985,23 @@
 
   }
 
+  // ps3stor ?
+  if (sscanf(type, "ps3stor,%d", &disknum) == 1) {
+    if(ps3stor_init()) {
+      return new linux_ps3stor_device(this, name, disknum);
+    } else {
+      pout("get_custom_smart_device: ps3stor_init failed.\n");
+      set_err_np(EINVAL, "get_custom_smart_device: ps3stor_init failed.\n");
+    }
+    
+  }
+
   return nullptr;
 }
 
 std::string linux_smart_interface::get_valid_custom_dev_types_str()
 {
-  return "areca,N/E, 3ware,N, hpt,L/M/N, megaraid,N, aacraid,H,L,ID, sssraid,E,S"
+  return "areca,N/E, 3ware,N, hpt,L/M/N, megaraid,N, aacraid,H,L,ID, sssraid,E,S, ps3stor,N"
 #ifdef HAVE_LINUX_CCISS_IOCTL_H
                                                                                 ", cciss,N"
 #endif
@@ -3625,6 +4008,493 @@
     ;
 }
 
+/// Linux ps3stor channel
+class linux_ps3stor_channel
+: public /*implements*/ ps3stor_channel
+{
+public:
+  linux_ps3stor_channel();
+  virtual ~linux_ps3stor_channel();
+  enum ps3stor_device_type {
+    PS3STOR_DEVICE_TYPE_PS3 = 0,
+    PS3STOR_DEVICE_TYPE_PS3STOR,
+    PS3STOR_DEVICE_TYPE_NR
+  };
+
+public:
+  virtual ps3stor_errno channel_init() override;
+
+  virtual ps3stor_errno get_host_list(std::vector<unsigned> &hostlist) override;
+
+protected:
+  virtual ps3stor_errno firecmd(unsigned hostid, struct ps3stor_msg_info * info, struct ps3stor_msg_info * ackinfo, unsigned acksize) override;
+
+  virtual ps3stor_errno firecmd_scsi(unsigned hostid, struct ps3stor_msg_info * reqinfo, struct ps3stor_msg_info * ackinfo,
+                                             unsigned acksize, struct ps3stor_data * scsidata, unsigned scsicount) override;
+
+
+  virtual struct ps3stor_tlv *add_tlv_data(struct ps3stor_tlv *tlv, unsigned type, const void *data, uint16_t size) override;
+  virtual bool set_err(int no, const char * msg) override;
+  virtual bool set_err(int no) override;
+
+private:
+  int   m_devfd_ps3;
+  int   m_devfd_ps3stor;
+  std::map<unsigned, struct ps3stor_pci_info> m_host_map;
+
+  int open_node(enum ps3stor_device_type dev_type);
+  ps3stor_errno get_func_by_host(unsigned hostid, uint8_t &funcid);
+  ps3stor_errno get_pci_info(unsigned host, struct ps3stor_pci_info &pci_info);
+  void find_device(enum ps3stor_device_type dev_type);
+  ps3stor_errno get_devfd(unsigned host, int &devfd);
+
+  void print_packet(struct ps3stor_ioctl_sync_cmd &packet); // print packet msg for debug
+
+};
+
+linux_ps3stor_channel::linux_ps3stor_channel()
+{
+  m_devfd_ps3 = -1;
+  m_devfd_ps3stor = -1;
+}
+
+linux_ps3stor_channel::~linux_ps3stor_channel()
+{
+  if(m_devfd_ps3 >= 0)
+    ::close(m_devfd_ps3);
+  
+  if(m_devfd_ps3stor >= 0)
+    ::close(m_devfd_ps3stor); 
+}
+
+ps3stor_errno linux_ps3stor_channel::channel_init()
+{
+  ps3stor_errno err = PS3STOR_ERRNO_SUCCESS;
+  if(m_init) {
+    return err;
+  }
+
+  m_devfd_ps3 = open_node(PS3STOR_DEVICE_TYPE_PS3);
+  if(m_devfd_ps3 > 0) {
+    find_device(PS3STOR_DEVICE_TYPE_PS3);
+  }
+  
+  m_devfd_ps3stor = open_node(PS3STOR_DEVICE_TYPE_PS3STOR);
+  if(m_devfd_ps3stor > 0) {
+    find_device(PS3STOR_DEVICE_TYPE_PS3STOR);
+  }
+
+  if(m_host_map.size() > 0) {
+    m_init = true;
+  }
+
+  return err;
+}
+
+ps3stor_errno linux_ps3stor_channel::get_host_list(std::vector<unsigned> &hostlist)
+{
+  for(auto it : m_host_map)
+  {
+    hostlist.push_back(it.first);
+  }
+  return PS3STOR_ERRNO_SUCCESS;
+}
+
+ps3stor_errno linux_ps3stor_channel::firecmd(unsigned hostid, struct ps3stor_msg_info * reqinfo, struct ps3stor_msg_info * ackinfo, unsigned acksize)
+{
+  ps3stor_errno err = PS3STOR_ERRNO_SUCCESS;
+  int   devfd = -1;
+  const size_t insize = (reqinfo == NULL ? 0 : reqinfo->length);
+  uint8_t funcid = 0;
+
+  reqinfo->magic = PS3STOR_MSG_MAGIC_CODE;
+  reqinfo->error = PS3STOR_ERRNO_SUCCESS;
+  reqinfo->timeout = 0;
+  reqinfo->start_time = 0;
+  reqinfo->runver = PS3STOR_ITR_VER;
+  reqinfo->uuid = 0;
+  reqinfo->service = PS3STOR_SMARTCTL_SERVICE;
+  reqinfo->traceid = 0xffc73a859920e01;
+  get_func_by_host(hostid, funcid);
+  reqinfo->funcid = funcid;
+
+  const uint16_t inblk = (uint16_t)((insize + PS3STOR_SGL_SIZE -1) / PS3STOR_SGL_SIZE);
+  const uint16_t outblk = (uint16_t)((acksize + PS3STOR_SGL_SIZE -1) / PS3STOR_SGL_SIZE);
+
+  struct ps3stor_ioctl_sync_cmd packet = {};
+  packet.hostid = (uint16_t)hostid;
+  packet.sge_count = inblk + outblk;
+  packet.sgl_offset = offsetof(struct ps3stor_ioctl_sync_cmd, sgl) / sizeof(uint32_t); // 4 bytes
+  packet.traceid = reqinfo->traceid;
+
+  reqinfo->index.tlv = 0;
+  reqinfo->index.ack = (uint8_t)(inblk);
+  reqinfo->ack_length = acksize;
+  reqinfo->ack_offset = (uint32_t)insize;
+
+  for(uint16_t i = 0; i < inblk; i++) {
+    packet.sgl[i].addr = (uint64_t)(intptr_t) & ((uint8_t*)reqinfo)[PS3STOR_SGL_SIZE * i];
+    packet.sgl[i].length = (uint32_t)((i == inblk - 1) ? (insize - (PS3STOR_SGL_SIZE * i)) : PS3STOR_SGL_SIZE);
+  }
+
+  for(uint16_t i = 0; i < outblk; i++) {
+    packet.sgl[inblk + i].addr = (uint64_t)(intptr_t) & ((uint8_t*)ackinfo)[PS3STOR_SGL_SIZE * i];
+    packet.sgl[inblk + i].length = (uint32_t)((i == outblk - 1) ? (acksize - (PS3STOR_SGL_SIZE * i)) : PS3STOR_SGL_SIZE);
+  }
+
+  err = get_devfd(hostid, devfd);
+  if(err != PS3STOR_ERRNO_SUCCESS) {
+    return err;
+  }
+  int res = ioctl(devfd, PS3STOR_CMD_IOCTL_SYNC_CMD, &packet);
+  if(res < 0) {
+    set_err(errno, "ioctl failed.\n");
+    return -1;
+  }
+  if(ackinfo->magic != PS3STOR_MSG_MAGIC_CODE) {
+    set_err(EIO, "check magic code failed.\n");
+    return -1;
+  }
+  if(ackinfo->error != PS3STOR_ERRNO_SUCCESS) {
+    set_err(EIO, "err form ioc.\n");
+    return -1;
+  }
+  return err;
+}
+
+ps3stor_errno linux_ps3stor_channel::firecmd_scsi(unsigned hostid, struct ps3stor_msg_info * reqinfo, struct ps3stor_msg_info * ackinfo,
+                                                          unsigned acksize, struct ps3stor_data * scsidata, unsigned scsicount)
+{
+  ps3stor_errno err = PS3STOR_ERRNO_SUCCESS;
+  int   devfd = -1;
+  const size_t insize = (reqinfo == NULL ? 0 : reqinfo->length);
+  uint8_t funcid = 0;
+
+  reqinfo->magic = PS3STOR_MSG_MAGIC_CODE;
+  reqinfo->error = PS3STOR_ERRNO_SUCCESS;
+  reqinfo->timeout = 0;
+  reqinfo->start_time = 0;
+  reqinfo->runver = PS3STOR_ITR_VER;
+  reqinfo->uuid = 0;
+  reqinfo->service = PS3STOR_SMARTCTL_SERVICE;
+  reqinfo->traceid = 0xffc73a859920e01;
+  get_func_by_host(hostid, funcid);
+  reqinfo->funcid = funcid;
+
+  const uint16_t inblk = (uint16_t)((insize + PS3STOR_SGL_SIZE -1) / PS3STOR_SGL_SIZE);
+  const uint16_t outblk = (uint16_t)((acksize + PS3STOR_SGL_SIZE -1) / PS3STOR_SGL_SIZE);
+  const uint16_t scsiblk = (uint16_t)scsicount;
+
+  struct ps3stor_ioctl_sync_cmd packet = {};
+  packet.hostid = (uint16_t)hostid;
+  packet.sge_count = inblk + outblk + scsiblk;
+  packet.sgl_offset = offsetof(struct ps3stor_ioctl_sync_cmd, sgl) / sizeof(uint32_t); // 4 bytes
+  packet.traceid = reqinfo->traceid;
+
+  //todo check for sge_count <= 16
+
+  reqinfo->index.tlv = 0;
+  reqinfo->index.ack = (uint8_t)(inblk);
+  reqinfo->ack_length = acksize;
+  reqinfo->ack_offset = (uint32_t)insize;
+
+  for(uint16_t i = 0; i < inblk; i++) {
+    packet.sgl[i].addr = (uint64_t)(intptr_t) & ((uint8_t*)reqinfo)[PS3STOR_SGL_SIZE * i];
+    packet.sgl[i].length = (uint32_t)((i == inblk - 1) ? (insize - (PS3STOR_SGL_SIZE * i)) : PS3STOR_SGL_SIZE);
+  }
+
+  for(uint16_t i = 0; i < outblk; i++) {
+    packet.sgl[inblk + i].addr = (uint64_t)(intptr_t) & ((uint8_t*)ackinfo)[PS3STOR_SGL_SIZE * i];
+    packet.sgl[inblk + i].length = (uint32_t)((i == outblk - 1) ? (acksize - (PS3STOR_SGL_SIZE * i)) : PS3STOR_SGL_SIZE);
+  }
+
+  for(uint16_t i = 0; i < scsiblk; i++) {
+    packet.sgl[inblk + outblk + i].addr = (uint64_t)(intptr_t)scsidata[i].pdata;
+    packet.sgl[inblk + outblk + i].length = scsidata[i].size;
+  }
+  err = get_devfd(hostid, devfd);
+  if(err != PS3STOR_ERRNO_SUCCESS) {
+    return err;
+  }
+
+  int res = ioctl(devfd, PS3STOR_CMD_IOCTL_SYNC_CMD, &packet);
+  if(res < 0) {
+    set_err(errno, "ioctl failed.\n");
+    return -1;
+  }
+  if(ackinfo->magic != PS3STOR_MSG_MAGIC_CODE) {
+    set_err(EIO, "check magic code failed.\n");
+    return -1;
+  }
+  if(ackinfo->error != PS3STOR_ERRNO_SUCCESS) {
+    set_err(EIO, "err form ioc.\n");
+    return -1;
+  }
+  return err;
+}
+
+struct ps3stor_tlv *linux_ps3stor_channel::add_tlv_data(struct ps3stor_tlv *tlv, unsigned type, const void *data, uint16_t size)
+{
+  if(data == NULL || size == 0) {
+    return tlv;
+  }
+
+  unsigned tlvcode = type;
+
+  // calculate tlvsize
+  uint16_t tlvsize = (uint16_t)(sizeof(*tlv)                      ///< tlv 头
+                                + ((tlv == NULL) ? 0 : tlv->size) ///< 已存在tlv的长度
+                                + sizeof(tlvcode)                 ///< 新 tlv:type 长度
+                                + sizeof(size)                    ///< 新 tlv:length 长度
+                                + size);
+
+  if(tlv == NULL) {
+    tlv = (struct ps3stor_tlv *)malloc(tlvsize);
+    memset(tlv, 0, tlvsize);
+  } else {
+    struct ps3stor_tlv * tmp = (struct ps3stor_tlv *)realloc(tlv, tlvsize);
+    if(tmp == NULL) {
+      free(tlv);
+    }
+    tlv = tmp;
+  }
+
+  // add data
+  if (tlv != NULL)
+  {
+    memcpy(tlv->buff + tlv->size, &tlvcode, sizeof(tlvcode));
+    tlv->size += sizeof(tlvcode);
+    memcpy(tlv->buff + tlv->size, &size, sizeof(size));
+    tlv->size += sizeof(size);
+    memcpy(tlv->buff + tlv->size, data, size);
+    tlv->size += size; 
+  }
+  
+  return tlv;
+}
+
+bool linux_ps3stor_channel::set_err(int no, const char * msg)
+{
+  return smi()->set_err(no, "%s", msg);
+}
+
+bool linux_ps3stor_channel::set_err(int no)
+{
+  return smi()->set_err(no);
+}
+
+int linux_ps3stor_channel::open_node(enum ps3stor_device_type dev_type)
+{
+  /* Scanning of disks on ps3stor device */
+  /* Perform mknod of device ioctl node */
+  char line[128];
+  int  mjr = -1; //major device number
+  int  n1 = -1;
+  int  devnode_fd = -1;
+  // scan ps3stor
+  FILE * fp = fopen(PS3STOR_DRIVE_DEVICE_ID, "r");
+  if (!fp)
+    return devnode_fd;
+
+  switch (dev_type)
+  {
+  case PS3STOR_DEVICE_TYPE_PS3:
+    while (fgets(line, sizeof(line), fp) != NULL) {
+      n1=0;
+      int tmp = sscanf(line, "%d ps3-ioctl%n", &mjr, &n1);
+      //if (sscanf(line, "%d ps3-ioctl%n", &mjr, &n1) == 1 && n1 == 13) {
+      if (tmp == 1 && n1 == 13) {
+        n1=mknod("/dev/ps3-ioctl", S_IFCHR|0600, makedev(mjr, 0));
+        if(scsi_debugmode > 0)
+          pout("Creating /dev/ps3-ioctl = %d\n", n1 >= 0 ? 0 : errno);
+        if (n1 >= 0 || errno == EEXIST) {
+          devnode_fd = ::open("/dev/ps3-ioctl", O_RDWR);
+          break;
+        } else {
+          break;
+        }        
+      }
+    }
+    break;
+  case PS3STOR_DEVICE_TYPE_PS3STOR:
+    while (fgets(line, sizeof(line), fp) != NULL) {
+      n1=0;
+      int tmp = sscanf(line, "%d ps3stor-ioctl%n", &mjr, &n1);
+      //if (sscanf(line, "%d ps3stor-ioctl%n", &mjr, &n1) == 1 && n1 == 17) {
+      if (tmp == 1 && n1 == 17) {
+        n1=mknod("/dev/ps3stor-ioctl", S_IFCHR|0600, makedev(mjr, 0));
+        if(scsi_debugmode > 0)
+          pout("Creating /dev/ps3stor-ioctl = %d\n", n1 >= 0 ? 0 : errno);
+        if (n1 >= 0 || errno == EEXIST) {
+          devnode_fd = ::open("/dev/ps3stor-ioctl", O_RDWR);
+          break;
+        } else {
+          break;
+        }        
+      }
+    }
+    break; 
+  default:
+    break;
+  }
+  fclose(fp);
+  return devnode_fd;
+}
+
+ps3stor_errno linux_ps3stor_channel::get_func_by_host(unsigned hostid, uint8_t &funcid)
+{
+  for(auto it : m_host_map)
+  {
+    if(it.first == hostid) {
+      funcid = it.second.function;
+      return PS3STOR_ERRNO_SUCCESS;
+    }
+  }
+  return -1;
+}
+
+ps3stor_errno linux_ps3stor_channel::get_pci_info(unsigned host, ps3stor_pci_info &pci_info)
+{
+  ps3stor_errno err = PS3STOR_ERRNO_SUCCESS;
+  char path[128] = {};
+  char filelink[128] = {};
+
+  snprintf(path, sizeof(path) - 1, "/sys/class/scsi_host/host%u", host);
+  err = readlink(path, filelink, sizeof(filelink));
+  if(err < 0) {
+    return errno;
+  }
+  err = -1;
+  const char templatestr[] = "0000:00:00.0"; // look up pci addr by file link
+  unsigned templen = strlen(templatestr);
+  if(strlen(path) < templen) {
+    return -1;
+  }
+  
+  for(unsigned i = 0; *(filelink + i + templen) != '\0'; i++){
+    char *begin = filelink + i;
+    bool match = true;
+    for(unsigned j = 0; j < templen; j++) {
+      if(begin[j] == templatestr[j]) {
+        continue;
+      } else if (isxdigit(begin[j]) && templatestr[j] == '0'){
+        continue;
+      } else {
+        match = false;
+        break;
+      }
+    }
+    if(match) {
+      err = PS3STOR_ERRNO_SUCCESS;
+      memcpy(pci_info.pci_addr, begin, templen + 1);
+      sscanf(pci_info.pci_addr, "%x:%hhx:%hhx.%hhx",
+             &pci_info.domainid, &pci_info.busid, &pci_info.deviceid, &pci_info.function);
+    }
+  }
+  return err;
+}
+
+void linux_ps3stor_channel::find_device(enum ps3stor_device_type dev_type)
+{
+  // getting bus numbers with ps3stor controllers
+  // we are using sysfs to get list of all scsi hosts
+  char procctx[16] = {};
+  size_t  proclen = 0;
+  switch (dev_type)
+  {
+  case PS3STOR_DEVICE_TYPE_PS3:
+    snprintf(procctx, sizeof(procctx) - 1, "%s", "ps3\n");
+    proclen = 4;
+    break;
+  case PS3STOR_DEVICE_TYPE_PS3STOR:
+    snprintf(procctx, sizeof(procctx) - 1, "%s", "ps3stor\n");
+    proclen = 7;
+    break;
+  default:
+    return;
+    break;
+  }
+
+  DIR * dp = opendir(PS3STOR_SYS_CLASS_SCSI_HOST_PATH);
+  if (dp != NULL)
+  {
+    struct dirent *ep;
+    FILE*   fp = NULL;
+    char line[128] = {};
+    while ((ep = readdir (dp)) != NULL) {
+      unsigned int host_no = 0;
+      if (!sscanf(ep->d_name, "host%u", &host_no))
+        continue;
+      /* proc_name should be  procctx */
+      char sysfsdir[256] = {};
+      snprintf(sysfsdir, sizeof(sysfsdir) - 1,
+        "/sys/class/scsi_host/host%u/proc_name", host_no);
+      if((fp = fopen(sysfsdir, "r")) == NULL)
+        continue;
+      if(fgets(line, sizeof(line), fp) != NULL && !strncmp(line, procctx, proclen)) {
+        struct ps3stor_pci_info pci = {};
+        if(get_pci_info(host_no, pci) == PS3STOR_ERRNO_SUCCESS) {
+          pci.devtype = dev_type;
+          m_host_map[host_no] = pci;
+        }
+      }
+      fclose(fp);
+    }
+    (void) closedir(dp);
+  }
+  return;
+}
+
+ps3stor_errno linux_ps3stor_channel::get_devfd(unsigned host, int &devfd)
+{
+  ps3stor_errno err = -1;
+  for(auto it : m_host_map)
+  {
+    if(it.first == host) {
+      switch (it.second.devtype)
+      {
+      case PS3STOR_DEVICE_TYPE_PS3:
+        devfd = m_devfd_ps3;
+        err = PS3STOR_ERRNO_SUCCESS;
+        return err;
+      case PS3STOR_DEVICE_TYPE_PS3STOR:
+        devfd = m_devfd_ps3stor;
+        err = PS3STOR_ERRNO_SUCCESS;
+        return err;      
+      default:
+        break;
+      };
+      break;
+    }
+  }
+  return err;
+}
+
+void linux_ps3stor_channel::print_packet(ps3stor_ioctl_sync_cmd &packet)
+{
+  for(uint16_t i = 0; i < packet.sge_count; i++) {
+    char tmp_buf[1024 * 4] = {};
+    uint32_t sz = 1024 * 4;
+    uint32_t len = 0;
+    len = snprintf(tmp_buf, sz, "=============sgl[%hu]:length = %u =============:\n", i, packet.sgl[i].length);
+    for(uint32_t j = 0; j < packet.sgl[i].length && len < sz ; j++) {
+      if(j % 32 == 0){
+        len += snprintf(&tmp_buf[len], (sz > len ? (sz - len) : 0), "0x%04x: ", j);
+      } 
+      uint8_t* buff = (uint8_t*)(intptr_t)packet.sgl[i].addr;
+      len += snprintf(&tmp_buf[len], (sz > len ? (sz - len) : 0), "%02x ", buff[j]);
+      if((j + 1) % 4 == 0) {
+        len += snprintf(&tmp_buf[len], (sz > len ? (sz - len) : 0), " ");
+      }
+      if((j + 1) % 32 == 0) {
+        len += snprintf(&tmp_buf[len], (sz > len ? (sz - len) : 0), "\n");
+      }
+    }
+    pout("%s\n=================================\n", tmp_buf);
+  }
+
+}
+
 } // namespace
 
 /////////////////////////////////////////////////////////////////////////////
@@ -3635,3 +4505,22 @@
   static os_linux::linux_smart_interface the_interface;
   smart_interface::set(&the_interface);
 }
+
+void ps3stor_channel::init()
+{
+  static os_linux::linux_ps3stor_channel the_ps3stor_instance;
+  ps3stor_channel::set(&the_ps3stor_instance);
+}
+
+bool ps3stor_init() 
+{
+  if(!ps3chn()) {
+    ps3stor_channel::init();
+    if (!ps3chn())
+      return false;    
+  }
+  if(PS3STOR_ERRNO_SUCCESS != ps3chn()->channel_init()) {
+    return false;
+  }  
+  return true;
+}
\ No newline at end of file
Index: ps3stor.cpp
===================================================================
--- ps3stor.cpp	(不存在的)
+++ ps3stor.cpp	(工作副本)
@@ -0,0 +1,290 @@
+/*
+ * ps3stor.cpp
+ *
+ * Home page of code is: http://www.smartmontools.org
+ *
+ * Copyright (C) 2024 Hong Xu
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "ps3stor.h"
+ps3stor_channel * ps3stor_channel::s_channel;
+
+ps3stor_errno ps3stor_channel::get_enclcount(unsigned hostid, uint8_t &enclcount)
+{
+    struct ps3stor_msg_info* reqinfo = NULL;
+    size_t insize = sizeof(struct ps3stor_msg_info);
+    reqinfo = (struct ps3stor_msg_info*)malloc(insize);
+    memset(reqinfo, 0, insize); 
+    reqinfo->length = insize;
+    reqinfo->opcode = PS3STOR_CMD_ENCL_GET_COUNT;
+
+    struct ps3stor_msg_info* rspinfo = NULL;
+    size_t outsize = sizeof(struct ps3stor_msg_info) + sizeof(enclcount);
+    rspinfo = (struct ps3stor_msg_info*)malloc(outsize);
+    memset(rspinfo, 0, outsize);
+
+    if(PS3STOR_ERRNO_SUCCESS != firecmd(hostid, reqinfo, rspinfo, outsize)) {
+      free(reqinfo);
+      free(rspinfo);
+      return -1;
+    }
+    memcpy(&enclcount, rspinfo->body, sizeof(enclcount));
+    free(reqinfo);
+    free(rspinfo); 
+    return 0;
+}
+
+ps3stor_errno ps3stor_channel::get_encllist(unsigned hostid, ps3stor_encl_list *&encllist, size_t listsize)
+{
+    struct ps3stor_msg_info* reqinfo = NULL;
+    size_t insize = sizeof(struct ps3stor_msg_info);
+    reqinfo = (struct ps3stor_msg_info*)malloc(insize);
+    memset(reqinfo, 0, insize); 
+    reqinfo->length = insize;
+    reqinfo->opcode = PS3STOR_CMD_ENCL_GET_LIST;
+
+    struct ps3stor_msg_info* rspinfo = NULL;
+    size_t outsize = sizeof(struct ps3stor_msg_info) + listsize;
+    rspinfo = (struct ps3stor_msg_info*)malloc(outsize);
+    memset(rspinfo, 0, outsize);
+
+    if(PS3STOR_ERRNO_SUCCESS != firecmd(hostid, reqinfo, rspinfo, outsize)) {
+      free(reqinfo);
+      free(rspinfo);
+      return -1;
+    }
+    memcpy(encllist, rspinfo->body, listsize);
+    free(reqinfo);
+    free(rspinfo); 
+    return 0;
+}
+
+ps3stor_errno ps3stor_channel::pd_get_devcount_by_encl(unsigned hostid, uint8_t enclid, uint16_t &devcount)
+{
+    struct ps3stor_msg_info* reqinfo = NULL;
+    size_t insize = sizeof(struct ps3stor_msg_info);
+    reqinfo = (struct ps3stor_msg_info*)malloc(insize);
+    memset(reqinfo, 0, insize); 
+    reqinfo->length = insize;
+    reqinfo->opcode = PS3STOR_CMD_PD_GET_COUNT_IN_ENCL;
+    reqinfo->id.type = PS3STOR_ID_GROUP_TYPE_PD_POSITION;
+    reqinfo->id.pd_position.enclid = enclid;
+
+    struct ps3stor_msg_info* rspinfo = NULL;
+    size_t outsize = sizeof(struct ps3stor_msg_info) + sizeof(devcount);
+    rspinfo = (struct ps3stor_msg_info*)malloc(outsize);
+    memset(rspinfo, 0, outsize);
+
+    if(PS3STOR_ERRNO_SUCCESS != firecmd(hostid, reqinfo, rspinfo, outsize)) {
+        free(reqinfo);
+        free(rspinfo);
+        return -1;
+    }
+    memcpy(&devcount, rspinfo->body, sizeof(devcount));
+    free(reqinfo);
+    free(rspinfo); 
+    return 0;
+}
+
+ps3stor_errno ps3stor_channel::pd_get_devlist_by_encl(unsigned hostid, uint8_t enclid, uint16_t *&devlist, size_t listsize)
+{
+  ps3stor_errno err = PS3STOR_ERRNO_SUCCESS;
+
+  struct ps3stor_msg_info* reqinfo = NULL;
+  size_t insize = sizeof(struct ps3stor_msg_info);
+  reqinfo = (struct ps3stor_msg_info*)malloc(insize);
+  memset(reqinfo, 0, insize); 
+  reqinfo->length = insize;
+  reqinfo->opcode = PS3STOR_CMD_PD_GET_DEV_LIST_IN_ENCL;
+
+  struct ps3stor_msg_info* rspinfo = NULL;
+  size_t outsize = sizeof(struct ps3stor_msg_info) + listsize;
+  rspinfo = (struct ps3stor_msg_info*)malloc(outsize);
+  memset(rspinfo, 0, outsize);
+  reqinfo->id.type = PS3STOR_ID_GROUP_TYPE_PD_POSITION;
+  reqinfo->id.pd_position.enclid = enclid;
+
+  if(PS3STOR_ERRNO_SUCCESS != firecmd(hostid, reqinfo, rspinfo, outsize)) {
+    free(reqinfo);
+    free(rspinfo);
+    return -1;
+  }
+  memcpy(devlist, rspinfo->body, listsize);
+  free(reqinfo);
+  free(rspinfo); 
+  return err;
+}
+
+ps3stor_errno ps3stor_channel::pd_get_baseinfo_by_devid(unsigned hostid, unsigned devid, ps3stor_pd_baseinfo_t &baseinfo)
+{
+    baseinfo.enclid = 0;
+    baseinfo.slotid = 0;
+    ps3stor_batch_req_t *batchreq = NULL;
+    struct ps3stor_tlv *req_tlv = NULL;
+    uint32_t id_count = 1;
+    uint32_t data_size = sizeof(ps3stor_pd_baseinfo_t);
+
+    unsigned reqsize = sizeof(ps3stor_batch_req_t) + id_count * sizeof(struct ps3stor_id_group);
+    batchreq = (ps3stor_batch_req_t*)malloc(reqsize);
+    memset(batchreq, 0, reqsize);
+    batchreq->idcount = id_count;
+    batchreq->datasize = data_size;
+    batchreq->idgroup[0].type = PS3STOR_ID_GROUP_TYPE_DEVICE_ID;
+    batchreq->idgroup[0].deviceid = (uint16_t)devid;
+
+    //packet batchreq
+
+    req_tlv = add_tlv_data(req_tlv, PS3STOR_MK_TLV_CODE(PS3STOR_BATCH_TLV_CODE_MASK, ps3stor_batch_req_t, idcount),
+                                    &batchreq->idcount, (uint16_t)sizeof(batchreq->idcount));
+    req_tlv = add_tlv_data(req_tlv, PS3STOR_MK_TLV_CODE(PS3STOR_BATCH_TLV_CODE_MASK, ps3stor_batch_req_t, datasize),
+                                    &batchreq->datasize, (uint16_t)sizeof(batchreq->datasize));
+    req_tlv = add_tlv_data(req_tlv, PS3STOR_MK_TLV_CODE(PS3STOR_BATCH_TLV_CODE_MASK, ps3stor_batch_req_t, idgroup),
+                                    &batchreq->idgroup, (uint16_t)(id_count * sizeof(struct ps3stor_id_group)));
+    if(req_tlv == NULL) {
+        free(batchreq);
+        return -1;
+    }
+
+    ps3stor_batch_rsp_t *batchrsp = NULL;                               
+    unsigned rspsize = sizeof(ps3stor_batch_rsp_t) + id_count * (sizeof(ps3stor_rsp_entry_t) + sizeof(ps3stor_pd_baseinfo_t));                                                                  
+    batchrsp = (ps3stor_batch_rsp_t*)malloc(rspsize);
+    memset(batchrsp, 0, rspsize);
+
+    // call ioc
+    struct ps3stor_msg_info* reqinfo = NULL;
+    size_t insize = sizeof(struct ps3stor_msg_info) + req_tlv->size;
+    reqinfo = (struct ps3stor_msg_info*)malloc(insize);
+    memset(reqinfo, 0, insize); 
+    reqinfo->length = insize;
+    reqinfo->opcode = PS3STOR_CMD_PD_BASE_INFO;
+    memcpy(reqinfo->body, req_tlv->buff, req_tlv->size);
+
+    struct ps3stor_msg_info* rspinfo = NULL;
+    size_t outsize = sizeof(struct ps3stor_msg_info) + rspsize;
+    rspinfo = (struct ps3stor_msg_info*)malloc(outsize);
+    memset(rspinfo, 0, outsize);
+
+    if(PS3STOR_ERRNO_SUCCESS != firecmd(hostid, reqinfo, rspinfo, outsize)) {
+        free(batchreq);
+        free(req_tlv);
+        free(batchrsp);
+        free(reqinfo);
+        free(rspinfo);
+        return -1;
+    }
+    memcpy(batchrsp, rspinfo->body, rspsize);
+    ps3stor_rsp_entry_t* entry = (ps3stor_rsp_entry_t*)batchrsp->rsp_entry; 
+    if(entry != NULL && entry->result == PS3STOR_ERRNO_SUCCESS) {
+        memcpy(&baseinfo, entry->data, PS3STOR_MIN(entry->size, sizeof(ps3stor_pd_baseinfo_t)));
+    } else {
+        free(batchreq);
+        free(req_tlv);
+        free(batchrsp);
+        free(reqinfo);
+        free(rspinfo);
+        return -1;
+    }
+
+    free(batchreq);
+    free(req_tlv);
+    free(batchrsp);
+    free(reqinfo);
+    free(rspinfo);
+    return 0;
+}
+
+
+ps3stor_errno ps3stor_channel::pd_scsi_passthrough(unsigned hostid, uint8_t eid, uint16_t sid,
+                                                              ps3stor_scsi_req_t &scsireq, ps3stor_scsi_rsp &scsirsp,
+                                                              uint8_t *&scsidata, const size_t scsilen)
+{
+    // 1. scsi data align
+    unsigned scsicount = scsilen / PS3STOR_SGL_SIZE;
+    scsicount = (scsilen % PS3STOR_SGL_SIZE == 0) ? (scsicount) : ((scsicount + 1));
+    struct ps3stor_data scsiblks[PS3STOR_SCSI_MAX_BLK_CNT] = {};
+    if (scsicount > PS3STOR_SCSI_MAX_BLK_CNT)
+    {
+        set_err(EIO, "linux_ps3stor_device::scsi_cmd: request for scsi data is too large.");
+        return -1;
+    }
+
+    for(unsigned i = 0; i < scsicount; i++) {
+        unsigned size = PS3STOR_SGL_SIZE;
+        if(i == scsicount - 1) {
+        size = scsilen - i * PS3STOR_SGL_SIZE;
+        }
+        size = (size % PS3STOR_SCSI_ALIGN_SIZE == 0) ? (size) : ((size / PS3STOR_SCSI_ALIGN_SIZE + 1) * PS3STOR_SCSI_ALIGN_SIZE);
+        scsiblks[i].pdata = malloc(size);
+        memset(scsiblks[i].pdata, 0, size);
+        scsiblks[i].size = size;
+        scsireq.req.datalen += size;
+    } 
+
+    scsireq.req.sgecount = (scsireq.req.datalen + PS3STOR_SCSI_BYTE_PER_CMD - 1) / PS3STOR_SCSI_BYTE_PER_CMD;
+    scsireq.req.sgeindex = PS3STOR_SCSI_SGE_INDEX_BASE;
+
+    scsireq.req.id.type = PS3STOR_ID_GROUP_TYPE_PD_POSITION;
+    scsireq.req.id.pd_position.enclid = eid;
+    scsireq.req.id.pd_position.slotid = sid; 
+
+    // 2. packet scsireq
+    struct ps3stor_tlv *scsireq_tlv = NULL;
+    scsireq_tlv = add_tlv_data(scsireq_tlv, PS3STOR_MK_TLV_CODE(PS3STOR_SCSI_TLV_CODE_MASK, ps3stor_scsi_req_t, reserved), 
+                                        &scsireq.reserved, (uint16_t)sizeof(scsireq.reserved));
+    scsireq_tlv = add_tlv_data(scsireq_tlv, PS3STOR_MK_TLV_CODE(PS3STOR_SCSI_TLV_CODE_MASK, ps3stor_scsi_req_t, cmddir), 
+                                        &scsireq.cmddir, (uint16_t)sizeof(scsireq.cmddir));                                          
+    scsireq_tlv = add_tlv_data(scsireq_tlv, PS3STOR_MK_TLV_CODE(PS3STOR_SCSI_TLV_CODE_MASK, ps3stor_scsi_req_t, checklen), 
+                                        &scsireq.checklen, (uint16_t)sizeof(scsireq.checklen));
+    scsireq_tlv = add_tlv_data(scsireq_tlv, PS3STOR_MK_TLV_CODE(PS3STOR_SCSI_TLV_CODE_MASK, ps3stor_scsi_req_t, cdb), 
+                                        &scsireq.cdb, (uint16_t)PS3STOR_SCSI_CDB_LEN);
+    scsireq_tlv = add_tlv_data(scsireq_tlv, PS3STOR_MK_TLV_CODE(PS3STOR_SCSI_TLV_CODE_MASK, ps3stor_scsi_req_t, req), 
+                                        &scsireq.req, (uint16_t)sizeof(scsireq.req));
+    if(scsireq_tlv == NULL) {
+        for(unsigned i = 0; i < scsicount; i++) {
+            free(scsiblks[i].pdata);
+        }
+        return -1;
+    }
+
+    // 3. call scsi passthrough 
+    struct ps3stor_msg_info* reqinfo = NULL;
+    size_t insize = sizeof(struct ps3stor_msg_info) + scsireq_tlv->size;
+    reqinfo = (struct ps3stor_msg_info*)malloc(insize);
+    memset(reqinfo, 0, insize); 
+    reqinfo->length = insize;
+    reqinfo->opcode = PS3STOR_CMD_PD_SCSI_PASSTHROUGH;
+    memcpy(&reqinfo->id, &scsireq.req.id, sizeof(struct ps3stor_id_group));
+    memcpy(reqinfo->body, scsireq_tlv->buff, scsireq_tlv->size);
+
+    struct ps3stor_msg_info* rspinfo = NULL;
+    size_t outsize = sizeof(struct ps3stor_msg_info) + sizeof( struct ps3stor_scsi_rsp);
+    rspinfo = (struct ps3stor_msg_info*)malloc(outsize);
+    memset(rspinfo, 0, outsize);
+
+    if(PS3STOR_ERRNO_SUCCESS != firecmd_scsi(hostid, reqinfo, rspinfo, outsize, scsiblks, scsicount)) {
+        for(unsigned i = 0; i < scsicount; i++) {
+            free(scsiblks[i].pdata);
+        }
+        free(reqinfo);
+        free(rspinfo); 
+        free(scsireq_tlv);
+        return -1;
+    }
+
+    // 4. copy and free memory
+    memcpy(&scsirsp, rspinfo->body, sizeof(scsirsp));
+    for(unsigned i = 0; i < scsicount; i++) {
+        unsigned size = PS3STOR_SGL_SIZE;
+        if(i == scsicount - 1) {
+        size = scsilen - i * PS3STOR_SGL_SIZE;
+        }
+        memcpy(scsidata + i * PS3STOR_SGL_SIZE, scsiblks[i].pdata, size);
+        free(scsiblks[i].pdata);
+    }
+    free(scsireq_tlv);
+    free(reqinfo);
+    free(rspinfo); 
+    return 0;
+}
Index: ps3stor.h
===================================================================
--- ps3stor.h	(不存在的)
+++ ps3stor.h	(工作副本)
@@ -0,0 +1,322 @@
+/*
+ * ps3stor.h
+ *
+ * Home page of code is: http://www.smartmontools.org
+ *
+ * Copyright (C) 2024 Hong Xu
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef __PS3STOR_H__
+#define __PS3STOR_H__
+
+#include "utility.h"
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+#include <stddef.h>
+
+typedef uint8_t                    encl_id_t;
+typedef uint16_t                   slot_id_t;
+typedef int32_t                    ps3stor_errno;
+
+#define PS3STOR_ERRNO_SUCCESS           (0)
+
+#define PS3STOR_ID_LIST_MAX_COUNT       (128)
+#define PS3STOR_SCSI_STATUS_UNDERRUN    (140)
+#define PS3STOR_MSG_MAGIC_CODE          (0x12345678)
+#define PS3STOR_SGL_SIZE                (4 * 1024)
+#define PS3STOR_ITR_VER                 (0x2000000)
+#define PS3STOR_SMARTCTL_SERVICE        (0)
+#define PS3STOR_SCSI_CDB_LEN            (32)
+#define PS3STOR_SCSI_MAX_CDB_LENGTH     (32)
+#define PS3STOR_SCSI_SENSE_BUFFER_LEN   (96)
+#define PS3STOR_SCSI_MAX_BLK_CNT        (14)
+#define PS3STOR_SCSI_ALIGN_SIZE         (512)
+#define PS3STOR_SCSI_BYTE_PER_CMD       (4 * 1024)
+#define PS3STOR_SCSI_SGE_INDEX_BASE     (2)
+
+#define  PS3STOR_DRIVE_DEVICE_ID                 "/proc/devices"
+#define  PS3STOR_SYS_CLASS_SCSI_HOST_PATH        "/sys/class/scsi_host/"
+#define  PS3STOR_SYS_BUS_PCI_DEVICE              "/sys/bus/pci/devices"
+
+#define PS3STOR_CMD_ENCL_GET_COUNT              (0x3040101)
+#define PS3STOR_CMD_ENCL_GET_LIST               (0x3040102)
+#define PS3STOR_CMD_PD_GET_COUNT_IN_ENCL        (0x3050101)
+#define PS3STOR_CMD_PD_GET_DEV_LIST_IN_ENCL     (0x3050115)
+#define PS3STOR_CMD_PD_SCSI_PASSTHROUGH         (0x23051c01)
+#define PS3STOR_CMD_PD_BASE_INFO                (0x3050104)
+
+#define PS3STOR_SCSI_TLV_CODE_MASK              (0x41c01000)
+#define PS3STOR_BATCH_TLV_CODE_MASK             (0x6000)
+
+#define PS3STOR_MIN(x,y)                ((x) < (y) ? (x) : (y))
+#define PS3STOR_MK_TLV_CODE(code, struct_name, struct_member) \
+        ((uint32_t) (code | offsetof(struct_name, struct_member)))
+
+#define PS3STOR_ID_GROUP_TYPE_UNKNOWN       (0)
+#define PS3STOR_ID_GROUP_TYPE_DEVICE_ID     (1)
+#define PS3STOR_ID_GROUP_TYPE_PD_POSITION   (2)
+#define PS3STOR_SCSI_CMD_DIR_FROM_HOST      (1)
+#define PS3STOR_SCSI_CMD_DIR_TO_HOST        (2)
+#define PS3STOR_SCSI_CMD_DIR_NONE           (0)
+
+#define PS3STOR_INVALID_U64         (0xFFFFFFFFFFFFFFFF)
+#define PS3STOR_INVALID_U32         (0xFFFFFFFF)
+#define PS3STOR_INVALID_U16         (0xFFFF)
+#define PS3STOR_INVALID_U8          (0xFF)
+
+#define PS3STOR_INVALID_ENCL_ID     (PS3STOR_INVALID_U8)
+#define PS3STOR_INVALID_SLOT_ID     (PS3STOR_INVALID_U16)
+
+
+struct ps3stor_pd_position {
+    uint8_t     enclid;
+    uint8_t     pad;
+    uint16_t    slotid;
+};
+
+struct ps3stor_id_group {
+    uint8_t type;
+    uint8_t pad[7];
+    union{
+        uint16_t                    deviceid;
+        struct ps3stor_pd_position  pd_position;
+        uint64_t                    reserved;   // align as 8 bytes
+        uint8_t                     value[24];  // max for 24 bytes
+    };
+};
+
+struct ps3stor_tlv {
+    int32_t size;
+    uint8_t buff[0];
+};
+
+struct ps3stor_encl_list
+{
+    uint8_t count;
+    uint8_t idlist[0];
+};
+
+struct ps3stor_msg_info {
+    uint32_t magic;
+    uint32_t opcode;
+    uint32_t error;
+    uint32_t timeout;
+    uint32_t start_time;
+    uint32_t runver;
+    uint32_t length;
+    uint32_t ack_offset;
+    uint32_t ack_length;
+    uint32_t leftmsg_count; 
+    uint32_t msg_index;
+    uint8_t  reserved[4];
+    uint64_t uuid;
+    uint8_t  service;
+    struct {
+        uint8_t ack : 4;
+        uint8_t tlv : 4;
+    } index;
+    uint8_t funcid : 1;
+    uint8_t pad    : 7;
+    uint8_t reserved2[5];
+    struct ps3stor_id_group id;
+    uint64_t traceid;
+    uint8_t  body[0];
+};
+
+#define PS3STOR_MSG_INFO_SIZE (sizeof(struct ps3stor_msg_info))
+
+struct ps3stor_sge {
+    uint64_t    addr;
+    uint32_t    length;
+    uint32_t    reserved1 : 30;
+    uint32_t    last_sge  : 1;
+    uint32_t    ext       : 1;
+};
+
+struct ps3stor_ioctl_sync_cmd {
+    uint16_t    hostid;
+    uint16_t    sgl_offset;
+    uint16_t    sge_count;
+    uint16_t    reserved1;
+    uint32_t    result_code;
+    uint8_t     reserved2[100];
+    uint64_t    traceid;
+    uint8_t     reserved3[120];
+    uint8_t     reserved4[128];
+    struct ps3stor_sge sgl[16];    
+};
+
+#define PS3STOR_CMD_IOCTL_SYNC_CMD      _IOWR('M', 1, struct ps3stor_ioctl_sync_cmd)
+
+typedef struct  {
+    uint32_t count;
+    uint8_t  pad[4];
+    int64_t  values[PS3STOR_ID_LIST_MAX_COUNT];
+} ps3stor_id_list;
+
+struct ps3stor_pci_info {
+    uint32_t domainid;
+    uint8_t  busid;
+    uint8_t  deviceid;
+    uint8_t  function;
+    uint8_t  devtype;
+    char     pci_addr[32];
+};
+
+struct ps3stor_data {
+    void*    pdata;
+    uint32_t size;
+};
+
+struct ps3stor_scsi_info
+{
+    uint32_t datalen;
+    uint32_t sgecount;
+    uint32_t sgeindex;
+    uint8_t  pad[4];
+    struct ps3stor_id_group id;
+};
+
+typedef struct ps3stor_scsi_req {
+    uint32_t reserved;
+    uint8_t  cmddir;
+    uint8_t  checklen;
+    uint8_t  pad[2];
+    uint8_t  cdb[PS3STOR_SCSI_CDB_LEN];
+    struct ps3stor_scsi_info req;
+} ps3stor_scsi_req_t;
+
+struct ps3stor_scsi_rsp_entry {
+    uint8_t  sensebuf[PS3STOR_SCSI_SENSE_BUFFER_LEN];
+    uint8_t  status;
+    uint8_t  pad1[3];
+    uint32_t xfercnt;
+    uint8_t  pad2[4];
+    uint32_t result;
+};
+
+struct ps3stor_scsi_rsp {
+    uint32_t reserved;
+    struct ps3stor_scsi_rsp_entry entry;
+};
+
+typedef struct ps3stor_pd_baseinfo{
+  uint16_t deviceid;
+  uint8_t  reserved1[2];
+  uint8_t  enclid;
+  uint8_t  reserved2;
+  uint16_t slotid;
+  uint64_t reserved[22];
+} ps3stor_pd_baseinfo_t;
+
+typedef struct ps3stor_batch_req{
+  uint32_t   idcount;
+  uint32_t   datasize;
+  struct ps3stor_id_group idgroup[0];
+} ps3stor_batch_req_t;
+
+typedef struct ps3stor_rsp_entry {
+  int32_t   result;
+  uint32_t  size;
+  uint8_t   data[0];
+} ps3stor_rsp_entry_t;
+
+typedef struct ps3stor_batch_rsp {
+  uint32_t count;
+  uint8_t rsp_entry[0]; // ps3stor_rsp_entry_t
+} ps3stor_batch_rsp_t;
+
+
+bool ps3stor_init();
+/////////////////////////////////////////////////////////////////////////////
+// ps3stor_channel
+
+/// The platform ps3stor channel abstraction
+class ps3stor_channel
+{
+public:
+  /// Initialize platform channel and register with ps3chn().
+  /// Must be implemented by platform module and register channel with set()
+  static void init();
+
+  ps3stor_channel()
+    { m_init = false; }
+
+  virtual ~ps3stor_channel()
+    { }
+ 
+   /// Error (number,message) pair
+  struct error_info {
+    explicit error_info(int n = 0)
+      : no(n) { }
+    error_info(int n, const char * m)
+      : no(n), msg(m) { }
+    void clear()
+      { no = 0; msg.erase(); }
+
+    int no;          ///< Error number
+    std::string msg; ///< Error message
+  };
+protected:
+  /// Set channel to use, must be called from init().
+  static void set(ps3stor_channel * channel)
+    { s_channel = channel; }
+
+public:
+  virtual ps3stor_errno channel_init() = 0;
+
+  virtual ps3stor_errno get_host_list(std::vector<unsigned> &hostlist) = 0;
+
+  ps3stor_errno get_enclcount(unsigned hostid, uint8_t &enclcount);  
+
+  ps3stor_errno get_encllist(unsigned hostid, struct ps3stor_encl_list* &encllist, size_t listsize);
+
+  ps3stor_errno pd_get_devcount_by_encl(unsigned hostid, uint8_t enclid, uint16_t &devcount);                                           
+
+  ps3stor_errno pd_get_devlist_by_encl(unsigned hostid, uint8_t enclid, uint16_t* &devlist, size_t listsize);
+
+  ps3stor_errno pd_get_baseinfo_by_devid(unsigned hostid, unsigned devid, ps3stor_pd_baseinfo_t &basinfo);
+
+  ps3stor_errno pd_scsi_passthrough(unsigned hostid, uint8_t eid, uint16_t sid, 
+                                            ps3stor_scsi_req_t &scsireq, ps3stor_scsi_rsp &scsirsp,
+                                            uint8_t * &scsidata, const size_t scsilen);
+
+protected:
+  virtual ps3stor_errno firecmd(unsigned hostid, struct ps3stor_msg_info *info, struct ps3stor_msg_info * ackinfo, unsigned acksize) = 0;
+
+  virtual ps3stor_errno firecmd_scsi(unsigned hostid, struct ps3stor_msg_info * reqinfo, struct ps3stor_msg_info * ackinfo,
+                                             unsigned acksize, struct ps3stor_data * scsidata, unsigned scsicount) = 0;
+
+  virtual struct ps3stor_tlv *add_tlv_data(struct ps3stor_tlv *tlv, unsigned type, const void *data, uint16_t size) = 0;
+  ///////////////////////////////////////////////
+  // Last error information
+  /// Set last error number and message.
+  /// Returns false always to allow use as a return expression.
+  virtual bool set_err(int no, const char * msg) = 0;
+
+  /// Set last error number and default message.
+  /// Message is retrieved from interface's get_msg_for_errno(no).
+  virtual bool set_err(int no) = 0;
+
+  bool   m_init;
+
+private:
+
+  friend ps3stor_channel * ps3chn(); // below
+  static ps3stor_channel * s_channel; ///< Pointer to the channel object.
+
+  // Prevent copy/assignment
+  ps3stor_channel(const ps3stor_channel &);
+  void operator=(const ps3stor_channel &);
+
+};
+
+/// Global access to the (usually singleton) ps3stor_channel
+inline ps3stor_channel * ps3chn()
+  { return ps3stor_channel::s_channel; }
+
+#endif
\ No newline at end of file
Index: smartctl.8.in
===================================================================
--- smartctl.8.in	(版本 5613)
+++ smartctl.8.in	(工作副本)
@@ -778,6 +778,21 @@
 \- the device consists of multiple SATA disks connected to a JMicron JMS56x
 USB to SATA RAID bridge.
 See \*(Aqjmb39x...\*(Aq above for valid arguments.
+.Sp
+.I ps3stor,n
+\- [Linux only]
+the device consists of one or more SCSI/SAS or SATA disks connected to a
+PS3StorRAID controller.
+The non-negative integer N (in the range of 0 to 127 inclusive) denotes the enclosure
+and S (range 0 to 128) denotes the slot.
+Use syntax such as:
+.br
+\fBsmartctl \-a \-d ps3stor,1 /dev/bus/0
+.br
+It is possible to set RAID device name as /dev/bus/N, where N is a
+SCSI bus number.
+.Sp
+.\" %ENDIF OS Linux
 .TP
 .B \-T TYPE, \-\-tolerance=TYPE
 [ATA only] Specifies how tolerant \fBsmartctl\fP should be of ATA and SMART
