4 * Home page of code is: http://www.smartmontools.org
6 * Copyright (C) 2016 Casey Biemiller <cbiemiller@intelliprop.com>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
13 * You should have received a copy of the GNU General Public License
14 * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
20 #include "atacmds.h" //ATTR_PACKED and ASSERT_SIZEOF_STRUCT
21 #include "dev_interface.h"
22 #include "dev_intelliprop.h"
23 #include "dev_tunnelled.h"
26 const char * dev_intelliprop_cpp_cvsid
= "$Id: dev_intelliprop.cpp 4370 2017-01-11 20:35:38Z chrfranke $"
27 DEV_INTELLIPROP_H_CVSID
;
29 //Vendor Specific log addresses
32 // VS LOG MODE CONTROL BITS
34 IPROP_VS_LOG_MODE_CTL_AUTO_SUPPORTED
= (0 << 0), // NOTE: Not supported
35 IPROP_VS_LOG_MODE_CTL_MANUAL_SUPPORTED
= (1 << 1),
36 IPROP_VS_LOG_MODE_CTL_AUTO_ENABLED
= (0 << 2), // NOTE: Not supported
37 IPROP_VS_LOG_MODE_CTL_MANUAL_ENABLED
= (1 << 3),
40 // VS LOG PORT SETTING BITS
42 IPROP_VS_LOG_PORT_WRITE_ENABLE_MASK
= 0xC000,
43 IPROP_VS_LOG_PORT_WRITE_ENABLE_VALID
= 0x8000,
44 IPROP_VS_LOG_PORT_RX_DC_GAIN_MASK
= 0x3000,
45 IPROP_VS_LOG_PORT_RX_DC_GAIN_SHIFT
= 12,
46 IPROP_VS_LOG_PORT_RX_EQ_MASK
= 0x0F00,
47 IPROP_VS_LOG_PORT_RX_EQ_SHIFT
= 8,
48 IPROP_VS_LOG_PORT_TX_PREEMP_MASK
= 0x00F8,
49 IPROP_VS_LOG_PORT_TX_PREEMP_SHIFT
= 3,
50 IPROP_VS_LOG_PORT_TX_VOD_MASK
= 0x0007,
51 IPROP_VS_LOG_PORT_TX_VOD_SHIFT
= 0,
54 //This struct is used for the Vendor Specific log C0 on devices that support it.
56 struct iprop_internal_log
58 uint32_t drive_select
; // Bytes - [ 3: 0] of Log C0
59 uint32_t obsolete
; // Bytes - [ 7: 4] of Log C0
60 uint8_t mode_control
; // Byte - [ 8] of Log C0
61 uint8_t log_passthrough
; // Byte - [ 9] of Log C0
62 uint16_t tier_id
; // Bytes - [ 11: 10] of Log C0
63 uint32_t hw_version
; // Bytes - [ 15: 12] of Log C0
64 uint32_t fw_version
; // Bytes - [ 19: 16] of Log C0
65 uint8_t variant
[8]; // Bytes - [ 27: 20] of Log C0
66 uint8_t reserved
[228]; // Bytes - [255: 28] of Log C0
67 uint16_t port_0_settings
[3]; // Bytes - [263:256] of Log C0
68 uint16_t port_0_reserved
;
69 uint16_t port_1_settings
[3]; // Bytes - [271:264] of Log C0
70 uint16_t port_1_reserved
;
71 uint16_t port_2_settings
[3]; // Bytes - [279:272] of Log C0
72 uint16_t port_2_reserved
;
73 uint16_t port_3_settings
[3]; // Bytes - [287:280] of Log C0
74 uint16_t port_3_reserved
;
75 uint16_t port_4_settings
[3]; // Bytes - [295:288] of Log C0
76 uint16_t port_4_reserved
;
77 uint8_t reserved2
[214]; // Bytes - [509:296] of Log C0
78 uint16_t crc
; // Bytes - [511:510] of Log C0
81 ASSERT_SIZEOF_STRUCT(iprop_internal_log
, 512);
84 * buffer is a pointer to a buffer of bytes, which should include data and
85 * also CRC if the function is being used to check CRC
86 * len is the number of bytes in the buffer (including CRC if it is present)
87 * check_crc is a boolean value, set true to check an existing CRC, false
88 * to calculate a new CRC
90 static uint16_t iprop_crc16_1(uint8_t * buffer
, uint32_t len
, bool check_crc
)
93 uint16_t crc_final
= 0;
98 // Initialize CRC array
99 for (uint32_t ii
= 0; ii
< 16; ii
++) {
101 //crc[ii] = (crc_in >> ii) & 1;
104 // If calculating a new CRC, we need to pad the data with extra zeroes
105 total_len
= check_crc
? len
: len
+ 2;
107 // Loop for each byte, plus extra for the CRC itself
108 for (uint32_t ii
= 0; ii
< total_len
; ii
++) {
109 uint8_t data
= (ii
< len
) ? buffer
[ii
] : 0;
112 for (uint32_t jj
= 0; jj
< 8; jj
++) {
114 data_msb
= (data
>> (8 - jj
- 1)) & 1;
116 crc
[15] = crc
[14] ^ crc_msb
;
120 crc
[11] = crc
[10] ^ crc_msb
;
122 crc
[9] = crc
[8] ^ crc_msb
;
123 crc
[8] = crc
[7] ^ crc_msb
;
124 crc
[7] = crc
[6] ^ crc_msb
;
126 crc
[5] = crc
[4] ^ crc_msb
;
127 crc
[4] = crc
[3] ^ crc_msb
;
129 crc
[2] = crc
[1] ^ crc_msb
;
130 crc
[1] = crc
[0] ^ crc_msb
;
131 crc
[0] = data_msb
^ crc_msb
;
135 // Convert CRC array to final value
136 for (uint32_t ii
= 0; ii
< 16; ii
++) {
138 crc_final
|= (1 << ii
);
140 crc_final
&= ~(1 << ii
);
147 static void iprop_dump_log_structure(struct iprop_internal_log
const * const log
)
149 pout("Dumping LOG Structure:\n");
150 pout(" drive_select: 0x%08x\n", log
->drive_select
);
151 pout(" obsolete: 0x%08x\n", log
->obsolete
);
152 pout(" mode_control: 0x%02x\n", log
->mode_control
);
153 pout(" log_passthrough: 0x%02x\n", log
->log_passthrough
);
154 pout(" tier_id: 0x%04x\n", log
->tier_id
);
155 pout(" hw_version: 0x%08x\n", log
->hw_version
);
156 pout(" fw_version: 0x%08x\n", log
->fw_version
);
157 pout(" variant: \"");
158 for (int ii
= 0; ii
< 8; ii
++) {
159 pout("%c", (char)log
->variant
[ii
]);
162 pout(" port_0_settings(Gen 1): 0x%08x\n", log
->port_0_settings
[0]);
163 pout(" port_0_settings(Gen 2): 0x%08x\n", log
->port_0_settings
[1]);
164 pout(" port_0_settings(Gen 3): 0x%08x\n", log
->port_0_settings
[2]);
165 pout(" port_1_settings(Gen 1): 0x%08x\n", log
->port_1_settings
[0]);
166 pout(" port_1_settings(Gen 2): 0x%08x\n", log
->port_1_settings
[1]);
167 pout(" port_1_settings(Gen 3): 0x%08x\n", log
->port_1_settings
[2]);
168 pout(" port_2_settings(Gen 1): 0x%08x\n", log
->port_2_settings
[0]);
169 pout(" port_2_settings(Gen 2): 0x%08x\n", log
->port_2_settings
[1]);
170 pout(" port_2_settings(Gen 3): 0x%08x\n", log
->port_2_settings
[2]);
171 pout(" port_3_settings(Gen 1): 0x%08x\n", log
->port_3_settings
[0]);
172 pout(" port_3_settings(Gen 2): 0x%08x\n", log
->port_3_settings
[1]);
173 pout(" port_3_settings(Gen 3): 0x%08x\n", log
->port_3_settings
[2]);
174 pout(" port_4_settings(Gen 1): 0x%08x\n", log
->port_4_settings
[0]);
175 pout(" port_4_settings(Gen 2): 0x%08x\n", log
->port_4_settings
[1]);
176 pout(" port_4_settings(Gen 3): 0x%08x\n", log
->port_4_settings
[2]);
177 pout(" crc: 0x%04x\n", log
->crc
);
181 static bool iprop_switch_routed_drive(ata_device
* device
, int drive_select
)
183 // Declare a log page buffer and initialize it with what is on the drive currently
184 iprop_internal_log write_payload
;
185 if (!ataReadLogExt(device
, LOG_C0
, 0, 0, &write_payload
, 1))
186 return device
->set_err(EIO
, "intelliprop: Initial Read Log failed: %s", device
->get_errmsg());
188 // Check the returned data is good
189 uint16_t const crc_check
= iprop_crc16_1((uint8_t *)&write_payload
,
190 sizeof(struct iprop_internal_log
),
194 //If this first read fails the crc check, the log can be still sent with routing information
195 //as long as everything else in the log is zeroed. So there is no need to return false.
196 if (crc_check
!= 0) {
198 pout("Intelliprop WARNING: Received log crc(0x%04X) is invalid!\n", crc_check
);
199 iprop_dump_log_structure(&write_payload
);
200 memset(&write_payload
, 0, sizeof(struct iprop_internal_log
));
203 //The option to read the log, even if successful, could be useful
205 iprop_dump_log_structure(&write_payload
);
207 // Modify the current drive select to what we were given
208 write_payload
.drive_select
= (uint32_t)drive_select
;
210 pout("Intelliprop - Change to port 0x%08X.\n", write_payload
.drive_select
);
211 write_payload
.log_passthrough
= 0; // TEST (Set to 1, non hydra member drive will abort --> test error handling)
212 write_payload
.tier_id
= 0; // TEST (Set to non-zero, non hydra member drive will abort --> test error handling)
214 // Update the CRC area
215 uint16_t const crc_new
= iprop_crc16_1((uint8_t *)&write_payload
,
216 sizeof(struct iprop_internal_log
) - sizeof(uint16_t),
218 write_payload
.crc
= (crc_new
>> 8) | (crc_new
<< 8);
220 // Check our CRC work
221 uint16_t const crc_check2
= iprop_crc16_1((uint8_t *)&write_payload
,
222 sizeof(struct iprop_internal_log
),
225 return device
->set_err(EIO
, "intelliprop: Re-calculated log crc(0x%04X) is invalid!", crc_check2
);
227 // Apply the Write LOG
228 if (!ataWriteLogExt(device
, LOG_C0
, 0, &write_payload
, 1))
229 return device
->set_err(EIO
, "intelliprop: Write Log failed: %s", device
->get_errmsg());
231 // Check that the Write LOG was applied
232 iprop_internal_log check_payload
;
233 if (!ataReadLogExt(device
, LOG_C0
, 0, 0, &check_payload
, 1))
234 return device
->set_err(EIO
, "intelliprop: Secondary Read Log failed: %s", device
->get_errmsg());
236 if (check_payload
.drive_select
!= write_payload
.drive_select
) {
237 if (ata_debugmode
> 1)
238 iprop_dump_log_structure(&check_payload
);
239 return device
->set_err(EIO
, "intelliprop: Current drive select val(0x%08X) is not expected(0x%08X)",
240 check_payload
.drive_select
,
241 write_payload
.drive_select
);
247 namespace intelliprop
{
249 class intelliprop_device
250 : public tunnelled_device
<
251 /*implements*/ ata_device
,
252 /*by using an*/ ata_device
256 intelliprop_device(smart_interface
* intf
, unsigned phydrive
, ata_device
* atadev
);
258 virtual ~intelliprop_device() throw();
262 virtual bool ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
);
269 intelliprop_device::intelliprop_device(smart_interface
* intf
, unsigned phydrive
, ata_device
* atadev
)
270 : smart_device(intf
, atadev
->get_dev_name(), "intelliprop", "intelliprop"),
271 tunnelled_device
<ata_device
, ata_device
>(atadev
),
274 set_info().info_name
= strprintf("%s [intelliprop_disk_%u]", atadev
->get_info_name(), phydrive
);
277 intelliprop_device::~intelliprop_device() throw()
281 bool intelliprop_device::open()
283 if (!tunnelled_device
<ata_device
, ata_device
>::open())
286 ata_device
* atadev
= get_tunnel_dev();
287 if (!iprop_switch_routed_drive(atadev
, m_phydrive
)) {
289 return set_err(atadev
->get_err());
295 bool intelliprop_device::ata_pass_through(const ata_cmd_in
& in
, ata_cmd_out
& out
)
297 return get_tunnel_dev()->ata_pass_through(in
, out
);
301 ata_device
* get_intelliprop_device(smart_interface
* intf
, unsigned phydrive
, ata_device
* atadev
)
303 return new intelliprop::intelliprop_device(intf
, phydrive
, atadev
);