]>
Commit | Line | Data |
---|---|---|
2127e193 GI |
1 | /* |
2 | * dev_interface.cpp | |
3 | * | |
a86ec89e | 4 | * Home page of code is: http://www.smartmontools.org |
2127e193 | 5 | * |
a86ec89e | 6 | * Copyright (C) 2008-16 Christian Franke <smartmontools-support@lists.sourceforge.net> |
2127e193 GI |
7 | * |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2, or (at your option) | |
11 | * any later version. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License | |
14 | * (for example COPYING); If not, see <http://www.gnu.org/licenses/>. | |
15 | * | |
16 | */ | |
17 | ||
18 | #include "config.h" | |
19 | #include "int64.h" | |
2127e193 GI |
20 | #include "dev_interface.h" |
21 | #include "dev_tunnelled.h" | |
ee38a438 | 22 | #include "atacmds.h" // ATA_SMART_CMD/STATUS |
2127e193 GI |
23 | #include "utility.h" |
24 | ||
cfbba5b9 GI |
25 | #include <errno.h> |
26 | #include <stdarg.h> | |
2127e193 GI |
27 | #include <stdexcept> |
28 | ||
e165493d GI |
29 | #if defined(HAVE_GETTIMEOFDAY) |
30 | #include <sys/time.h> | |
31 | #elif defined(HAVE_FTIME) | |
32 | #include <sys/timeb.h> | |
33 | #endif | |
34 | ||
a86ec89e | 35 | const char * dev_interface_cpp_cvsid = "$Id: dev_interface.cpp 4283 2016-04-10 12:55:59Z chrfranke $" |
2127e193 GI |
36 | DEV_INTERFACE_H_CVSID; |
37 | ||
38 | ///////////////////////////////////////////////////////////////////////////// | |
39 | // smart_device | |
40 | ||
a86ec89e GI |
41 | int smart_device::s_num_objects = 0; |
42 | ||
2127e193 GI |
43 | smart_device::smart_device(smart_interface * intf, const char * dev_name, |
44 | const char * dev_type, const char * req_type) | |
45 | : m_intf(intf), m_info(dev_name, dev_type, req_type), | |
a86ec89e | 46 | m_ata_ptr(0), m_scsi_ptr(0), m_nvme_ptr(0) |
2127e193 | 47 | { |
a86ec89e | 48 | s_num_objects++; |
2127e193 GI |
49 | } |
50 | ||
51 | smart_device::smart_device(do_not_use_in_implementation_classes) | |
a86ec89e | 52 | : m_intf(0), m_ata_ptr(0), m_scsi_ptr(0), m_nvme_ptr(0) |
2127e193 GI |
53 | { |
54 | throw std::logic_error("smart_device: wrong constructor called in implementation class"); | |
55 | } | |
56 | ||
57 | smart_device::~smart_device() throw() | |
58 | { | |
a86ec89e | 59 | s_num_objects--; |
2127e193 GI |
60 | } |
61 | ||
f4e463df GI |
62 | bool smart_device::is_syscall_unsup() const |
63 | { | |
64 | if (get_errno() == ENOSYS) | |
65 | return true; | |
66 | #ifdef ENOTSUP | |
67 | if (get_errno() == ENOTSUP) | |
68 | return true; | |
69 | #endif | |
70 | return false; | |
71 | } | |
72 | ||
2127e193 GI |
73 | bool smart_device::set_err(int no, const char * msg, ...) |
74 | { | |
75 | if (!msg) | |
76 | return set_err(no); | |
77 | m_err.no = no; | |
78 | va_list ap; va_start(ap, msg); | |
79 | m_err.msg = vstrprintf(msg, ap); | |
80 | va_end(ap); | |
81 | return false; | |
82 | } | |
83 | ||
84 | bool smart_device::set_err(int no) | |
85 | { | |
d008864d | 86 | return smi()->set_err_var(&m_err, no); |
2127e193 GI |
87 | } |
88 | ||
89 | smart_device * smart_device::autodetect_open() | |
90 | { | |
91 | open(); | |
92 | return this; | |
93 | } | |
94 | ||
a86ec89e GI |
95 | bool smart_device::is_powered_down() |
96 | { | |
97 | return false; | |
98 | } | |
99 | ||
2127e193 GI |
100 | bool smart_device::owns(const smart_device * /*dev*/) const |
101 | { | |
102 | return false; | |
103 | } | |
104 | ||
105 | void smart_device::release(const smart_device * /*dev*/) | |
106 | { | |
107 | } | |
108 | ||
109 | ||
110 | ///////////////////////////////////////////////////////////////////////////// | |
111 | // ata_device | |
112 | ||
113 | ata_in_regs_48bit::ata_in_regs_48bit() | |
114 | : features_16(features, prev.features), | |
115 | sector_count_16(sector_count, prev.sector_count), | |
116 | lba_low_16(lba_low, prev.lba_low), | |
117 | lba_mid_16(lba_mid, prev.lba_mid), | |
a23d5117 GI |
118 | lba_high_16(lba_high, prev.lba_high), |
119 | lba_48( lba_low, lba_mid, lba_high, | |
120 | prev.lba_low, prev.lba_mid, prev.lba_high) | |
2127e193 GI |
121 | { |
122 | } | |
123 | ||
124 | ata_out_regs_48bit::ata_out_regs_48bit() | |
125 | : sector_count_16(sector_count, prev.sector_count), | |
126 | lba_low_16(lba_low, prev.lba_low), | |
127 | lba_mid_16(lba_mid, prev.lba_mid), | |
a23d5117 GI |
128 | lba_high_16(lba_high, prev.lba_high), |
129 | lba_48( lba_low, lba_mid, lba_high, | |
130 | prev.lba_low, prev.lba_mid, prev.lba_high) | |
2127e193 GI |
131 | { |
132 | } | |
133 | ||
134 | ata_cmd_in::ata_cmd_in() | |
135 | : direction(no_data), | |
136 | buffer(0), | |
137 | size(0) | |
138 | { | |
139 | } | |
140 | ||
141 | ata_cmd_out::ata_cmd_out() | |
142 | { | |
143 | } | |
144 | ||
145 | bool ata_device::ata_pass_through(const ata_cmd_in & in) | |
146 | { | |
147 | ata_cmd_out dummy; | |
148 | return ata_pass_through(in, dummy); | |
149 | } | |
150 | ||
ee38a438 GI |
151 | bool ata_device::ata_cmd_is_supported(const ata_cmd_in & in, |
152 | unsigned flags, const char * type /* = 0 */) | |
2127e193 GI |
153 | { |
154 | // Check DATA IN/OUT | |
155 | switch (in.direction) { | |
156 | case ata_cmd_in::no_data: break; | |
157 | case ata_cmd_in::data_in: break; | |
158 | case ata_cmd_in::data_out: break; | |
159 | default: | |
160 | return set_err(EINVAL, "Invalid data direction %d", (int)in.direction); | |
161 | } | |
162 | ||
163 | // Check buffer size | |
164 | if (in.direction == ata_cmd_in::no_data) { | |
165 | if (in.size) | |
166 | return set_err(EINVAL, "Buffer size %u > 0 for NO DATA command", in.size); | |
167 | } | |
168 | else { | |
169 | if (!in.buffer) | |
170 | return set_err(EINVAL, "Buffer not set for DATA IN/OUT command"); | |
171 | unsigned count = (in.in_regs.prev.sector_count<<16)|in.in_regs.sector_count; | |
172 | // TODO: Add check for sector count == 0 | |
173 | if (count * 512 != in.size) | |
174 | return set_err(EINVAL, "Sector count %u does not match buffer size %u", count, in.size); | |
175 | } | |
176 | ||
177 | // Check features | |
ee38a438 GI |
178 | const char * errmsg = 0; |
179 | if (in.direction == ata_cmd_in::data_out && !(flags & supports_data_out)) | |
180 | errmsg = "DATA OUT ATA commands not implemented"; | |
181 | else if ( in.out_needed.is_set() && !(flags & supports_output_regs) | |
182 | && !( in.in_regs.command == ATA_SMART_CMD | |
183 | && in.in_regs.features == ATA_SMART_STATUS | |
184 | && (flags & supports_smart_status))) | |
185 | errmsg = "Read of ATA output registers not implemented"; | |
186 | else if (!(in.size == 0 || in.size == 512) && !(flags & supports_multi_sector)) | |
187 | errmsg = "Multi-sector ATA commands not implemented"; | |
188 | else if (in.in_regs.is_48bit_cmd() && !(flags & (supports_48bit_hi_null|supports_48bit))) | |
189 | errmsg = "48-bit ATA commands not implemented"; | |
190 | else if (in.in_regs.is_real_48bit_cmd() && !(flags & supports_48bit)) | |
191 | errmsg = "48-bit ATA commands not fully implemented"; | |
192 | ||
193 | if (errmsg) | |
194 | return set_err(ENOSYS, "%s%s%s%s", errmsg, | |
195 | (type ? " [" : ""), (type ? type : ""), (type ? "]" : "")); | |
196 | ||
2127e193 GI |
197 | return true; |
198 | } | |
199 | ||
200 | bool ata_device::ata_identify_is_cached() const | |
201 | { | |
202 | return false; | |
203 | } | |
204 | ||
205 | ||
a86ec89e GI |
206 | ///////////////////////////////////////////////////////////////////////////// |
207 | // nvme_device | |
208 | ||
209 | bool nvme_device::set_nvme_err(nvme_cmd_out & out, unsigned status, const char * msg /* = 0 */) | |
210 | { | |
211 | if (!status) | |
212 | throw std::logic_error("nvme_device: set_nvme_err() called with status=0"); | |
213 | ||
214 | out.status = status; | |
215 | out.status_valid = true; | |
216 | return set_err(EIO, "%sNVMe Status 0x%02x", (msg ? msg : ""), status); | |
217 | } | |
218 | ||
219 | ||
2127e193 GI |
220 | ///////////////////////////////////////////////////////////////////////////// |
221 | // tunnelled_device_base | |
222 | ||
223 | tunnelled_device_base::tunnelled_device_base(smart_device * tunnel_dev) | |
224 | : smart_device(never_called), | |
225 | m_tunnel_base_dev(tunnel_dev) | |
226 | { | |
227 | } | |
228 | ||
229 | tunnelled_device_base::~tunnelled_device_base() throw() | |
230 | { | |
231 | delete m_tunnel_base_dev; | |
232 | } | |
233 | ||
234 | bool tunnelled_device_base::is_open() const | |
235 | { | |
236 | return (m_tunnel_base_dev && m_tunnel_base_dev->is_open()); | |
237 | } | |
238 | ||
239 | bool tunnelled_device_base::open() | |
240 | { | |
241 | if (!m_tunnel_base_dev) | |
242 | return set_err(ENOSYS); | |
243 | if (!m_tunnel_base_dev->open()) | |
244 | return set_err(m_tunnel_base_dev->get_err()); | |
245 | return true; | |
246 | } | |
247 | ||
248 | bool tunnelled_device_base::close() | |
249 | { | |
250 | if (!m_tunnel_base_dev) | |
251 | return true; | |
252 | if (!m_tunnel_base_dev->close()) | |
253 | return set_err(m_tunnel_base_dev->get_err()); | |
254 | return true; | |
255 | } | |
256 | ||
257 | bool tunnelled_device_base::owns(const smart_device * dev) const | |
258 | { | |
259 | return (m_tunnel_base_dev && (m_tunnel_base_dev == dev)); | |
260 | } | |
261 | ||
262 | void tunnelled_device_base::release(const smart_device * dev) | |
263 | { | |
264 | if (m_tunnel_base_dev == dev) | |
265 | m_tunnel_base_dev = 0; | |
266 | } | |
267 | ||
268 | ||
269 | ///////////////////////////////////////////////////////////////////////////// | |
270 | // smart_interface | |
271 | ||
272 | // Pointer to (usually singleton) interface object returned by ::smi() | |
273 | smart_interface * smart_interface::s_instance; | |
274 | ||
54965743 | 275 | std::string smart_interface::get_os_version_str() |
2127e193 GI |
276 | { |
277 | return SMARTMONTOOLS_BUILD_HOST; | |
278 | } | |
279 | ||
54965743 | 280 | std::string smart_interface::get_valid_dev_types_str() |
2127e193 | 281 | { |
2127e193 | 282 | // default |
54965743 | 283 | std::string s = |
a86ec89e GI |
284 | "ata, scsi, nvme[,NSID], sat[,auto][,N][+TYPE], " |
285 | "usbcypress[,X], usbjmicron[,p][,x][,N], usbprolific, usbsunplus"; | |
2127e193 | 286 | // append custom |
54965743 GI |
287 | std::string s2 = get_valid_custom_dev_types_str(); |
288 | if (!s2.empty()) { | |
289 | s += ", "; s += s2; | |
290 | } | |
291 | return s; | |
2127e193 GI |
292 | } |
293 | ||
54965743 | 294 | std::string smart_interface::get_app_examples(const char * /*appname*/) |
2127e193 | 295 | { |
54965743 | 296 | return ""; |
2127e193 GI |
297 | } |
298 | ||
e165493d GI |
299 | int64_t smart_interface::get_timer_usec() |
300 | { | |
301 | #if defined(HAVE_GETTIMEOFDAY) | |
302 | #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC) | |
303 | { | |
304 | static bool have_clock_monotonic = true; | |
305 | if (have_clock_monotonic) { | |
306 | struct timespec ts; | |
307 | if (!clock_gettime(CLOCK_MONOTONIC, &ts)) | |
308 | return ts.tv_sec * 1000000LL + ts.tv_nsec/1000; | |
309 | have_clock_monotonic = false; | |
310 | } | |
311 | } | |
312 | #endif | |
313 | { | |
314 | struct timeval tv; | |
315 | gettimeofday(&tv, 0); | |
316 | return tv.tv_sec * 1000000LL + tv.tv_usec; | |
317 | } | |
318 | #elif defined(HAVE_FTIME) | |
319 | { | |
320 | struct timeb tb; | |
321 | ftime(&tb); | |
322 | return tb.time * 1000000LL + tb.millitm * 1000; | |
323 | } | |
324 | #else | |
325 | return -1; | |
326 | #endif | |
327 | } | |
328 | ||
d008864d | 329 | bool smart_interface::disable_system_auto_standby(bool /*disable*/) |
2127e193 | 330 | { |
d008864d GI |
331 | return set_err(ENOSYS); |
332 | } | |
333 | ||
334 | bool smart_interface::set_err(int no, const char * msg, ...) | |
335 | { | |
336 | if (!msg) | |
337 | return set_err(no); | |
2127e193 GI |
338 | m_err.no = no; |
339 | va_list ap; va_start(ap, msg); | |
340 | m_err.msg = vstrprintf(msg, ap); | |
341 | va_end(ap); | |
d008864d | 342 | return false; |
2127e193 GI |
343 | } |
344 | ||
d008864d | 345 | bool smart_interface::set_err(int no) |
2127e193 | 346 | { |
d008864d | 347 | return set_err_var(&m_err, no); |
2127e193 GI |
348 | } |
349 | ||
d008864d | 350 | bool smart_interface::set_err_var(smart_device::error_info * err, int no) |
2127e193 GI |
351 | { |
352 | err->no = no; | |
353 | err->msg = get_msg_for_errno(no); | |
354 | if (err->msg.empty() && no != 0) | |
355 | err->msg = strprintf("Unknown error %d", no); | |
d008864d | 356 | return false; |
2127e193 GI |
357 | } |
358 | ||
359 | const char * smart_interface::get_msg_for_errno(int no) | |
360 | { | |
361 | return strerror(no); | |
362 | } | |
363 | ||
364 | ||
365 | ///////////////////////////////////////////////////////////////////////////// | |
366 | // Default device factory | |
367 | ||
368 | smart_device * smart_interface::get_smart_device(const char * name, const char * type) | |
369 | { | |
370 | clear_err(); | |
cfbba5b9 GI |
371 | |
372 | // Call platform specific autodetection if no device type specified | |
373 | smart_device * dev; | |
2127e193 | 374 | if (!type || !*type) { |
cfbba5b9 | 375 | dev = autodetect_smart_device(name); |
2127e193 GI |
376 | if (!dev && !get_errno()) |
377 | set_err(EINVAL, "Unable to detect device type"); | |
378 | return dev; | |
379 | } | |
380 | ||
cfbba5b9 GI |
381 | // First check for platform specific device types |
382 | dev = get_custom_smart_device(name, type); | |
2127e193 GI |
383 | if (dev || get_errno()) |
384 | return dev; | |
385 | ||
386 | if (!strcmp(type, "ata")) | |
387 | dev = get_ata_device(name, type); | |
388 | else if (!strcmp(type, "scsi")) | |
389 | dev = get_scsi_device(name, type); | |
390 | ||
a86ec89e GI |
391 | else if (str_starts_with(type, "nvme")) { |
392 | int n1 = -1, n2 = -1, len = strlen(type); | |
393 | unsigned nsid = 0; // invalid namespace id -> use default | |
394 | sscanf(type, "nvme%n,0x%x%n", &n1, &nsid, &n2); | |
395 | if (!(n1 == len || n2 == len)) { | |
396 | set_err(EINVAL, "Invalid NVMe namespace id in '%s'", type); | |
397 | return 0; | |
398 | } | |
399 | dev = get_nvme_device(name, type, nsid); | |
400 | } | |
401 | ||
2127e193 GI |
402 | else if ( ((!strncmp(type, "sat", 3) && (!type[3] || strchr(",+", type[3]))) |
403 | || (!strncmp(type, "usb", 3)))) { | |
404 | // Split "sat...+base..." -> ("sat...", "base...") | |
405 | unsigned satlen = strcspn(type, "+"); | |
406 | std::string sattype(type, satlen); | |
407 | const char * basetype = (type[satlen] ? type+satlen+1 : ""); | |
408 | // Recurse to allocate base device, default is standard SCSI | |
409 | if (!*basetype) | |
410 | basetype = "scsi"; | |
bed94269 GI |
411 | smart_device_auto_ptr basedev( get_smart_device(name, basetype) ); |
412 | if (!basedev) { | |
2127e193 GI |
413 | set_err(EINVAL, "Type '%s+...': %s", sattype.c_str(), get_errmsg()); |
414 | return 0; | |
415 | } | |
416 | // Result must be SCSI | |
bed94269 | 417 | if (!basedev->is_scsi()) { |
2127e193 GI |
418 | set_err(EINVAL, "Type '%s+...': Device type '%s' is not SCSI", sattype.c_str(), basetype); |
419 | return 0; | |
420 | } | |
421 | // Attach SAT tunnel | |
a86ec89e | 422 | return get_sat_device(sattype.c_str(), basedev.release()->to_scsi()); |
2127e193 GI |
423 | } |
424 | ||
425 | else { | |
426 | set_err(EINVAL, "Unknown device type '%s'", type); | |
427 | return 0; | |
428 | } | |
429 | if (!dev && !get_errno()) | |
430 | set_err(EINVAL, "Not a device of type '%s'", type); | |
431 | return dev; | |
432 | } | |
433 | ||
a86ec89e GI |
434 | bool smart_interface::scan_smart_devices(smart_device_list & devlist, |
435 | const smart_devtype_list & types, const char * pattern /* = 0 */) | |
436 | { | |
437 | unsigned n = types.size(); | |
438 | if (n == 0) | |
439 | return scan_smart_devices(devlist, (const char *)0, pattern); | |
440 | if (n == 1) | |
441 | return scan_smart_devices(devlist, types.front().c_str(), pattern); | |
442 | ||
443 | for (unsigned i = 0; i < n; i++) { | |
444 | smart_device_list tmplist; | |
445 | if (!scan_smart_devices(tmplist, types[i].c_str(), pattern)) | |
446 | return false; | |
447 | devlist.append(tmplist); | |
448 | } | |
449 | ||
450 | return true; | |
451 | } | |
452 | ||
453 | nvme_device * smart_interface::get_nvme_device(const char * /*name*/, const char * /*type*/, unsigned /*nsid*/) | |
454 | { | |
455 | set_err(ENOSYS, "NVMe devices are not supported in this version of smartmontools"); | |
456 | return 0; | |
457 | } | |
458 | ||
2127e193 GI |
459 | smart_device * smart_interface::get_custom_smart_device(const char * /*name*/, const char * /*type*/) |
460 | { | |
461 | return 0; | |
462 | } | |
463 | ||
54965743 | 464 | std::string smart_interface::get_valid_custom_dev_types_str() |
2127e193 | 465 | { |
54965743 | 466 | return ""; |
2127e193 | 467 | } |