Merge lp:~psusi/ubuntu/raring/libatasmart/fix-status into lp:ubuntu/raring/libatasmart
- Raring (13.04)
- fix-status
- Merge into raring
Proposed by
Phillip Susi
Status: | Merged |
---|---|
Merge reported by: | Martin Pitt |
Merged at revision: | not available |
Proposed branch: | lp:~psusi/ubuntu/raring/libatasmart/fix-status |
Merge into: | lp:ubuntu/raring/libatasmart |
Diff against target: |
3141 lines (+3094/-2) 6 files modified
.pc/applied-patches (+1/-0) .pc/fix-status-io-error.patch/atasmart.c (+3056/-0) atasmart.c (+2/-2) debian/changelog (+8/-0) debian/patches/fix-status-io-error.patch (+26/-0) debian/patches/series (+1/-0) |
To merge this branch: | bzr merge lp:~psusi/ubuntu/raring/libatasmart/fix-status |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Martin Pitt | Approve | ||
Marc Deslauriers | Needs Fixing | ||
Review via email: mp+153954@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
Revision history for this message
Martin Pitt (pitti) wrote : | # |
Thanks for this! I just noticed the bug before, and applied the patch you sent upstream to Debian. I'll sync the Debian package once it's imported, so this MP is obsolete. Thanks!
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file '.pc/applied-patches' | |||
2 | --- .pc/applied-patches 2012-05-23 08:44:25 +0000 | |||
3 | +++ .pc/applied-patches 2013-03-18 23:14:22 +0000 | |||
4 | @@ -1,1 +1,2 @@ | |||
5 | 1 | 0002-Drop-our-own-many-bad-sectors-heuristic.patch | 1 | 0002-Drop-our-own-many-bad-sectors-heuristic.patch |
6 | 2 | fix-status-io-error.patch | ||
7 | 2 | 3 | ||
8 | === added directory '.pc/fix-status-io-error.patch' | |||
9 | === added file '.pc/fix-status-io-error.patch/.timestamp' | |||
10 | === added file '.pc/fix-status-io-error.patch/atasmart.c' | |||
11 | --- .pc/fix-status-io-error.patch/atasmart.c 1970-01-01 00:00:00 +0000 | |||
12 | +++ .pc/fix-status-io-error.patch/atasmart.c 2013-03-18 23:14:22 +0000 | |||
13 | @@ -0,0 +1,3056 @@ | |||
14 | 1 | /*-*- Mode: C; c-basic-offset: 8 -*-*/ | ||
15 | 2 | |||
16 | 3 | /*** | ||
17 | 4 | This file is part of libatasmart. | ||
18 | 5 | |||
19 | 6 | Copyright 2008 Lennart Poettering | ||
20 | 7 | |||
21 | 8 | libatasmart is free software; you can redistribute it and/or modify | ||
22 | 9 | it under the terms of the GNU Lesser General Public License as | ||
23 | 10 | published by the Free Software Foundation, either version 2.1 of the | ||
24 | 11 | License, or (at your option) any later version. | ||
25 | 12 | |||
26 | 13 | libatasmart is distributed in the hope that it will be useful, but | ||
27 | 14 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
28 | 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
29 | 16 | Lesser General Public License for more details. | ||
30 | 17 | |||
31 | 18 | You should have received a copy of the GNU Lesser General Public | ||
32 | 19 | License along with libatasmart. If not, If not, see | ||
33 | 20 | <http://www.gnu.org/licenses/>. | ||
34 | 21 | ***/ | ||
35 | 22 | |||
36 | 23 | #ifdef HAVE_CONFIG_H | ||
37 | 24 | #include <config.h> | ||
38 | 25 | #endif | ||
39 | 26 | |||
40 | 27 | #include <arpa/inet.h> | ||
41 | 28 | #include <stdlib.h> | ||
42 | 29 | #include <alloca.h> | ||
43 | 30 | #include <assert.h> | ||
44 | 31 | #include <fcntl.h> | ||
45 | 32 | #include <unistd.h> | ||
46 | 33 | #include <errno.h> | ||
47 | 34 | #include <string.h> | ||
48 | 35 | #include <stdio.h> | ||
49 | 36 | #include <sys/stat.h> | ||
50 | 37 | #include <sys/ioctl.h> | ||
51 | 38 | #include <scsi/scsi.h> | ||
52 | 39 | #include <scsi/sg.h> | ||
53 | 40 | #include <scsi/scsi_ioctl.h> | ||
54 | 41 | #include <linux/hdreg.h> | ||
55 | 42 | #include <linux/fs.h> | ||
56 | 43 | #include <sys/types.h> | ||
57 | 44 | #include <regex.h> | ||
58 | 45 | #include <sys/param.h> | ||
59 | 46 | #include <libudev.h> | ||
60 | 47 | |||
61 | 48 | #include "atasmart.h" | ||
62 | 49 | |||
63 | 50 | #ifndef STRPOOL | ||
64 | 51 | #define _P(x) x | ||
65 | 52 | #endif | ||
66 | 53 | |||
67 | 54 | #define SK_TIMEOUT 2000 | ||
68 | 55 | |||
69 | 56 | typedef enum SkDirection { | ||
70 | 57 | SK_DIRECTION_NONE, | ||
71 | 58 | SK_DIRECTION_IN, | ||
72 | 59 | SK_DIRECTION_OUT, | ||
73 | 60 | _SK_DIRECTION_MAX | ||
74 | 61 | } SkDirection; | ||
75 | 62 | |||
76 | 63 | typedef enum SkDiskType { | ||
77 | 64 | /* These three will be autotested for: */ | ||
78 | 65 | SK_DISK_TYPE_ATA_PASSTHROUGH_12, /* ATA passthrough over SCSI transport, 12-byte version */ | ||
79 | 66 | SK_DISK_TYPE_ATA_PASSTHROUGH_16, /* ATA passthrough over SCSI transport, 16-byte version */ | ||
80 | 67 | SK_DISK_TYPE_LINUX_IDE, /* Classic Linux /dev/hda ioctls */ | ||
81 | 68 | |||
82 | 69 | /* These three will not be autotested for */ | ||
83 | 70 | SK_DISK_TYPE_SUNPLUS, /* SunPlus USB/ATA bridges */ | ||
84 | 71 | SK_DISK_TYPE_JMICRON, /* JMicron USB/ATA bridges */ | ||
85 | 72 | SK_DISK_TYPE_BLOB, /* From a file */ | ||
86 | 73 | SK_DISK_TYPE_NONE, /* No access method */ | ||
87 | 74 | SK_DISK_TYPE_AUTO, /* We don't know yet */ | ||
88 | 75 | _SK_DISK_TYPE_MAX, | ||
89 | 76 | _SK_DISK_TYPE_TEST_MAX = SK_DISK_TYPE_SUNPLUS /* only auto test until here */ | ||
90 | 77 | } SkDiskType; | ||
91 | 78 | |||
92 | 79 | #if __BYTE_ORDER == __LITTLE_ENDIAN | ||
93 | 80 | #define MAKE_TAG(a,b,c,d) \ | ||
94 | 81 | (((uint32_t) d << 24) | \ | ||
95 | 82 | ((uint32_t) c << 16) | \ | ||
96 | 83 | ((uint32_t) b << 8) | \ | ||
97 | 84 | ((uint32_t) a)) | ||
98 | 85 | #else | ||
99 | 86 | #define MAKE_TAG(a,b,c,d) \ | ||
100 | 87 | (((uint32_t) a << 24) | \ | ||
101 | 88 | ((uint32_t) b << 16) | \ | ||
102 | 89 | ((uint32_t) c << 8) | \ | ||
103 | 90 | ((uint32_t) d)) | ||
104 | 91 | #endif | ||
105 | 92 | |||
106 | 93 | typedef enum SkBlobTag { | ||
107 | 94 | SK_BLOB_TAG_IDENTIFY = MAKE_TAG('I', 'D', 'F', 'Y'), | ||
108 | 95 | SK_BLOB_TAG_SMART_STATUS = MAKE_TAG('S', 'M', 'S', 'T'), | ||
109 | 96 | SK_BLOB_TAG_SMART_DATA = MAKE_TAG('S', 'M', 'D', 'T'), | ||
110 | 97 | SK_BLOB_TAG_SMART_THRESHOLDS = MAKE_TAG('S', 'M', 'T', 'H') | ||
111 | 98 | } SkBlobTag; | ||
112 | 99 | |||
113 | 100 | struct SkDisk { | ||
114 | 101 | char *name; | ||
115 | 102 | int fd; | ||
116 | 103 | SkDiskType type; | ||
117 | 104 | |||
118 | 105 | uint64_t size; | ||
119 | 106 | |||
120 | 107 | uint8_t identify[512]; | ||
121 | 108 | uint8_t smart_data[512]; | ||
122 | 109 | uint8_t smart_thresholds[512]; | ||
123 | 110 | |||
124 | 111 | SkBool smart_initialized:1; | ||
125 | 112 | |||
126 | 113 | SkBool identify_valid:1; | ||
127 | 114 | SkBool smart_data_valid:1; | ||
128 | 115 | SkBool smart_thresholds_valid:1; | ||
129 | 116 | |||
130 | 117 | SkBool blob_smart_status:1; | ||
131 | 118 | SkBool blob_smart_status_valid:1; | ||
132 | 119 | |||
133 | 120 | SkBool attribute_verification_bad:1; | ||
134 | 121 | |||
135 | 122 | SkIdentifyParsedData identify_parsed_data; | ||
136 | 123 | SkSmartParsedData smart_parsed_data; | ||
137 | 124 | |||
138 | 125 | /* cache for commonly used attributes */ | ||
139 | 126 | SkBool attribute_cache_valid:1; | ||
140 | 127 | SkBool bad_attribute_now:1; | ||
141 | 128 | SkBool bad_attribute_in_the_past:1; | ||
142 | 129 | SkBool reallocated_sector_count_found:1; | ||
143 | 130 | SkBool current_pending_sector_found:1; | ||
144 | 131 | uint64_t reallocated_sector_count; | ||
145 | 132 | uint64_t current_pending_sector; | ||
146 | 133 | SkBool reallocated_sector_count_bad:1; | ||
147 | 134 | SkBool current_pending_sector_bad:1; | ||
148 | 135 | |||
149 | 136 | void *blob; | ||
150 | 137 | }; | ||
151 | 138 | |||
152 | 139 | /* ATA commands */ | ||
153 | 140 | typedef enum SkAtaCommand { | ||
154 | 141 | SK_ATA_COMMAND_IDENTIFY_DEVICE = 0xEC, | ||
155 | 142 | SK_ATA_COMMAND_IDENTIFY_PACKET_DEVICE = 0xA1, | ||
156 | 143 | SK_ATA_COMMAND_SMART = 0xB0, | ||
157 | 144 | SK_ATA_COMMAND_CHECK_POWER_MODE = 0xE5 | ||
158 | 145 | } SkAtaCommand; | ||
159 | 146 | |||
160 | 147 | /* ATA SMART subcommands (ATA8 7.52.1) */ | ||
161 | 148 | typedef enum SkSmartCommand { | ||
162 | 149 | SK_SMART_COMMAND_READ_DATA = 0xD0, | ||
163 | 150 | SK_SMART_COMMAND_READ_THRESHOLDS = 0xD1, | ||
164 | 151 | SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE = 0xD4, | ||
165 | 152 | SK_SMART_COMMAND_ENABLE_OPERATIONS = 0xD8, | ||
166 | 153 | SK_SMART_COMMAND_DISABLE_OPERATIONS = 0xD9, | ||
167 | 154 | SK_SMART_COMMAND_RETURN_STATUS = 0xDA | ||
168 | 155 | } SkSmartCommand; | ||
169 | 156 | |||
170 | 157 | /* Hmm, if the data we parse is out of a certain range just consider it misparsed */ | ||
171 | 158 | #define SK_MKELVIN_VALID_MIN ((uint64_t) ((-15LL*1000LL) + 273150LL)) | ||
172 | 159 | #define SK_MKELVIN_VALID_MAX ((uint64_t) ((100LL*1000LL) + 273150LL)) | ||
173 | 160 | |||
174 | 161 | #define SK_MSECOND_VALID_MIN 1ULL | ||
175 | 162 | #define SK_MSECOND_VALID_SHORT_MAX (60ULL * 60ULL * 1000ULL) | ||
176 | 163 | #define SK_MSECOND_VALID_LONG_MAX (30ULL * 365ULL * 24ULL * 60ULL * 60ULL * 1000ULL) | ||
177 | 164 | |||
178 | 165 | static int init_smart(SkDisk *d); | ||
179 | 166 | |||
180 | 167 | static const char *disk_type_to_human_string(SkDiskType type) { | ||
181 | 168 | |||
182 | 169 | /* %STRINGPOOLSTART% */ | ||
183 | 170 | static const char* const map[_SK_DISK_TYPE_MAX] = { | ||
184 | 171 | [SK_DISK_TYPE_ATA_PASSTHROUGH_16] = "16 Byte SCSI ATA SAT Passthru", | ||
185 | 172 | [SK_DISK_TYPE_ATA_PASSTHROUGH_12] = "12 Byte SCSI ATA SAT Passthru", | ||
186 | 173 | [SK_DISK_TYPE_LINUX_IDE] = "Native Linux IDE", | ||
187 | 174 | [SK_DISK_TYPE_SUNPLUS] = "Sunplus SCSI ATA Passthru", | ||
188 | 175 | [SK_DISK_TYPE_JMICRON] = "JMicron SCSI ATA Passthru", | ||
189 | 176 | [SK_DISK_TYPE_BLOB] = "Blob", | ||
190 | 177 | [SK_DISK_TYPE_AUTO] = "Automatic", | ||
191 | 178 | [SK_DISK_TYPE_NONE] = "None" | ||
192 | 179 | }; | ||
193 | 180 | /* %STRINGPOOLSTOP% */ | ||
194 | 181 | |||
195 | 182 | if (type >= _SK_DISK_TYPE_MAX) | ||
196 | 183 | return NULL; | ||
197 | 184 | |||
198 | 185 | return _P(map[type]); | ||
199 | 186 | } | ||
200 | 187 | |||
201 | 188 | static const char *disk_type_to_prefix_string(SkDiskType type) { | ||
202 | 189 | |||
203 | 190 | /* %STRINGPOOLSTART% */ | ||
204 | 191 | static const char* const map[_SK_DISK_TYPE_MAX] = { | ||
205 | 192 | [SK_DISK_TYPE_ATA_PASSTHROUGH_16] = "sat16", | ||
206 | 193 | [SK_DISK_TYPE_ATA_PASSTHROUGH_12] = "sat12", | ||
207 | 194 | [SK_DISK_TYPE_LINUX_IDE] = "linux-ide", | ||
208 | 195 | [SK_DISK_TYPE_SUNPLUS] = "sunplus", | ||
209 | 196 | [SK_DISK_TYPE_JMICRON] = "jmicron", | ||
210 | 197 | [SK_DISK_TYPE_NONE] = "none", | ||
211 | 198 | [SK_DISK_TYPE_AUTO] = "auto", | ||
212 | 199 | }; | ||
213 | 200 | /* %STRINGPOOLSTOP% */ | ||
214 | 201 | |||
215 | 202 | if (type >= _SK_DISK_TYPE_MAX) | ||
216 | 203 | return NULL; | ||
217 | 204 | |||
218 | 205 | return _P(map[type]); | ||
219 | 206 | } | ||
220 | 207 | |||
221 | 208 | static const char *disk_type_from_string(const char *s, SkDiskType *type) { | ||
222 | 209 | unsigned u; | ||
223 | 210 | |||
224 | 211 | assert(s); | ||
225 | 212 | assert(type); | ||
226 | 213 | |||
227 | 214 | for (u = 0; u < _SK_DISK_TYPE_MAX; u++) { | ||
228 | 215 | const char *t; | ||
229 | 216 | size_t l; | ||
230 | 217 | |||
231 | 218 | if (!(t = disk_type_to_prefix_string(u))) | ||
232 | 219 | continue; | ||
233 | 220 | |||
234 | 221 | l = strlen(t); | ||
235 | 222 | |||
236 | 223 | if (strncmp(s, t, l)) | ||
237 | 224 | continue; | ||
238 | 225 | |||
239 | 226 | if (s[l] != ':') | ||
240 | 227 | continue; | ||
241 | 228 | |||
242 | 229 | *type = u; | ||
243 | 230 | |||
244 | 231 | return s + l + 1; | ||
245 | 232 | } | ||
246 | 233 | |||
247 | 234 | return NULL; | ||
248 | 235 | } | ||
249 | 236 | |||
250 | 237 | static SkBool disk_smart_is_available(SkDisk *d) { | ||
251 | 238 | return d->identify_valid && !!(d->identify[164] & 1); | ||
252 | 239 | } | ||
253 | 240 | |||
254 | 241 | static SkBool disk_smart_is_enabled(SkDisk *d) { | ||
255 | 242 | return d->identify_valid && !!(d->identify[170] & 1); | ||
256 | 243 | } | ||
257 | 244 | |||
258 | 245 | static SkBool disk_smart_is_conveyance_test_available(SkDisk *d) { | ||
259 | 246 | assert(d->smart_data_valid); | ||
260 | 247 | |||
261 | 248 | return !!(d->smart_data[367] & 32); | ||
262 | 249 | } | ||
263 | 250 | static SkBool disk_smart_is_short_and_extended_test_available(SkDisk *d) { | ||
264 | 251 | assert(d->smart_data_valid); | ||
265 | 252 | |||
266 | 253 | return !!(d->smart_data[367] & 16); | ||
267 | 254 | } | ||
268 | 255 | |||
269 | 256 | static SkBool disk_smart_is_start_test_available(SkDisk *d) { | ||
270 | 257 | assert(d->smart_data_valid); | ||
271 | 258 | |||
272 | 259 | return !!(d->smart_data[367] & 1); | ||
273 | 260 | } | ||
274 | 261 | |||
275 | 262 | static SkBool disk_smart_is_abort_test_available(SkDisk *d) { | ||
276 | 263 | assert(d->smart_data_valid); | ||
277 | 264 | |||
278 | 265 | return !!(d->smart_data[367] & 41); | ||
279 | 266 | } | ||
280 | 267 | |||
281 | 268 | static int disk_linux_ide_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) { | ||
282 | 269 | uint8_t *bytes = cmd_data; | ||
283 | 270 | int ret; | ||
284 | 271 | |||
285 | 272 | assert(d->type == SK_DISK_TYPE_LINUX_IDE); | ||
286 | 273 | |||
287 | 274 | switch (direction) { | ||
288 | 275 | |||
289 | 276 | case SK_DIRECTION_OUT: | ||
290 | 277 | |||
291 | 278 | /* We could use HDIO_DRIVE_TASKFILE here, but | ||
292 | 279 | * that's a deprecated ioctl(), hence we don't | ||
293 | 280 | * do it. And we don't need writing anyway. */ | ||
294 | 281 | |||
295 | 282 | errno = ENOTSUP; | ||
296 | 283 | return -1; | ||
297 | 284 | |||
298 | 285 | case SK_DIRECTION_IN: { | ||
299 | 286 | uint8_t *ioctl_data; | ||
300 | 287 | |||
301 | 288 | /* We have HDIO_DRIVE_CMD which can only read, but not write, | ||
302 | 289 | * and cannot do LBA. We use it for all read commands. */ | ||
303 | 290 | |||
304 | 291 | ioctl_data = alloca(4 + *len); | ||
305 | 292 | memset(ioctl_data, 0, 4 + *len); | ||
306 | 293 | |||
307 | 294 | ioctl_data[0] = (uint8_t) command; /* COMMAND */ | ||
308 | 295 | ioctl_data[1] = ioctl_data[0] == WIN_SMART ? bytes[9] : bytes[3]; /* SECTOR/NSECTOR */ | ||
309 | 296 | ioctl_data[2] = bytes[1]; /* FEATURE */ | ||
310 | 297 | ioctl_data[3] = bytes[3]; /* NSECTOR */ | ||
311 | 298 | |||
312 | 299 | if ((ret = ioctl(d->fd, HDIO_DRIVE_CMD, ioctl_data)) < 0) | ||
313 | 300 | return ret; | ||
314 | 301 | |||
315 | 302 | memset(bytes, 0, 12); | ||
316 | 303 | bytes[11] = ioctl_data[0]; | ||
317 | 304 | bytes[1] = ioctl_data[1]; | ||
318 | 305 | bytes[3] = ioctl_data[2]; | ||
319 | 306 | |||
320 | 307 | memcpy(data, ioctl_data+4, *len); | ||
321 | 308 | |||
322 | 309 | return ret; | ||
323 | 310 | } | ||
324 | 311 | |||
325 | 312 | case SK_DIRECTION_NONE: { | ||
326 | 313 | uint8_t ioctl_data[7]; | ||
327 | 314 | |||
328 | 315 | /* We have HDIO_DRIVE_TASK which can neither read nor | ||
329 | 316 | * write, but can do LBA. We use it for all commands that | ||
330 | 317 | * do neither read nor write */ | ||
331 | 318 | |||
332 | 319 | memset(ioctl_data, 0, sizeof(ioctl_data)); | ||
333 | 320 | |||
334 | 321 | ioctl_data[0] = (uint8_t) command; /* COMMAND */ | ||
335 | 322 | ioctl_data[1] = bytes[1]; /* FEATURE */ | ||
336 | 323 | ioctl_data[2] = bytes[3]; /* NSECTOR */ | ||
337 | 324 | |||
338 | 325 | ioctl_data[3] = bytes[9]; /* LBA LOW */ | ||
339 | 326 | ioctl_data[4] = bytes[8]; /* LBA MID */ | ||
340 | 327 | ioctl_data[5] = bytes[7]; /* LBA HIGH */ | ||
341 | 328 | ioctl_data[6] = bytes[10]; /* SELECT */ | ||
342 | 329 | |||
343 | 330 | if ((ret = ioctl(d->fd, HDIO_DRIVE_TASK, ioctl_data))) | ||
344 | 331 | return ret; | ||
345 | 332 | |||
346 | 333 | memset(bytes, 0, 12); | ||
347 | 334 | bytes[11] = ioctl_data[0]; | ||
348 | 335 | bytes[1] = ioctl_data[1]; | ||
349 | 336 | bytes[3] = ioctl_data[2]; | ||
350 | 337 | |||
351 | 338 | bytes[9] = ioctl_data[3]; | ||
352 | 339 | bytes[8] = ioctl_data[4]; | ||
353 | 340 | bytes[7] = ioctl_data[5]; | ||
354 | 341 | |||
355 | 342 | bytes[10] = ioctl_data[6]; | ||
356 | 343 | |||
357 | 344 | return ret; | ||
358 | 345 | } | ||
359 | 346 | |||
360 | 347 | default: | ||
361 | 348 | assert(FALSE); | ||
362 | 349 | return -1; | ||
363 | 350 | } | ||
364 | 351 | } | ||
365 | 352 | |||
366 | 353 | /* Sends a SCSI command block */ | ||
367 | 354 | static int sg_io(int fd, int direction, | ||
368 | 355 | const void *cdb, size_t cdb_len, | ||
369 | 356 | void *data, size_t data_len, | ||
370 | 357 | void *sense, size_t sense_len) { | ||
371 | 358 | |||
372 | 359 | struct sg_io_hdr io_hdr; | ||
373 | 360 | |||
374 | 361 | memset(&io_hdr, 0, sizeof(struct sg_io_hdr)); | ||
375 | 362 | |||
376 | 363 | io_hdr.interface_id = 'S'; | ||
377 | 364 | io_hdr.cmdp = (unsigned char*) cdb; | ||
378 | 365 | io_hdr.cmd_len = cdb_len; | ||
379 | 366 | io_hdr.dxferp = data; | ||
380 | 367 | io_hdr.dxfer_len = data_len; | ||
381 | 368 | io_hdr.sbp = sense; | ||
382 | 369 | io_hdr.mx_sb_len = sense_len; | ||
383 | 370 | io_hdr.dxfer_direction = direction; | ||
384 | 371 | io_hdr.timeout = SK_TIMEOUT; | ||
385 | 372 | |||
386 | 373 | return ioctl(fd, SG_IO, &io_hdr); | ||
387 | 374 | } | ||
388 | 375 | |||
389 | 376 | static int disk_passthrough_16_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) { | ||
390 | 377 | uint8_t *bytes = cmd_data; | ||
391 | 378 | uint8_t cdb[16]; | ||
392 | 379 | uint8_t sense[32]; | ||
393 | 380 | uint8_t *desc = sense+8; | ||
394 | 381 | int ret; | ||
395 | 382 | |||
396 | 383 | static const int direction_map[] = { | ||
397 | 384 | [SK_DIRECTION_NONE] = SG_DXFER_NONE, | ||
398 | 385 | [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV, | ||
399 | 386 | [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV | ||
400 | 387 | }; | ||
401 | 388 | |||
402 | 389 | assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_16); | ||
403 | 390 | |||
404 | 391 | /* ATA Pass-Through 16 byte command, as described in "T10 04-262r8 | ||
405 | 392 | * ATA Command Pass-Through": | ||
406 | 393 | * http://www.t10.org/ftp/t10/document.04/04-262r8.pdf */ | ||
407 | 394 | |||
408 | 395 | memset(cdb, 0, sizeof(cdb)); | ||
409 | 396 | |||
410 | 397 | cdb[0] = 0x85; /* OPERATION CODE: 16 byte pass through */ | ||
411 | 398 | |||
412 | 399 | if (direction == SK_DIRECTION_NONE) { | ||
413 | 400 | cdb[1] = 3 << 1; /* PROTOCOL: Non-Data */ | ||
414 | 401 | cdb[2] = 0x20; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=0, T_LENGTH=0 */ | ||
415 | 402 | |||
416 | 403 | } else if (direction == SK_DIRECTION_IN) { | ||
417 | 404 | cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */ | ||
418 | 405 | cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */ | ||
419 | 406 | |||
420 | 407 | } else if (direction == SK_DIRECTION_OUT) { | ||
421 | 408 | cdb[1] = 5 << 1; /* PROTOCOL: PIO Data-Out */ | ||
422 | 409 | cdb[2] = 0x26; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=1, T_LENGTH=2 */ | ||
423 | 410 | } | ||
424 | 411 | |||
425 | 412 | cdb[3] = bytes[0]; /* FEATURES */ | ||
426 | 413 | cdb[4] = bytes[1]; | ||
427 | 414 | |||
428 | 415 | cdb[5] = bytes[2]; /* SECTORS */ | ||
429 | 416 | cdb[6] = bytes[3]; | ||
430 | 417 | |||
431 | 418 | cdb[8] = bytes[9]; /* LBA LOW */ | ||
432 | 419 | cdb[10] = bytes[8]; /* LBA MID */ | ||
433 | 420 | cdb[12] = bytes[7]; /* LBA HIGH */ | ||
434 | 421 | |||
435 | 422 | cdb[13] = bytes[10] & 0x4F; /* SELECT */ | ||
436 | 423 | cdb[14] = (uint8_t) command; | ||
437 | 424 | |||
438 | 425 | memset(sense, 0, sizeof(sense)); | ||
439 | 426 | |||
440 | 427 | if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len ? *len : 0, sense, sizeof(sense))) < 0) | ||
441 | 428 | return ret; | ||
442 | 429 | |||
443 | 430 | if (sense[0] != 0x72 || desc[0] != 0x9 || desc[1] != 0x0c) { | ||
444 | 431 | errno = EIO; | ||
445 | 432 | return -1; | ||
446 | 433 | } | ||
447 | 434 | |||
448 | 435 | memset(bytes, 0, 12); | ||
449 | 436 | |||
450 | 437 | bytes[1] = desc[3]; | ||
451 | 438 | bytes[2] = desc[4]; | ||
452 | 439 | bytes[3] = desc[5]; | ||
453 | 440 | bytes[9] = desc[7]; | ||
454 | 441 | bytes[8] = desc[9]; | ||
455 | 442 | bytes[7] = desc[11]; | ||
456 | 443 | bytes[10] = desc[12]; | ||
457 | 444 | bytes[11] = desc[13]; | ||
458 | 445 | |||
459 | 446 | return ret; | ||
460 | 447 | } | ||
461 | 448 | |||
462 | 449 | static int disk_passthrough_12_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) { | ||
463 | 450 | uint8_t *bytes = cmd_data; | ||
464 | 451 | uint8_t cdb[12]; | ||
465 | 452 | uint8_t sense[32]; | ||
466 | 453 | uint8_t *desc = sense+8; | ||
467 | 454 | int ret; | ||
468 | 455 | |||
469 | 456 | static const int direction_map[] = { | ||
470 | 457 | [SK_DIRECTION_NONE] = SG_DXFER_NONE, | ||
471 | 458 | [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV, | ||
472 | 459 | [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV | ||
473 | 460 | }; | ||
474 | 461 | |||
475 | 462 | assert(d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12); | ||
476 | 463 | |||
477 | 464 | /* ATA Pass-Through 12 byte command, as described in "T10 04-262r8 | ||
478 | 465 | * ATA Command Pass-Through": | ||
479 | 466 | * http://www.t10.org/ftp/t10/document.04/04-262r8.pdf */ | ||
480 | 467 | |||
481 | 468 | memset(cdb, 0, sizeof(cdb)); | ||
482 | 469 | |||
483 | 470 | cdb[0] = 0xa1; /* OPERATION CODE: 12 byte pass through */ | ||
484 | 471 | |||
485 | 472 | if (direction == SK_DIRECTION_NONE) { | ||
486 | 473 | cdb[1] = 3 << 1; /* PROTOCOL: Non-Data */ | ||
487 | 474 | cdb[2] = 0x20; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=0, T_LENGTH=0 */ | ||
488 | 475 | |||
489 | 476 | } else if (direction == SK_DIRECTION_IN) { | ||
490 | 477 | cdb[1] = 4 << 1; /* PROTOCOL: PIO Data-in */ | ||
491 | 478 | cdb[2] = 0x2e; /* OFF_LINE=0, CK_COND=1, T_DIR=1, BYT_BLOK=1, T_LENGTH=2 */ | ||
492 | 479 | |||
493 | 480 | } else if (direction == SK_DIRECTION_OUT) { | ||
494 | 481 | cdb[1] = 5 << 1; /* PROTOCOL: PIO Data-Out */ | ||
495 | 482 | cdb[2] = 0x26; /* OFF_LINE=0, CK_COND=1, T_DIR=0, BYT_BLOK=1, T_LENGTH=2 */ | ||
496 | 483 | } | ||
497 | 484 | |||
498 | 485 | cdb[3] = bytes[1]; /* FEATURES */ | ||
499 | 486 | cdb[4] = bytes[3]; /* SECTORS */ | ||
500 | 487 | |||
501 | 488 | cdb[5] = bytes[9]; /* LBA LOW */ | ||
502 | 489 | cdb[6] = bytes[8]; /* LBA MID */ | ||
503 | 490 | cdb[7] = bytes[7]; /* LBA HIGH */ | ||
504 | 491 | |||
505 | 492 | cdb[8] = bytes[10] & 0x4F; /* SELECT */ | ||
506 | 493 | cdb[9] = (uint8_t) command; | ||
507 | 494 | |||
508 | 495 | memset(sense, 0, sizeof(sense)); | ||
509 | 496 | |||
510 | 497 | if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len ? *len : 0, sense, sizeof(sense))) < 0) | ||
511 | 498 | return ret; | ||
512 | 499 | |||
513 | 500 | if (sense[0] != 0x72 || desc[0] != 0x9 || desc[1] != 0x0c) { | ||
514 | 501 | errno = EIO; | ||
515 | 502 | return -1; | ||
516 | 503 | } | ||
517 | 504 | |||
518 | 505 | memset(bytes, 0, 12); | ||
519 | 506 | |||
520 | 507 | bytes[1] = desc[3]; /* FEATURES */ | ||
521 | 508 | bytes[2] = desc[4]; /* STATUS */ | ||
522 | 509 | bytes[3] = desc[5]; /* SECTORS */ | ||
523 | 510 | bytes[9] = desc[7]; /* LBA LOW */ | ||
524 | 511 | bytes[8] = desc[9]; /* LBA MID */ | ||
525 | 512 | bytes[7] = desc[11]; /* LBA HIGH */ | ||
526 | 513 | bytes[10] = desc[12]; /* SELECT */ | ||
527 | 514 | bytes[11] = desc[13]; /* ERROR */ | ||
528 | 515 | |||
529 | 516 | return ret; | ||
530 | 517 | } | ||
531 | 518 | |||
532 | 519 | static int disk_sunplus_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) { | ||
533 | 520 | uint8_t *bytes = cmd_data; | ||
534 | 521 | uint8_t cdb[12]; | ||
535 | 522 | uint8_t sense[32], buf[8]; | ||
536 | 523 | int ret; | ||
537 | 524 | static const int direction_map[] = { | ||
538 | 525 | [SK_DIRECTION_NONE] = SG_DXFER_NONE, | ||
539 | 526 | [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV, | ||
540 | 527 | [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV | ||
541 | 528 | }; | ||
542 | 529 | |||
543 | 530 | assert(d->type == SK_DISK_TYPE_SUNPLUS); | ||
544 | 531 | |||
545 | 532 | /* SunplusIT specific SCSI ATA pass-thru. Inspired by smartmonutils' support for these bridges */ | ||
546 | 533 | |||
547 | 534 | memset(cdb, 0, sizeof(cdb)); | ||
548 | 535 | |||
549 | 536 | cdb[0] = 0xF8; /* OPERATION CODE: Sunplus specific */ | ||
550 | 537 | cdb[1] = 0x00; /* Subcommand: Pass-thru */ | ||
551 | 538 | cdb[2] = 0x22; | ||
552 | 539 | |||
553 | 540 | if (direction == SK_DIRECTION_NONE) | ||
554 | 541 | cdb[3] = 0x00; /* protocol */ | ||
555 | 542 | else if (direction == SK_DIRECTION_IN) | ||
556 | 543 | cdb[3] = 0x10; /* protocol */ | ||
557 | 544 | else if (direction == SK_DIRECTION_OUT) | ||
558 | 545 | cdb[3] = 0x11; /* protocol */ | ||
559 | 546 | |||
560 | 547 | cdb[4] = bytes[3]; /* size? */ | ||
561 | 548 | cdb[5] = bytes[1]; /* FEATURES */ | ||
562 | 549 | cdb[6] = bytes[3]; /* SECTORS */ | ||
563 | 550 | cdb[7] = bytes[9]; /* LBA LOW */ | ||
564 | 551 | cdb[8] = bytes[8]; /* LBA MID */ | ||
565 | 552 | cdb[9] = bytes[7]; /* LBA HIGH */ | ||
566 | 553 | cdb[10] = bytes[10] | 0xA0; /* SELECT */ | ||
567 | 554 | cdb[11] = (uint8_t) command; | ||
568 | 555 | |||
569 | 556 | memset(sense, 0, sizeof(sense)); | ||
570 | 557 | |||
571 | 558 | /* Issue request */ | ||
572 | 559 | if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len ? *len : 0, sense, sizeof(sense))) < 0) | ||
573 | 560 | return ret; | ||
574 | 561 | |||
575 | 562 | memset(cdb, 0, sizeof(cdb)); | ||
576 | 563 | |||
577 | 564 | cdb[0] = 0xF8; | ||
578 | 565 | cdb[1] = 0x00; | ||
579 | 566 | cdb[2] = 0x21; | ||
580 | 567 | |||
581 | 568 | memset(buf, 0, sizeof(buf)); | ||
582 | 569 | |||
583 | 570 | /* Ask for response */ | ||
584 | 571 | if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), buf, sizeof(buf), sense, sizeof(sense))) < 0) | ||
585 | 572 | return ret; | ||
586 | 573 | |||
587 | 574 | memset(bytes, 0, 12); | ||
588 | 575 | |||
589 | 576 | bytes[2] = buf[1]; /* ERROR */ | ||
590 | 577 | bytes[3] = buf[2]; /* SECTORS */ | ||
591 | 578 | bytes[9] = buf[3]; /* LBA LOW */ | ||
592 | 579 | bytes[8] = buf[4]; /* LBA MID */ | ||
593 | 580 | bytes[7] = buf[5]; /* LBA HIGH */ | ||
594 | 581 | bytes[10] = buf[6]; /* SELECT */ | ||
595 | 582 | bytes[11] = buf[7]; /* STATUS */ | ||
596 | 583 | |||
597 | 584 | return ret; | ||
598 | 585 | } | ||
599 | 586 | |||
600 | 587 | static int disk_jmicron_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* _data, size_t *_len) { | ||
601 | 588 | uint8_t *bytes = cmd_data; | ||
602 | 589 | uint8_t cdb[12]; | ||
603 | 590 | uint8_t sense[32]; | ||
604 | 591 | uint8_t port; | ||
605 | 592 | int ret; | ||
606 | 593 | SkBool is_smart_status = FALSE; | ||
607 | 594 | void *data = _data; | ||
608 | 595 | size_t len = _len ? *_len : 0; | ||
609 | 596 | uint8_t smart_status = 0; | ||
610 | 597 | |||
611 | 598 | static const int direction_map[] = { | ||
612 | 599 | [SK_DIRECTION_NONE] = SG_DXFER_NONE, | ||
613 | 600 | [SK_DIRECTION_IN] = SG_DXFER_FROM_DEV, | ||
614 | 601 | [SK_DIRECTION_OUT] = SG_DXFER_TO_DEV | ||
615 | 602 | }; | ||
616 | 603 | |||
617 | 604 | assert(d->type == SK_DISK_TYPE_JMICRON); | ||
618 | 605 | |||
619 | 606 | /* JMicron specific SCSI ATA pass-thru. Inspired by smartmonutils' support for these bridges */ | ||
620 | 607 | |||
621 | 608 | memset(cdb, 0, sizeof(cdb)); | ||
622 | 609 | |||
623 | 610 | cdb[0] = 0xdf; /* operation code */ | ||
624 | 611 | cdb[1] = 0x10; | ||
625 | 612 | cdb[2] = 0x00; | ||
626 | 613 | cdb[3] = 0x00; /* size HI */ | ||
627 | 614 | cdb[4] = sizeof(port); /* size LO */ | ||
628 | 615 | cdb[5] = 0x00; | ||
629 | 616 | cdb[6] = 0x72; /* register address HI */ | ||
630 | 617 | cdb[7] = 0x0f; /* register address LO */ | ||
631 | 618 | cdb[8] = 0x00; | ||
632 | 619 | cdb[9] = 0x00; | ||
633 | 620 | cdb[10] = 0x00; | ||
634 | 621 | cdb[11] = 0xfd; | ||
635 | 622 | |||
636 | 623 | memset(sense, 0, sizeof(sense)); | ||
637 | 624 | |||
638 | 625 | if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), &port, sizeof(port), sense, sizeof(sense))) < 0) | ||
639 | 626 | return ret; | ||
640 | 627 | |||
641 | 628 | /* Port & 0x04 is port #0, Port & 0x40 is port #1 */ | ||
642 | 629 | if (!(port & 0x44)) | ||
643 | 630 | return -EIO; | ||
644 | 631 | |||
645 | 632 | cdb[0] = 0xdf; /* OPERATION CODE: 12 byte pass through */ | ||
646 | 633 | |||
647 | 634 | if (command == SK_ATA_COMMAND_SMART && bytes[1] == SK_SMART_COMMAND_RETURN_STATUS) { | ||
648 | 635 | /* We need to rewrite the SMART status request */ | ||
649 | 636 | is_smart_status = TRUE; | ||
650 | 637 | direction = SK_DIRECTION_IN; | ||
651 | 638 | data = &smart_status; | ||
652 | 639 | len = sizeof(smart_status); | ||
653 | 640 | cdb[1] = 0x10; | ||
654 | 641 | } else if (direction == SK_DIRECTION_NONE) | ||
655 | 642 | cdb[1] = 0x10; | ||
656 | 643 | else if (direction == SK_DIRECTION_IN) | ||
657 | 644 | cdb[1] = 0x10; | ||
658 | 645 | else if (direction == SK_DIRECTION_OUT) | ||
659 | 646 | cdb[1] = 0x00; | ||
660 | 647 | |||
661 | 648 | cdb[2] = 0x00; | ||
662 | 649 | |||
663 | 650 | cdb[3] = (uint8_t) (len >> 8); | ||
664 | 651 | cdb[4] = (uint8_t) (len & 0xFF); | ||
665 | 652 | |||
666 | 653 | cdb[5] = bytes[1]; /* FEATURES */ | ||
667 | 654 | cdb[6] = bytes[3]; /* SECTORS */ | ||
668 | 655 | |||
669 | 656 | cdb[7] = bytes[9]; /* LBA LOW */ | ||
670 | 657 | cdb[8] = bytes[8]; /* LBA MID */ | ||
671 | 658 | cdb[9] = bytes[7]; /* LBA HIGH */ | ||
672 | 659 | |||
673 | 660 | cdb[10] = bytes[10] | ((port & 0x04) ? 0xA0 : 0xB0); /* SELECT */ | ||
674 | 661 | cdb[11] = (uint8_t) command; | ||
675 | 662 | |||
676 | 663 | memset(sense, 0, sizeof(sense)); | ||
677 | 664 | |||
678 | 665 | if ((ret = sg_io(d->fd, direction_map[direction], cdb, sizeof(cdb), data, len, sense, sizeof(sense))) < 0) | ||
679 | 666 | return ret; | ||
680 | 667 | |||
681 | 668 | memset(bytes, 0, 12); | ||
682 | 669 | |||
683 | 670 | if (is_smart_status) { | ||
684 | 671 | if (smart_status == 0x01 || smart_status == 0xc2) { | ||
685 | 672 | bytes[7] = 0xc2; /* LBA HIGH */ | ||
686 | 673 | bytes[8] = 0x4f; /* LBA MID */ | ||
687 | 674 | } else if (smart_status == 0x00 || smart_status == 0x2c) { | ||
688 | 675 | bytes[7] = 0x2c; /* LBA HIGH */ | ||
689 | 676 | bytes[8] = 0xf4; /* LBA MID */ | ||
690 | 677 | } else | ||
691 | 678 | return -EIO; | ||
692 | 679 | } else { | ||
693 | 680 | uint8_t regbuf[16]; | ||
694 | 681 | |||
695 | 682 | cdb[0] = 0xdf; /* operation code */ | ||
696 | 683 | cdb[1] = 0x10; | ||
697 | 684 | cdb[2] = 0x00; | ||
698 | 685 | cdb[3] = 0x00; /* size HI */ | ||
699 | 686 | cdb[4] = sizeof(regbuf); /* size LO */ | ||
700 | 687 | cdb[5] = 0x00; | ||
701 | 688 | cdb[6] = (port & 0x04) ? 0x80 : 0x90; /* register address HI */ | ||
702 | 689 | cdb[7] = 0x00; /* register address LO */ | ||
703 | 690 | cdb[8] = 0x00; | ||
704 | 691 | cdb[9] = 0x00; | ||
705 | 692 | cdb[10] = 0x00; | ||
706 | 693 | cdb[11] = 0xfd; | ||
707 | 694 | |||
708 | 695 | if ((ret = sg_io(d->fd, SG_DXFER_FROM_DEV, cdb, sizeof(cdb), regbuf, sizeof(regbuf), sense, sizeof(sense))) < 0) | ||
709 | 696 | return ret; | ||
710 | 697 | |||
711 | 698 | bytes[2] = regbuf[14]; /* STATUS */ | ||
712 | 699 | bytes[3] = regbuf[0]; /* SECTORS */ | ||
713 | 700 | bytes[9] = regbuf[6]; /* LBA LOW */ | ||
714 | 701 | bytes[8] = regbuf[4]; /* LBA MID */ | ||
715 | 702 | bytes[7] = regbuf[10]; /* LBA HIGH */ | ||
716 | 703 | bytes[10] = regbuf[9]; /* SELECT */ | ||
717 | 704 | bytes[11] = regbuf[13]; /* ERROR */ | ||
718 | 705 | } | ||
719 | 706 | |||
720 | 707 | return ret; | ||
721 | 708 | } | ||
722 | 709 | |||
723 | 710 | static int disk_command(SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) { | ||
724 | 711 | |||
725 | 712 | static int (* const disk_command_table[_SK_DISK_TYPE_MAX]) (SkDisk *d, SkAtaCommand command, SkDirection direction, void* cmd_data, void* data, size_t *len) = { | ||
726 | 713 | [SK_DISK_TYPE_LINUX_IDE] = disk_linux_ide_command, | ||
727 | 714 | [SK_DISK_TYPE_ATA_PASSTHROUGH_12] = disk_passthrough_12_command, | ||
728 | 715 | [SK_DISK_TYPE_ATA_PASSTHROUGH_16] = disk_passthrough_16_command, | ||
729 | 716 | [SK_DISK_TYPE_SUNPLUS] = disk_sunplus_command, | ||
730 | 717 | [SK_DISK_TYPE_JMICRON] = disk_jmicron_command, | ||
731 | 718 | [SK_DISK_TYPE_BLOB] = NULL, | ||
732 | 719 | [SK_DISK_TYPE_AUTO] = NULL, | ||
733 | 720 | [SK_DISK_TYPE_NONE] = NULL | ||
734 | 721 | }; | ||
735 | 722 | |||
736 | 723 | assert(d); | ||
737 | 724 | assert(d->type <= _SK_DISK_TYPE_MAX); | ||
738 | 725 | assert(direction <= _SK_DIRECTION_MAX); | ||
739 | 726 | |||
740 | 727 | assert(direction == SK_DIRECTION_NONE || (data && len && *len > 0)); | ||
741 | 728 | assert(direction != SK_DIRECTION_NONE || (!data && !len)); | ||
742 | 729 | |||
743 | 730 | if (!disk_command_table[d->type]) { | ||
744 | 731 | errno = -ENOTSUP; | ||
745 | 732 | return -1; | ||
746 | 733 | } | ||
747 | 734 | |||
748 | 735 | return disk_command_table[d->type](d, command, direction, cmd_data, data, len); | ||
749 | 736 | } | ||
750 | 737 | |||
751 | 738 | static int disk_identify_device(SkDisk *d) { | ||
752 | 739 | uint16_t cmd[6]; | ||
753 | 740 | int ret; | ||
754 | 741 | size_t len = 512; | ||
755 | 742 | const uint8_t *p; | ||
756 | 743 | |||
757 | 744 | if (d->type == SK_DISK_TYPE_BLOB) | ||
758 | 745 | return 0; | ||
759 | 746 | |||
760 | 747 | memset(d->identify, 0, len); | ||
761 | 748 | memset(cmd, 0, sizeof(cmd)); | ||
762 | 749 | |||
763 | 750 | cmd[1] = htons(1); | ||
764 | 751 | |||
765 | 752 | if ((ret = disk_command(d, SK_ATA_COMMAND_IDENTIFY_DEVICE, SK_DIRECTION_IN, cmd, d->identify, &len)) < 0) | ||
766 | 753 | return ret; | ||
767 | 754 | |||
768 | 755 | if (len != 512) { | ||
769 | 756 | errno = EIO; | ||
770 | 757 | return -1; | ||
771 | 758 | } | ||
772 | 759 | |||
773 | 760 | /* Check if IDENTIFY data is all NULs */ | ||
774 | 761 | for (p = d->identify; p < (const uint8_t*) d->identify+len; p++) | ||
775 | 762 | if (*p) { | ||
776 | 763 | p = NULL; | ||
777 | 764 | break; | ||
778 | 765 | } | ||
779 | 766 | |||
780 | 767 | if (p) { | ||
781 | 768 | errno = EIO; | ||
782 | 769 | return -1; | ||
783 | 770 | } | ||
784 | 771 | |||
785 | 772 | d->identify_valid = TRUE; | ||
786 | 773 | |||
787 | 774 | return 0; | ||
788 | 775 | } | ||
789 | 776 | |||
790 | 777 | int sk_disk_check_sleep_mode(SkDisk *d, SkBool *awake) { | ||
791 | 778 | int ret; | ||
792 | 779 | uint16_t cmd[6]; | ||
793 | 780 | uint8_t status; | ||
794 | 781 | |||
795 | 782 | if (!d->identify_valid) { | ||
796 | 783 | errno = ENOTSUP; | ||
797 | 784 | return -1; | ||
798 | 785 | } | ||
799 | 786 | |||
800 | 787 | if (d->type == SK_DISK_TYPE_BLOB) { | ||
801 | 788 | errno = ENOTSUP; | ||
802 | 789 | return -1; | ||
803 | 790 | } | ||
804 | 791 | |||
805 | 792 | memset(cmd, 0, sizeof(cmd)); | ||
806 | 793 | |||
807 | 794 | if ((ret = disk_command(d, SK_ATA_COMMAND_CHECK_POWER_MODE, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0) | ||
808 | 795 | return ret; | ||
809 | 796 | |||
810 | 797 | if (cmd[0] != 0 || (ntohs(cmd[5]) & 1) != 0) { | ||
811 | 798 | errno = EIO; | ||
812 | 799 | return -1; | ||
813 | 800 | } | ||
814 | 801 | |||
815 | 802 | status = ntohs(cmd[1]) & 0xFF; | ||
816 | 803 | *awake = status == 0xFF || status == 0x80; /* idle and active/idle is considered awake */ | ||
817 | 804 | |||
818 | 805 | return 0; | ||
819 | 806 | } | ||
820 | 807 | |||
821 | 808 | static int disk_smart_enable(SkDisk *d, SkBool b) { | ||
822 | 809 | uint16_t cmd[6]; | ||
823 | 810 | |||
824 | 811 | if (!disk_smart_is_available(d)) { | ||
825 | 812 | errno = ENOTSUP; | ||
826 | 813 | return -1; | ||
827 | 814 | } | ||
828 | 815 | |||
829 | 816 | if (d->type == SK_DISK_TYPE_BLOB) { | ||
830 | 817 | errno = ENOTSUP; | ||
831 | 818 | return -1; | ||
832 | 819 | } | ||
833 | 820 | |||
834 | 821 | memset(cmd, 0, sizeof(cmd)); | ||
835 | 822 | |||
836 | 823 | cmd[0] = htons(b ? SK_SMART_COMMAND_ENABLE_OPERATIONS : SK_SMART_COMMAND_DISABLE_OPERATIONS); | ||
837 | 824 | cmd[2] = htons(0x0000U); | ||
838 | 825 | cmd[3] = htons(0x00C2U); | ||
839 | 826 | cmd[4] = htons(0x4F00U); | ||
840 | 827 | |||
841 | 828 | return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, 0); | ||
842 | 829 | } | ||
843 | 830 | |||
844 | 831 | int sk_disk_smart_read_data(SkDisk *d) { | ||
845 | 832 | uint16_t cmd[6]; | ||
846 | 833 | int ret; | ||
847 | 834 | size_t len = 512; | ||
848 | 835 | |||
849 | 836 | if (init_smart(d) < 0) | ||
850 | 837 | return -1; | ||
851 | 838 | |||
852 | 839 | if (!disk_smart_is_available(d)) { | ||
853 | 840 | errno = ENOTSUP; | ||
854 | 841 | return -1; | ||
855 | 842 | } | ||
856 | 843 | |||
857 | 844 | if (d->type == SK_DISK_TYPE_BLOB) | ||
858 | 845 | return 0; | ||
859 | 846 | |||
860 | 847 | memset(cmd, 0, sizeof(cmd)); | ||
861 | 848 | |||
862 | 849 | cmd[0] = htons(SK_SMART_COMMAND_READ_DATA); | ||
863 | 850 | cmd[1] = htons(1); | ||
864 | 851 | cmd[2] = htons(0x0000U); | ||
865 | 852 | cmd[3] = htons(0x00C2U); | ||
866 | 853 | cmd[4] = htons(0x4F00U); | ||
867 | 854 | |||
868 | 855 | if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_data, &len)) < 0) | ||
869 | 856 | return ret; | ||
870 | 857 | |||
871 | 858 | d->smart_data_valid = TRUE; | ||
872 | 859 | |||
873 | 860 | return ret; | ||
874 | 861 | } | ||
875 | 862 | |||
876 | 863 | static int disk_smart_read_thresholds(SkDisk *d) { | ||
877 | 864 | uint16_t cmd[6]; | ||
878 | 865 | int ret; | ||
879 | 866 | size_t len = 512; | ||
880 | 867 | |||
881 | 868 | if (!disk_smart_is_available(d)) { | ||
882 | 869 | errno = ENOTSUP; | ||
883 | 870 | return -1; | ||
884 | 871 | } | ||
885 | 872 | |||
886 | 873 | if (d->type == SK_DISK_TYPE_BLOB) | ||
887 | 874 | return 0; | ||
888 | 875 | |||
889 | 876 | memset(cmd, 0, sizeof(cmd)); | ||
890 | 877 | |||
891 | 878 | cmd[0] = htons(SK_SMART_COMMAND_READ_THRESHOLDS); | ||
892 | 879 | cmd[1] = htons(1); | ||
893 | 880 | cmd[2] = htons(0x0000U); | ||
894 | 881 | cmd[3] = htons(0x00C2U); | ||
895 | 882 | cmd[4] = htons(0x4F00U); | ||
896 | 883 | |||
897 | 884 | if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_IN, cmd, d->smart_thresholds, &len)) < 0) | ||
898 | 885 | return ret; | ||
899 | 886 | |||
900 | 887 | d->smart_thresholds_valid = TRUE; | ||
901 | 888 | |||
902 | 889 | return ret; | ||
903 | 890 | } | ||
904 | 891 | |||
905 | 892 | int sk_disk_smart_status(SkDisk *d, SkBool *good) { | ||
906 | 893 | uint16_t cmd[6]; | ||
907 | 894 | int ret; | ||
908 | 895 | |||
909 | 896 | if (init_smart(d) < 0) | ||
910 | 897 | return -1; | ||
911 | 898 | |||
912 | 899 | if (!disk_smart_is_available(d)) { | ||
913 | 900 | errno = ENOTSUP; | ||
914 | 901 | return -1; | ||
915 | 902 | } | ||
916 | 903 | |||
917 | 904 | if (d->type == SK_DISK_TYPE_BLOB) { | ||
918 | 905 | |||
919 | 906 | if (d->blob_smart_status_valid) { | ||
920 | 907 | *good = d->blob_smart_status; | ||
921 | 908 | return 0; | ||
922 | 909 | } | ||
923 | 910 | |||
924 | 911 | errno = ENXIO; | ||
925 | 912 | return -1; | ||
926 | 913 | } | ||
927 | 914 | |||
928 | 915 | memset(cmd, 0, sizeof(cmd)); | ||
929 | 916 | |||
930 | 917 | cmd[0] = htons(SK_SMART_COMMAND_RETURN_STATUS); | ||
931 | 918 | cmd[1] = htons(0x0000U); | ||
932 | 919 | cmd[3] = htons(0x00C2U); | ||
933 | 920 | cmd[4] = htons(0x4F00U); | ||
934 | 921 | |||
935 | 922 | if ((ret = disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, 0)) < 0) | ||
936 | 923 | return ret; | ||
937 | 924 | |||
938 | 925 | /* SAT/USB bridges truncate packets, so we only check for 4F, | ||
939 | 926 | * not for 2C on those */ | ||
940 | 927 | if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x00C2U)) && | ||
941 | 928 | cmd[4] == htons(0x4F00U)) | ||
942 | 929 | *good = TRUE; | ||
943 | 930 | else if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x002CU)) && | ||
944 | 931 | cmd[4] == htons(0xF400U)) | ||
945 | 932 | *good = FALSE; | ||
946 | 933 | else { | ||
947 | 934 | errno = EIO; | ||
948 | 935 | return -1; | ||
949 | 936 | } | ||
950 | 937 | |||
951 | 938 | return ret; | ||
952 | 939 | } | ||
953 | 940 | |||
954 | 941 | int sk_disk_smart_self_test(SkDisk *d, SkSmartSelfTest test) { | ||
955 | 942 | uint16_t cmd[6]; | ||
956 | 943 | int ret; | ||
957 | 944 | |||
958 | 945 | if (init_smart(d) < 0) | ||
959 | 946 | return -1; | ||
960 | 947 | |||
961 | 948 | if (!disk_smart_is_available(d)) { | ||
962 | 949 | errno = ENOTSUP; | ||
963 | 950 | return -1; | ||
964 | 951 | } | ||
965 | 952 | |||
966 | 953 | if (d->type == SK_DISK_TYPE_BLOB) { | ||
967 | 954 | errno = ENOTSUP; | ||
968 | 955 | return -1; | ||
969 | 956 | } | ||
970 | 957 | |||
971 | 958 | if (!d->smart_data_valid) | ||
972 | 959 | if ((ret = sk_disk_smart_read_data(d)) < 0) | ||
973 | 960 | return -1; | ||
974 | 961 | |||
975 | 962 | assert(d->smart_data_valid); | ||
976 | 963 | |||
977 | 964 | if (test != SK_SMART_SELF_TEST_SHORT && | ||
978 | 965 | test != SK_SMART_SELF_TEST_EXTENDED && | ||
979 | 966 | test != SK_SMART_SELF_TEST_CONVEYANCE && | ||
980 | 967 | test != SK_SMART_SELF_TEST_ABORT) { | ||
981 | 968 | errno = EINVAL; | ||
982 | 969 | return -1; | ||
983 | 970 | } | ||
984 | 971 | |||
985 | 972 | if (!disk_smart_is_start_test_available(d) | ||
986 | 973 | || (test == SK_SMART_SELF_TEST_ABORT && !disk_smart_is_abort_test_available(d)) | ||
987 | 974 | || ((test == SK_SMART_SELF_TEST_SHORT || test == SK_SMART_SELF_TEST_EXTENDED) && !disk_smart_is_short_and_extended_test_available(d)) | ||
988 | 975 | || (test == SK_SMART_SELF_TEST_CONVEYANCE && !disk_smart_is_conveyance_test_available(d))) { | ||
989 | 976 | errno = ENOTSUP; | ||
990 | 977 | return -1; | ||
991 | 978 | } | ||
992 | 979 | |||
993 | 980 | if (test == SK_SMART_SELF_TEST_ABORT && | ||
994 | 981 | !disk_smart_is_abort_test_available(d)) { | ||
995 | 982 | errno = ENOTSUP; | ||
996 | 983 | return -1; | ||
997 | 984 | } | ||
998 | 985 | |||
999 | 986 | memset(cmd, 0, sizeof(cmd)); | ||
1000 | 987 | |||
1001 | 988 | cmd[0] = htons(SK_SMART_COMMAND_EXECUTE_OFFLINE_IMMEDIATE); | ||
1002 | 989 | cmd[2] = htons(0x0000U); | ||
1003 | 990 | cmd[3] = htons(0x00C2U); | ||
1004 | 991 | cmd[4] = htons(0x4F00U | (uint16_t) test); | ||
1005 | 992 | |||
1006 | 993 | return disk_command(d, SK_ATA_COMMAND_SMART, SK_DIRECTION_NONE, cmd, NULL, NULL); | ||
1007 | 994 | } | ||
1008 | 995 | |||
1009 | 996 | static void swap_strings(char *s, size_t len) { | ||
1010 | 997 | assert((len & 1) == 0); | ||
1011 | 998 | |||
1012 | 999 | for (; len > 0; s += 2, len -= 2) { | ||
1013 | 1000 | char t; | ||
1014 | 1001 | t = s[0]; | ||
1015 | 1002 | s[0] = s[1]; | ||
1016 | 1003 | s[1] = t; | ||
1017 | 1004 | } | ||
1018 | 1005 | } | ||
1019 | 1006 | |||
1020 | 1007 | static void clean_strings(char *s) { | ||
1021 | 1008 | char *e; | ||
1022 | 1009 | |||
1023 | 1010 | for (e = s; *e; e++) | ||
1024 | 1011 | if (*e < ' ' || *e >= 127) | ||
1025 | 1012 | *e = ' '; | ||
1026 | 1013 | } | ||
1027 | 1014 | |||
1028 | 1015 | static void drop_spaces(char *s) { | ||
1029 | 1016 | char *d = s; | ||
1030 | 1017 | SkBool prev_space = FALSE; | ||
1031 | 1018 | |||
1032 | 1019 | s += strspn(s, " "); | ||
1033 | 1020 | |||
1034 | 1021 | for (;*s; s++) { | ||
1035 | 1022 | |||
1036 | 1023 | if (prev_space) { | ||
1037 | 1024 | if (*s != ' ') { | ||
1038 | 1025 | prev_space = FALSE; | ||
1039 | 1026 | *(d++) = ' '; | ||
1040 | 1027 | *(d++) = *s; | ||
1041 | 1028 | } | ||
1042 | 1029 | } else { | ||
1043 | 1030 | if (*s == ' ') | ||
1044 | 1031 | prev_space = TRUE; | ||
1045 | 1032 | else | ||
1046 | 1033 | *(d++) = *s; | ||
1047 | 1034 | } | ||
1048 | 1035 | } | ||
1049 | 1036 | |||
1050 | 1037 | *d = 0; | ||
1051 | 1038 | } | ||
1052 | 1039 | |||
1053 | 1040 | static void read_string(char *d, uint8_t *s, size_t len) { | ||
1054 | 1041 | memcpy(d, s, len); | ||
1055 | 1042 | d[len] = 0; | ||
1056 | 1043 | swap_strings(d, len); | ||
1057 | 1044 | clean_strings(d); | ||
1058 | 1045 | drop_spaces(d); | ||
1059 | 1046 | } | ||
1060 | 1047 | |||
1061 | 1048 | int sk_disk_identify_parse(SkDisk *d, const SkIdentifyParsedData **ipd) { | ||
1062 | 1049 | assert(d); | ||
1063 | 1050 | assert(ipd); | ||
1064 | 1051 | |||
1065 | 1052 | if (!d->identify_valid) { | ||
1066 | 1053 | errno = ENOENT; | ||
1067 | 1054 | return -1; | ||
1068 | 1055 | } | ||
1069 | 1056 | |||
1070 | 1057 | read_string(d->identify_parsed_data.serial, d->identify+20, 20); | ||
1071 | 1058 | read_string(d->identify_parsed_data.firmware, d->identify+46, 8); | ||
1072 | 1059 | read_string(d->identify_parsed_data.model, d->identify+54, 40); | ||
1073 | 1060 | |||
1074 | 1061 | *ipd = &d->identify_parsed_data; | ||
1075 | 1062 | |||
1076 | 1063 | return 0; | ||
1077 | 1064 | } | ||
1078 | 1065 | |||
1079 | 1066 | int sk_disk_smart_is_available(SkDisk *d, SkBool *b) { | ||
1080 | 1067 | assert(d); | ||
1081 | 1068 | assert(b); | ||
1082 | 1069 | |||
1083 | 1070 | if (!d->identify_valid) { | ||
1084 | 1071 | errno = ENOTSUP; | ||
1085 | 1072 | return -1; | ||
1086 | 1073 | } | ||
1087 | 1074 | |||
1088 | 1075 | *b = disk_smart_is_available(d); | ||
1089 | 1076 | return 0; | ||
1090 | 1077 | } | ||
1091 | 1078 | |||
1092 | 1079 | int sk_disk_identify_is_available(SkDisk *d, SkBool *b) { | ||
1093 | 1080 | assert(d); | ||
1094 | 1081 | assert(b); | ||
1095 | 1082 | |||
1096 | 1083 | *b = d->identify_valid; | ||
1097 | 1084 | return 0; | ||
1098 | 1085 | } | ||
1099 | 1086 | |||
1100 | 1087 | const char *sk_smart_offline_data_collection_status_to_string(SkSmartOfflineDataCollectionStatus status) { | ||
1101 | 1088 | |||
1102 | 1089 | /* %STRINGPOOLSTART% */ | ||
1103 | 1090 | static const char* const map[] = { | ||
1104 | 1091 | [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER] = "Off-line data collection activity was never started.", | ||
1105 | 1092 | [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS] = "Off-line data collection activity was completed without error.", | ||
1106 | 1093 | [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS] = "Off-line activity in progress.", | ||
1107 | 1094 | [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED] = "Off-line data collection activity was suspended by an interrupting command from host.", | ||
1108 | 1095 | [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED] = "Off-line data collection activity was aborted by an interrupting command from host.", | ||
1109 | 1096 | [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL] = "Off-line data collection activity was aborted by the device with a fatal error.", | ||
1110 | 1097 | [SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN] = "Unknown status" | ||
1111 | 1098 | }; | ||
1112 | 1099 | /* %STRINGPOOLSTOP% */ | ||
1113 | 1100 | |||
1114 | 1101 | if (status >= _SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_MAX) | ||
1115 | 1102 | return NULL; | ||
1116 | 1103 | |||
1117 | 1104 | return _P(map[status]); | ||
1118 | 1105 | } | ||
1119 | 1106 | |||
1120 | 1107 | const char *sk_smart_self_test_execution_status_to_string(SkSmartSelfTestExecutionStatus status) { | ||
1121 | 1108 | |||
1122 | 1109 | /* %STRINGPOOLSTART% */ | ||
1123 | 1110 | static const char* const map[] = { | ||
1124 | 1111 | [SK_SMART_SELF_TEST_EXECUTION_STATUS_SUCCESS_OR_NEVER] = "The previous self-test routine completed without error or no self-test has ever been run.", | ||
1125 | 1112 | [SK_SMART_SELF_TEST_EXECUTION_STATUS_ABORTED] = "The self-test routine was aborted by the host.", | ||
1126 | 1113 | [SK_SMART_SELF_TEST_EXECUTION_STATUS_INTERRUPTED] = "The self-test routine was interrupted by the host with a hardware or software reset.", | ||
1127 | 1114 | [SK_SMART_SELF_TEST_EXECUTION_STATUS_FATAL] = "A fatal error or unknown test error occurred while the device was executing its self-test routine and the device was unable to complete the self-test routine.", | ||
1128 | 1115 | [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_UNKNOWN] = "The previous self-test completed having a test element that failed and the test element that failed.", | ||
1129 | 1116 | [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_ELECTRICAL] = "The previous self-test completed having the electrical element of the test failed.", | ||
1130 | 1117 | [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_SERVO] = "The previous self-test completed having the servo (and/or seek) test element of the test failed.", | ||
1131 | 1118 | [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_READ] = "The previous self-test completed having the read element of the test failed.", | ||
1132 | 1119 | [SK_SMART_SELF_TEST_EXECUTION_STATUS_ERROR_HANDLING] = "The previous self-test completed having a test element that failed and the device is suspected of having handling damage.", | ||
1133 | 1120 | [SK_SMART_SELF_TEST_EXECUTION_STATUS_INPROGRESS] = "Self-test routine in progress" | ||
1134 | 1121 | }; | ||
1135 | 1122 | /* %STRINGPOOLSTOP% */ | ||
1136 | 1123 | |||
1137 | 1124 | if (status >= _SK_SMART_SELF_TEST_EXECUTION_STATUS_MAX) | ||
1138 | 1125 | return NULL; | ||
1139 | 1126 | |||
1140 | 1127 | return _P(map[status]); | ||
1141 | 1128 | } | ||
1142 | 1129 | |||
1143 | 1130 | const char* sk_smart_self_test_to_string(SkSmartSelfTest test) { | ||
1144 | 1131 | |||
1145 | 1132 | switch (test) { | ||
1146 | 1133 | case SK_SMART_SELF_TEST_SHORT: | ||
1147 | 1134 | return "short"; | ||
1148 | 1135 | case SK_SMART_SELF_TEST_EXTENDED: | ||
1149 | 1136 | return "extended"; | ||
1150 | 1137 | case SK_SMART_SELF_TEST_CONVEYANCE: | ||
1151 | 1138 | return "conveyance"; | ||
1152 | 1139 | case SK_SMART_SELF_TEST_ABORT: | ||
1153 | 1140 | return "abort"; | ||
1154 | 1141 | } | ||
1155 | 1142 | |||
1156 | 1143 | return NULL; | ||
1157 | 1144 | } | ||
1158 | 1145 | |||
1159 | 1146 | SkBool sk_smart_self_test_available(const SkSmartParsedData *d, SkSmartSelfTest test) { | ||
1160 | 1147 | assert(d); | ||
1161 | 1148 | |||
1162 | 1149 | if (!d->start_test_available) | ||
1163 | 1150 | return FALSE; | ||
1164 | 1151 | |||
1165 | 1152 | switch (test) { | ||
1166 | 1153 | case SK_SMART_SELF_TEST_SHORT: | ||
1167 | 1154 | case SK_SMART_SELF_TEST_EXTENDED: | ||
1168 | 1155 | return d->short_and_extended_test_available; | ||
1169 | 1156 | case SK_SMART_SELF_TEST_CONVEYANCE: | ||
1170 | 1157 | return d->conveyance_test_available; | ||
1171 | 1158 | case SK_SMART_SELF_TEST_ABORT: | ||
1172 | 1159 | return d->abort_test_available; | ||
1173 | 1160 | default: | ||
1174 | 1161 | return FALSE; | ||
1175 | 1162 | } | ||
1176 | 1163 | } | ||
1177 | 1164 | |||
1178 | 1165 | unsigned sk_smart_self_test_polling_minutes(const SkSmartParsedData *d, SkSmartSelfTest test) { | ||
1179 | 1166 | assert(d); | ||
1180 | 1167 | |||
1181 | 1168 | if (!sk_smart_self_test_available(d, test)) | ||
1182 | 1169 | return 0; | ||
1183 | 1170 | |||
1184 | 1171 | switch (test) { | ||
1185 | 1172 | case SK_SMART_SELF_TEST_SHORT: | ||
1186 | 1173 | return d->short_test_polling_minutes; | ||
1187 | 1174 | case SK_SMART_SELF_TEST_EXTENDED: | ||
1188 | 1175 | return d->extended_test_polling_minutes; | ||
1189 | 1176 | case SK_SMART_SELF_TEST_CONVEYANCE: | ||
1190 | 1177 | return d->conveyance_test_polling_minutes; | ||
1191 | 1178 | default: | ||
1192 | 1179 | return 0; | ||
1193 | 1180 | } | ||
1194 | 1181 | } | ||
1195 | 1182 | |||
1196 | 1183 | static void make_pretty(SkSmartAttributeParsedData *a) { | ||
1197 | 1184 | uint64_t fourtyeight; | ||
1198 | 1185 | |||
1199 | 1186 | if (!a->name) | ||
1200 | 1187 | return; | ||
1201 | 1188 | |||
1202 | 1189 | if (a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_UNKNOWN) | ||
1203 | 1190 | return; | ||
1204 | 1191 | |||
1205 | 1192 | fourtyeight = | ||
1206 | 1193 | ((uint64_t) a->raw[0]) | | ||
1207 | 1194 | (((uint64_t) a->raw[1]) << 8) | | ||
1208 | 1195 | (((uint64_t) a->raw[2]) << 16) | | ||
1209 | 1196 | (((uint64_t) a->raw[3]) << 24) | | ||
1210 | 1197 | (((uint64_t) a->raw[4]) << 32) | | ||
1211 | 1198 | (((uint64_t) a->raw[5]) << 40); | ||
1212 | 1199 | |||
1213 | 1200 | if (!strcmp(a->name, "spin-up-time")) | ||
1214 | 1201 | a->pretty_value = fourtyeight & 0xFFFF; | ||
1215 | 1202 | else if (!strcmp(a->name, "airflow-temperature-celsius") || | ||
1216 | 1203 | !strcmp(a->name, "temperature-celsius") || | ||
1217 | 1204 | !strcmp(a->name, "temperature-celsius-2")) | ||
1218 | 1205 | a->pretty_value = (fourtyeight & 0xFFFF)*1000 + 273150; | ||
1219 | 1206 | else if (!strcmp(a->name, "temperature-centi-celsius")) | ||
1220 | 1207 | a->pretty_value = (fourtyeight & 0xFFFF)*100 + 273150; | ||
1221 | 1208 | else if (!strcmp(a->name, "power-on-minutes")) | ||
1222 | 1209 | a->pretty_value = fourtyeight * 60 * 1000; | ||
1223 | 1210 | else if (!strcmp(a->name, "power-on-seconds") || | ||
1224 | 1211 | !strcmp(a->name, "power-on-seconds-2")) | ||
1225 | 1212 | a->pretty_value = fourtyeight * 1000; | ||
1226 | 1213 | else if (!strcmp(a->name, "power-on-half-minutes")) | ||
1227 | 1214 | a->pretty_value = fourtyeight * 30 * 1000; | ||
1228 | 1215 | else if (!strcmp(a->name, "power-on-hours") || | ||
1229 | 1216 | !strcmp(a->name, "loaded-hours") || | ||
1230 | 1217 | !strcmp(a->name, "head-flying-hours")) | ||
1231 | 1218 | a->pretty_value = (fourtyeight & 0xFFFFFFFFU) * 60 * 60 * 1000; | ||
1232 | 1219 | else if (!strcmp(a->name, "reallocated-sector-count") || | ||
1233 | 1220 | !strcmp(a->name, "current-pending-sector")) | ||
1234 | 1221 | a->pretty_value = fourtyeight & 0xFFFFFFFFU; | ||
1235 | 1222 | else if (!strcmp(a->name, "endurance-remaining") || | ||
1236 | 1223 | !strcmp(a->name, "available-reserved-space")) | ||
1237 | 1224 | a->pretty_value = a->current_value; | ||
1238 | 1225 | else if (!strcmp(a->name, "total-lbas-written") || | ||
1239 | 1226 | !strcmp(a->name, "total-lbas-read")) | ||
1240 | 1227 | a->pretty_value = fourtyeight * 65536LLU * 512LLU / 1000000LLU; | ||
1241 | 1228 | else if (!strcmp(a->name, "timed-workload-media-wear") || | ||
1242 | 1229 | !strcmp(a->name, "timed-workload-host-reads")) | ||
1243 | 1230 | a->pretty_value = (double)fourtyeight / 1024LLU; | ||
1244 | 1231 | else if (!strcmp(a->name, "workload-timer")) | ||
1245 | 1232 | a->pretty_value = fourtyeight * 60 * 1000; | ||
1246 | 1233 | else | ||
1247 | 1234 | a->pretty_value = fourtyeight; | ||
1248 | 1235 | } | ||
1249 | 1236 | |||
1250 | 1237 | typedef void (*SkSmartAttributeVerify)(SkDisk *d, SkSmartAttributeParsedData *a); | ||
1251 | 1238 | |||
1252 | 1239 | typedef struct SkSmartAttributeInfo { | ||
1253 | 1240 | const char *name; | ||
1254 | 1241 | SkSmartAttributeUnit unit; | ||
1255 | 1242 | SkSmartAttributeVerify verify; | ||
1256 | 1243 | } SkSmartAttributeInfo; | ||
1257 | 1244 | |||
1258 | 1245 | static void verify_temperature(SkDisk *d, SkSmartAttributeParsedData *a) { | ||
1259 | 1246 | assert(a); | ||
1260 | 1247 | assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_MKELVIN); | ||
1261 | 1248 | |||
1262 | 1249 | if (a->pretty_value < SK_MKELVIN_VALID_MIN || | ||
1263 | 1250 | a->pretty_value > SK_MKELVIN_VALID_MAX) { | ||
1264 | 1251 | a->pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN; | ||
1265 | 1252 | d->attribute_verification_bad = TRUE; | ||
1266 | 1253 | } | ||
1267 | 1254 | } | ||
1268 | 1255 | |||
1269 | 1256 | static void verify_short_time(SkDisk *d, SkSmartAttributeParsedData *a) { | ||
1270 | 1257 | assert(a); | ||
1271 | 1258 | assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_MSECONDS); | ||
1272 | 1259 | |||
1273 | 1260 | if (a->pretty_value < SK_MSECOND_VALID_MIN || | ||
1274 | 1261 | a->pretty_value > SK_MSECOND_VALID_SHORT_MAX) { | ||
1275 | 1262 | a->pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN; | ||
1276 | 1263 | d->attribute_verification_bad = TRUE; | ||
1277 | 1264 | } | ||
1278 | 1265 | } | ||
1279 | 1266 | |||
1280 | 1267 | static void verify_long_time(SkDisk *d, SkSmartAttributeParsedData *a) { | ||
1281 | 1268 | assert(a); | ||
1282 | 1269 | assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_MSECONDS); | ||
1283 | 1270 | |||
1284 | 1271 | if (a->pretty_value < SK_MSECOND_VALID_MIN || | ||
1285 | 1272 | a->pretty_value > SK_MSECOND_VALID_LONG_MAX) { | ||
1286 | 1273 | a->pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN; | ||
1287 | 1274 | d->attribute_verification_bad = TRUE; | ||
1288 | 1275 | } | ||
1289 | 1276 | } | ||
1290 | 1277 | |||
1291 | 1278 | static void verify_sectors(SkDisk *d, SkSmartAttributeParsedData *a) { | ||
1292 | 1279 | uint64_t max_sectors; | ||
1293 | 1280 | |||
1294 | 1281 | assert(d); | ||
1295 | 1282 | assert(a); | ||
1296 | 1283 | assert(a->pretty_unit == SK_SMART_ATTRIBUTE_UNIT_SECTORS); | ||
1297 | 1284 | |||
1298 | 1285 | max_sectors = d->size / 512ULL; | ||
1299 | 1286 | |||
1300 | 1287 | if (a->pretty_value == 0xffffffffULL || | ||
1301 | 1288 | a->pretty_value == 0xffffffffffffULL || | ||
1302 | 1289 | (max_sectors > 0 && a->pretty_value > max_sectors)) { | ||
1303 | 1290 | a->pretty_value = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN; | ||
1304 | 1291 | d->attribute_verification_bad = TRUE; | ||
1305 | 1292 | } else { | ||
1306 | 1293 | if ((!strcmp(a->name, "reallocated-sector-count") || | ||
1307 | 1294 | !strcmp(a->name, "current-pending-sector")) && | ||
1308 | 1295 | a->pretty_value > 0) | ||
1309 | 1296 | a->warn = TRUE; | ||
1310 | 1297 | } | ||
1311 | 1298 | } | ||
1312 | 1299 | |||
1313 | 1300 | /* This data is stolen from smartmontools */ | ||
1314 | 1301 | |||
1315 | 1302 | /* %STRINGPOOLSTART% */ | ||
1316 | 1303 | static const SkSmartAttributeInfo const attribute_info[256] = { | ||
1317 | 1304 | [1] = { "raw-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1318 | 1305 | [2] = { "throughput-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL }, | ||
1319 | 1306 | [3] = { "spin-up-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_short_time }, | ||
1320 | 1307 | [4] = { "start-stop-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1321 | 1308 | [5] = { "reallocated-sector-count", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors }, | ||
1322 | 1309 | [6] = { "read-channel-margin", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL }, | ||
1323 | 1310 | [7] = { "seek-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1324 | 1311 | [8] = { "seek-time-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL }, | ||
1325 | 1312 | [9] = { "power-on-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time }, | ||
1326 | 1313 | [10] = { "spin-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1327 | 1314 | [11] = { "calibration-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1328 | 1315 | [12] = { "power-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1329 | 1316 | [13] = { "read-soft-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1330 | 1317 | [170] = { "available-reserved-space", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL }, | ||
1331 | 1318 | [171] = { "program-fail-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1332 | 1319 | [172] = { "erase-fail-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1333 | 1320 | [175] = { "program-fail-count-chip", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1334 | 1321 | [176] = { "erase-fail-count-chip", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1335 | 1322 | [177] = { "wear-leveling-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1336 | 1323 | [178] = { "used-reserved-blocks-chip", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1337 | 1324 | [179] = { "used-reserved-blocks-total", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1338 | 1325 | [180] = { "unused-reserved-blocks", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1339 | 1326 | [181] = { "program-fail-count-total", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1340 | 1327 | [182] = { "erase-fail-count-total", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1341 | 1328 | [183] = { "runtime-bad-block-total", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1342 | 1329 | [184] = { "end-to-end-error", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1343 | 1330 | [187] = { "reported-uncorrect", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors }, | ||
1344 | 1331 | [188] = { "command-timeout", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1345 | 1332 | [189] = { "high-fly-writes", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1346 | 1333 | [190] = { "airflow-temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature }, | ||
1347 | 1334 | [191] = { "g-sense-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1348 | 1335 | [192] = { "power-off-retract-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1349 | 1336 | [193] = { "load-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1350 | 1337 | [194] = { "temperature-celsius-2", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature }, | ||
1351 | 1338 | [195] = { "hardware-ecc-recovered", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1352 | 1339 | [196] = { "reallocated-event-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1353 | 1340 | [197] = { "current-pending-sector", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors }, | ||
1354 | 1341 | [198] = { "offline-uncorrectable", SK_SMART_ATTRIBUTE_UNIT_SECTORS, verify_sectors }, | ||
1355 | 1342 | [199] = { "udma-crc-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1356 | 1343 | [200] = { "multi-zone-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1357 | 1344 | [201] = { "soft-read-error-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1358 | 1345 | [202] = { "ta-increase-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1359 | 1346 | [203] = { "run-out-cancel", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL }, | ||
1360 | 1347 | [204] = { "shock-count-write-open", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1361 | 1348 | [205] = { "shock-rate-write-open", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1362 | 1349 | [206] = { "flying-height", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL }, | ||
1363 | 1350 | [207] = { "spin-high-current", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL }, | ||
1364 | 1351 | [208] = { "spin-buzz", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL }, | ||
1365 | 1352 | [209] = { "offline-seek-performance", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL }, | ||
1366 | 1353 | [220] = { "disk-shift", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL }, | ||
1367 | 1354 | [221] = { "g-sense-error-rate-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1368 | 1355 | [222] = { "loaded-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time }, | ||
1369 | 1356 | [223] = { "load-retry-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1370 | 1357 | [224] = { "load-friction", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL }, | ||
1371 | 1358 | [225] = { "load-cycle-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1372 | 1359 | [226] = { "load-in-time", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_short_time }, | ||
1373 | 1360 | [227] = { "torq-amp-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1374 | 1361 | [228] = { "power-off-retract-count-2", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL }, | ||
1375 | 1362 | [230] = { "head-amplitude", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL }, | ||
1376 | 1363 | [231] = { "temperature-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature }, | ||
1377 | 1364 | |||
1378 | 1365 | /* http://www.adtron.com/pdf/SMART_for_XceedLite_SATA_RevA.pdf */ | ||
1379 | 1366 | [232] = { "endurance-remaining", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL }, | ||
1380 | 1367 | [233] = { "power-on-seconds-2", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL }, | ||
1381 | 1368 | [234] = { "uncorrectable-ecc-count", SK_SMART_ATTRIBUTE_UNIT_SECTORS, NULL }, | ||
1382 | 1369 | [235] = { "good-block-rate", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL }, | ||
1383 | 1370 | |||
1384 | 1371 | [240] = { "head-flying-hours", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time }, | ||
1385 | 1372 | [241] = { "total-lbas-written", SK_SMART_ATTRIBUTE_UNIT_MB, NULL }, | ||
1386 | 1373 | [242] = { "total-lbas-read", SK_SMART_ATTRIBUTE_UNIT_MB, NULL }, | ||
1387 | 1374 | [250] = { "read-error-retry-rate", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL } | ||
1388 | 1375 | }; | ||
1389 | 1376 | /* %STRINGPOOLSTOP% */ | ||
1390 | 1377 | |||
1391 | 1378 | typedef enum SkSmartQuirk { | ||
1392 | 1379 | SK_SMART_QUIRK_9_POWERONMINUTES = 0x000001, | ||
1393 | 1380 | SK_SMART_QUIRK_9_POWERONSECONDS = 0x000002, | ||
1394 | 1381 | SK_SMART_QUIRK_9_POWERONHALFMINUTES = 0x000004, | ||
1395 | 1382 | SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT = 0x000008, | ||
1396 | 1383 | SK_SMART_QUIRK_193_LOADUNLOAD = 0x000010, | ||
1397 | 1384 | SK_SMART_QUIRK_194_10XCELSIUS = 0x000020, | ||
1398 | 1385 | SK_SMART_QUIRK_194_UNKNOWN = 0x000040, | ||
1399 | 1386 | SK_SMART_QUIRK_200_WRITEERRORCOUNT = 0x000080, | ||
1400 | 1387 | SK_SMART_QUIRK_201_DETECTEDTACOUNT = 0x000100, | ||
1401 | 1388 | SK_SMART_QUIRK_5_UNKNOWN = 0x000200, | ||
1402 | 1389 | SK_SMART_QUIRK_9_UNKNOWN = 0x000400, | ||
1403 | 1390 | SK_SMART_QUIRK_197_UNKNOWN = 0x000800, | ||
1404 | 1391 | SK_SMART_QUIRK_198_UNKNOWN = 0x001000, | ||
1405 | 1392 | SK_SMART_QUIRK_190_UNKNOWN = 0x002000, | ||
1406 | 1393 | SK_SMART_QUIRK_232_AVAILABLERESERVEDSPACE = 0x004000, | ||
1407 | 1394 | SK_SMART_QUIRK_233_MEDIAWEAROUTINDICATOR = 0x008000, | ||
1408 | 1395 | SK_SMART_QUIRK_225_TOTALLBASWRITTEN = 0x010000, | ||
1409 | 1396 | SK_SMART_QUIRK_4_UNUSED = 0x020000, | ||
1410 | 1397 | SK_SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR = 0x040000, | ||
1411 | 1398 | SK_SMART_QUIRK_227_TIMEWORKLOADHOSTREADS = 0x080000, | ||
1412 | 1399 | SK_SMART_QUIRK_228_WORKLOADTIMER = 0x100000, | ||
1413 | 1400 | SK_SMART_QUIRK_3_UNUSED = 0x200000 | ||
1414 | 1401 | } SkSmartQuirk; | ||
1415 | 1402 | |||
1416 | 1403 | /* %STRINGPOOLSTART% */ | ||
1417 | 1404 | static const char *quirk_name[] = { | ||
1418 | 1405 | "9_POWERONMINUTES", | ||
1419 | 1406 | "9_POWERONSECONDS", | ||
1420 | 1407 | "9_POWERONHALFMINUTES", | ||
1421 | 1408 | "192_EMERGENCYRETRACTCYCLECT", | ||
1422 | 1409 | "193_LOADUNLOAD", | ||
1423 | 1410 | "194_10XCELSIUS", | ||
1424 | 1411 | "194_UNKNOWN", | ||
1425 | 1412 | "200_WRITEERRORCOUNT", | ||
1426 | 1413 | "201_DETECTEDTACOUNT", | ||
1427 | 1414 | "5_UNKNOWN", | ||
1428 | 1415 | "9_UNKNOWN", | ||
1429 | 1416 | "197_UNKNOWN", | ||
1430 | 1417 | "198_UNKNOWN", | ||
1431 | 1418 | "190_UNKNOWN", | ||
1432 | 1419 | "232_AVAILABLERESERVEDSPACE", | ||
1433 | 1420 | "233_MEDIAWEAROUTINDICATOR", | ||
1434 | 1421 | "225_TOTALLBASWRITTEN", | ||
1435 | 1422 | "4_UNUSED", | ||
1436 | 1423 | "226_TIMEWORKLOADMEDIAWEAR", | ||
1437 | 1424 | "227_TIMEWORKLOADHOSTREADS", | ||
1438 | 1425 | "228_WORKLOADTIMER", | ||
1439 | 1426 | "3_UNUSED", | ||
1440 | 1427 | NULL | ||
1441 | 1428 | }; | ||
1442 | 1429 | /* %STRINGPOOLSTOP% */ | ||
1443 | 1430 | |||
1444 | 1431 | typedef struct SkSmartQuirkDatabase { | ||
1445 | 1432 | const char *model; | ||
1446 | 1433 | const char *firmware; | ||
1447 | 1434 | SkSmartQuirk quirk; | ||
1448 | 1435 | } SkSmartQuirkDatabase; | ||
1449 | 1436 | |||
1450 | 1437 | static const SkSmartQuirkDatabase quirk_database[] = { { | ||
1451 | 1438 | |||
1452 | 1439 | /*** Fujitsu */ | ||
1453 | 1440 | "^(" | ||
1454 | 1441 | "FUJITSU MHY2120BH|" | ||
1455 | 1442 | "FUJITSU MHY2250BH" | ||
1456 | 1443 | ")$", | ||
1457 | 1444 | "^0085000B$", /* seems to be specific to this firmware */ | ||
1458 | 1445 | SK_SMART_QUIRK_9_POWERONMINUTES| | ||
1459 | 1446 | SK_SMART_QUIRK_197_UNKNOWN| | ||
1460 | 1447 | SK_SMART_QUIRK_198_UNKNOWN | ||
1461 | 1448 | }, { | ||
1462 | 1449 | "^FUJITSU MHR2040AT$", | ||
1463 | 1450 | NULL, | ||
1464 | 1451 | SK_SMART_QUIRK_9_POWERONSECONDS| | ||
1465 | 1452 | SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT| | ||
1466 | 1453 | SK_SMART_QUIRK_200_WRITEERRORCOUNT | ||
1467 | 1454 | }, { | ||
1468 | 1455 | "^FUJITSU MHS20[6432]0AT( .)?$", | ||
1469 | 1456 | NULL, | ||
1470 | 1457 | SK_SMART_QUIRK_9_POWERONSECONDS| | ||
1471 | 1458 | SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT| | ||
1472 | 1459 | SK_SMART_QUIRK_200_WRITEERRORCOUNT| | ||
1473 | 1460 | SK_SMART_QUIRK_201_DETECTEDTACOUNT | ||
1474 | 1461 | }, { | ||
1475 | 1462 | "^(" | ||
1476 | 1463 | "FUJITSU M1623TAU|" | ||
1477 | 1464 | "FUJITSU MHG2...ATU?.*|" | ||
1478 | 1465 | "FUJITSU MHH2...ATU?.*|" | ||
1479 | 1466 | "FUJITSU MHJ2...ATU?.*|" | ||
1480 | 1467 | "FUJITSU MHK2...ATU?.*|" | ||
1481 | 1468 | "FUJITSU MHL2300AT|" | ||
1482 | 1469 | "FUJITSU MHM2(20|15|10|06)0AT|" | ||
1483 | 1470 | "FUJITSU MHN2...AT|" | ||
1484 | 1471 | "FUJITSU MHR2020AT|" | ||
1485 | 1472 | "FUJITSU MHT2...(AH|AS|AT|BH)U?.*|" | ||
1486 | 1473 | "FUJITSU MHU2...ATU?.*|" | ||
1487 | 1474 | "FUJITSU MHV2...(AH|AS|AT|BH|BS|BT).*|" | ||
1488 | 1475 | "FUJITSU MP[A-G]3...A[HTEV]U?.*" | ||
1489 | 1476 | ")$", | ||
1490 | 1477 | NULL, | ||
1491 | 1478 | SK_SMART_QUIRK_9_POWERONSECONDS | ||
1492 | 1479 | }, { | ||
1493 | 1480 | |||
1494 | 1481 | /*** Samsung ***/ | ||
1495 | 1482 | "^(" | ||
1496 | 1483 | "SAMSUNG SV4012H|" | ||
1497 | 1484 | "SAMSUNG SP(0451|08[0124]2|12[0145]3|16[0145]4)[CN]" | ||
1498 | 1485 | ")$", | ||
1499 | 1486 | NULL, | ||
1500 | 1487 | SK_SMART_QUIRK_9_POWERONHALFMINUTES | ||
1501 | 1488 | }, { | ||
1502 | 1489 | "^(" | ||
1503 | 1490 | "SAMSUNG SV0412H|" | ||
1504 | 1491 | "SAMSUNG SV1204H" | ||
1505 | 1492 | ")$", | ||
1506 | 1493 | NULL, | ||
1507 | 1494 | SK_SMART_QUIRK_9_POWERONHALFMINUTES| | ||
1508 | 1495 | SK_SMART_QUIRK_194_10XCELSIUS | ||
1509 | 1496 | }, { | ||
1510 | 1497 | "^SAMSUNG SP40A2H$", | ||
1511 | 1498 | "^RR100-07$", | ||
1512 | 1499 | SK_SMART_QUIRK_9_POWERONHALFMINUTES | ||
1513 | 1500 | }, { | ||
1514 | 1501 | "^SAMSUNG SP80A4H$", | ||
1515 | 1502 | "^RT100-06$", | ||
1516 | 1503 | SK_SMART_QUIRK_9_POWERONHALFMINUTES | ||
1517 | 1504 | }, { | ||
1518 | 1505 | "^SAMSUNG SP8004H$", | ||
1519 | 1506 | "^QW100-61$", | ||
1520 | 1507 | SK_SMART_QUIRK_9_POWERONHALFMINUTES | ||
1521 | 1508 | }, { | ||
1522 | 1509 | |||
1523 | 1510 | /*** Maxtor */ | ||
1524 | 1511 | "^(" | ||
1525 | 1512 | "Maxtor 2B0(0[468]|1[05]|20)H1|" | ||
1526 | 1513 | "Maxtor 4G(120J6|160J[68])|" | ||
1527 | 1514 | "Maxtor 4D0(20H1|40H2|60H3|80H4)" | ||
1528 | 1515 | ")$", | ||
1529 | 1516 | NULL, | ||
1530 | 1517 | SK_SMART_QUIRK_9_POWERONMINUTES| | ||
1531 | 1518 | SK_SMART_QUIRK_194_UNKNOWN | ||
1532 | 1519 | }, { | ||
1533 | 1520 | "^(" | ||
1534 | 1521 | "Maxtor 2F0[234]0[JL]0|" | ||
1535 | 1522 | "Maxtor 8(1280A2|2160A4|2560A4|3840A6|4000A6|5120A8)|" | ||
1536 | 1523 | "Maxtor 8(2160D2|3228D3|3240D3|4320D4|6480D6|8400D8|8455D8)|" | ||
1537 | 1524 | "Maxtor 9(0510D4|0576D4|0648D5|0720D5|0840D6|0845D6|0864D6|1008D7|1080D8|1152D8)|" | ||
1538 | 1525 | "Maxtor 9(1(360|350|202)D8|1190D7|10[12]0D6|0840D5|06[48]0D4|0510D3|1(350|202)E8|1010E6|0840E5|0640E4)|" | ||
1539 | 1526 | "Maxtor 9(0512D2|0680D3|0750D3|0913D4|1024D4|1360D6|1536D6|1792D7|2048D8)|" | ||
1540 | 1527 | "Maxtor 9(2732U8|2390U7|204[09]U6|1707U5|1366U4|1024U3|0845U3|0683U2)|" | ||
1541 | 1528 | "Maxtor 4(R0[68]0[JL]0|R1[26]0L0|A160J0|R120L4)|" | ||
1542 | 1529 | "Maxtor (91728D8|91512D7|91303D6|91080D5|90845D4|90645D3|90648D[34]|90432D2)|" | ||
1543 | 1530 | "Maxtor 9(0431U1|0641U2|0871U2|1301U3|1741U4)|" | ||
1544 | 1531 | "Maxtor (94091U8|93071U6|92561U5|92041U4|91731U4|91531U3|91361U3|91021U2|90841U2|90651U2)|" | ||
1545 | 1532 | "Maxtor (33073U4|32049U3|31536U2|30768U1|33073H4|32305H3|31536H2|30768H1)|" | ||
1546 | 1533 | "Maxtor (93652U8|92739U6|91826U4|91369U3|90913U2|90845U2|90435U1)|" | ||
1547 | 1534 | "Maxtor 9(0684U2|1024U2|1362U3|1536U3|2049U4|2562U5|3073U6|4098U8)|" | ||
1548 | 1535 | "Maxtor (54098[UH]8|53073[UH]6|52732[UH]6|52049[UH]4|51536[UH]3|51369[UH]3|51024[UH]2)|" | ||
1549 | 1536 | "Maxtor 3(1024H1|1535H2|2049H2|3073H3|4098H4)( B)?|" | ||
1550 | 1537 | "Maxtor 5(4610H6|4098H6|3073H4|2049H3|1536H2|1369H2|1023H2)|" | ||
1551 | 1538 | "Maxtor 9(1023U2|1536U2|2049U3|2305U3|3073U4|4610U6|6147U8)|" | ||
1552 | 1539 | "Maxtor 9(1023H2|1536H2|2049H3|2305H3|3073H4|4098H6|4610H6|6147H8)|" | ||
1553 | 1540 | "Maxtor 5T0(60H6|40H4|30H3|20H2|10H1)|" | ||
1554 | 1541 | "Maxtor (98196H8|96147H6)|" | ||
1555 | 1542 | "Maxtor 4W(100H6|080H6|060H4|040H3|030H2)|" | ||
1556 | 1543 | "Maxtor 6(E0[234]|K04)0L0|" | ||
1557 | 1544 | "Maxtor 6(B(30|25|20|16|12|10|08)0[MPRS]|L(080[MLP]|(100|120)[MP]|160[MP]|200[MPRS]|250[RS]|300[RS]))0|" | ||
1558 | 1545 | "Maxtor 6Y((060|080|120|160)L0|(060|080|120|160|200|250)P0|(060|080|120|160|200|250)M0)|" | ||
1559 | 1546 | "Maxtor 7Y250[PM]0|" | ||
1560 | 1547 | "Maxtor [45]A(25|30|32)0[JN]0|" | ||
1561 | 1548 | "Maxtor 7L(25|30)0[SR]0" | ||
1562 | 1549 | ")$", | ||
1563 | 1550 | NULL, | ||
1564 | 1551 | SK_SMART_QUIRK_9_POWERONMINUTES | ||
1565 | 1552 | }, { | ||
1566 | 1553 | |||
1567 | 1554 | |||
1568 | 1555 | /*** Hitachi */ | ||
1569 | 1556 | "^(" | ||
1570 | 1557 | "HITACHI_DK14FA-20B|" | ||
1571 | 1558 | "HITACHI_DK23..-..B?|" | ||
1572 | 1559 | "HITACHI_DK23FA-20J|HTA422020F9AT[JN]0|" | ||
1573 | 1560 | "HE[JN]4230[23]0F9AT00|" | ||
1574 | 1561 | "HTC4260[23]0G5CE00|HTC4260[56]0G8CE00" | ||
1575 | 1562 | ")$", | ||
1576 | 1563 | NULL, | ||
1577 | 1564 | SK_SMART_QUIRK_9_POWERONMINUTES| | ||
1578 | 1565 | SK_SMART_QUIRK_193_LOADUNLOAD | ||
1579 | 1566 | }, { | ||
1580 | 1567 | "^HTS541010G9SA00$", | ||
1581 | 1568 | "^MBZOC60P$", | ||
1582 | 1569 | SK_SMART_QUIRK_5_UNKNOWN | ||
1583 | 1570 | }, { | ||
1584 | 1571 | |||
1585 | 1572 | /*** Apple SSD (?) http://bugs.freedesktop.org/show_bug.cgi?id=24700 | ||
1586 | 1573 | https://bugs.launchpad.net/ubuntu/+source/gnome-disk-utility/+bug/438136/comments/4 */ | ||
1587 | 1574 | "^MCCOE64GEMPP$", | ||
1588 | 1575 | "^2.9.0[3-9]$", | ||
1589 | 1576 | SK_SMART_QUIRK_5_UNKNOWN| | ||
1590 | 1577 | SK_SMART_QUIRK_190_UNKNOWN | ||
1591 | 1578 | }, { | ||
1592 | 1579 | |||
1593 | 1580 | /*** Intel */ | ||
1594 | 1581 | "^INTEL SSDSA2(CT|BT|CW|BW)[0-9]{3}G3.*$", /* 320 Series */ | ||
1595 | 1582 | NULL, | ||
1596 | 1583 | SK_SMART_QUIRK_3_UNUSED| | ||
1597 | 1584 | SK_SMART_QUIRK_4_UNUSED| | ||
1598 | 1585 | SK_SMART_QUIRK_225_TOTALLBASWRITTEN| | ||
1599 | 1586 | SK_SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR| | ||
1600 | 1587 | SK_SMART_QUIRK_227_TIMEWORKLOADHOSTREADS| | ||
1601 | 1588 | SK_SMART_QUIRK_228_WORKLOADTIMER| | ||
1602 | 1589 | SK_SMART_QUIRK_232_AVAILABLERESERVEDSPACE| | ||
1603 | 1590 | SK_SMART_QUIRK_233_MEDIAWEAROUTINDICATOR | ||
1604 | 1591 | }, { | ||
1605 | 1592 | NULL, | ||
1606 | 1593 | NULL, | ||
1607 | 1594 | 0 | ||
1608 | 1595 | } | ||
1609 | 1596 | }; | ||
1610 | 1597 | |||
1611 | 1598 | static int match(const char*regex, const char *s, SkBool *result) { | ||
1612 | 1599 | int k; | ||
1613 | 1600 | regex_t re; | ||
1614 | 1601 | |||
1615 | 1602 | *result = FALSE; | ||
1616 | 1603 | |||
1617 | 1604 | if (regcomp(&re, regex, REG_EXTENDED|REG_NOSUB) != 0) { | ||
1618 | 1605 | errno = EINVAL; | ||
1619 | 1606 | return -1; | ||
1620 | 1607 | } | ||
1621 | 1608 | |||
1622 | 1609 | if ((k = regexec(&re, s, 0, NULL, 0)) != 0) { | ||
1623 | 1610 | |||
1624 | 1611 | if (k != REG_NOMATCH) { | ||
1625 | 1612 | regfree(&re); | ||
1626 | 1613 | errno = EINVAL; | ||
1627 | 1614 | return -1; | ||
1628 | 1615 | } | ||
1629 | 1616 | |||
1630 | 1617 | } else | ||
1631 | 1618 | *result = TRUE; | ||
1632 | 1619 | |||
1633 | 1620 | regfree(&re); | ||
1634 | 1621 | |||
1635 | 1622 | return 0; | ||
1636 | 1623 | } | ||
1637 | 1624 | |||
1638 | 1625 | static int lookup_quirks(const char *model, const char *firmware, SkSmartQuirk *quirk) { | ||
1639 | 1626 | int k; | ||
1640 | 1627 | const SkSmartQuirkDatabase *db; | ||
1641 | 1628 | |||
1642 | 1629 | *quirk = 0; | ||
1643 | 1630 | |||
1644 | 1631 | for (db = quirk_database; db->model || db->firmware; db++) { | ||
1645 | 1632 | |||
1646 | 1633 | if (db->model) { | ||
1647 | 1634 | SkBool matching = FALSE; | ||
1648 | 1635 | |||
1649 | 1636 | if ((k = match(db->model, model, &matching)) < 0) | ||
1650 | 1637 | return k; | ||
1651 | 1638 | |||
1652 | 1639 | if (!matching) | ||
1653 | 1640 | continue; | ||
1654 | 1641 | } | ||
1655 | 1642 | |||
1656 | 1643 | if (db->firmware) { | ||
1657 | 1644 | SkBool matching = FALSE; | ||
1658 | 1645 | |||
1659 | 1646 | if ((k = match(db->firmware, firmware, &matching)) < 0) | ||
1660 | 1647 | return k; | ||
1661 | 1648 | |||
1662 | 1649 | if (!matching) | ||
1663 | 1650 | continue; | ||
1664 | 1651 | } | ||
1665 | 1652 | |||
1666 | 1653 | *quirk = db->quirk; | ||
1667 | 1654 | return 0; | ||
1668 | 1655 | } | ||
1669 | 1656 | |||
1670 | 1657 | return 0; | ||
1671 | 1658 | } | ||
1672 | 1659 | |||
1673 | 1660 | static const SkSmartAttributeInfo *lookup_attribute(SkDisk *d, uint8_t id) { | ||
1674 | 1661 | const SkIdentifyParsedData *ipd; | ||
1675 | 1662 | SkSmartQuirk quirk = 0; | ||
1676 | 1663 | |||
1677 | 1664 | /* These are the complex ones */ | ||
1678 | 1665 | if (sk_disk_identify_parse(d, &ipd) < 0) | ||
1679 | 1666 | return NULL; | ||
1680 | 1667 | |||
1681 | 1668 | if (lookup_quirks(ipd->model, ipd->firmware, &quirk) < 0) | ||
1682 | 1669 | return NULL; | ||
1683 | 1670 | |||
1684 | 1671 | if (quirk) { | ||
1685 | 1672 | switch (id) { | ||
1686 | 1673 | case 3: | ||
1687 | 1674 | /* %STRINGPOOLSTART% */ | ||
1688 | 1675 | if (quirk & SK_SMART_QUIRK_3_UNUSED) { | ||
1689 | 1676 | static const SkSmartAttributeInfo a = { | ||
1690 | 1677 | "spin-up-time", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL | ||
1691 | 1678 | }; | ||
1692 | 1679 | return &a; | ||
1693 | 1680 | } | ||
1694 | 1681 | /* %STRINGPOOLSTOP% */ | ||
1695 | 1682 | |||
1696 | 1683 | break; | ||
1697 | 1684 | |||
1698 | 1685 | case 4: | ||
1699 | 1686 | /* %STRINGPOOLSTART% */ | ||
1700 | 1687 | if (quirk & SK_SMART_QUIRK_4_UNUSED) { | ||
1701 | 1688 | static const SkSmartAttributeInfo a = { | ||
1702 | 1689 | "start-stop-count", SK_SMART_ATTRIBUTE_UNIT_UNKNOWN, NULL | ||
1703 | 1690 | }; | ||
1704 | 1691 | return &a; | ||
1705 | 1692 | } | ||
1706 | 1693 | /* %STRINGPOOLSTOP% */ | ||
1707 | 1694 | |||
1708 | 1695 | break; | ||
1709 | 1696 | |||
1710 | 1697 | case 5: | ||
1711 | 1698 | if (quirk & SK_SMART_QUIRK_5_UNKNOWN) | ||
1712 | 1699 | return NULL; | ||
1713 | 1700 | |||
1714 | 1701 | break; | ||
1715 | 1702 | |||
1716 | 1703 | case 9: | ||
1717 | 1704 | /* %STRINGPOOLSTART% */ | ||
1718 | 1705 | if (quirk & SK_SMART_QUIRK_9_POWERONMINUTES) { | ||
1719 | 1706 | static const SkSmartAttributeInfo a = { | ||
1720 | 1707 | "power-on-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time | ||
1721 | 1708 | }; | ||
1722 | 1709 | return &a; | ||
1723 | 1710 | |||
1724 | 1711 | } else if (quirk & SK_SMART_QUIRK_9_POWERONSECONDS) { | ||
1725 | 1712 | static const SkSmartAttributeInfo a = { | ||
1726 | 1713 | "power-on-seconds", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time | ||
1727 | 1714 | }; | ||
1728 | 1715 | return &a; | ||
1729 | 1716 | |||
1730 | 1717 | } else if (quirk & SK_SMART_QUIRK_9_POWERONHALFMINUTES) { | ||
1731 | 1718 | static const SkSmartAttributeInfo a = { | ||
1732 | 1719 | "power-on-half-minutes", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, verify_long_time | ||
1733 | 1720 | }; | ||
1734 | 1721 | return &a; | ||
1735 | 1722 | } else if (quirk & SK_SMART_QUIRK_9_UNKNOWN) | ||
1736 | 1723 | return NULL; | ||
1737 | 1724 | /* %STRINGPOOLSTOP% */ | ||
1738 | 1725 | |||
1739 | 1726 | break; | ||
1740 | 1727 | |||
1741 | 1728 | case 190: | ||
1742 | 1729 | if (quirk & SK_SMART_QUIRK_190_UNKNOWN) | ||
1743 | 1730 | return NULL; | ||
1744 | 1731 | |||
1745 | 1732 | break; | ||
1746 | 1733 | |||
1747 | 1734 | case 192: | ||
1748 | 1735 | /* %STRINGPOOLSTART% */ | ||
1749 | 1736 | if (quirk & SK_SMART_QUIRK_192_EMERGENCYRETRACTCYCLECT) { | ||
1750 | 1737 | static const SkSmartAttributeInfo a = { | ||
1751 | 1738 | "emergency-retract-cycle-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL | ||
1752 | 1739 | }; | ||
1753 | 1740 | return &a; | ||
1754 | 1741 | } | ||
1755 | 1742 | /* %STRINGPOOLSTOP% */ | ||
1756 | 1743 | |||
1757 | 1744 | break; | ||
1758 | 1745 | |||
1759 | 1746 | case 194: | ||
1760 | 1747 | /* %STRINGPOOLSTART% */ | ||
1761 | 1748 | if (quirk & SK_SMART_QUIRK_194_10XCELSIUS) { | ||
1762 | 1749 | static const SkSmartAttributeInfo a = { | ||
1763 | 1750 | "temperature-centi-celsius", SK_SMART_ATTRIBUTE_UNIT_MKELVIN, verify_temperature | ||
1764 | 1751 | }; | ||
1765 | 1752 | return &a; | ||
1766 | 1753 | } else if (quirk & SK_SMART_QUIRK_194_UNKNOWN) | ||
1767 | 1754 | return NULL; | ||
1768 | 1755 | /* %STRINGPOOLSTOP% */ | ||
1769 | 1756 | |||
1770 | 1757 | break; | ||
1771 | 1758 | |||
1772 | 1759 | case 197: | ||
1773 | 1760 | if (quirk & SK_SMART_QUIRK_197_UNKNOWN) | ||
1774 | 1761 | return NULL; | ||
1775 | 1762 | |||
1776 | 1763 | break; | ||
1777 | 1764 | |||
1778 | 1765 | case 198: | ||
1779 | 1766 | if (quirk & SK_SMART_QUIRK_198_UNKNOWN) | ||
1780 | 1767 | return NULL; | ||
1781 | 1768 | |||
1782 | 1769 | break; | ||
1783 | 1770 | |||
1784 | 1771 | case 200: | ||
1785 | 1772 | /* %STRINGPOOLSTART% */ | ||
1786 | 1773 | if (quirk & SK_SMART_QUIRK_200_WRITEERRORCOUNT) { | ||
1787 | 1774 | static const SkSmartAttributeInfo a = { | ||
1788 | 1775 | "write-error-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL | ||
1789 | 1776 | }; | ||
1790 | 1777 | return &a; | ||
1791 | 1778 | } | ||
1792 | 1779 | /* %STRINGPOOLSTOP% */ | ||
1793 | 1780 | |||
1794 | 1781 | break; | ||
1795 | 1782 | |||
1796 | 1783 | case 201: | ||
1797 | 1784 | /* %STRINGPOOLSTART% */ | ||
1798 | 1785 | if (quirk & SK_SMART_QUIRK_201_DETECTEDTACOUNT) { | ||
1799 | 1786 | static const SkSmartAttributeInfo a = { | ||
1800 | 1787 | "detected-ta-count", SK_SMART_ATTRIBUTE_UNIT_NONE, NULL | ||
1801 | 1788 | }; | ||
1802 | 1789 | return &a; | ||
1803 | 1790 | } | ||
1804 | 1791 | /* %STRINGPOOLSTOP% */ | ||
1805 | 1792 | |||
1806 | 1793 | break; | ||
1807 | 1794 | |||
1808 | 1795 | case 225: | ||
1809 | 1796 | /* %STRINGPOOLSTART% */ | ||
1810 | 1797 | if (quirk & SK_SMART_QUIRK_225_TOTALLBASWRITTEN) { | ||
1811 | 1798 | static const SkSmartAttributeInfo a = { | ||
1812 | 1799 | "total-lbas-written", SK_SMART_ATTRIBUTE_UNIT_MB, NULL | ||
1813 | 1800 | }; | ||
1814 | 1801 | return &a; | ||
1815 | 1802 | } | ||
1816 | 1803 | /* %STRINGPOOLSTOP% */ | ||
1817 | 1804 | |||
1818 | 1805 | break; | ||
1819 | 1806 | |||
1820 | 1807 | case 226: | ||
1821 | 1808 | /* %STRINGPOOLSTART% */ | ||
1822 | 1809 | if (quirk & SK_SMART_QUIRK_226_TIMEWORKLOADMEDIAWEAR) { | ||
1823 | 1810 | static const SkSmartAttributeInfo a = { | ||
1824 | 1811 | "timed-workload-media-wear", SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT, NULL | ||
1825 | 1812 | }; | ||
1826 | 1813 | return &a; | ||
1827 | 1814 | } | ||
1828 | 1815 | /* %STRINGPOOLSTOP% */ | ||
1829 | 1816 | |||
1830 | 1817 | break; | ||
1831 | 1818 | |||
1832 | 1819 | case 227: | ||
1833 | 1820 | /* %STRINGPOOLSTART% */ | ||
1834 | 1821 | if (quirk & SK_SMART_QUIRK_227_TIMEWORKLOADHOSTREADS) { | ||
1835 | 1822 | static const SkSmartAttributeInfo a = { | ||
1836 | 1823 | "timed-workload-host-reads", SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT, NULL | ||
1837 | 1824 | }; | ||
1838 | 1825 | return &a; | ||
1839 | 1826 | } | ||
1840 | 1827 | /* %STRINGPOOLSTOP% */ | ||
1841 | 1828 | |||
1842 | 1829 | break; | ||
1843 | 1830 | |||
1844 | 1831 | case 228: | ||
1845 | 1832 | /* %STRINGPOOLSTART% */ | ||
1846 | 1833 | if (quirk & SK_SMART_QUIRK_228_WORKLOADTIMER) { | ||
1847 | 1834 | static const SkSmartAttributeInfo a = { | ||
1848 | 1835 | "workload-timer", SK_SMART_ATTRIBUTE_UNIT_MSECONDS, NULL | ||
1849 | 1836 | }; | ||
1850 | 1837 | return &a; | ||
1851 | 1838 | } | ||
1852 | 1839 | /* %STRINGPOOLSTOP% */ | ||
1853 | 1840 | |||
1854 | 1841 | break; | ||
1855 | 1842 | |||
1856 | 1843 | case 232: | ||
1857 | 1844 | /* %STRINGPOOLSTART% */ | ||
1858 | 1845 | if (quirk & SK_SMART_QUIRK_232_AVAILABLERESERVEDSPACE) { | ||
1859 | 1846 | static const SkSmartAttributeInfo a = { | ||
1860 | 1847 | "available-reserved-space", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL | ||
1861 | 1848 | }; | ||
1862 | 1849 | return &a; | ||
1863 | 1850 | } | ||
1864 | 1851 | /* %STRINGPOOLSTOP% */ | ||
1865 | 1852 | break; | ||
1866 | 1853 | |||
1867 | 1854 | case 233: | ||
1868 | 1855 | /* %STRINGPOOLSTART% */ | ||
1869 | 1856 | if (quirk & SK_SMART_QUIRK_233_MEDIAWEAROUTINDICATOR) { | ||
1870 | 1857 | static const SkSmartAttributeInfo a = { | ||
1871 | 1858 | "media-wearout-indicator", SK_SMART_ATTRIBUTE_UNIT_PERCENT, NULL | ||
1872 | 1859 | }; | ||
1873 | 1860 | return &a; | ||
1874 | 1861 | } | ||
1875 | 1862 | /* %STRINGPOOLSTOP% */ | ||
1876 | 1863 | break; | ||
1877 | 1864 | |||
1878 | 1865 | } | ||
1879 | 1866 | } | ||
1880 | 1867 | |||
1881 | 1868 | /* These are the simple cases */ | ||
1882 | 1869 | if (attribute_info[id].name) | ||
1883 | 1870 | return &attribute_info[id]; | ||
1884 | 1871 | |||
1885 | 1872 | return NULL; | ||
1886 | 1873 | } | ||
1887 | 1874 | |||
1888 | 1875 | int sk_disk_smart_parse(SkDisk *d, const SkSmartParsedData **spd) { | ||
1889 | 1876 | |||
1890 | 1877 | if (!d->smart_data_valid) { | ||
1891 | 1878 | errno = ENOENT; | ||
1892 | 1879 | return -1; | ||
1893 | 1880 | } | ||
1894 | 1881 | |||
1895 | 1882 | switch (d->smart_data[362]) { | ||
1896 | 1883 | case 0x00: | ||
1897 | 1884 | case 0x80: | ||
1898 | 1885 | d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_NEVER; | ||
1899 | 1886 | break; | ||
1900 | 1887 | |||
1901 | 1888 | case 0x02: | ||
1902 | 1889 | case 0x82: | ||
1903 | 1890 | d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUCCESS; | ||
1904 | 1891 | break; | ||
1905 | 1892 | |||
1906 | 1893 | case 0x03: | ||
1907 | 1894 | d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_INPROGRESS; | ||
1908 | 1895 | break; | ||
1909 | 1896 | |||
1910 | 1897 | case 0x04: | ||
1911 | 1898 | case 0x84: | ||
1912 | 1899 | d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_SUSPENDED; | ||
1913 | 1900 | break; | ||
1914 | 1901 | |||
1915 | 1902 | case 0x05: | ||
1916 | 1903 | case 0x85: | ||
1917 | 1904 | d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_ABORTED; | ||
1918 | 1905 | break; | ||
1919 | 1906 | |||
1920 | 1907 | case 0x06: | ||
1921 | 1908 | case 0x86: | ||
1922 | 1909 | d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_FATAL; | ||
1923 | 1910 | break; | ||
1924 | 1911 | |||
1925 | 1912 | default: | ||
1926 | 1913 | d->smart_parsed_data.offline_data_collection_status = SK_SMART_OFFLINE_DATA_COLLECTION_STATUS_UNKNOWN; | ||
1927 | 1914 | break; | ||
1928 | 1915 | } | ||
1929 | 1916 | |||
1930 | 1917 | d->smart_parsed_data.self_test_execution_percent_remaining = 10*(d->smart_data[363] & 0xF); | ||
1931 | 1918 | d->smart_parsed_data.self_test_execution_status = (d->smart_data[363] >> 4) & 0xF; | ||
1932 | 1919 | |||
1933 | 1920 | d->smart_parsed_data.total_offline_data_collection_seconds = (uint16_t) d->smart_data[364] | ((uint16_t) d->smart_data[365] << 8); | ||
1934 | 1921 | |||
1935 | 1922 | d->smart_parsed_data.conveyance_test_available = disk_smart_is_conveyance_test_available(d); | ||
1936 | 1923 | d->smart_parsed_data.short_and_extended_test_available = disk_smart_is_short_and_extended_test_available(d); | ||
1937 | 1924 | d->smart_parsed_data.start_test_available = disk_smart_is_start_test_available(d); | ||
1938 | 1925 | d->smart_parsed_data.abort_test_available = disk_smart_is_abort_test_available(d); | ||
1939 | 1926 | |||
1940 | 1927 | d->smart_parsed_data.short_test_polling_minutes = d->smart_data[372]; | ||
1941 | 1928 | d->smart_parsed_data.extended_test_polling_minutes = d->smart_data[373] != 0xFF ? d->smart_data[373] : ((uint16_t) d->smart_data[376] << 8 | (uint16_t) d->smart_data[375]); | ||
1942 | 1929 | d->smart_parsed_data.conveyance_test_polling_minutes = d->smart_data[374]; | ||
1943 | 1930 | |||
1944 | 1931 | *spd = &d->smart_parsed_data; | ||
1945 | 1932 | |||
1946 | 1933 | return 0; | ||
1947 | 1934 | } | ||
1948 | 1935 | |||
1949 | 1936 | static void find_threshold(SkDisk *d, SkSmartAttributeParsedData *a) { | ||
1950 | 1937 | uint8_t *p; | ||
1951 | 1938 | unsigned n; | ||
1952 | 1939 | |||
1953 | 1940 | if (!d->smart_thresholds_valid) | ||
1954 | 1941 | goto fail; | ||
1955 | 1942 | |||
1956 | 1943 | for (n = 0, p = d->smart_thresholds+2; n < 30; n++, p+=12) | ||
1957 | 1944 | if (p[0] == a->id) | ||
1958 | 1945 | break; | ||
1959 | 1946 | |||
1960 | 1947 | if (n >= 30) | ||
1961 | 1948 | goto fail; | ||
1962 | 1949 | |||
1963 | 1950 | a->threshold = p[1]; | ||
1964 | 1951 | a->threshold_valid = p[1] != 0xFE; | ||
1965 | 1952 | |||
1966 | 1953 | a->good_now_valid = FALSE; | ||
1967 | 1954 | a->good_now = TRUE; | ||
1968 | 1955 | a->good_in_the_past_valid = FALSE; | ||
1969 | 1956 | a->good_in_the_past = TRUE; | ||
1970 | 1957 | |||
1971 | 1958 | /* Always-Fail and Always-Passing thresholds are not relevant | ||
1972 | 1959 | * for our assessment. */ | ||
1973 | 1960 | if (p[1] >= 1 && p[1] <= 0xFD) { | ||
1974 | 1961 | |||
1975 | 1962 | if (a->worst_value_valid) { | ||
1976 | 1963 | a->good_in_the_past = a->good_in_the_past && (a->worst_value > a->threshold); | ||
1977 | 1964 | a->good_in_the_past_valid = TRUE; | ||
1978 | 1965 | } | ||
1979 | 1966 | |||
1980 | 1967 | if (a->current_value_valid) { | ||
1981 | 1968 | a->good_now = a->good_now && (a->current_value > a->threshold); | ||
1982 | 1969 | a->good_now_valid = TRUE; | ||
1983 | 1970 | } | ||
1984 | 1971 | } | ||
1985 | 1972 | |||
1986 | 1973 | a->warn = | ||
1987 | 1974 | (a->good_now_valid && !a->good_now) || | ||
1988 | 1975 | (a->good_in_the_past_valid && !a->good_in_the_past); | ||
1989 | 1976 | |||
1990 | 1977 | return; | ||
1991 | 1978 | |||
1992 | 1979 | fail: | ||
1993 | 1980 | a->threshold_valid = FALSE; | ||
1994 | 1981 | a->good_now_valid = FALSE; | ||
1995 | 1982 | a->good_in_the_past_valid = FALSE; | ||
1996 | 1983 | a->warn = FALSE; | ||
1997 | 1984 | } | ||
1998 | 1985 | |||
1999 | 1986 | int sk_disk_smart_parse_attributes(SkDisk *d, SkSmartAttributeParseCallback cb, void* userdata) { | ||
2000 | 1987 | uint8_t *p; | ||
2001 | 1988 | unsigned n; | ||
2002 | 1989 | |||
2003 | 1990 | if (!d->smart_data_valid) { | ||
2004 | 1991 | errno = ENOENT; | ||
2005 | 1992 | return -1; | ||
2006 | 1993 | } | ||
2007 | 1994 | |||
2008 | 1995 | for (n = 0, p = d->smart_data + 2; n < 30; n++, p+=12) { | ||
2009 | 1996 | SkSmartAttributeParsedData a; | ||
2010 | 1997 | const SkSmartAttributeInfo *i; | ||
2011 | 1998 | char *an = NULL; | ||
2012 | 1999 | |||
2013 | 2000 | if (p[0] == 0) | ||
2014 | 2001 | continue; | ||
2015 | 2002 | |||
2016 | 2003 | memset(&a, 0, sizeof(a)); | ||
2017 | 2004 | a.id = p[0]; | ||
2018 | 2005 | a.current_value = p[3]; | ||
2019 | 2006 | a.current_value_valid = p[3] >= 1 && p[3] <= 0xFD; | ||
2020 | 2007 | a.worst_value = p[4]; | ||
2021 | 2008 | a.worst_value_valid = p[4] >= 1 && p[4] <= 0xFD; | ||
2022 | 2009 | |||
2023 | 2010 | a.flags = ((uint16_t) p[2] << 8) | p[1]; | ||
2024 | 2011 | a.prefailure = !!(p[1] & 1); | ||
2025 | 2012 | a.online = !!(p[1] & 2); | ||
2026 | 2013 | |||
2027 | 2014 | memcpy(a.raw, p+5, 6); | ||
2028 | 2015 | |||
2029 | 2016 | if ((i = lookup_attribute(d, p[0]))) { | ||
2030 | 2017 | a.name = _P(i->name); | ||
2031 | 2018 | a.pretty_unit = i->unit; | ||
2032 | 2019 | } else { | ||
2033 | 2020 | if (asprintf(&an, "attribute-%u", a.id) < 0) { | ||
2034 | 2021 | errno = ENOMEM; | ||
2035 | 2022 | return -1; | ||
2036 | 2023 | } | ||
2037 | 2024 | |||
2038 | 2025 | a.name = an; | ||
2039 | 2026 | a.pretty_unit = SK_SMART_ATTRIBUTE_UNIT_UNKNOWN; | ||
2040 | 2027 | } | ||
2041 | 2028 | |||
2042 | 2029 | make_pretty(&a); | ||
2043 | 2030 | |||
2044 | 2031 | find_threshold(d, &a); | ||
2045 | 2032 | |||
2046 | 2033 | if (i && i->verify) | ||
2047 | 2034 | i->verify(d, &a); | ||
2048 | 2035 | |||
2049 | 2036 | cb(d, &a, userdata); | ||
2050 | 2037 | free(an); | ||
2051 | 2038 | } | ||
2052 | 2039 | |||
2053 | 2040 | return 0; | ||
2054 | 2041 | } | ||
2055 | 2042 | |||
2056 | 2043 | static const char *yes_no(SkBool b) { | ||
2057 | 2044 | return b ? "yes" : "no"; | ||
2058 | 2045 | } | ||
2059 | 2046 | |||
2060 | 2047 | const char* sk_smart_attribute_unit_to_string(SkSmartAttributeUnit unit) { | ||
2061 | 2048 | |||
2062 | 2049 | /* %STRINGPOOLSTART% */ | ||
2063 | 2050 | const char * const map[] = { | ||
2064 | 2051 | [SK_SMART_ATTRIBUTE_UNIT_UNKNOWN] = NULL, | ||
2065 | 2052 | [SK_SMART_ATTRIBUTE_UNIT_NONE] = "", | ||
2066 | 2053 | [SK_SMART_ATTRIBUTE_UNIT_MSECONDS] = "ms", | ||
2067 | 2054 | [SK_SMART_ATTRIBUTE_UNIT_SECTORS] = "sectors", | ||
2068 | 2055 | [SK_SMART_ATTRIBUTE_UNIT_MKELVIN] = "mK", | ||
2069 | 2056 | [SK_SMART_ATTRIBUTE_UNIT_PERCENT] = "%", | ||
2070 | 2057 | [SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT] = "%", | ||
2071 | 2058 | [SK_SMART_ATTRIBUTE_UNIT_MB] = "MB" | ||
2072 | 2059 | }; | ||
2073 | 2060 | /* %STRINGPOOLSTOP% */ | ||
2074 | 2061 | |||
2075 | 2062 | if (unit >= _SK_SMART_ATTRIBUTE_UNIT_MAX) | ||
2076 | 2063 | return NULL; | ||
2077 | 2064 | |||
2078 | 2065 | return _P(map[unit]); | ||
2079 | 2066 | } | ||
2080 | 2067 | |||
2081 | 2068 | struct attr_helper { | ||
2082 | 2069 | uint64_t *value; | ||
2083 | 2070 | SkBool found; | ||
2084 | 2071 | }; | ||
2085 | 2072 | |||
2086 | 2073 | static void temperature_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) { | ||
2087 | 2074 | |||
2088 | 2075 | if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MKELVIN) | ||
2089 | 2076 | return; | ||
2090 | 2077 | |||
2091 | 2078 | if (!strcmp(a->name, "temperature-centi-celsius") || | ||
2092 | 2079 | !strcmp(a->name, "temperature-celsius") || | ||
2093 | 2080 | !strcmp(a->name, "temperature-celsius-2") || | ||
2094 | 2081 | !strcmp(a->name, "airflow-temperature-celsius")) { | ||
2095 | 2082 | |||
2096 | 2083 | if (!ah->found || a->pretty_value > *ah->value) | ||
2097 | 2084 | *ah->value = a->pretty_value; | ||
2098 | 2085 | |||
2099 | 2086 | ah->found = TRUE; | ||
2100 | 2087 | } | ||
2101 | 2088 | } | ||
2102 | 2089 | |||
2103 | 2090 | int sk_disk_smart_get_temperature(SkDisk *d, uint64_t *kelvin) { | ||
2104 | 2091 | struct attr_helper ah; | ||
2105 | 2092 | |||
2106 | 2093 | assert(d); | ||
2107 | 2094 | assert(kelvin); | ||
2108 | 2095 | |||
2109 | 2096 | ah.found = FALSE; | ||
2110 | 2097 | ah.value = kelvin; | ||
2111 | 2098 | |||
2112 | 2099 | if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) temperature_cb, &ah) < 0) | ||
2113 | 2100 | return -1; | ||
2114 | 2101 | |||
2115 | 2102 | if (!ah.found) { | ||
2116 | 2103 | errno = ENOENT; | ||
2117 | 2104 | return -1; | ||
2118 | 2105 | } | ||
2119 | 2106 | |||
2120 | 2107 | return 0; | ||
2121 | 2108 | } | ||
2122 | 2109 | |||
2123 | 2110 | static void power_on_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) { | ||
2124 | 2111 | |||
2125 | 2112 | if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_MSECONDS) | ||
2126 | 2113 | return; | ||
2127 | 2114 | |||
2128 | 2115 | if (!strcmp(a->name, "power-on-minutes") || | ||
2129 | 2116 | !strcmp(a->name, "power-on-seconds") || | ||
2130 | 2117 | !strcmp(a->name, "power-on-seconds-2") || | ||
2131 | 2118 | !strcmp(a->name, "power-on-half-minutes") || | ||
2132 | 2119 | !strcmp(a->name, "power-on-hours")) { | ||
2133 | 2120 | |||
2134 | 2121 | if (!ah->found || a->pretty_value > *ah->value) | ||
2135 | 2122 | *ah->value = a->pretty_value; | ||
2136 | 2123 | |||
2137 | 2124 | ah->found = TRUE; | ||
2138 | 2125 | } | ||
2139 | 2126 | } | ||
2140 | 2127 | |||
2141 | 2128 | int sk_disk_smart_get_power_on(SkDisk *d, uint64_t *mseconds) { | ||
2142 | 2129 | struct attr_helper ah; | ||
2143 | 2130 | |||
2144 | 2131 | assert(d); | ||
2145 | 2132 | assert(mseconds); | ||
2146 | 2133 | |||
2147 | 2134 | ah.found = FALSE; | ||
2148 | 2135 | ah.value = mseconds; | ||
2149 | 2136 | |||
2150 | 2137 | if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_on_cb, &ah) < 0) | ||
2151 | 2138 | return -1; | ||
2152 | 2139 | |||
2153 | 2140 | if (!ah.found) { | ||
2154 | 2141 | errno = ENOENT; | ||
2155 | 2142 | return -1; | ||
2156 | 2143 | } | ||
2157 | 2144 | |||
2158 | 2145 | return 0; | ||
2159 | 2146 | } | ||
2160 | 2147 | |||
2161 | 2148 | static void power_cycle_cb(SkDisk *d, const SkSmartAttributeParsedData *a, struct attr_helper *ah) { | ||
2162 | 2149 | |||
2163 | 2150 | if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_NONE) | ||
2164 | 2151 | return; | ||
2165 | 2152 | |||
2166 | 2153 | if (!strcmp(a->name, "power-cycle-count")) { | ||
2167 | 2154 | |||
2168 | 2155 | if (!ah->found || a->pretty_value > *ah->value) | ||
2169 | 2156 | *ah->value = a->pretty_value; | ||
2170 | 2157 | |||
2171 | 2158 | ah->found = TRUE; | ||
2172 | 2159 | } | ||
2173 | 2160 | } | ||
2174 | 2161 | |||
2175 | 2162 | int sk_disk_smart_get_power_cycle(SkDisk *d, uint64_t *count) { | ||
2176 | 2163 | struct attr_helper ah; | ||
2177 | 2164 | |||
2178 | 2165 | assert(d); | ||
2179 | 2166 | assert(count); | ||
2180 | 2167 | |||
2181 | 2168 | ah.found = FALSE; | ||
2182 | 2169 | ah.value = count; | ||
2183 | 2170 | |||
2184 | 2171 | if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) power_cycle_cb, &ah) < 0) | ||
2185 | 2172 | return -1; | ||
2186 | 2173 | |||
2187 | 2174 | if (!ah.found) { | ||
2188 | 2175 | errno = ENOENT; | ||
2189 | 2176 | return -1; | ||
2190 | 2177 | } | ||
2191 | 2178 | |||
2192 | 2179 | return 0; | ||
2193 | 2180 | } | ||
2194 | 2181 | |||
2195 | 2182 | static void fill_cache_cb(SkDisk *d, const SkSmartAttributeParsedData *a, void* userdata) { | ||
2196 | 2183 | |||
2197 | 2184 | if (a->prefailure) { | ||
2198 | 2185 | if (a->good_now_valid && !a->good_now) | ||
2199 | 2186 | d->bad_attribute_now = TRUE; | ||
2200 | 2187 | |||
2201 | 2188 | if (a->good_in_the_past_valid && !a->good_in_the_past) | ||
2202 | 2189 | d->bad_attribute_in_the_past = TRUE; | ||
2203 | 2190 | } | ||
2204 | 2191 | |||
2205 | 2192 | if (a->pretty_unit != SK_SMART_ATTRIBUTE_UNIT_SECTORS) | ||
2206 | 2193 | return; | ||
2207 | 2194 | |||
2208 | 2195 | if (!a->current_value_valid) | ||
2209 | 2196 | return; | ||
2210 | 2197 | |||
2211 | 2198 | if (!strcmp(a->name, "reallocated-sector-count")) { | ||
2212 | 2199 | if (a->pretty_value > d->reallocated_sector_count) | ||
2213 | 2200 | d->reallocated_sector_count = a->pretty_value; | ||
2214 | 2201 | d->reallocated_sector_count_found = TRUE; | ||
2215 | 2202 | if (a->good_now_valid && !a->good_now) | ||
2216 | 2203 | d->reallocated_sector_count_bad = TRUE; | ||
2217 | 2204 | } | ||
2218 | 2205 | |||
2219 | 2206 | if (!strcmp(a->name, "current-pending-sector")) { | ||
2220 | 2207 | if (a->pretty_value > d->current_pending_sector) | ||
2221 | 2208 | d->current_pending_sector = a->pretty_value; | ||
2222 | 2209 | d->current_pending_sector_found = TRUE; | ||
2223 | 2210 | if (a->good_now_valid && !a->good_now) | ||
2224 | 2211 | d->current_pending_sector_bad = TRUE; | ||
2225 | 2212 | } | ||
2226 | 2213 | } | ||
2227 | 2214 | |||
2228 | 2215 | static int fill_cache(SkDisk *d) { | ||
2229 | 2216 | if (d->attribute_cache_valid) | ||
2230 | 2217 | return 0; | ||
2231 | 2218 | |||
2232 | 2219 | if (sk_disk_smart_parse_attributes(d, (SkSmartAttributeParseCallback) fill_cache_cb, NULL) >= 0) { | ||
2233 | 2220 | d->attribute_cache_valid = TRUE; | ||
2234 | 2221 | return 0; | ||
2235 | 2222 | } else | ||
2236 | 2223 | return -1; | ||
2237 | 2224 | } | ||
2238 | 2225 | |||
2239 | 2226 | int sk_disk_smart_get_bad(SkDisk *d, uint64_t *sectors) { | ||
2240 | 2227 | assert(d); | ||
2241 | 2228 | assert(sectors); | ||
2242 | 2229 | |||
2243 | 2230 | if (fill_cache (d) < 0) | ||
2244 | 2231 | return -1; | ||
2245 | 2232 | |||
2246 | 2233 | if (!d->reallocated_sector_count_found && !d->current_pending_sector_found) { | ||
2247 | 2234 | errno = ENOENT; | ||
2248 | 2235 | return -1; | ||
2249 | 2236 | } | ||
2250 | 2237 | |||
2251 | 2238 | if (d->reallocated_sector_count_found && d->current_pending_sector_found) | ||
2252 | 2239 | *sectors = d->reallocated_sector_count + d->current_pending_sector; | ||
2253 | 2240 | else if (d->reallocated_sector_count_found) | ||
2254 | 2241 | *sectors = d->reallocated_sector_count; | ||
2255 | 2242 | else | ||
2256 | 2243 | *sectors = d->current_pending_sector; | ||
2257 | 2244 | |||
2258 | 2245 | return 0; | ||
2259 | 2246 | } | ||
2260 | 2247 | |||
2261 | 2248 | const char* sk_smart_overall_to_string(SkSmartOverall overall) { | ||
2262 | 2249 | |||
2263 | 2250 | /* %STRINGPOOLSTART% */ | ||
2264 | 2251 | const char * const map[] = { | ||
2265 | 2252 | [SK_SMART_OVERALL_GOOD] = "GOOD", | ||
2266 | 2253 | [SK_SMART_OVERALL_BAD_ATTRIBUTE_IN_THE_PAST] = "BAD_ATTRIBUTE_IN_THE_PAST", | ||
2267 | 2254 | [SK_SMART_OVERALL_BAD_SECTOR] = "BAD_SECTOR", | ||
2268 | 2255 | [SK_SMART_OVERALL_BAD_ATTRIBUTE_NOW] = "BAD_ATTRIBUTE_NOW", | ||
2269 | 2256 | [SK_SMART_OVERALL_BAD_SECTOR_MANY] = "BAD_SECTOR_MANY", | ||
2270 | 2257 | [SK_SMART_OVERALL_BAD_STATUS] = "BAD_STATUS", | ||
2271 | 2258 | }; | ||
2272 | 2259 | /* %STRINGPOOLSTOP% */ | ||
2273 | 2260 | |||
2274 | 2261 | if (overall >= _SK_SMART_OVERALL_MAX) | ||
2275 | 2262 | return NULL; | ||
2276 | 2263 | |||
2277 | 2264 | return _P(map[overall]); | ||
2278 | 2265 | } | ||
2279 | 2266 | |||
2280 | 2267 | int sk_disk_smart_get_overall(SkDisk *d, SkSmartOverall *overall) { | ||
2281 | 2268 | SkBool good; | ||
2282 | 2269 | uint64_t sectors; | ||
2283 | 2270 | |||
2284 | 2271 | assert(d); | ||
2285 | 2272 | assert(overall); | ||
2286 | 2273 | |||
2287 | 2274 | /* First, check SMART self-assesment */ | ||
2288 | 2275 | if (sk_disk_smart_status(d, &good) < 0) | ||
2289 | 2276 | return -1; | ||
2290 | 2277 | |||
2291 | 2278 | if (!good) { | ||
2292 | 2279 | *overall = SK_SMART_OVERALL_BAD_STATUS; | ||
2293 | 2280 | return 0; | ||
2294 | 2281 | } | ||
2295 | 2282 | |||
2296 | 2283 | /* Second, check if the number of bad sectors is greater than | ||
2297 | 2284 | * a certain threshold */ | ||
2298 | 2285 | if (sk_disk_smart_get_bad(d, §ors) < 0) { | ||
2299 | 2286 | if (errno != ENOENT) | ||
2300 | 2287 | return -1; | ||
2301 | 2288 | sectors = 0; | ||
2302 | 2289 | } else { | ||
2303 | 2290 | if (d->reallocated_sector_count_bad || d->current_pending_sector_bad) { | ||
2304 | 2291 | *overall = SK_SMART_OVERALL_BAD_SECTOR_MANY; | ||
2305 | 2292 | return 0; | ||
2306 | 2293 | } | ||
2307 | 2294 | } | ||
2308 | 2295 | |||
2309 | 2296 | /* Third, check if any of the SMART attributes is bad */ | ||
2310 | 2297 | if (fill_cache (d) < 0) | ||
2311 | 2298 | return -1; | ||
2312 | 2299 | |||
2313 | 2300 | if (d->bad_attribute_now) { | ||
2314 | 2301 | *overall = SK_SMART_OVERALL_BAD_ATTRIBUTE_NOW; | ||
2315 | 2302 | return 0; | ||
2316 | 2303 | } | ||
2317 | 2304 | |||
2318 | 2305 | /* Fourth, check if there are any bad sectors at all */ | ||
2319 | 2306 | if (sectors > 0) { | ||
2320 | 2307 | *overall = SK_SMART_OVERALL_BAD_SECTOR; | ||
2321 | 2308 | return 0; | ||
2322 | 2309 | } | ||
2323 | 2310 | |||
2324 | 2311 | /* Fifth, check if any of the SMART attributes ever was bad */ | ||
2325 | 2312 | if (d->bad_attribute_in_the_past) { | ||
2326 | 2313 | *overall = SK_SMART_OVERALL_BAD_ATTRIBUTE_IN_THE_PAST; | ||
2327 | 2314 | return 0; | ||
2328 | 2315 | } | ||
2329 | 2316 | |||
2330 | 2317 | /* Sixth, there's really nothing to complain about, so give it a pass */ | ||
2331 | 2318 | *overall = SK_SMART_OVERALL_GOOD; | ||
2332 | 2319 | return 0; | ||
2333 | 2320 | } | ||
2334 | 2321 | |||
2335 | 2322 | static char* print_name(char *s, size_t len, uint8_t id, const char *k) { | ||
2336 | 2323 | |||
2337 | 2324 | if (k) | ||
2338 | 2325 | strncpy(s, k, len); | ||
2339 | 2326 | else | ||
2340 | 2327 | snprintf(s, len, "%u", id); | ||
2341 | 2328 | |||
2342 | 2329 | s[len-1] = 0; | ||
2343 | 2330 | |||
2344 | 2331 | return s; | ||
2345 | 2332 | } | ||
2346 | 2333 | |||
2347 | 2334 | static char *print_value(char *s, size_t len, uint64_t pretty_value, SkSmartAttributeUnit pretty_unit) { | ||
2348 | 2335 | |||
2349 | 2336 | switch (pretty_unit) { | ||
2350 | 2337 | case SK_SMART_ATTRIBUTE_UNIT_MSECONDS: | ||
2351 | 2338 | |||
2352 | 2339 | if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*365LLU) | ||
2353 | 2340 | snprintf(s, len, "%0.1f years", ((double) pretty_value)/(1000.0*60*60*24*365)); | ||
2354 | 2341 | else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU*30LLU) | ||
2355 | 2342 | snprintf(s, len, "%0.1f months", ((double) pretty_value)/(1000.0*60*60*24*30)); | ||
2356 | 2343 | else if (pretty_value >= 1000LLU*60LLU*60LLU*24LLU) | ||
2357 | 2344 | snprintf(s, len, "%0.1f days", ((double) pretty_value)/(1000.0*60*60*24)); | ||
2358 | 2345 | else if (pretty_value >= 1000LLU*60LLU*60LLU) | ||
2359 | 2346 | snprintf(s, len, "%0.1f h", ((double) pretty_value)/(1000.0*60*60)); | ||
2360 | 2347 | else if (pretty_value >= 1000LLU*60LLU) | ||
2361 | 2348 | snprintf(s, len, "%0.1f min", ((double) pretty_value)/(1000.0*60)); | ||
2362 | 2349 | else if (pretty_value >= 1000LLU) | ||
2363 | 2350 | snprintf(s, len, "%0.1f s", ((double) pretty_value)/(1000.0)); | ||
2364 | 2351 | else | ||
2365 | 2352 | snprintf(s, len, "%llu ms", (unsigned long long) pretty_value); | ||
2366 | 2353 | |||
2367 | 2354 | break; | ||
2368 | 2355 | |||
2369 | 2356 | case SK_SMART_ATTRIBUTE_UNIT_MKELVIN: | ||
2370 | 2357 | snprintf(s, len, "%0.1f C", ((double) pretty_value - 273150) / 1000); | ||
2371 | 2358 | break; | ||
2372 | 2359 | |||
2373 | 2360 | case SK_SMART_ATTRIBUTE_UNIT_SECTORS: | ||
2374 | 2361 | snprintf(s, len, "%llu sectors", (unsigned long long) pretty_value); | ||
2375 | 2362 | break; | ||
2376 | 2363 | |||
2377 | 2364 | case SK_SMART_ATTRIBUTE_UNIT_PERCENT: | ||
2378 | 2365 | snprintf(s, len, "%llu%%", (unsigned long long) pretty_value); | ||
2379 | 2366 | break; | ||
2380 | 2367 | |||
2381 | 2368 | case SK_SMART_ATTRIBUTE_UNIT_SMALL_PERCENT: | ||
2382 | 2369 | snprintf(s, len, "%0.3f%%", (double) pretty_value); | ||
2383 | 2370 | break; | ||
2384 | 2371 | |||
2385 | 2372 | case SK_SMART_ATTRIBUTE_UNIT_MB: | ||
2386 | 2373 | if (pretty_value >= 1000000LLU) | ||
2387 | 2374 | snprintf(s, len, "%0.3f TB", (double) pretty_value / 1000000LLU); | ||
2388 | 2375 | else if (pretty_value >= 1000LLU) | ||
2389 | 2376 | snprintf(s, len, "%0.3f GB", (double) pretty_value / 1000LLU); | ||
2390 | 2377 | else | ||
2391 | 2378 | snprintf(s, len, "%llu MB", (unsigned long long) pretty_value); | ||
2392 | 2379 | break; | ||
2393 | 2380 | |||
2394 | 2381 | case SK_SMART_ATTRIBUTE_UNIT_NONE: | ||
2395 | 2382 | snprintf(s, len, "%llu", (unsigned long long) pretty_value); | ||
2396 | 2383 | break; | ||
2397 | 2384 | |||
2398 | 2385 | case SK_SMART_ATTRIBUTE_UNIT_UNKNOWN: | ||
2399 | 2386 | snprintf(s, len, "n/a"); | ||
2400 | 2387 | break; | ||
2401 | 2388 | |||
2402 | 2389 | case _SK_SMART_ATTRIBUTE_UNIT_MAX: | ||
2403 | 2390 | assert(FALSE); | ||
2404 | 2391 | } | ||
2405 | 2392 | |||
2406 | 2393 | s[len-1] = 0; | ||
2407 | 2394 | |||
2408 | 2395 | return s; | ||
2409 | 2396 | } | ||
2410 | 2397 | |||
2411 | 2398 | #define HIGHLIGHT "\x1B[1m" | ||
2412 | 2399 | #define ENDHIGHLIGHT "\x1B[0m" | ||
2413 | 2400 | |||
2414 | 2401 | static void disk_dump_attributes(SkDisk *d, const SkSmartAttributeParsedData *a, void* userdata) { | ||
2415 | 2402 | char name[32]; | ||
2416 | 2403 | char pretty[32]; | ||
2417 | 2404 | char tt[32], tw[32], tc[32]; | ||
2418 | 2405 | SkBool highlight; | ||
2419 | 2406 | |||
2420 | 2407 | snprintf(tt, sizeof(tt), "%3u", a->threshold); | ||
2421 | 2408 | tt[sizeof(tt)-1] = 0; | ||
2422 | 2409 | snprintf(tw, sizeof(tw), "%3u", a->worst_value); | ||
2423 | 2410 | tw[sizeof(tw)-1] = 0; | ||
2424 | 2411 | snprintf(tc, sizeof(tc), "%3u", a->current_value); | ||
2425 | 2412 | tc[sizeof(tc)-1] = 0; | ||
2426 | 2413 | |||
2427 | 2414 | highlight = a->warn && isatty(1); | ||
2428 | 2415 | |||
2429 | 2416 | if (highlight) | ||
2430 | 2417 | fprintf(stderr, HIGHLIGHT); | ||
2431 | 2418 | |||
2432 | 2419 | printf("%3u %-27s %-3s %-3s %-3s %-11s 0x%02x%02x%02x%02x%02x%02x %-7s %-7s %-4s %-4s\n", | ||
2433 | 2420 | a->id, | ||
2434 | 2421 | print_name(name, sizeof(name), a->id, a->name), | ||
2435 | 2422 | a->current_value_valid ? tc : "n/a", | ||
2436 | 2423 | a->worst_value_valid ? tw : "n/a", | ||
2437 | 2424 | a->threshold_valid ? tt : "n/a", | ||
2438 | 2425 | print_value(pretty, sizeof(pretty), a->pretty_value, a->pretty_unit), | ||
2439 | 2426 | a->raw[0], a->raw[1], a->raw[2], a->raw[3], a->raw[4], a->raw[5], | ||
2440 | 2427 | a->prefailure ? "prefail" : "old-age", | ||
2441 | 2428 | a->online ? "online" : "offline", | ||
2442 | 2429 | a->good_now_valid ? yes_no(a->good_now) : "n/a", | ||
2443 | 2430 | a->good_in_the_past_valid ? yes_no(a->good_in_the_past) : "n/a"); | ||
2444 | 2431 | |||
2445 | 2432 | if (highlight) | ||
2446 | 2433 | fprintf(stderr, ENDHIGHLIGHT); | ||
2447 | 2434 | } | ||
2448 | 2435 | |||
2449 | 2436 | int sk_disk_dump(SkDisk *d) { | ||
2450 | 2437 | int ret; | ||
2451 | 2438 | SkBool awake = FALSE; | ||
2452 | 2439 | uint64_t size; | ||
2453 | 2440 | |||
2454 | 2441 | assert(d); | ||
2455 | 2442 | |||
2456 | 2443 | printf("Device: %s%s%s\n" | ||
2457 | 2444 | "Type: %s\n", | ||
2458 | 2445 | d->name ? disk_type_to_prefix_string(d->type) : "", | ||
2459 | 2446 | d->name ? ":" : "", | ||
2460 | 2447 | d->name ? d->name : "n/a", | ||
2461 | 2448 | disk_type_to_human_string(d->type)); | ||
2462 | 2449 | |||
2463 | 2450 | ret = sk_disk_get_size(d, &size); | ||
2464 | 2451 | if (ret >= 0) | ||
2465 | 2452 | printf("Size: %lu MiB\n", (unsigned long) (d->size/1024/1024)); | ||
2466 | 2453 | else | ||
2467 | 2454 | printf("Size: %s\n", strerror(errno)); | ||
2468 | 2455 | |||
2469 | 2456 | if (d->identify_valid) { | ||
2470 | 2457 | const SkIdentifyParsedData *ipd; | ||
2471 | 2458 | SkSmartQuirk quirk = 0; | ||
2472 | 2459 | unsigned i; | ||
2473 | 2460 | |||
2474 | 2461 | if ((ret = sk_disk_identify_parse(d, &ipd)) < 0) | ||
2475 | 2462 | return ret; | ||
2476 | 2463 | |||
2477 | 2464 | printf("Model: [%s]\n" | ||
2478 | 2465 | "Serial: [%s]\n" | ||
2479 | 2466 | "Firmware: [%s]\n" | ||
2480 | 2467 | "SMART Available: %s\n", | ||
2481 | 2468 | ipd->model, | ||
2482 | 2469 | ipd->serial, | ||
2483 | 2470 | ipd->firmware, | ||
2484 | 2471 | yes_no(disk_smart_is_available(d))); | ||
2485 | 2472 | |||
2486 | 2473 | if ((ret = lookup_quirks(ipd->model, ipd->firmware, &quirk))) | ||
2487 | 2474 | return ret; | ||
2488 | 2475 | |||
2489 | 2476 | printf("Quirks:"); | ||
2490 | 2477 | |||
2491 | 2478 | for (i = 0; quirk_name[i]; i++) | ||
2492 | 2479 | if (quirk & (1<<i)) | ||
2493 | 2480 | printf(" %s", _P(quirk_name[i])); | ||
2494 | 2481 | |||
2495 | 2482 | printf("\n"); | ||
2496 | 2483 | } | ||
2497 | 2484 | |||
2498 | 2485 | ret = sk_disk_check_sleep_mode(d, &awake); | ||
2499 | 2486 | printf("Awake: %s\n", | ||
2500 | 2487 | ret >= 0 ? yes_no(awake) : strerror(errno)); | ||
2501 | 2488 | |||
2502 | 2489 | if (disk_smart_is_available(d)) { | ||
2503 | 2490 | SkSmartOverall overall; | ||
2504 | 2491 | const SkSmartParsedData *spd; | ||
2505 | 2492 | SkBool good; | ||
2506 | 2493 | char pretty[32]; | ||
2507 | 2494 | uint64_t value, power_on; | ||
2508 | 2495 | |||
2509 | 2496 | ret = sk_disk_smart_status(d, &good); | ||
2510 | 2497 | printf("%sSMART Disk Health Good: %s%s\n", | ||
2511 | 2498 | ret >= 0 && !good ? HIGHLIGHT : "", | ||
2512 | 2499 | ret >= 0 ? yes_no(good) : strerror(errno), | ||
2513 | 2500 | ret >= 0 && !good ? ENDHIGHLIGHT : ""); | ||
2514 | 2501 | if ((ret = sk_disk_smart_read_data(d)) < 0) | ||
2515 | 2502 | return ret; | ||
2516 | 2503 | |||
2517 | 2504 | if ((ret = sk_disk_smart_parse(d, &spd)) < 0) | ||
2518 | 2505 | return ret; | ||
2519 | 2506 | |||
2520 | 2507 | printf("Off-line Data Collection Status: [%s]\n" | ||
2521 | 2508 | "Total Time To Complete Off-Line Data Collection: %u s\n" | ||
2522 | 2509 | "Self-Test Execution Status: [%s]\n" | ||
2523 | 2510 | "Percent Self-Test Remaining: %u%%\n" | ||
2524 | 2511 | "Conveyance Self-Test Available: %s\n" | ||
2525 | 2512 | "Short/Extended Self-Test Available: %s\n" | ||
2526 | 2513 | "Start Self-Test Available: %s\n" | ||
2527 | 2514 | "Abort Self-Test Available: %s\n" | ||
2528 | 2515 | "Short Self-Test Polling Time: %u min\n" | ||
2529 | 2516 | "Extended Self-Test Polling Time: %u min\n" | ||
2530 | 2517 | "Conveyance Self-Test Polling Time: %u min\n", | ||
2531 | 2518 | sk_smart_offline_data_collection_status_to_string(spd->offline_data_collection_status), | ||
2532 | 2519 | spd->total_offline_data_collection_seconds, | ||
2533 | 2520 | sk_smart_self_test_execution_status_to_string(spd->self_test_execution_status), | ||
2534 | 2521 | spd->self_test_execution_percent_remaining, | ||
2535 | 2522 | yes_no(spd->conveyance_test_available), | ||
2536 | 2523 | yes_no(spd->short_and_extended_test_available), | ||
2537 | 2524 | yes_no(spd->start_test_available), | ||
2538 | 2525 | yes_no(spd->abort_test_available), | ||
2539 | 2526 | spd->short_test_polling_minutes, | ||
2540 | 2527 | spd->extended_test_polling_minutes, | ||
2541 | 2528 | spd->conveyance_test_polling_minutes); | ||
2542 | 2529 | |||
2543 | 2530 | if (sk_disk_smart_get_bad(d, &value) < 0) | ||
2544 | 2531 | printf("Bad Sectors: %s\n", strerror(errno)); | ||
2545 | 2532 | else | ||
2546 | 2533 | printf("%sBad Sectors: %s%s\n", | ||
2547 | 2534 | value > 0 ? HIGHLIGHT : "", | ||
2548 | 2535 | print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_SECTORS), | ||
2549 | 2536 | value > 0 ? ENDHIGHLIGHT : ""); | ||
2550 | 2537 | |||
2551 | 2538 | if (sk_disk_smart_get_power_on(d, &power_on) < 0) { | ||
2552 | 2539 | printf("Powered On: %s\n", strerror(errno)); | ||
2553 | 2540 | power_on = 0; | ||
2554 | 2541 | } else | ||
2555 | 2542 | printf("Powered On: %s\n", print_value(pretty, sizeof(pretty), power_on, SK_SMART_ATTRIBUTE_UNIT_MSECONDS)); | ||
2556 | 2543 | |||
2557 | 2544 | if (sk_disk_smart_get_power_cycle(d, &value) < 0) | ||
2558 | 2545 | printf("Power Cycles: %s\n", strerror(errno)); | ||
2559 | 2546 | else { | ||
2560 | 2547 | printf("Power Cycles: %llu\n", (unsigned long long) value); | ||
2561 | 2548 | |||
2562 | 2549 | if (value > 0 && power_on > 0) | ||
2563 | 2550 | printf("Average Powered On Per Power Cycle: %s\n", print_value(pretty, sizeof(pretty), power_on/value, SK_SMART_ATTRIBUTE_UNIT_MSECONDS)); | ||
2564 | 2551 | } | ||
2565 | 2552 | |||
2566 | 2553 | if (sk_disk_smart_get_temperature(d, &value) < 0) | ||
2567 | 2554 | printf("Temperature: %s\n", strerror(errno)); | ||
2568 | 2555 | else | ||
2569 | 2556 | printf("Temperature: %s\n", print_value(pretty, sizeof(pretty), value, SK_SMART_ATTRIBUTE_UNIT_MKELVIN)); | ||
2570 | 2557 | |||
2571 | 2558 | printf("Attribute Parsing Verification: %s\n", | ||
2572 | 2559 | d->attribute_verification_bad ? "Bad" : "Good"); | ||
2573 | 2560 | |||
2574 | 2561 | if (sk_disk_smart_get_overall(d, &overall) < 0) | ||
2575 | 2562 | printf("Overall Status: %s\n", strerror(errno)); | ||
2576 | 2563 | else | ||
2577 | 2564 | printf("%sOverall Status: %s%s\n", | ||
2578 | 2565 | overall != SK_SMART_OVERALL_GOOD ? HIGHLIGHT : "", | ||
2579 | 2566 | sk_smart_overall_to_string(overall), | ||
2580 | 2567 | overall != SK_SMART_OVERALL_GOOD ? ENDHIGHLIGHT : ""); | ||
2581 | 2568 | |||
2582 | 2569 | printf("%3s %-27s %5s %5s %5s %-11s %-14s %-7s %-7s %-4s %-4s\n", | ||
2583 | 2570 | "ID#", | ||
2584 | 2571 | "Name", | ||
2585 | 2572 | "Value", | ||
2586 | 2573 | "Worst", | ||
2587 | 2574 | "Thres", | ||
2588 | 2575 | "Pretty", | ||
2589 | 2576 | "Raw", | ||
2590 | 2577 | "Type", | ||
2591 | 2578 | "Updates", | ||
2592 | 2579 | "Good", | ||
2593 | 2580 | "Good/Past"); | ||
2594 | 2581 | |||
2595 | 2582 | if ((ret = sk_disk_smart_parse_attributes(d, disk_dump_attributes, NULL)) < 0) | ||
2596 | 2583 | return ret; | ||
2597 | 2584 | } else | ||
2598 | 2585 | printf("ATA SMART not supported.\n"); | ||
2599 | 2586 | |||
2600 | 2587 | return 0; | ||
2601 | 2588 | } | ||
2602 | 2589 | |||
2603 | 2590 | int sk_disk_get_size(SkDisk *d, uint64_t *bytes) { | ||
2604 | 2591 | assert(d); | ||
2605 | 2592 | assert(bytes); | ||
2606 | 2593 | |||
2607 | 2594 | if (d->size == (uint64_t) -1) { | ||
2608 | 2595 | errno = ENODATA; | ||
2609 | 2596 | return -1; | ||
2610 | 2597 | } | ||
2611 | 2598 | |||
2612 | 2599 | *bytes = d->size; | ||
2613 | 2600 | return 0; | ||
2614 | 2601 | } | ||
2615 | 2602 | |||
2616 | 2603 | static int disk_find_type(SkDisk *d, dev_t devnum) { | ||
2617 | 2604 | struct udev *udev; | ||
2618 | 2605 | struct udev_device *dev = NULL, *usb; | ||
2619 | 2606 | int r = -1; | ||
2620 | 2607 | const char *a; | ||
2621 | 2608 | |||
2622 | 2609 | assert(d); | ||
2623 | 2610 | |||
2624 | 2611 | if (!(udev = udev_new())) { | ||
2625 | 2612 | errno = ENXIO; | ||
2626 | 2613 | goto finish; | ||
2627 | 2614 | } | ||
2628 | 2615 | |||
2629 | 2616 | if (!(dev = udev_device_new_from_devnum(udev, 'b', devnum))) { | ||
2630 | 2617 | errno = ENODEV; | ||
2631 | 2618 | goto finish; | ||
2632 | 2619 | } | ||
2633 | 2620 | |||
2634 | 2621 | if ((a = udev_device_get_property_value(dev, "ID_ATA_SMART_ACCESS"))) { | ||
2635 | 2622 | unsigned u; | ||
2636 | 2623 | |||
2637 | 2624 | for (u = 0; u < _SK_DISK_TYPE_MAX; u++) { | ||
2638 | 2625 | const char *t; | ||
2639 | 2626 | |||
2640 | 2627 | if (!(t = disk_type_to_prefix_string(u))) | ||
2641 | 2628 | continue; | ||
2642 | 2629 | |||
2643 | 2630 | if (!strcmp(a, t)) { | ||
2644 | 2631 | d->type = u; | ||
2645 | 2632 | r = 0; | ||
2646 | 2633 | goto finish; | ||
2647 | 2634 | } | ||
2648 | 2635 | } | ||
2649 | 2636 | |||
2650 | 2637 | d->type = SK_DISK_TYPE_NONE; | ||
2651 | 2638 | r = 0; | ||
2652 | 2639 | goto finish; | ||
2653 | 2640 | } | ||
2654 | 2641 | |||
2655 | 2642 | if ((usb = udev_device_get_parent_with_subsystem_devtype(dev, "usb", "usb_device"))) { | ||
2656 | 2643 | const char *product, *vendor; | ||
2657 | 2644 | uint32_t pid, vid; | ||
2658 | 2645 | |||
2659 | 2646 | if (!(product = udev_device_get_sysattr_value(usb, "idProduct")) || | ||
2660 | 2647 | sscanf(product, "%04x", &pid) != 1) { | ||
2661 | 2648 | errno = ENODEV; | ||
2662 | 2649 | goto finish; | ||
2663 | 2650 | } | ||
2664 | 2651 | |||
2665 | 2652 | if (!(vendor = udev_device_get_sysattr_value(usb, "idVendor")) || | ||
2666 | 2653 | sscanf(vendor, "%04x", &vid) != 1) { | ||
2667 | 2654 | errno = ENODEV; | ||
2668 | 2655 | goto finish; | ||
2669 | 2656 | } | ||
2670 | 2657 | |||
2671 | 2658 | if ((vid == 0x0928 && pid == 0x0000)) | ||
2672 | 2659 | /* This Oxford Semiconductor bridge seems to | ||
2673 | 2660 | * choke on SAT commands. Let's explicitly | ||
2674 | 2661 | * black list it here. | ||
2675 | 2662 | * | ||
2676 | 2663 | * http://bugs.freedesktop.org/show_bug.cgi?id=24951 */ | ||
2677 | 2664 | d->type = SK_DISK_TYPE_NONE; | ||
2678 | 2665 | else if ((vid == 0x152d && pid == 0x2329) || | ||
2679 | 2666 | (vid == 0x152d && pid == 0x2338) || | ||
2680 | 2667 | (vid == 0x152d && pid == 0x2339)) | ||
2681 | 2668 | /* Some JMicron bridges seem to choke on SMART | ||
2682 | 2669 | * commands, so let's explicitly black list | ||
2683 | 2670 | * them here. | ||
2684 | 2671 | * | ||
2685 | 2672 | * https://bugzilla.redhat.com/show_bug.cgi?id=515881 | ||
2686 | 2673 | * | ||
2687 | 2674 | * At least some of the JMicron bridges with | ||
2688 | 2675 | * these vids/pids choke on the jmicron access | ||
2689 | 2676 | * mode. To make sure we don't break things | ||
2690 | 2677 | * for people we now disable this by | ||
2691 | 2678 | * default. */ | ||
2692 | 2679 | d->type = SK_DISK_TYPE_NONE; | ||
2693 | 2680 | else if ((vid == 0x152d && pid == 0x2336)) | ||
2694 | 2681 | /* This JMicron bridge seems to always work | ||
2695 | 2682 | * with SMART commands send with the jmicron | ||
2696 | 2683 | * access mode. */ | ||
2697 | 2684 | d->type = SK_DISK_TYPE_JMICRON; | ||
2698 | 2685 | else if ((vid == 0x0c0b && pid == 0xb159) || | ||
2699 | 2686 | (vid == 0x04fc && pid == 0x0c25) || | ||
2700 | 2687 | (vid == 0x04fc && pid == 0x0c15)) | ||
2701 | 2688 | d->type = SK_DISK_TYPE_SUNPLUS; | ||
2702 | 2689 | else | ||
2703 | 2690 | d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_12; | ||
2704 | 2691 | |||
2705 | 2692 | } else if (udev_device_get_parent_with_subsystem_devtype(dev, "ide", NULL)) | ||
2706 | 2693 | d->type = SK_DISK_TYPE_LINUX_IDE; | ||
2707 | 2694 | else if (udev_device_get_parent_with_subsystem_devtype(dev, "scsi", NULL)) | ||
2708 | 2695 | d->type = SK_DISK_TYPE_ATA_PASSTHROUGH_16; | ||
2709 | 2696 | else | ||
2710 | 2697 | d->type = SK_DISK_TYPE_AUTO; | ||
2711 | 2698 | |||
2712 | 2699 | r = 0; | ||
2713 | 2700 | |||
2714 | 2701 | finish: | ||
2715 | 2702 | if (dev) | ||
2716 | 2703 | udev_device_unref(dev); | ||
2717 | 2704 | |||
2718 | 2705 | if (udev) | ||
2719 | 2706 | udev_unref(udev); | ||
2720 | 2707 | |||
2721 | 2708 | return r; | ||
2722 | 2709 | } | ||
2723 | 2710 | |||
2724 | 2711 | static int init_smart(SkDisk *d) { | ||
2725 | 2712 | /* We don't do the SMART initialization right-away, since some | ||
2726 | 2713 | * drivers spin up when we do that */ | ||
2727 | 2714 | |||
2728 | 2715 | int ret; | ||
2729 | 2716 | |||
2730 | 2717 | if (d->smart_initialized) | ||
2731 | 2718 | return 0; | ||
2732 | 2719 | |||
2733 | 2720 | d->smart_initialized = TRUE; | ||
2734 | 2721 | |||
2735 | 2722 | /* Check if driver can do SMART, and enable if necessary */ | ||
2736 | 2723 | if (!disk_smart_is_available(d)) | ||
2737 | 2724 | return 0; | ||
2738 | 2725 | |||
2739 | 2726 | if (!disk_smart_is_enabled(d)) { | ||
2740 | 2727 | if ((ret = disk_smart_enable(d, TRUE)) < 0) | ||
2741 | 2728 | goto fail; | ||
2742 | 2729 | |||
2743 | 2730 | if ((ret = disk_identify_device(d)) < 0) | ||
2744 | 2731 | goto fail; | ||
2745 | 2732 | |||
2746 | 2733 | if (!disk_smart_is_enabled(d)) { | ||
2747 | 2734 | errno = EIO; | ||
2748 | 2735 | ret = -1; | ||
2749 | 2736 | goto fail; | ||
2750 | 2737 | } | ||
2751 | 2738 | } | ||
2752 | 2739 | |||
2753 | 2740 | disk_smart_read_thresholds(d); | ||
2754 | 2741 | ret = 0; | ||
2755 | 2742 | |||
2756 | 2743 | fail: | ||
2757 | 2744 | return ret; | ||
2758 | 2745 | } | ||
2759 | 2746 | |||
2760 | 2747 | int sk_disk_open(const char *name, SkDisk **_d) { | ||
2761 | 2748 | SkDisk *d; | ||
2762 | 2749 | int ret = -1; | ||
2763 | 2750 | struct stat st; | ||
2764 | 2751 | |||
2765 | 2752 | assert(_d); | ||
2766 | 2753 | |||
2767 | 2754 | if (!(d = calloc(1, sizeof(SkDisk)))) { | ||
2768 | 2755 | errno = ENOMEM; | ||
2769 | 2756 | goto fail; | ||
2770 | 2757 | } | ||
2771 | 2758 | |||
2772 | 2759 | d->fd = -1; | ||
2773 | 2760 | d->size = (uint64_t) -1; | ||
2774 | 2761 | |||
2775 | 2762 | if (!name) | ||
2776 | 2763 | d->type = SK_DISK_TYPE_BLOB; | ||
2777 | 2764 | else { | ||
2778 | 2765 | const char *dn; | ||
2779 | 2766 | |||
2780 | 2767 | d->type = SK_DISK_TYPE_AUTO; | ||
2781 | 2768 | |||
2782 | 2769 | if (!(dn = disk_type_from_string(name, &d->type))) | ||
2783 | 2770 | dn = name; | ||
2784 | 2771 | |||
2785 | 2772 | if (!(d->name = strdup(dn))) { | ||
2786 | 2773 | errno = ENOMEM; | ||
2787 | 2774 | goto fail; | ||
2788 | 2775 | } | ||
2789 | 2776 | |||
2790 | 2777 | if ((d->fd = open(d->name, | ||
2791 | 2778 | O_RDONLY|O_NOCTTY|O_NONBLOCK | ||
2792 | 2779 | #ifdef O_CLOEXEC | ||
2793 | 2780 | |O_CLOEXEC | ||
2794 | 2781 | #endif | ||
2795 | 2782 | |||
2796 | 2783 | )) < 0) { | ||
2797 | 2784 | ret = d->fd; | ||
2798 | 2785 | goto fail; | ||
2799 | 2786 | } | ||
2800 | 2787 | |||
2801 | 2788 | if ((ret = fstat(d->fd, &st)) < 0) | ||
2802 | 2789 | goto fail; | ||
2803 | 2790 | |||
2804 | 2791 | if (!S_ISBLK(st.st_mode)) { | ||
2805 | 2792 | errno = ENODEV; | ||
2806 | 2793 | ret = -1; | ||
2807 | 2794 | goto fail; | ||
2808 | 2795 | } | ||
2809 | 2796 | |||
2810 | 2797 | /* So, it's a block device. Let's make sure the ioctls work */ | ||
2811 | 2798 | if ((ret = ioctl(d->fd, BLKGETSIZE64, &d->size)) < 0) | ||
2812 | 2799 | goto fail; | ||
2813 | 2800 | |||
2814 | 2801 | if (d->size <= 0 || d->size == (uint64_t) -1) { | ||
2815 | 2802 | errno = EIO; | ||
2816 | 2803 | ret = -1; | ||
2817 | 2804 | goto fail; | ||
2818 | 2805 | } | ||
2819 | 2806 | |||
2820 | 2807 | /* OK, it's a real block device with a size. Now let's find the suitable API */ | ||
2821 | 2808 | if (d->type == SK_DISK_TYPE_AUTO) | ||
2822 | 2809 | if ((ret = disk_find_type(d, st.st_rdev)) < 0) | ||
2823 | 2810 | goto fail; | ||
2824 | 2811 | |||
2825 | 2812 | if (d->type == SK_DISK_TYPE_AUTO) { | ||
2826 | 2813 | /* We have no clue, so let's autotest for a working API */ | ||
2827 | 2814 | for (d->type = 0; d->type < _SK_DISK_TYPE_TEST_MAX; d->type++) | ||
2828 | 2815 | if (disk_identify_device(d) >= 0) | ||
2829 | 2816 | break; | ||
2830 | 2817 | if (d->type >= _SK_DISK_TYPE_TEST_MAX) | ||
2831 | 2818 | d->type = SK_DISK_TYPE_NONE; | ||
2832 | 2819 | } else | ||
2833 | 2820 | disk_identify_device(d); | ||
2834 | 2821 | } | ||
2835 | 2822 | |||
2836 | 2823 | *_d = d; | ||
2837 | 2824 | |||
2838 | 2825 | return 0; | ||
2839 | 2826 | |||
2840 | 2827 | fail: | ||
2841 | 2828 | |||
2842 | 2829 | if (d) | ||
2843 | 2830 | sk_disk_free(d); | ||
2844 | 2831 | |||
2845 | 2832 | return ret; | ||
2846 | 2833 | } | ||
2847 | 2834 | |||
2848 | 2835 | void sk_disk_free(SkDisk *d) { | ||
2849 | 2836 | assert(d); | ||
2850 | 2837 | |||
2851 | 2838 | if (d->fd >= 0) | ||
2852 | 2839 | close(d->fd); | ||
2853 | 2840 | |||
2854 | 2841 | free(d->name); | ||
2855 | 2842 | free(d->blob); | ||
2856 | 2843 | free(d); | ||
2857 | 2844 | } | ||
2858 | 2845 | |||
2859 | 2846 | int sk_disk_get_blob(SkDisk *d, const void **blob, size_t *rsize) { | ||
2860 | 2847 | size_t size; | ||
2861 | 2848 | SkBool good, have_good = FALSE; | ||
2862 | 2849 | uint32_t *p; | ||
2863 | 2850 | |||
2864 | 2851 | assert(d); | ||
2865 | 2852 | assert(blob); | ||
2866 | 2853 | assert(rsize); | ||
2867 | 2854 | |||
2868 | 2855 | size = | ||
2869 | 2856 | (d->identify_valid ? 8 + sizeof(d->identify) : 0) + | ||
2870 | 2857 | (d->smart_data_valid ? 8 + sizeof(d->smart_data) : 0) + | ||
2871 | 2858 | (d->smart_thresholds_valid ? 8 + sizeof(d->smart_thresholds) : 0); | ||
2872 | 2859 | |||
2873 | 2860 | if (sk_disk_smart_status(d, &good) >= 0) { | ||
2874 | 2861 | size += 12; | ||
2875 | 2862 | have_good = TRUE; | ||
2876 | 2863 | } | ||
2877 | 2864 | |||
2878 | 2865 | if (size <= 0) { | ||
2879 | 2866 | errno = ENODATA; | ||
2880 | 2867 | return -1; | ||
2881 | 2868 | } | ||
2882 | 2869 | |||
2883 | 2870 | free(d->blob); | ||
2884 | 2871 | if (!(d->blob = malloc(size))) { | ||
2885 | 2872 | errno = ENOMEM; | ||
2886 | 2873 | return -1; | ||
2887 | 2874 | } | ||
2888 | 2875 | |||
2889 | 2876 | p = d->blob; | ||
2890 | 2877 | |||
2891 | 2878 | /* These memory accesses are only OK as long as all our | ||
2892 | 2879 | * objects are sensibly aligned, which they are... */ | ||
2893 | 2880 | |||
2894 | 2881 | if (d->identify_valid) { | ||
2895 | 2882 | p[0] = SK_BLOB_TAG_IDENTIFY; | ||
2896 | 2883 | p[1] = htonl(sizeof(d->identify)); | ||
2897 | 2884 | p += 2; | ||
2898 | 2885 | |||
2899 | 2886 | memcpy(p, d->identify, sizeof(d->identify)); | ||
2900 | 2887 | p = (uint32_t*) ((uint8_t*) p + sizeof(d->identify)); | ||
2901 | 2888 | } | ||
2902 | 2889 | |||
2903 | 2890 | if (have_good) { | ||
2904 | 2891 | p[0] = SK_BLOB_TAG_SMART_STATUS; | ||
2905 | 2892 | p[1] = htonl(4); | ||
2906 | 2893 | p[2] = htonl(!!good); | ||
2907 | 2894 | p += 3; | ||
2908 | 2895 | } | ||
2909 | 2896 | |||
2910 | 2897 | if (d->smart_data_valid) { | ||
2911 | 2898 | p[0] = SK_BLOB_TAG_SMART_DATA; | ||
2912 | 2899 | p[1] = htonl(sizeof(d->smart_data)); | ||
2913 | 2900 | p += 2; | ||
2914 | 2901 | |||
2915 | 2902 | memcpy(p, d->smart_data, sizeof(d->smart_data)); | ||
2916 | 2903 | p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_data)); | ||
2917 | 2904 | } | ||
2918 | 2905 | |||
2919 | 2906 | if (d->smart_thresholds_valid) { | ||
2920 | 2907 | p[0] = SK_BLOB_TAG_SMART_THRESHOLDS; | ||
2921 | 2908 | p[1] = htonl(sizeof(d->smart_thresholds)); | ||
2922 | 2909 | p += 2; | ||
2923 | 2910 | |||
2924 | 2911 | memcpy(p, d->smart_thresholds, sizeof(d->smart_thresholds)); | ||
2925 | 2912 | p = (uint32_t*) ((uint8_t*) p + sizeof(d->smart_thresholds)); | ||
2926 | 2913 | } | ||
2927 | 2914 | |||
2928 | 2915 | assert((size_t) ((uint8_t*) p - (uint8_t*) d->blob) == size); | ||
2929 | 2916 | |||
2930 | 2917 | *blob = d->blob; | ||
2931 | 2918 | *rsize = size; | ||
2932 | 2919 | |||
2933 | 2920 | return 0; | ||
2934 | 2921 | } | ||
2935 | 2922 | |||
2936 | 2923 | int sk_disk_set_blob(SkDisk *d, const void *blob, size_t size) { | ||
2937 | 2924 | const uint32_t *p; | ||
2938 | 2925 | size_t left; | ||
2939 | 2926 | SkBool idv = FALSE, sdv = FALSE, stv = FALSE, bssv = FALSE; | ||
2940 | 2927 | |||
2941 | 2928 | assert(d); | ||
2942 | 2929 | assert(blob); | ||
2943 | 2930 | |||
2944 | 2931 | if (d->type != SK_DISK_TYPE_BLOB) { | ||
2945 | 2932 | errno = ENODEV; | ||
2946 | 2933 | return -1; | ||
2947 | 2934 | } | ||
2948 | 2935 | |||
2949 | 2936 | if (size <= 0) { | ||
2950 | 2937 | errno = EINVAL; | ||
2951 | 2938 | return -1; | ||
2952 | 2939 | } | ||
2953 | 2940 | |||
2954 | 2941 | /* First run, verify if everything makes sense */ | ||
2955 | 2942 | p = blob; | ||
2956 | 2943 | left = size; | ||
2957 | 2944 | while (left > 0) { | ||
2958 | 2945 | uint32_t tag, tsize; | ||
2959 | 2946 | |||
2960 | 2947 | if (left < 8) { | ||
2961 | 2948 | errno = EINVAL; | ||
2962 | 2949 | return -1; | ||
2963 | 2950 | } | ||
2964 | 2951 | |||
2965 | 2952 | memcpy(&tag, p, 4); | ||
2966 | 2953 | memcpy(&tsize, p+1, 4); | ||
2967 | 2954 | p += 2; | ||
2968 | 2955 | left -= 8; | ||
2969 | 2956 | |||
2970 | 2957 | if (left < ntohl(tsize)) { | ||
2971 | 2958 | errno = EINVAL; | ||
2972 | 2959 | return -1; | ||
2973 | 2960 | } | ||
2974 | 2961 | |||
2975 | 2962 | switch (tag) { | ||
2976 | 2963 | |||
2977 | 2964 | case SK_BLOB_TAG_IDENTIFY: | ||
2978 | 2965 | if (ntohl(tsize) != sizeof(d->identify) || idv) { | ||
2979 | 2966 | errno = EINVAL; | ||
2980 | 2967 | return -1; | ||
2981 | 2968 | } | ||
2982 | 2969 | idv = TRUE; | ||
2983 | 2970 | break; | ||
2984 | 2971 | |||
2985 | 2972 | case SK_BLOB_TAG_SMART_STATUS: | ||
2986 | 2973 | if (ntohl(tsize) != 4 || bssv) { | ||
2987 | 2974 | errno = EINVAL; | ||
2988 | 2975 | return -1; | ||
2989 | 2976 | } | ||
2990 | 2977 | bssv = TRUE; | ||
2991 | 2978 | break; | ||
2992 | 2979 | |||
2993 | 2980 | case SK_BLOB_TAG_SMART_DATA: | ||
2994 | 2981 | if (ntohl(tsize) != sizeof(d->smart_data) || sdv) { | ||
2995 | 2982 | errno = EINVAL; | ||
2996 | 2983 | return -1; | ||
2997 | 2984 | } | ||
2998 | 2985 | sdv = TRUE; | ||
2999 | 2986 | break; | ||
3000 | 2987 | |||
3001 | 2988 | case SK_BLOB_TAG_SMART_THRESHOLDS: | ||
3002 | 2989 | if (ntohl(tsize) != sizeof(d->smart_thresholds) || stv) { | ||
3003 | 2990 | errno = EINVAL; | ||
3004 | 2991 | return -1; | ||
3005 | 2992 | } | ||
3006 | 2993 | stv = TRUE; | ||
3007 | 2994 | break; | ||
3008 | 2995 | } | ||
3009 | 2996 | |||
3010 | 2997 | p = (uint32_t*) ((uint8_t*) p + ntohl(tsize)); | ||
3011 | 2998 | left -= ntohl(tsize); | ||
3012 | 2999 | } | ||
3013 | 3000 | |||
3014 | 3001 | if (!idv) { | ||
3015 | 3002 | errno = -ENODATA; | ||
3016 | 3003 | return -1; | ||
3017 | 3004 | } | ||
3018 | 3005 | |||
3019 | 3006 | d->identify_valid = idv; | ||
3020 | 3007 | d->smart_data_valid = sdv; | ||
3021 | 3008 | d->smart_thresholds_valid = stv; | ||
3022 | 3009 | d->blob_smart_status_valid = bssv; | ||
3023 | 3010 | |||
3024 | 3011 | /* Second run, actually copy things in */ | ||
3025 | 3012 | p = blob; | ||
3026 | 3013 | left = size; | ||
3027 | 3014 | while (left > 0) { | ||
3028 | 3015 | uint32_t tag, tsize; | ||
3029 | 3016 | |||
3030 | 3017 | assert(left >= 8); | ||
3031 | 3018 | memcpy(&tag, p, 4); | ||
3032 | 3019 | memcpy(&tsize, p+1, 4); | ||
3033 | 3020 | p += 2; | ||
3034 | 3021 | left -= 8; | ||
3035 | 3022 | |||
3036 | 3023 | assert(left >= ntohl(tsize)); | ||
3037 | 3024 | |||
3038 | 3025 | switch (tag) { | ||
3039 | 3026 | |||
3040 | 3027 | case SK_BLOB_TAG_IDENTIFY: | ||
3041 | 3028 | assert(ntohl(tsize) == sizeof(d->identify)); | ||
3042 | 3029 | memcpy(d->identify, p, sizeof(d->identify)); | ||
3043 | 3030 | break; | ||
3044 | 3031 | |||
3045 | 3032 | case SK_BLOB_TAG_SMART_STATUS: { | ||
3046 | 3033 | uint32_t ok; | ||
3047 | 3034 | assert(ntohl(tsize) == 4); | ||
3048 | 3035 | memcpy(&ok, p, 4); | ||
3049 | 3036 | d->blob_smart_status = !!ok; | ||
3050 | 3037 | break; | ||
3051 | 3038 | } | ||
3052 | 3039 | |||
3053 | 3040 | case SK_BLOB_TAG_SMART_DATA: | ||
3054 | 3041 | assert(ntohl(tsize) == sizeof(d->smart_data)); | ||
3055 | 3042 | memcpy(d->smart_data, p, sizeof(d->smart_data)); | ||
3056 | 3043 | break; | ||
3057 | 3044 | |||
3058 | 3045 | case SK_BLOB_TAG_SMART_THRESHOLDS: | ||
3059 | 3046 | assert(ntohl(tsize) == sizeof(d->smart_thresholds)); | ||
3060 | 3047 | memcpy(d->smart_thresholds, p, sizeof(d->smart_thresholds)); | ||
3061 | 3048 | break; | ||
3062 | 3049 | } | ||
3063 | 3050 | |||
3064 | 3051 | p = (uint32_t*) ((uint8_t*) p + ntohl(tsize)); | ||
3065 | 3052 | left -= ntohl(tsize); | ||
3066 | 3053 | } | ||
3067 | 3054 | |||
3068 | 3055 | return 0; | ||
3069 | 3056 | } | ||
3070 | 0 | 3057 | ||
3071 | === modified file 'atasmart.c' | |||
3072 | --- atasmart.c 2012-05-23 08:44:25 +0000 | |||
3073 | +++ atasmart.c 2013-03-18 23:14:22 +0000 | |||
3074 | @@ -925,10 +925,10 @@ | |||
3075 | 925 | /* SAT/USB bridges truncate packets, so we only check for 4F, | 925 | /* SAT/USB bridges truncate packets, so we only check for 4F, |
3076 | 926 | * not for 2C on those */ | 926 | * not for 2C on those */ |
3077 | 927 | if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x00C2U)) && | 927 | if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x00C2U)) && |
3079 | 928 | cmd[4] == htons(0x4F00U)) | 928 | (cmd[4] & htons(0xFF00U)) == htons(0x4F00U)) |
3080 | 929 | *good = TRUE; | 929 | *good = TRUE; |
3081 | 930 | else if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x002CU)) && | 930 | else if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x002CU)) && |
3083 | 931 | cmd[4] == htons(0xF400U)) | 931 | (cmd[4] & htons(0xFF00U)) == htons(0xF400U)) |
3084 | 932 | *good = FALSE; | 932 | *good = FALSE; |
3085 | 933 | else { | 933 | else { |
3086 | 934 | errno = EIO; | 934 | errno = EIO; |
3087 | 935 | 935 | ||
3088 | === modified file 'debian/changelog' | |||
3089 | --- debian/changelog 2013-03-13 07:01:06 +0000 | |||
3090 | +++ debian/changelog 2013-03-18 23:14:22 +0000 | |||
3091 | @@ -1,3 +1,11 @@ | |||
3092 | 1 | libatasmart (0.19-1git1ubuntu1) raring; urgency=low | ||
3093 | 2 | |||
3094 | 3 | * debian/patches/fix-status-io-error.patch: Fix an error that | ||
3095 | 4 | resulted in an IO error reading the SMART health of some drives. | ||
3096 | 5 | (LP: #1143495) | ||
3097 | 6 | |||
3098 | 7 | -- Phillip Susi <psusi@ubuntu.com> Mon, 18 Mar 2013 18:32:58 -0400 | ||
3099 | 8 | |||
3100 | 1 | libatasmart (0.19-1git1build1) raring; urgency=low | 9 | libatasmart (0.19-1git1build1) raring; urgency=low |
3101 | 2 | 10 | ||
3102 | 3 | * No-change rebuild against libudev1 | 11 | * No-change rebuild against libudev1 |
3103 | 4 | 12 | ||
3104 | === added file 'debian/patches/fix-status-io-error.patch' | |||
3105 | --- debian/patches/fix-status-io-error.patch 1970-01-01 00:00:00 +0000 | |||
3106 | +++ debian/patches/fix-status-io-error.patch 2013-03-18 23:14:22 +0000 | |||
3107 | @@ -0,0 +1,26 @@ | |||
3108 | 1 | Author: Phillip Susi <psusi@ubuntu.com> | ||
3109 | 2 | Subject: fix an incorrect IO error reading SMART status | ||
3110 | 3 | Description: The read SMART status command's return status | ||
3111 | 4 | was testing for a success/failure value that included 8 | ||
3112 | 5 | bits that are "N/A" according to the standard, and required | ||
3113 | 6 | that they be zeros. At least some drives do not fill them | ||
3114 | 7 | with zeros, so correct this by masking off the undefined | ||
3115 | 8 | bits. | ||
3116 | 9 | |||
3117 | 10 | Index: b/atasmart.c | ||
3118 | 11 | =================================================================== | ||
3119 | 12 | --- a/atasmart.c | ||
3120 | 13 | +++ b/atasmart.c | ||
3121 | 14 | @@ -925,10 +925,10 @@ | ||
3122 | 15 | /* SAT/USB bridges truncate packets, so we only check for 4F, | ||
3123 | 16 | * not for 2C on those */ | ||
3124 | 17 | if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x00C2U)) && | ||
3125 | 18 | - cmd[4] == htons(0x4F00U)) | ||
3126 | 19 | + (cmd[4] & htons(0xFF00U)) == htons(0x4F00U)) | ||
3127 | 20 | *good = TRUE; | ||
3128 | 21 | else if ((d->type == SK_DISK_TYPE_ATA_PASSTHROUGH_12 || cmd[3] == htons(0x002CU)) && | ||
3129 | 22 | - cmd[4] == htons(0xF400U)) | ||
3130 | 23 | + (cmd[4] & htons(0xFF00U)) == htons(0xF400U)) | ||
3131 | 24 | *good = FALSE; | ||
3132 | 25 | else { | ||
3133 | 26 | errno = EIO; | ||
3134 | 0 | 27 | ||
3135 | === modified file 'debian/patches/series' | |||
3136 | --- debian/patches/series 2012-05-23 08:44:25 +0000 | |||
3137 | +++ debian/patches/series 2013-03-18 23:14:22 +0000 | |||
3138 | @@ -1,2 +1,3 @@ | |||
3139 | 1 | # Debian patches for libatasmart | 1 | # Debian patches for libatasmart |
3140 | 2 | 0002-Drop-our-own-many-bad-sectors-heuristic.patch | 2 | 0002-Drop-our-own-many-bad-sectors-heuristic.patch |
3141 | 3 | fix-status-io-error.patch |
Could you please update the patch tagging to add "Bug" and "Forwarded" tags so we can trace where this patch has been submitted upstream?
Thanks.