smartmontools  SVN Rev 5304
Utility to control and monitor storage systems with "S.M.A.R.T."
dev_intelliprop.cpp
Go to the documentation of this file.
1 /*
2  * dev_intelliprop.cpp
3  *
4  * Home page of code is: https://www.smartmontools.org
5  *
6  * Copyright (C) 2016 Casey Biemiller <cbiemiller@intelliprop.com>
7  *
8  * SPDX-License-Identifier: GPL-2.0-or-later
9  */
10 
11 #include "config.h"
12 
13 #include "atacmds.h" // ATTR_PACKED, STATIC_ASSERT, ata_debugmode
14 #include "dev_interface.h"
15 #include "dev_tunnelled.h"
16 #include <errno.h>
17 
18 const char * dev_intelliprop_cpp_cvsid = "$Id: dev_intelliprop.cpp 5198 2021-02-01 20:36:02Z chrfranke $";
19 
20 //Vendor Specific log addresses
21 #define LOG_C0 0xc0
22 
23 // VS LOG MODE CONTROL BITS
24 enum {
25  IPROP_VS_LOG_MODE_CTL_AUTO_SUPPORTED = (0 << 0), // NOTE: Not supported
27  IPROP_VS_LOG_MODE_CTL_AUTO_ENABLED = (0 << 2), // NOTE: Not supported
29 };
30 
31 // VS LOG PORT SETTING BITS
32 enum {
43 };
44 
45 //This struct is used for the Vendor Specific log C0 on devices that support it.
46 #pragma pack(1)
48 {
49  uint32_t drive_select; // Bytes - [ 3: 0] of Log C0
50  uint32_t obsolete; // Bytes - [ 7: 4] of Log C0
51  uint8_t mode_control; // Byte - [ 8] of Log C0
52  uint8_t log_passthrough; // Byte - [ 9] of Log C0
53  uint16_t tier_id; // Bytes - [ 11: 10] of Log C0
54  uint32_t hw_version; // Bytes - [ 15: 12] of Log C0
55  uint32_t fw_version; // Bytes - [ 19: 16] of Log C0
56  uint8_t variant[8]; // Bytes - [ 27: 20] of Log C0
57  uint8_t reserved[228]; // Bytes - [255: 28] of Log C0
58  uint16_t port_0_settings[3]; // Bytes - [263:256] of Log C0
59  uint16_t port_0_reserved;
60  uint16_t port_1_settings[3]; // Bytes - [271:264] of Log C0
61  uint16_t port_1_reserved;
62  uint16_t port_2_settings[3]; // Bytes - [279:272] of Log C0
63  uint16_t port_2_reserved;
64  uint16_t port_3_settings[3]; // Bytes - [287:280] of Log C0
65  uint16_t port_3_reserved;
66  uint16_t port_4_settings[3]; // Bytes - [295:288] of Log C0
67  uint16_t port_4_reserved;
68  uint8_t reserved2[214]; // Bytes - [509:296] of Log C0
69  uint16_t crc; // Bytes - [511:510] of Log C0
71 #pragma pack()
73 
74 /**
75  * buffer is a pointer to a buffer of bytes, which should include data and
76  * also CRC if the function is being used to check CRC
77  * len is the number of bytes in the buffer (including CRC if it is present)
78  * check_crc is a boolean value, set true to check an existing CRC, false
79  * to calculate a new CRC
80  */
81 static uint16_t iprop_crc16_1(uint8_t * buffer, uint32_t len, bool check_crc)
82 {
83  uint8_t crc[16];
84  uint16_t crc_final = 0;
85  uint8_t crc_msb;
86  uint8_t data_msb;
87  uint32_t total_len;
88 
89  // Initialize CRC array
90  for (uint32_t ii = 0; ii < 16; ii++) {
91  crc[ii] = 0;
92  //crc[ii] = (crc_in >> ii) & 1;
93  }
94 
95  // If calculating a new CRC, we need to pad the data with extra zeroes
96  total_len = check_crc ? len : len + 2;
97 
98  // Loop for each byte, plus extra for the CRC itself
99  for (uint32_t ii = 0; ii < total_len; ii++) {
100  uint8_t data = (ii < len) ? buffer[ii] : 0;
101 
102  // Loop for each bit
103  for (uint32_t jj = 0; jj < 8; jj++) {
104  crc_msb = crc[15];
105  data_msb = (data >> (8 - jj - 1)) & 1;
106 
107  crc[15] = crc[14] ^ crc_msb;
108  crc[14] = crc[13];
109  crc[13] = crc[12];
110  crc[12] = crc[11];
111  crc[11] = crc[10] ^ crc_msb;
112  crc[10] = crc[9];
113  crc[9] = crc[8] ^ crc_msb;
114  crc[8] = crc[7] ^ crc_msb;
115  crc[7] = crc[6] ^ crc_msb;
116  crc[6] = crc[5];
117  crc[5] = crc[4] ^ crc_msb;
118  crc[4] = crc[3] ^ crc_msb;
119  crc[3] = crc[2];
120  crc[2] = crc[1] ^ crc_msb;
121  crc[1] = crc[0] ^ crc_msb;
122  crc[0] = data_msb ^ crc_msb;
123  }
124  }
125 
126  // Convert CRC array to final value
127  for (uint32_t ii = 0; ii < 16; ii++) {
128  if (crc[ii] == 1) {
129  crc_final |= (1 << ii);
130  } else {
131  crc_final &= ~(1 << ii);
132  }
133  }
134 
135  return crc_final;
136 }
137 
138 static void iprop_dump_log_structure(struct iprop_internal_log const * const log)
139 {
140  pout("Dumping LOG Structure:\n");
141  pout(" drive_select: 0x%08x\n", log->drive_select);
142  pout(" obsolete: 0x%08x\n", log->obsolete);
143  pout(" mode_control: 0x%02x\n", log->mode_control);
144  pout(" log_passthrough: 0x%02x\n", log->log_passthrough);
145  pout(" tier_id: 0x%04x\n", log->tier_id);
146  pout(" hw_version: 0x%08x\n", log->hw_version);
147  pout(" fw_version: 0x%08x\n", log->fw_version);
148  pout(" variant: \"");
149  for (int ii = 0; ii < 8; ii++) {
150  pout("%c", (char)log->variant[ii]);
151  }
152  pout("\"\n");
153  pout(" port_0_settings(Gen 1): 0x%08x\n", log->port_0_settings[0]);
154  pout(" port_0_settings(Gen 2): 0x%08x\n", log->port_0_settings[1]);
155  pout(" port_0_settings(Gen 3): 0x%08x\n", log->port_0_settings[2]);
156  pout(" port_1_settings(Gen 1): 0x%08x\n", log->port_1_settings[0]);
157  pout(" port_1_settings(Gen 2): 0x%08x\n", log->port_1_settings[1]);
158  pout(" port_1_settings(Gen 3): 0x%08x\n", log->port_1_settings[2]);
159  pout(" port_2_settings(Gen 1): 0x%08x\n", log->port_2_settings[0]);
160  pout(" port_2_settings(Gen 2): 0x%08x\n", log->port_2_settings[1]);
161  pout(" port_2_settings(Gen 3): 0x%08x\n", log->port_2_settings[2]);
162  pout(" port_3_settings(Gen 1): 0x%08x\n", log->port_3_settings[0]);
163  pout(" port_3_settings(Gen 2): 0x%08x\n", log->port_3_settings[1]);
164  pout(" port_3_settings(Gen 3): 0x%08x\n", log->port_3_settings[2]);
165  pout(" port_4_settings(Gen 1): 0x%08x\n", log->port_4_settings[0]);
166  pout(" port_4_settings(Gen 2): 0x%08x\n", log->port_4_settings[1]);
167  pout(" port_4_settings(Gen 3): 0x%08x\n", log->port_4_settings[2]);
168  pout(" crc: 0x%04x\n", log->crc);
169  pout("\n");
170 }
171 
172 static bool iprop_switch_routed_drive(ata_device * device, int drive_select)
173 {
174  // Declare a log page buffer and initialize it with what is on the drive currently
175  iprop_internal_log write_payload;
176  if (!ataReadLogExt(device, LOG_C0, 0, 0, &write_payload, 1))
177  return device->set_err(EIO, "intelliprop: Initial Read Log failed: %s", device->get_errmsg());
178 
179  // Check the returned data is good
180  uint16_t const crc_check = iprop_crc16_1((uint8_t *)&write_payload,
181  sizeof(struct iprop_internal_log),
182  false);
183 
184 
185  //If this first read fails the crc check, the log can be still sent with routing information
186  //as long as everything else in the log is zeroed. So there is no need to return false.
187  if (crc_check != 0) {
188  if (ata_debugmode)
189  pout("Intelliprop WARNING: Received log crc(0x%04X) is invalid!\n", crc_check);
190  iprop_dump_log_structure(&write_payload);
191  memset(&write_payload, 0, sizeof(struct iprop_internal_log));
192  }
193 
194  //The option to read the log, even if successful, could be useful
195  if (ata_debugmode)
196  iprop_dump_log_structure(&write_payload);
197 
198  // Modify the current drive select to what we were given
199  write_payload.drive_select = (uint32_t)drive_select;
200  if (ata_debugmode)
201  pout("Intelliprop - Change to port 0x%08X.\n", write_payload.drive_select);
202  write_payload.log_passthrough = 0; // TEST (Set to 1, non hydra member drive will abort --> test error handling)
203  write_payload.tier_id = 0; // TEST (Set to non-zero, non hydra member drive will abort --> test error handling)
204 
205  // Update the CRC area
206  uint16_t const crc_new = iprop_crc16_1((uint8_t *)&write_payload,
207  sizeof(struct iprop_internal_log) - sizeof(uint16_t),
208  false);
209  write_payload.crc = (crc_new >> 8) | (crc_new << 8);
210 
211  // Check our CRC work
212  uint16_t const crc_check2 = iprop_crc16_1((uint8_t *)&write_payload,
213  sizeof(struct iprop_internal_log),
214  false);
215  if (crc_check2 != 0)
216  return device->set_err(EIO, "intelliprop: Re-calculated log crc(0x%04X) is invalid!", crc_check2);
217 
218  // Apply the Write LOG
219  if (!ataWriteLogExt(device, LOG_C0, 0, &write_payload, 1))
220  return device->set_err(EIO, "intelliprop: Write Log failed: %s", device->get_errmsg());
221 
222  // Check that the Write LOG was applied
223  iprop_internal_log check_payload;
224  if (!ataReadLogExt(device, LOG_C0, 0, 0, &check_payload, 1))
225  return device->set_err(EIO, "intelliprop: Secondary Read Log failed: %s", device->get_errmsg());
226 
227  if (check_payload.drive_select != write_payload.drive_select) {
228  if (ata_debugmode > 1)
229  iprop_dump_log_structure(&check_payload);
230  return device->set_err(EIO, "intelliprop: Current drive select val(0x%08X) is not expected(0x%08X)",
231  check_payload.drive_select,
232  write_payload.drive_select);
233  }
234 
235  return true;
236 }
237 
238 namespace intelliprop {
239 
241 : public tunnelled_device<
242  /*implements*/ ata_device,
243  /*by using an*/ ata_device
244  >
245 {
246 public:
247  intelliprop_device(smart_interface * intf, unsigned phydrive, ata_device * atadev);
248 
249  virtual ~intelliprop_device();
250 
251  virtual bool open() override;
252 
253  virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) override;
254 
255 private:
256  unsigned m_phydrive;
257 };
258 
259 
261 : smart_device(intf, atadev->get_dev_name(), "intelliprop", "intelliprop"),
263  m_phydrive(phydrive)
264 {
265  set_info().info_name = strprintf("%s [intelliprop_disk_%u]", atadev->get_info_name(), phydrive);
266 }
267 
269 {
270 }
271 
273 {
275  return false;
276 
277  ata_device * atadev = get_tunnel_dev();
278  if (!iprop_switch_routed_drive(atadev, m_phydrive)) {
279  close();
280  return set_err(atadev->get_err());
281  }
282 
283  return true;
284 }
285 
287 {
288  return get_tunnel_dev()->ata_pass_through(in, out);
289 }
290 }//namespace
291 
293 {
294  // Take temporary ownership of 'atadev' to delete it on error
295  ata_device_auto_ptr atadev_holder(atadev);
296 
297  unsigned phydrive = ~0; int n = -1;
298  sscanf(type, "intelliprop,%u%n", &phydrive, &n);
299  if (!(n == (int)strlen(type) && phydrive <= 3)) {
300  set_err(EINVAL, "Option '-d intelliprop,N' must have 0 <= N <= 3");
301  return 0;
302  }
303 
304  ata_device * itldev = new intelliprop::intelliprop_device(this, phydrive, atadev);
305  // 'atadev' is now owned by 'itldev'
306  atadev_holder.release();
307  return itldev;
308 }
bool ataWriteLogExt(ata_device *device, unsigned char logaddr, unsigned page, void *data, unsigned nsectors)
Definition: atacmds.cpp:1080
bool ataReadLogExt(ata_device *device, unsigned char logaddr, unsigned char features, unsigned page, void *data, unsigned nsectors)
Definition: atacmds.cpp:1111
unsigned char ata_debugmode
Definition: atacmds.cpp:33
Smart pointer class for device pointers.
device_type * release()
Return the pointer and release ownership.
ATA device access.
virtual bool ata_pass_through(const ata_cmd_in &in, ata_cmd_out &out)=0
ATA pass through.
intelliprop_device(smart_interface *intf, unsigned phydrive, ata_device *atadev)
virtual bool open() override
Open device, return false on error.
virtual bool ata_pass_through(const ata_cmd_in &in, ata_cmd_out &out) override
ATA pass through.
Base class for all devices.
Definition: dev_interface.h:33
const char * get_info_name() const
Get informal name.
const char * get_errmsg() const
Get last error message.
const error_info & get_err() const
Get last error info struct.
bool set_err(int no, const char *msg,...) __attribute_format_printf(3
Set last error number and message.
device_info & set_info()
R/W access to device info struct.
The platform interface abstraction.
virtual ata_device * get_intelliprop_device(const char *type, ata_device *atadev)
Return filter for Intelliprop controllers.
bool set_err(int no, const char *msg,...) __attribute_format_printf(3
Set last error number and message.
virtual bool close() override
Close device, return false on error.
Implement a device by tunneling through another device.
Definition: dev_tunnelled.h:56
STATIC_ASSERT(sizeof(iprop_internal_log)==512)
const char * dev_intelliprop_cpp_cvsid
static bool iprop_switch_routed_drive(ata_device *device, int drive_select)
@ IPROP_VS_LOG_PORT_TX_VOD_MASK
@ IPROP_VS_LOG_PORT_TX_PREEMP_SHIFT
@ IPROP_VS_LOG_PORT_TX_PREEMP_MASK
@ IPROP_VS_LOG_PORT_RX_EQ_SHIFT
@ IPROP_VS_LOG_PORT_WRITE_ENABLE_VALID
@ IPROP_VS_LOG_PORT_WRITE_ENABLE_MASK
@ IPROP_VS_LOG_PORT_RX_DC_GAIN_MASK
@ IPROP_VS_LOG_PORT_RX_EQ_MASK
@ IPROP_VS_LOG_PORT_RX_DC_GAIN_SHIFT
@ IPROP_VS_LOG_PORT_TX_VOD_SHIFT
#define LOG_C0
static void iprop_dump_log_structure(struct iprop_internal_log const *const log)
@ IPROP_VS_LOG_MODE_CTL_AUTO_ENABLED
@ IPROP_VS_LOG_MODE_CTL_MANUAL_ENABLED
@ IPROP_VS_LOG_MODE_CTL_MANUAL_SUPPORTED
@ IPROP_VS_LOG_MODE_CTL_AUTO_SUPPORTED
static uint16_t iprop_crc16_1(uint8_t *buffer, uint32_t len, bool check_crc)
buffer is a pointer to a buffer of bytes, which should include data and also CRC if the function is b...
struct iprop_internal_log ATTR_PACKED
ptr_t buffer
Definition: megaraid.h:3
ptr_t data
Definition: megaraid.h:15
void pout(const char *fmt,...)
Definition: smartd.cpp:1308
ATA pass through input parameters.
ATA pass through output parameters.
uint16_t port_1_settings[3]
uint16_t port_0_settings[3]
uint16_t port_3_settings[3]
uint16_t port_2_settings[3]
uint16_t port_4_settings[3]
uint8_t reserved2[214]
std::string info_name
Informal name.
Definition: dev_interface.h:46
std::string strprintf(const char *fmt,...)
Definition: utility.cpp:772