smartmontools  SVN Rev 4039
Utility to control and monitor storage systems with "S.M.A.R.T."
dev_interface.cpp
Go to the documentation of this file.
1 /*
2  * dev_interface.cpp
3  *
4  * Home page of code is: http://smartmontools.sourceforge.net
5  *
6  * Copyright (C) 2008-13 Christian Franke <smartmontools-support@lists.sourceforge.net>
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"
20 #include "dev_interface.h"
21 #include "dev_tunnelled.h"
22 #include "atacmds.h" // ATA_SMART_CMD/STATUS
23 #include "utility.h"
24 
25 #include <errno.h>
26 #include <stdarg.h>
27 #include <stdexcept>
28 
29 #if defined(HAVE_GETTIMEOFDAY)
30 #include <sys/time.h>
31 #elif defined(HAVE_FTIME)
32 #include <sys/timeb.h>
33 #endif
34 
35 const char * dev_interface_cpp_cvsid = "$Id: dev_interface.cpp 3741 2013-01-02 17:06:54Z chrfranke $"
37 
38 /////////////////////////////////////////////////////////////////////////////
39 // smart_device
40 
41 smart_device::smart_device(smart_interface * intf, const char * dev_name,
42  const char * dev_type, const char * req_type)
43 : m_intf(intf), m_info(dev_name, dev_type, req_type),
44  m_ata_ptr(0), m_scsi_ptr(0)
45 {
46 }
47 
49 : m_intf(0), m_ata_ptr(0), m_scsi_ptr(0)
50 {
51  throw std::logic_error("smart_device: wrong constructor called in implementation class");
52 }
53 
55 {
56 }
57 
59 {
60  if (get_errno() == ENOSYS)
61  return true;
62 #ifdef ENOTSUP
63  if (get_errno() == ENOTSUP)
64  return true;
65 #endif
66  return false;
67 }
68 
69 bool smart_device::set_err(int no, const char * msg, ...)
70 {
71  if (!msg)
72  return set_err(no);
73  m_err.no = no;
74  va_list ap; va_start(ap, msg);
75  m_err.msg = vstrprintf(msg, ap);
76  va_end(ap);
77  return false;
78 }
79 
81 {
82  return smi()->set_err_var(&m_err, no);
83 }
84 
86 {
87  open();
88  return this;
89 }
90 
91 bool smart_device::owns(const smart_device * /*dev*/) const
92 {
93  return false;
94 }
95 
96 void smart_device::release(const smart_device * /*dev*/)
97 {
98 }
99 
100 
101 /////////////////////////////////////////////////////////////////////////////
102 // ata_device
103 
105 : features_16(features, prev.features),
106  sector_count_16(sector_count, prev.sector_count),
107  lba_low_16(lba_low, prev.lba_low),
108  lba_mid_16(lba_mid, prev.lba_mid),
109  lba_high_16(lba_high, prev.lba_high),
110  lba_48( lba_low, lba_mid, lba_high,
111  prev.lba_low, prev.lba_mid, prev.lba_high)
112 {
113 }
114 
116 : sector_count_16(sector_count, prev.sector_count),
117  lba_low_16(lba_low, prev.lba_low),
118  lba_mid_16(lba_mid, prev.lba_mid),
119  lba_high_16(lba_high, prev.lba_high),
120  lba_48( lba_low, lba_mid, lba_high,
121  prev.lba_low, prev.lba_mid, prev.lba_high)
122 {
123 }
124 
126 : direction(no_data),
127  buffer(0),
128  size(0)
129 {
130 }
131 
133 {
134 }
135 
137 {
138  ata_cmd_out dummy;
139  return ata_pass_through(in, dummy);
140 }
141 
143  unsigned flags, const char * type /* = 0 */)
144 {
145  // Check DATA IN/OUT
146  switch (in.direction) {
147  case ata_cmd_in::no_data: break;
148  case ata_cmd_in::data_in: break;
149  case ata_cmd_in::data_out: break;
150  default:
151  return set_err(EINVAL, "Invalid data direction %d", (int)in.direction);
152  }
153 
154  // Check buffer size
155  if (in.direction == ata_cmd_in::no_data) {
156  if (in.size)
157  return set_err(EINVAL, "Buffer size %u > 0 for NO DATA command", in.size);
158  }
159  else {
160  if (!in.buffer)
161  return set_err(EINVAL, "Buffer not set for DATA IN/OUT command");
162  unsigned count = (in.in_regs.prev.sector_count<<16)|in.in_regs.sector_count;
163  // TODO: Add check for sector count == 0
164  if (count * 512 != in.size)
165  return set_err(EINVAL, "Sector count %u does not match buffer size %u", count, in.size);
166  }
167 
168  // Check features
169  const char * errmsg = 0;
170  if (in.direction == ata_cmd_in::data_out && !(flags & supports_data_out))
171  errmsg = "DATA OUT ATA commands not implemented";
172  else if ( in.out_needed.is_set() && !(flags & supports_output_regs)
173  && !( in.in_regs.command == ATA_SMART_CMD
175  && (flags & supports_smart_status)))
176  errmsg = "Read of ATA output registers not implemented";
177  else if (!(in.size == 0 || in.size == 512) && !(flags & supports_multi_sector))
178  errmsg = "Multi-sector ATA commands not implemented";
179  else if (in.in_regs.is_48bit_cmd() && !(flags & (supports_48bit_hi_null|supports_48bit)))
180  errmsg = "48-bit ATA commands not implemented";
181  else if (in.in_regs.is_real_48bit_cmd() && !(flags & supports_48bit))
182  errmsg = "48-bit ATA commands not fully implemented";
183 
184  if (errmsg)
185  return set_err(ENOSYS, "%s%s%s%s", errmsg,
186  (type ? " [" : ""), (type ? type : ""), (type ? "]" : ""));
187 
188  return true;
189 }
190 
192 {
193  return false;
194 }
195 
196 
197 /////////////////////////////////////////////////////////////////////////////
198 // tunnelled_device_base
199 
201 : smart_device(never_called),
202  m_tunnel_base_dev(tunnel_dev)
203 {
204 }
205 
207 {
208  delete m_tunnel_base_dev;
209 }
210 
212 {
214 }
215 
217 {
218  if (!m_tunnel_base_dev)
219  return set_err(ENOSYS);
220  if (!m_tunnel_base_dev->open())
221  return set_err(m_tunnel_base_dev->get_err());
222  return true;
223 }
224 
226 {
227  if (!m_tunnel_base_dev)
228  return true;
229  if (!m_tunnel_base_dev->close())
230  return set_err(m_tunnel_base_dev->get_err());
231  return true;
232 }
233 
235 {
236  return (m_tunnel_base_dev && (m_tunnel_base_dev == dev));
237 }
238 
240 {
241  if (m_tunnel_base_dev == dev)
242  m_tunnel_base_dev = 0;
243 }
244 
245 
246 /////////////////////////////////////////////////////////////////////////////
247 // smart_interface
248 
249 // Pointer to (usually singleton) interface object returned by ::smi()
251 
253 {
254  return SMARTMONTOOLS_BUILD_HOST;
255 }
256 
258 {
259  // default
260  std::string s =
261  "ata, scsi, sat[,auto][,N][+TYPE], usbcypress[,X], usbjmicron[,p][,x][,N], usbsunplus";
262  // append custom
263  std::string s2 = get_valid_custom_dev_types_str();
264  if (!s2.empty()) {
265  s += ", "; s += s2;
266  }
267  return s;
268 }
269 
270 std::string smart_interface::get_app_examples(const char * /*appname*/)
271 {
272  return "";
273 }
274 
276 {
277 #if defined(HAVE_GETTIMEOFDAY)
278  #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
279  {
280  static bool have_clock_monotonic = true;
281  if (have_clock_monotonic) {
282  struct timespec ts;
283  if (!clock_gettime(CLOCK_MONOTONIC, &ts))
284  return ts.tv_sec * 1000000LL + ts.tv_nsec/1000;
285  have_clock_monotonic = false;
286  }
287  }
288  #endif
289  {
290  struct timeval tv;
291  gettimeofday(&tv, 0);
292  return tv.tv_sec * 1000000LL + tv.tv_usec;
293  }
294 #elif defined(HAVE_FTIME)
295  {
296  struct timeb tb;
297  ftime(&tb);
298  return tb.time * 1000000LL + tb.millitm * 1000;
299  }
300 #else
301  return -1;
302 #endif
303 }
304 
306 {
307  return set_err(ENOSYS);
308 }
309 
310 bool smart_interface::set_err(int no, const char * msg, ...)
311 {
312  if (!msg)
313  return set_err(no);
314  m_err.no = no;
315  va_list ap; va_start(ap, msg);
316  m_err.msg = vstrprintf(msg, ap);
317  va_end(ap);
318  return false;
319 }
320 
322 {
323  return set_err_var(&m_err, no);
324 }
325 
327 {
328  err->no = no;
329  err->msg = get_msg_for_errno(no);
330  if (err->msg.empty() && no != 0)
331  err->msg = strprintf("Unknown error %d", no);
332  return false;
333 }
334 
336 {
337  return strerror(no);
338 }
339 
340 
341 /////////////////////////////////////////////////////////////////////////////
342 // Default device factory
343 
344 smart_device * smart_interface::get_smart_device(const char * name, const char * type)
345 {
346  clear_err();
347 
348  // Call platform specific autodetection if no device type specified
349  smart_device * dev;
350  if (!type || !*type) {
351  dev = autodetect_smart_device(name);
352  if (!dev && !get_errno())
353  set_err(EINVAL, "Unable to detect device type");
354  return dev;
355  }
356 
357  // First check for platform specific device types
358  dev = get_custom_smart_device(name, type);
359  if (dev || get_errno())
360  return dev;
361 
362  if (!strcmp(type, "ata"))
363  dev = get_ata_device(name, type);
364  else if (!strcmp(type, "scsi"))
365  dev = get_scsi_device(name, type);
366 
367  else if ( ((!strncmp(type, "sat", 3) && (!type[3] || strchr(",+", type[3])))
368  || (!strncmp(type, "usb", 3)))) {
369  // Split "sat...+base..." -> ("sat...", "base...")
370  unsigned satlen = strcspn(type, "+");
371  std::string sattype(type, satlen);
372  const char * basetype = (type[satlen] ? type+satlen+1 : "");
373  // Recurse to allocate base device, default is standard SCSI
374  if (!*basetype)
375  basetype = "scsi";
376  smart_device_auto_ptr basedev( get_smart_device(name, basetype) );
377  if (!basedev) {
378  set_err(EINVAL, "Type '%s+...': %s", sattype.c_str(), get_errmsg());
379  return 0;
380  }
381  // Result must be SCSI
382  if (!basedev->is_scsi()) {
383  set_err(EINVAL, "Type '%s+...': Device type '%s' is not SCSI", sattype.c_str(), basetype);
384  return 0;
385  }
386  // Attach SAT tunnel
387  ata_device * satdev = get_sat_device(sattype.c_str(), basedev->to_scsi());
388  if (!satdev)
389  return 0;
390  basedev.release();
391  return satdev;
392  }
393 
394  else {
395  set_err(EINVAL, "Unknown device type '%s'", type);
396  return 0;
397  }
398  if (!dev && !get_errno())
399  set_err(EINVAL, "Not a device of type '%s'", type);
400  return dev;
401 }
402 
403 smart_device * smart_interface::get_custom_smart_device(const char * /*name*/, const char * /*type*/)
404 {
405  return 0;
406 }
407 
409 {
410  return "";
411 }
virtual int64_t get_timer_usec()
Get microseconds since some unspecified starting point.
Error (number,message) pair.
Definition: dev_interface.h:58
int get_errno() const
Get last error number.
virtual void release(const smart_device *dev)
Release ownership of other device.
u16 s[6]
Definition: megaraid.h:97
ata_out_regs_flags out_needed
True if output register value needed.
const char * get_errmsg() const
Get last error message.
virtual std::string get_valid_dev_types_str()
Return valid args for device type option/directive.
u32 size
Definition: megaraid.h:79
std::string msg
Error message.
Definition: dev_interface.h:67
virtual ata_device * get_sat_device(const char *type, scsi_device *scsidev)
Return ATA->SCSI filter for SAT or USB.
Definition: scsiata.cpp:1423
do_not_use_in_implementation_classes
Dummy enum for dummy constructor.
Definition: dev_interface.h:78
ata_in_regs prev
"previous content"
#define ATA_SMART_CMD
Definition: atacmds.h:75
smart_device::error_info m_err
tunnelled_device_base(smart_device *tunnel_dev)
int get_errno() const
Get last error number.
virtual void release(const smart_device *dev)
Release ownership of other device.
virtual bool open()=0
Open device, return false on error.
void clear_err()
Clear last error info.
Smart pointer class for device pointers.
virtual const char * get_msg_for_errno(int no)
Convert error number into message, used by set_err(no).
virtual std::string get_valid_custom_dev_types_str()
Return valid 'type' args accepted by above.
virtual bool close()=0
Close device, return false on error.
u32 count
Definition: megaraid.h:80
#define ENOTSUP
Definition: os_linux.cpp:97
u16 flags
Definition: megaraid.h:93
void * buffer
Pointer to data buffer.
#define DEV_INTERFACE_H_CVSID
Definition: dev_interface.h:21
bool is_real_48bit_cmd() const
Return true if 48-bit command with any nonzero high byte.
virtual smart_device * get_smart_device(const char *name, const char *type)
Return device object for device 'name' with some 'type'.
bool ata_cmd_is_supported(const ata_cmd_in &in, unsigned flags, const char *type=0)
Check command input parameters.
ata_in_regs_48bit in_regs
Input registers.
virtual ata_device * get_ata_device(const char *name, const char *type)=0
Return standard ATA device.
enum ata_cmd_in::@27 direction
I/O direction.
device_type * release()
Return the pointer and release ownership.
bool set_err_var(smart_device::error_info *err, int no)
Set last error number and default message to any error_info.
virtual bool is_open() const =0
Return true if device is open.
The platform interface abstraction.
virtual bool ata_pass_through(const ata_cmd_in &in, ata_cmd_out &out)=0
ATA pass through.
error_info m_err
long long int64_t
Definition: int64.h:51
static smart_interface * s_instance
Pointer to the interface object.
virtual bool open()
Open device, return false on error.
virtual smart_device * autodetect_smart_device(const char *name)=0
Autodetect device if no device type specified.
virtual smart_device * get_custom_smart_device(const char *name, const char *type)
Return device for platform specific 'type'.
virtual std::string get_app_examples(const char *appname)
Return example string for program 'appname'.
std::string vstrprintf(const char *fmt, va_list ap)
Definition: utility.cpp:779
ptr_t buffer
Definition: megaraid.h:89
bool set_err(int no, const char *msg,...) __attribute_format_printf(3
Set last error number and message.
bool is_set() const
Return true if any flag is set.
virtual bool owns(const smart_device *dev) const
Return true if other device is owned by this device.
unsigned size
Size of buffer.
virtual ~tunnelled_device_base()
ata_register features
bool is_48bit_cmd() const
Return true if 48-bit command.
Base class for all devices.
Definition: dev_interface.h:38
int no
Error number.
Definition: dev_interface.h:66
ATA device access.
ata_register sector_count
virtual bool disable_system_auto_standby(bool disable)
Disable/Enable system auto standby/sleep mode.
smart_device * m_tunnel_base_dev
Definition: dev_tunnelled.h:50
virtual scsi_device * get_scsi_device(const char *name, const char *type)=0
Return standard SCSI device.
ata_register command
virtual bool close()
Close device, return false on error.
virtual smart_device * autodetect_open()
Open device with autodetection support.
ATA pass through input parameters.
std::string strprintf(const char *fmt,...)
Definition: utility.cpp:787
const char * dev_interface_cpp_cvsid
virtual ~smart_device()
smart_interface * smi()
Get interface which produced this object.
const error_info & get_err() const
Get last error info struct.
bool set_err(int no, const char *msg,...) __attribute_format_printf(3
Set last error number and message.
virtual bool is_open() const
Return true if device is open.
virtual bool ata_identify_is_cached() const
Return true if OS caches ATA identify sector.
virtual bool is_syscall_unsup() const
Return true if last error indicates an unsupported system call.
ATA pass through output parameters.
smart_device(smart_interface *intf, const char *dev_name, const char *dev_type, const char *req_type)
Constructor to init interface and device info.
virtual bool owns(const smart_device *dev) const
Return true if other device is owned by this device.
virtual std::string get_os_version_str()
Return info string about build host and/or OS version.
#define ATA_SMART_STATUS
Definition: atacmds.h:105