4 * Home page of code is: http://www.smartmontools.org
6 * Copyright (C) 2016 Casey Biemiller <cbiemiller@intelliprop.com>
8 * SPDX-License-Identifier: GPL-2.0-or-later
13 #include "atacmds.h" //ATTR_PACKED and ASSERT_SIZEOF_STRUCT
14 #include "dev_interface.h"
15 #include "dev_intelliprop.h"
16 #include "dev_tunnelled.h"
19 const char * dev_intelliprop_cpp_cvsid
= "$Id: dev_intelliprop.cpp 4760 2018-08-19 18:45:53Z chrfranke $"
20 DEV_INTELLIPROP_H_CVSID
;
22 //Vendor Specific log addresses
25 // VS LOG MODE CONTROL BITS
27 IPROP_VS_LOG_MODE_CTL_AUTO_SUPPORTED
= (0 << 0), // NOTE: Not supported
28 IPROP_VS_LOG_MODE_CTL_MANUAL_SUPPORTED
= (1 << 1),
29 IPROP_VS_LOG_MODE_CTL_AUTO_ENABLED
= (0 << 2), // NOTE: Not supported
30 IPROP_VS_LOG_MODE_CTL_MANUAL_ENABLED
= (1 << 3),
33 // VS LOG PORT SETTING BITS
35 IPROP_VS_LOG_PORT_WRITE_ENABLE_MASK
= 0xC000,
36 IPROP_VS_LOG_PORT_WRITE_ENABLE_VALID
= 0x8000,
37 IPROP_VS_LOG_PORT_RX_DC_GAIN_MASK
= 0x3000,
38 IPROP_VS_LOG_PORT_RX_DC_GAIN_SHIFT
= 12,
39 IPROP_VS_LOG_PORT_RX_EQ_MASK
= 0x0F00,
40 IPROP_VS_LOG_PORT_RX_EQ_SHIFT
= 8,
41 IPROP_VS_LOG_PORT_TX_PREEMP_MASK
= 0x00F8,
42 IPROP_VS_LOG_PORT_TX_PREEMP_SHIFT
= 3,
43 IPROP_VS_LOG_PORT_TX_VOD_MASK
= 0x0007,
44 IPROP_VS_LOG_PORT_TX_VOD_SHIFT
= 0,
47 //This struct is used for the Vendor Specific log C0 on devices that support it.
49 struct iprop_internal_log
51 uint32_t drive_select
; // Bytes - [ 3: 0] of Log C0
52 uint32_t obsolete
; // Bytes - [ 7: 4] of Log C0
53 uint8_t mode_control
; // Byte - [ 8] of Log C0
54 uint8_t log_passthrough
; // Byte - [ 9] of Log C0
55 uint16_t tier_id
; // Bytes - [ 11: 10] of Log C0
56 uint32_t hw_version
; // Bytes - [ 15: 12] of Log C0
57 uint32_t fw_version
; // Bytes - [ 19: 16] of Log C0
58 uint8_t variant
[8]; // Bytes - [ 27: 20] of Log C0
59 uint8_t reserved
[228]; // Bytes - [255: 28] of Log C0
60 uint16_t port_0_settings
[3]; // Bytes - [263:256] of Log C0
61 uint16_t port_0_reserved
;
62 uint16_t port_1_settings
[3]; // Bytes - [271:264] of Log C0
63 uint16_t port_1_reserved
;
64 uint16_t port_2_settings
[3]; // Bytes - [279:272] of Log C0
65 uint16_t port_2_reserved
;
66 uint16_t port_3_settings
[3]; // Bytes - [287:280] of Log C0
67 uint16_t port_3_reserved
;
68 uint16_t port_4_settings
[3]; // Bytes - [295:288] of Log C0
69 uint16_t port_4_reserved
;
70 uint8_t reserved2
[214]; // Bytes - [509:296] of Log C0
71 uint16_t crc
; // Bytes - [511:510] of Log C0
74 ASSERT_SIZEOF_STRUCT(iprop_internal_log
, 512);
77 * buffer is a pointer to a buffer of bytes, which should include data and
78 * also CRC if the function is being used to check CRC
79 * len is the number of bytes in the buffer (including CRC if it is present)
80 * check_crc is a boolean value, set true to check an existing CRC, false
81 * to calculate a new CRC
83 static uint16_t iprop_crc16_1(uint8_t * buffer
, uint32_t len
, bool check_crc
)
86 uint16_t crc_final
= 0;
91 // Initialize CRC array
92 for (uint32_t ii
= 0; ii
< 16; ii
++) {
94 //crc[ii] = (crc_in >> ii) & 1;
97 // If calculating a new CRC, we need to pad the data with extra zeroes
98 total_len
= check_crc
? len
: len
+ 2;
100 // Loop for each byte, plus extra for the CRC itself
101 for (uint32_t ii
= 0; ii
< total_len
; ii
++) {
102 uint8_t data
= (ii
< len
) ? buffer
[ii
] : 0;
105 for (uint32_t jj
= 0; jj
< 8; jj
++) {
107 data_msb
= (data
>> (8 - jj
- 1)) & 1;
109 crc
[15] = crc
[14] ^ crc_msb
;
113 crc
[11] = crc
[10] ^ crc_msb
;
115 crc
[9] = crc
[8] ^ crc_msb
;
116 crc
[8] = crc
[7] ^ crc_msb
;
117 crc
[7] = crc
[6] ^ crc_msb
;
119 crc
[5] = crc
[4] ^ crc_msb
;
120 crc
[4] = crc
[3] ^ crc_msb
;
122 crc
[2] = crc
[1] ^ crc_msb
;
123 crc
[1] = crc
[0] ^ crc_msb
;
124 crc
[0] = data_msb
^ crc_msb
;
128 // Convert CRC array to final value
129 for (uint32_t ii
= 0; ii
< 16; ii
++) {
131 crc_final
|= (1 << ii
);
133 crc_final
&= ~(1 << ii
);
140 static void iprop_dump_log_structure(struct iprop_internal_log
const * const log
)
142 pout("Dumping LOG Structure:\n");
143 pout(" drive_select: 0x%08x\n", log
->drive_select
);
144 pout(" obsolete: 0x%08x\n", log
->obsolete
);
145 pout(" mode_control: 0x%02x\n", log
->mode_control
);
146 pout(" log_passthrough: 0x%02x\n", log
->log_passthrough
);
147 pout(" tier_id: 0x%04x\n", log
->tier_id
);
148 pout(" hw_version: 0x%08x\n", log
->hw_version
);
149 pout(" fw_version: 0x%08x\n", log
->fw_version
);
150 pout(" variant: \"");
151 for (int ii
= 0; ii
< 8; ii
++) {
152 pout("%c", (char)log
->variant
[ii
]);
155 pout(" port_0_settings(Gen 1): 0x%08x\n", log
->port_0_settings
[0]);
156 pout(" port_0_settings(Gen 2): 0x%08x\n", log
->port_0_settings
[1]);
157 pout(" port_0_settings(Gen 3): 0x%08x\n", log
->port_0_settings
[2]);
158 pout(" port_1_settings(Gen 1): 0x%08x\n", log
->port_1_settings
[0]);
159 pout(" port_1_settings(Gen 2): 0x%08x\n", log
->port_1_settings
[1]);
160 pout(" port_1_settings(Gen 3): 0x%08x\n", log
->port_1_settings
[2]);
161 pout(" port_2_settings(Gen 1): 0x%08x\n", log
->port_2_settings
[0]);
162 pout(" port_2_settings(Gen 2): 0x%08x\n", log
->port_2_settings
[1]);
163 pout(" port_2_settings(Gen 3): 0x%08x\n", log
->port_2_settings
[2]);
164 pout(" port_3_settings(Gen 1): 0x%08x\n", log
->port_3_settings
[0]);
165 pout(" port_3_settings(Gen 2): 0x%08x\n", log
->port_3_settings
[1]);
166 pout(" port_3_settings(Gen 3): 0x%08x\n", log
->port_3_settings
[2]);
167 pout(" port_4_settings(Gen 1): 0x%08x\n", log
->port_4_settings
[0]);
168 pout(" port_4_settings(Gen 2): 0x%08x\n", log
->port_4_settings
[1]);
169 pout(" port_4_settings(Gen 3): 0x%08x\n", log
->port_4_settings
[2]);
170 pout(" crc: 0x%04x\n", log
->crc
);
174 static bool iprop_switch_routed_drive(ata_device
* device
, int drive_select
)
176 // Declare a log page buffer and initialize it with what is on the drive currently
177 iprop_internal_log write_payload
;
178 if (!ataReadLogExt(device
, LOG_C0
, 0, 0, &write_payload
, 1))
179 return device
->set_err(EIO
, "intelliprop: Initial Read Log failed: %s", device
->get_errmsg());
181 // Check the returned data is good
182 uint16_t const crc_check
= iprop_crc16_1((uint8_t *)&write_payload
,
183 sizeof(struct iprop_internal_log
),
187 //If this first read fails the crc check, the log can be still sent with routing information
188 //as long as everything else in the log is zeroed. So there is no need to return false.
189 if (crc_check
!= 0) {
191 pout("Intelliprop WARNING: Received log crc(0x%04X) is invalid!\n", crc_check
);
192 iprop_dump_log_structure(&write_payload
);
193 memset(&write_payload
, 0, sizeof(struct iprop_internal_log
));
196 //The option to read the log, even if successful, could be useful
198 iprop_dump_log_structure(&write_payload
);
200 // Modify the current drive select to what we were given
201 write_payload
.drive_select
= (uint32_t)drive_select
;
203 pout("Intelliprop - Change to port 0x%08X.\n", write_payload
.drive_select
);
204 write_payload
.log_passthrough
= 0; // TEST (Set to 1, non hydra member drive will abort --> test error handling)
205 write_payload
.tier_id
= 0; // TEST (Set to non-zero, non hydra member drive will abort --> test error handling)
207 // Update the CRC area
208 uint16_t const crc_new
= iprop_crc16_1((uint8_t *)&write_payload
,
209 sizeof(struct iprop_internal_log
) - sizeof(uint16_t),
211 write_payload
.crc
= (crc_new
>> 8) | (crc_new
<< 8);
213 // Check our CRC work
214 uint16_t const crc_check2
= iprop_crc16_1((uint8_t *)&write_payload
,
215 sizeof(struct iprop_internal_log
),
218 return device
->set_err(EIO
, "intelliprop: Re-calculated log crc(0x%04X) is invalid!", crc_check2
);
220 // Apply the Write LOG
221 if (!ataWriteLogExt(device
, LOG_C0
, 0, &write_payload
, 1))
222 return device
->set_err(EIO
, "intelliprop: Write Log failed: %s", device
->get_errmsg());
224 // Check that the Write LOG was applied
225 iprop_internal_log check_payload
;
226 if (!ataReadLogExt(device
, LOG_C0
, 0, 0, &check_payload
, 1))
227 return device
->set_err(EIO
, "intelliprop: Secondary Read Log failed: %s", device
->get_errmsg());
229 if (check_payload
.drive_select
!= write_payload
.drive_select
) {
230 if (ata_debugmode
> 1)
231 iprop_dump_log_structure(&check_payload
);
232 return device
->set_err(EIO
, "intelliprop: Current drive select val(0x%08X) is not expected(0x%08X)",
233 check_payload
.drive_select
,
234 write_payload
.drive_select
);
240 namespace intelliprop
{
242 class intelliprop_device
243 : public tunnelled_device
<
244 /*implements*/ ata_device
,
245 /*by using an*/ ata_device
249 intelliprop_device(smart_interface
* intf
, unsigned phydrive
, ata_device
* atadev
);
251 virtual ~intelliprop_device() throw();
255 virtual bool ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
);
262 intelliprop_device::intelliprop_device(smart_interface
* intf
, unsigned phydrive
, ata_device
* atadev
)
263 : smart_device(intf
, atadev
->get_dev_name(), "intelliprop", "intelliprop"),
264 tunnelled_device
<ata_device
, ata_device
>(atadev
),
267 set_info().info_name
= strprintf("%s [intelliprop_disk_%u]", atadev
->get_info_name(), phydrive
);
270 intelliprop_device::~intelliprop_device() throw()
274 bool intelliprop_device::open()
276 if (!tunnelled_device
<ata_device
, ata_device
>::open())
279 ata_device
* atadev
= get_tunnel_dev();
280 if (!iprop_switch_routed_drive(atadev
, m_phydrive
)) {
282 return set_err(atadev
->get_err());
288 bool intelliprop_device::ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
)
290 return get_tunnel_dev()->ata_pass_through(in
, out
);
294 ata_device
* get_intelliprop_device(smart_interface
* intf
, unsigned phydrive
, ata_device
* atadev
)
296 return new intelliprop::intelliprop_device(intf
, phydrive
, atadev
);