4 * Home page of code is: http://smartmontools.sourceforge.net
6 * Copyright (C) 2008-9 Christian Franke <smartmontools-support@lists.sourceforge.net>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2, or (at your option)
13 * You should have received a copy of the GNU General Public License
14 * (for example COPYING); If not, see <http://www.gnu.org/licenses/>.
24 #include "dev_interface.h"
25 #include "dev_ata_cmd_set.h"
27 const char * dev_legacy_cpp_cvsid
= "$Id: dev_legacy.cpp 2973 2009-10-26 22:38:19Z chrfranke $"
28 DEV_INTERFACE_H_CVSID
;
30 extern smartmonctrl
* con
; // con->reportscsiioctl
32 /////////////////////////////////////////////////////////////////////////////
34 // Legacy interface declarations (now commented out globally):
37 int guess_device_type(const char * dev_name
);
38 int make_device_names (char ***devlist
, const char* name
);
39 int deviceopen(const char *pathname
, char *type
);
40 int deviceclose(int fd
);
41 #ifdef HAVE_GET_OS_VERSION_STR
42 const char * get_os_version_str(void);
46 int ata_command_interface(int device
, smart_command_set command
, int select
, char *data
);
47 int escalade_command_interface(int fd
, int escalade_port
, int escalade_type
, smart_command_set command
, int select
, char *data
);
48 int marvell_command_interface(int device
, smart_command_set command
, int select
, char *data
);
49 int highpoint_command_interface(int device
, smart_command_set command
, int select
, char *data
);
50 int areca_command_interface(int fd
, int disknum
, smart_command_set command
, int select
, char *data
);
51 #ifdef HAVE_ATA_IDENTIFY_IS_CACHED
52 int ata_identify_is_cached(int fd
);
56 int do_scsi_cmnd_io(int dev_fd
, struct scsi_cmnd_io
* iop
, int report
);
59 void print_smartctl_examples();
61 /////////////////////////////////////////////////////////////////////////////
63 namespace os
{ // No need to publish anything, name provided for Doxygen
65 /////////////////////////////////////////////////////////////////////////////
66 /// Implement shared open/close routines with old functions.
68 class legacy_smart_device
69 : virtual public /*implements*/ smart_device
72 explicit legacy_smart_device(const char * mode
)
73 : smart_device(never_called
),
74 m_fd(-1), m_mode(mode
) { }
76 virtual ~legacy_smart_device() throw();
78 virtual bool is_open() const;
85 /// Return filedesc for derived classes.
90 int m_fd
; ///< filedesc, -1 if not open.
91 const char * m_mode
; ///< Mode string for deviceopen().
95 legacy_smart_device::~legacy_smart_device() throw()
101 bool legacy_smart_device::is_open() const
106 bool legacy_smart_device::open()
108 m_fd
= ::deviceopen(get_dev_name(), (char*)m_mode
);
110 set_err((errno
==ENOENT
|| errno
==ENOTDIR
) ? ENODEV
: errno
);
116 bool legacy_smart_device::close()
118 int fd
= m_fd
; m_fd
= -1;
119 if (::deviceclose(fd
) < 0) {
126 /////////////////////////////////////////////////////////////////////////////
127 /// Implement standard ATA support with old functions
129 class legacy_ata_device
130 : public /*implements*/ ata_device_with_command_set
,
131 public /*extends*/ legacy_smart_device
134 legacy_ata_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
136 #ifdef HAVE_ATA_IDENTIFY_IS_CACHED
137 virtual bool ata_identify_is_cached() const;
141 virtual int ata_command_interface(smart_command_set command
, int select
, char * data
);
144 legacy_ata_device::legacy_ata_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
)
145 : smart_device(intf
, dev_name
, "ata", req_type
),
146 legacy_smart_device("ATA")
150 int legacy_ata_device::ata_command_interface(smart_command_set command
, int select
, char * data
)
152 return ::ata_command_interface(get_fd(), command
, select
, data
);
155 #ifdef HAVE_ATA_IDENTIFY_IS_CACHED
156 bool legacy_ata_device::ata_identify_is_cached() const
158 return !!::ata_identify_is_cached(get_fd());
163 /////////////////////////////////////////////////////////////////////////////
164 /// Implement AMCC/3ware RAID support with old functions
166 class legacy_escalade_device
167 : public /*implements*/ ata_device_with_command_set
,
168 public /*extends*/ legacy_smart_device
171 legacy_escalade_device(smart_interface
* intf
, const char * dev_name
,
172 int escalade_type
, int disknum
);
175 virtual int ata_command_interface(smart_command_set command
, int select
, char * data
);
178 int m_escalade_type
; ///< Type string for escalade_command_interface().
179 int m_disknum
; ///< Disk number.
182 legacy_escalade_device::legacy_escalade_device(smart_interface
* intf
, const char * dev_name
,
183 int escalade_type
, int disknum
)
184 : smart_device(intf
, dev_name
, "3ware", "3ware"),
186 escalade_type
==CONTROLLER_3WARE_9000_CHAR
? "ATA_3WARE_9000" :
187 escalade_type
==CONTROLLER_3WARE_678K_CHAR
? "ATA_3WARE_678K" :
188 /* CONTROLLER_3WARE_678K */ "ATA" ),
189 m_escalade_type(escalade_type
), m_disknum(disknum
)
191 set_info().info_name
= strprintf("%s [3ware_disk_%02d]", dev_name
, disknum
);
194 int legacy_escalade_device::ata_command_interface(smart_command_set command
, int select
, char * data
)
196 return ::escalade_command_interface(get_fd(), m_disknum
, m_escalade_type
, command
, select
, data
);
200 /////////////////////////////////////////////////////////////////////////////
201 /// Implement Areca RAID support with old functions
203 class legacy_areca_device
204 : public /*implements*/ ata_device_with_command_set
,
205 public /*extends*/ legacy_smart_device
208 legacy_areca_device(smart_interface
* intf
, const char * dev_name
, int disknum
);
211 virtual int ata_command_interface(smart_command_set command
, int select
, char * data
);
214 int m_disknum
; ///< Disk number.
217 legacy_areca_device::legacy_areca_device(smart_interface
* intf
, const char * dev_name
, int disknum
)
218 : smart_device(intf
, dev_name
, "areca", "areca"),
219 legacy_smart_device("ATA_ARECA"),
222 set_info().info_name
= strprintf("%s [areca_%02d]", dev_name
, disknum
);
225 int legacy_areca_device::ata_command_interface(smart_command_set command
, int select
, char * data
)
227 return ::areca_command_interface(get_fd(), m_disknum
, command
, select
, data
);
231 /////////////////////////////////////////////////////////////////////////////
232 /// Implement Marvell support with old functions
234 class legacy_marvell_device
235 : public /*implements*/ ata_device_with_command_set
,
236 public /*extends*/ legacy_smart_device
239 legacy_marvell_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
242 virtual int ata_command_interface(smart_command_set command
, int select
, char * data
);
246 legacy_marvell_device::legacy_marvell_device(smart_interface
* intf
,
247 const char * dev_name
, const char * req_type
)
248 : smart_device(intf
, dev_name
, "marvell", req_type
),
249 legacy_smart_device("ATA")
253 int legacy_marvell_device::ata_command_interface(smart_command_set command
, int select
, char * data
)
255 return ::marvell_command_interface(get_fd(), command
, select
, data
);
259 /////////////////////////////////////////////////////////////////////////////
260 /// Implement Highpoint RAID support with old functions
262 class legacy_highpoint_device
263 : public /*implements*/ ata_device_with_command_set
,
264 public /*extends*/ legacy_smart_device
267 legacy_highpoint_device(smart_interface
* intf
, const char * dev_name
,
268 unsigned char controller
, unsigned char channel
, unsigned char port
);
271 virtual int ata_command_interface(smart_command_set command
, int select
, char * data
);
274 unsigned char m_hpt_data
[3]; ///< controller/channel/port
278 legacy_highpoint_device::legacy_highpoint_device(smart_interface
* intf
, const char * dev_name
,
279 unsigned char controller
, unsigned char channel
, unsigned char port
)
280 : smart_device(intf
, dev_name
, "hpt", "hpt"),
281 legacy_smart_device("ATA")
283 m_hpt_data
[0] = controller
; m_hpt_data
[1] = channel
; m_hpt_data
[2] = port
;
284 set_info().info_name
= strprintf("%s [hpt_disk_%u/%u/%u]", dev_name
, m_hpt_data
[0], m_hpt_data
[1], m_hpt_data
[2]);
287 int legacy_highpoint_device::ata_command_interface(smart_command_set command
, int select
, char * data
)
289 unsigned char old_hpt_data
[3];
290 memcpy(old_hpt_data
, con
->hpt_data
, 3);
291 memcpy(con
->hpt_data
, m_hpt_data
, 3);
292 int status
= ::highpoint_command_interface(get_fd(), command
, select
, data
);
293 memcpy(con
->hpt_data
, old_hpt_data
, 3);
298 /////////////////////////////////////////////////////////////////////////////
299 /// Implement standard SCSI support with old functions
301 class legacy_scsi_device
302 : public /*implements*/ scsi_device
,
303 public /*extends*/ legacy_smart_device
306 legacy_scsi_device(smart_interface
* intf
, const char * dev_name
, const char * req_type
);
308 virtual smart_device
* autodetect_open();
310 virtual bool scsi_pass_through(scsi_cmnd_io
* iop
);
313 legacy_scsi_device::legacy_scsi_device(smart_interface
* intf
,
314 const char * dev_name
, const char * req_type
)
315 : smart_device(intf
, dev_name
, "scsi", req_type
),
316 legacy_smart_device("SCSI")
320 bool legacy_scsi_device::scsi_pass_through(scsi_cmnd_io
* iop
)
322 unsigned char oldtype
= con
->controller_type
, oldport
= con
->controller_port
;
323 con
->controller_type
= CONTROLLER_SCSI
; con
->controller_port
= 0;
324 int status
= ::do_scsi_cmnd_io(get_fd(), iop
, con
->reportscsiioctl
);
325 con
->controller_type
= oldtype
; con
->controller_port
= oldport
;
334 /////////////////////////////////////////////////////////////////////////////
335 /// Implement CCISS RAID support with old functions
337 class legacy_cciss_device
338 : public /*implements*/ scsi_device
,
339 public /*extends*/ legacy_smart_device
342 legacy_cciss_device(smart_interface
* intf
, const char * name
, unsigned char disknum
);
344 virtual bool scsi_pass_through(scsi_cmnd_io
* iop
);
347 unsigned char m_disknum
; ///< Disk number.
351 legacy_cciss_device::legacy_cciss_device(smart_interface
* intf
,
352 const char * dev_name
, unsigned char disknum
)
353 : smart_device(intf
, dev_name
, "cciss", "cciss"),
354 legacy_smart_device("SCSI"),
357 set_info().info_name
= strprintf("%s [cciss_disk_%02d]", dev_name
, disknum
);
360 bool legacy_cciss_device::scsi_pass_through(scsi_cmnd_io
* iop
)
363 unsigned char oldtype
= con
->controller_type
, oldport
= con
->controller_port
;
364 con
->controller_type
= CONTROLLER_CCISS
; con
->controller_port
= m_disknum
+1;
365 int status
= ::do_scsi_cmnd_io(get_fd(), iop
, con
->reportscsiioctl
);
366 con
->controller_type
= oldtype
; con
->controller_port
= oldport
;
375 /////////////////////////////////////////////////////////////////////////////
376 /// SCSI open with autodetection support
378 smart_device
* legacy_scsi_device::autodetect_open()
384 // No Autodetection if device type was specified by user
388 // The code below is based on smartd.cpp:SCSIFilterKnown()
391 unsigned char req_buff
[64] = {0, };
393 if (scsiStdInquiry(this, req_buff
, req_len
)) {
394 // Marvell controllers fail on a 36 bytes StdInquiry, but 64 suffices
395 // watch this spot ... other devices could lock up here
397 if (scsiStdInquiry(this, req_buff
, req_len
)) {
398 // device doesn't like INQUIRY commands
400 set_err(EIO
, "INQUIRY failed");
405 int avail_len
= req_buff
[4] + 5;
406 int len
= (avail_len
< req_len
? avail_len
: req_len
);
410 // Use INQUIRY to detect type
413 if (!memcmp(req_buff
+ 8, "3ware", 5) || !memcmp(req_buff
+ 8, "AMCC", 4)) {
415 #if defined(_WIN32) || defined(__CYGWIN__)
416 set_err(EINVAL
, "AMCC/3ware controller, please try changing device to %s,N", get_dev_name());
418 set_err(EINVAL
, "AMCC/3ware controller, please try adding '-d 3ware,N',\n"
419 "you may need to replace %s with /dev/twaN or /dev/tweN", get_dev_name());
425 if (len
>= 42 && !memcmp(req_buff
+ 36, "MVSATA", 6)) { // TODO: Linux-specific?
426 //pout("Device %s: using '-d marvell' for ATA disk with Marvell driver\n", get_dev_name());
428 smart_device_auto_ptr
newdev(
429 new legacy_marvell_device(smi(), get_dev_name(), get_req_type()),
432 newdev
->open(); // TODO: Can possibly pass open fd
434 return newdev
.release();
439 smart_device
* newdev
= smi()->autodetect_sat_device(this, req_buff
, len
);
441 // NOTE: 'this' is now owned by '*newdev'
445 // Nothing special found
450 /////////////////////////////////////////////////////////////////////////////
451 /// Implement platform interface with old functions.
453 class legacy_smart_interface
454 : public /*implements*/ smart_interface
457 #ifdef HAVE_GET_OS_VERSION_STR
458 virtual std::string
get_os_version_str();
461 virtual std::string
get_app_examples(const char * appname
);
463 virtual bool scan_smart_devices(smart_device_list
& devlist
, const char * type
,
464 const char * pattern
= 0);
467 virtual ata_device
* get_ata_device(const char * name
, const char * type
);
469 virtual scsi_device
* get_scsi_device(const char * name
, const char * type
);
471 virtual smart_device
* autodetect_smart_device(const char * name
);
473 virtual smart_device
* get_custom_smart_device(const char * name
, const char * type
);
475 virtual std::string
get_valid_custom_dev_types_str();
479 //////////////////////////////////////////////////////////////////////
481 #ifdef HAVE_GET_OS_VERSION_STR
482 std::string
legacy_smart_interface::get_os_version_str()
484 return ::get_os_version_str();
488 std::string
legacy_smart_interface::get_app_examples(const char * appname
)
490 if (!strcmp(appname
, "smartctl"))
491 ::print_smartctl_examples(); // this prints to stdout ...
492 return ""; // ... so don't print again.
495 ata_device
* legacy_smart_interface::get_ata_device(const char * name
, const char * type
)
497 return new legacy_ata_device(this, name
, type
);
500 scsi_device
* legacy_smart_interface::get_scsi_device(const char * name
, const char * type
)
502 return new legacy_scsi_device(this, name
, type
);
506 smart_device
* legacy_smart_interface::autodetect_smart_device(const char * name
)
508 switch (::guess_device_type(name
)) {
509 case CONTROLLER_ATA
: return new legacy_ata_device(this, name
, "");
510 case CONTROLLER_SCSI
: return new legacy_scsi_device(this, name
, "");
512 // TODO: Test autodetect device here
517 static void free_devnames(char * * devnames
, int numdevs
)
519 static const char version
[] = "$Id: dev_legacy.cpp 2973 2009-10-26 22:38:19Z chrfranke $";
520 for (int i
= 0; i
< numdevs
; i
++)
521 FreeNonZero(devnames
[i
], -1,__LINE__
, version
);
522 FreeNonZero(devnames
, (sizeof (char*) * numdevs
),__LINE__
, version
);
525 bool legacy_smart_interface::scan_smart_devices(smart_device_list
& devlist
,
526 const char * type
, const char * pattern
/*= 0*/)
529 set_err(EINVAL
, "DEVICESCAN with pattern not implemented yet");
534 char * * atanames
= 0; int numata
= 0;
535 if (!type
|| !strcmp(type
, "ata")) {
536 numata
= ::make_device_names(&atanames
, "ATA");
543 char * * scsinames
= 0; int numscsi
= 0;
544 if (!type
|| !strcmp(type
, "scsi")) {
545 numscsi
= ::make_device_names(&scsinames
, "SCSI");
547 free_devnames(atanames
, numata
);
557 for (i
= 0; i
< numata
; i
++) {
558 ata_device
* atadev
= get_ata_device(atanames
[i
], type
);
560 devlist
.push_back(atadev
);
562 free_devnames(atanames
, numata
);
564 for (i
= 0; i
< numscsi
; i
++) {
565 scsi_device
* scsidev
= get_scsi_device(scsinames
[i
], type
);
567 devlist
.push_back(scsidev
);
569 free_devnames(scsinames
, numscsi
);
574 smart_device
* legacy_smart_interface::get_custom_smart_device(const char * name
, const char * type
)
577 if (!strcmp(type
, "marvell"))
578 return new legacy_marvell_device(this, name
, type
);
581 int disknum
= -1, n1
= -1, n2
= -1;
582 if (sscanf(type
, "3ware,%n%d%n", &n1
, &disknum
, &n2
) == 1 || n1
== 6) {
583 if (n2
!= (int)strlen(type
)) {
584 set_err(EINVAL
, "Option -d 3ware,N requires N to be a non-negative integer");
587 if (!(0 <= disknum
&& disknum
<= 127)) {
588 set_err(EINVAL
, "Option -d 3ware,N (N=%d) must have 0 <= N <= 127", disknum
);
591 int contr
= ::guess_device_type(name
);
592 if (contr
!= CONTROLLER_3WARE_9000_CHAR
&& contr
!= CONTROLLER_3WARE_678K_CHAR
)
593 contr
= CONTROLLER_3WARE_678K
;
594 return new legacy_escalade_device(this, name
, contr
, disknum
);
598 disknum
= n1
= n2
= -1;
599 if (sscanf(type
, "areca,%n%d%n", &n1
, &disknum
, &n2
) == 1 || n1
== 6) {
600 if (n2
!= (int)strlen(type
)) {
601 set_err(EINVAL
, "Option -d areca,N requires N to be a non-negative integer");
604 if (!(1 <= disknum
&& disknum
<= 24)) {
605 set_err(EINVAL
, "Option -d areca,N (N=%d) must have 1 <= N <= 24", disknum
);
608 return new legacy_areca_device(this, name
, disknum
);
612 int controller
= -1, channel
= -1; disknum
= 1;
613 n1
= n2
= -1; int n3
= -1;
614 if (sscanf(type
, "hpt,%n%d/%d%n/%d%n", &n1
, &controller
, &channel
, &n2
, &disknum
, &n3
) >= 2 || n1
== 4) {
615 int len
= strlen(type
);
616 if (!(n2
== len
|| n3
== len
)) {
617 set_err(EINVAL
, "Option '-d hpt,L/M/N' supports 2-3 items");
620 if (!(1 <= controller
&& controller
<= 8)) {
621 set_err(EINVAL
, "Option '-d hpt,L/M/N' invalid controller id L supplied");
624 if (!(1 <= channel
&& channel
<= 8)) {
625 set_err(EINVAL
, "Option '-d hpt,L/M/N' invalid channel number M supplied");
628 if (!(1 <= disknum
&& disknum
<= 15)) {
629 set_err(EINVAL
, "Option '-d hpt,L/M/N' invalid pmport number N supplied");
632 return new legacy_highpoint_device(this, name
, controller
, channel
, disknum
);
636 disknum
= n1
= n2
= -1;
637 if (sscanf(type
, "cciss,%n%d%n", &n1
, &disknum
, &n2
) == 1 || n1
== 6) {
638 if (n2
!= (int)strlen(type
)) {
639 set_err(EINVAL
, "Option -d cciss,N requires N to be a non-negative integer");
642 if (!(0 <= disknum
&& disknum
<= 15)) {
643 set_err(EINVAL
, "Option -d cciss,N (N=%d) must have 0 <= N <= 15", disknum
);
646 return new legacy_cciss_device(this, name
, disknum
);
652 std::string
legacy_smart_interface::get_valid_custom_dev_types_str()
654 return "marvell, areca,N, 3ware,N, hpt,L/M/N, cciss,N";
660 /////////////////////////////////////////////////////////////////////////////
661 /// Initialize platform interface and register with smi()
663 void smart_interface::init()
665 static os::legacy_smart_interface the_interface
;
666 smart_interface::set(&the_interface
);