]> git.proxmox.com Git - mirror_smartmontools-debian.git/blame - dev_legacy.cpp
Imported Upstream version 5.39.1+svn3124
[mirror_smartmontools-debian.git] / dev_legacy.cpp
CommitLineData
2127e193
GI
1/*
2 * dev_legacy.cpp
3 *
4 * Home page of code is: http://smartmontools.sourceforge.net
5 *
e9583e0c 6 * Copyright (C) 2008-10 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"
20#include "extern.h"
21#include "utility.h"
22#include "atacmds.h"
23#include "scsicmds.h"
24#include "dev_interface.h"
25#include "dev_ata_cmd_set.h"
26
e9583e0c 27const char * dev_legacy_cpp_cvsid = "$Id: dev_legacy.cpp 3098 2010-04-30 17:35:35Z chrfranke $"
2127e193
GI
28 DEV_INTERFACE_H_CVSID;
29
30extern smartmonctrl * con; // con->reportscsiioctl
31
32/////////////////////////////////////////////////////////////////////////////
33
34// Legacy interface declarations (now commented out globally):
35
36// from utility.h:
37int guess_device_type(const char * dev_name);
38int make_device_names (char ***devlist, const char* name);
39int deviceopen(const char *pathname, char *type);
40int deviceclose(int fd);
41#ifdef HAVE_GET_OS_VERSION_STR
42const char * get_os_version_str(void);
43#endif
44
45// from atacmds.h:
46int ata_command_interface(int device, smart_command_set command, int select, char *data);
47int escalade_command_interface(int fd, int escalade_port, int escalade_type, smart_command_set command, int select, char *data);
48int marvell_command_interface(int device, smart_command_set command, int select, char *data);
49int highpoint_command_interface(int device, smart_command_set command, int select, char *data);
50int areca_command_interface(int fd, int disknum, smart_command_set command, int select, char *data);
51#ifdef HAVE_ATA_IDENTIFY_IS_CACHED
52int ata_identify_is_cached(int fd);
53#endif
54
55// from scsicmds.h:
56int do_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report);
57
58// from smartctl.h:
59void print_smartctl_examples();
60
61/////////////////////////////////////////////////////////////////////////////
62
63namespace os { // No need to publish anything, name provided for Doxygen
64
65/////////////////////////////////////////////////////////////////////////////
66/// Implement shared open/close routines with old functions.
67
68class legacy_smart_device
69: virtual public /*implements*/ smart_device
70{
71public:
72 explicit legacy_smart_device(const char * mode)
73 : smart_device(never_called),
74 m_fd(-1), m_mode(mode) { }
75
76 virtual ~legacy_smart_device() throw();
77
78 virtual bool is_open() const;
79
80 virtual bool open();
81
82 virtual bool close();
83
84protected:
85 /// Return filedesc for derived classes.
86 int get_fd() const
87 { return m_fd; }
88
89private:
90 int m_fd; ///< filedesc, -1 if not open.
91 const char * m_mode; ///< Mode string for deviceopen().
92};
93
94
95legacy_smart_device::~legacy_smart_device() throw()
96{
97 if (m_fd >= 0)
98 ::deviceclose(m_fd);
99}
100
101bool legacy_smart_device::is_open() const
102{
103 return (m_fd >= 0);
104}
105
106bool legacy_smart_device::open()
107{
108 m_fd = ::deviceopen(get_dev_name(), (char*)m_mode);
109 if (m_fd < 0) {
110 set_err((errno==ENOENT || errno==ENOTDIR) ? ENODEV : errno);
111 return false;
112 }
113 return true;
114}
115
116bool legacy_smart_device::close()
117{
118 int fd = m_fd; m_fd = -1;
119 if (::deviceclose(fd) < 0) {
120 set_err(errno);
121 return false;
122 }
123 return true;
124}
125
126/////////////////////////////////////////////////////////////////////////////
127/// Implement standard ATA support with old functions
128
129class legacy_ata_device
130: public /*implements*/ ata_device_with_command_set,
131 public /*extends*/ legacy_smart_device
132{
133public:
134 legacy_ata_device(smart_interface * intf, const char * dev_name, const char * req_type);
135
136#ifdef HAVE_ATA_IDENTIFY_IS_CACHED
137 virtual bool ata_identify_is_cached() const;
138#endif
139
140protected:
141 virtual int ata_command_interface(smart_command_set command, int select, char * data);
142};
143
144legacy_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")
147{
148}
149
150int legacy_ata_device::ata_command_interface(smart_command_set command, int select, char * data)
151{
152 return ::ata_command_interface(get_fd(), command, select, data);
153}
154
155#ifdef HAVE_ATA_IDENTIFY_IS_CACHED
156bool legacy_ata_device::ata_identify_is_cached() const
157{
158 return !!::ata_identify_is_cached(get_fd());
159}
160#endif
161
162
163/////////////////////////////////////////////////////////////////////////////
164/// Implement AMCC/3ware RAID support with old functions
165
166class legacy_escalade_device
167: public /*implements*/ ata_device_with_command_set,
168 public /*extends*/ legacy_smart_device
169{
170public:
171 legacy_escalade_device(smart_interface * intf, const char * dev_name,
172 int escalade_type, int disknum);
173
174protected:
175 virtual int ata_command_interface(smart_command_set command, int select, char * data);
176
177private:
178 int m_escalade_type; ///< Type string for escalade_command_interface().
179 int m_disknum; ///< Disk number.
180};
181
182legacy_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"),
185 legacy_smart_device(
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)
190{
191 set_info().info_name = strprintf("%s [3ware_disk_%02d]", dev_name, disknum);
192}
193
194int legacy_escalade_device::ata_command_interface(smart_command_set command, int select, char * data)
195{
196 return ::escalade_command_interface(get_fd(), m_disknum, m_escalade_type, command, select, data);
197}
198
199
200/////////////////////////////////////////////////////////////////////////////
201/// Implement Areca RAID support with old functions
202
203class legacy_areca_device
204: public /*implements*/ ata_device_with_command_set,
205 public /*extends*/ legacy_smart_device
206{
207public:
208 legacy_areca_device(smart_interface * intf, const char * dev_name, int disknum);
209
210protected:
211 virtual int ata_command_interface(smart_command_set command, int select, char * data);
212
213private:
214 int m_disknum; ///< Disk number.
215};
216
217legacy_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"),
220 m_disknum(disknum)
221{
222 set_info().info_name = strprintf("%s [areca_%02d]", dev_name, disknum);
223}
224
225int legacy_areca_device::ata_command_interface(smart_command_set command, int select, char * data)
226{
227 return ::areca_command_interface(get_fd(), m_disknum, command, select, data);
228}
229
230
231/////////////////////////////////////////////////////////////////////////////
232/// Implement Marvell support with old functions
233
234class legacy_marvell_device
235: public /*implements*/ ata_device_with_command_set,
236 public /*extends*/ legacy_smart_device
237{
238public:
239 legacy_marvell_device(smart_interface * intf, const char * dev_name, const char * req_type);
240
241protected:
242 virtual int ata_command_interface(smart_command_set command, int select, char * data);
243};
244
245
246legacy_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")
250{
251}
252
253int legacy_marvell_device::ata_command_interface(smart_command_set command, int select, char * data)
254{
255 return ::marvell_command_interface(get_fd(), command, select, data);
256}
257
258
259/////////////////////////////////////////////////////////////////////////////
260/// Implement Highpoint RAID support with old functions
261
262class legacy_highpoint_device
263: public /*implements*/ ata_device_with_command_set,
264 public /*extends*/ legacy_smart_device
265{
266public:
267 legacy_highpoint_device(smart_interface * intf, const char * dev_name,
268 unsigned char controller, unsigned char channel, unsigned char port);
269
270protected:
271 virtual int ata_command_interface(smart_command_set command, int select, char * data);
272
273private:
274 unsigned char m_hpt_data[3]; ///< controller/channel/port
275};
276
277
278legacy_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")
282{
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]);
285}
286
287int legacy_highpoint_device::ata_command_interface(smart_command_set command, int select, char * data)
288{
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);
294 return status;
295}
296
297
298/////////////////////////////////////////////////////////////////////////////
299/// Implement standard SCSI support with old functions
300
301class legacy_scsi_device
302: public /*implements*/ scsi_device,
303 public /*extends*/ legacy_smart_device
304{
305public:
306 legacy_scsi_device(smart_interface * intf, const char * dev_name, const char * req_type);
307
308 virtual smart_device * autodetect_open();
309
310 virtual bool scsi_pass_through(scsi_cmnd_io * iop);
311};
312
313legacy_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")
317{
318}
319
320bool legacy_scsi_device::scsi_pass_through(scsi_cmnd_io * iop)
321{
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;
326 if (status < 0) {
327 set_err(-status);
328 return false;
329 }
330 return true;
331}
332
333
334/////////////////////////////////////////////////////////////////////////////
335/// Implement CCISS RAID support with old functions
336
337class legacy_cciss_device
338: public /*implements*/ scsi_device,
339 public /*extends*/ legacy_smart_device
340{
341public:
342 legacy_cciss_device(smart_interface * intf, const char * name, unsigned char disknum);
343
344 virtual bool scsi_pass_through(scsi_cmnd_io * iop);
345
346private:
347 unsigned char m_disknum; ///< Disk number.
348};
349
350
351legacy_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"),
355 m_disknum(disknum)
356{
357 set_info().info_name = strprintf("%s [cciss_disk_%02d]", dev_name, disknum);
358}
359
360bool legacy_cciss_device::scsi_pass_through(scsi_cmnd_io * iop)
361{
362 // See os_linux.cpp
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;
367 if (status < 0) {
368 set_err(-status);
369 return false;
370 }
371 return true;
372}
373
374
375/////////////////////////////////////////////////////////////////////////////
376/// SCSI open with autodetection support
377
378smart_device * legacy_scsi_device::autodetect_open()
379{
380 // Open device
381 if (!open())
382 return this;
383
384 // No Autodetection if device type was specified by user
385 if (*get_req_type())
386 return this;
387
388 // The code below is based on smartd.cpp:SCSIFilterKnown()
389
390 // Get INQUIRY
391 unsigned char req_buff[64] = {0, };
392 int req_len = 36;
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
396 req_len = 64;
397 if (scsiStdInquiry(this, req_buff, req_len)) {
398 // device doesn't like INQUIRY commands
399 close();
400 set_err(EIO, "INQUIRY failed");
401 return this;
402 }
403 }
404
405 int avail_len = req_buff[4] + 5;
406 int len = (avail_len < req_len ? avail_len : req_len);
407 if (len < 36)
bed94269 408 return this;
2127e193
GI
409
410 // Use INQUIRY to detect type
bed94269
GI
411
412 // 3ware ?
413 if (!memcmp(req_buff + 8, "3ware", 5) || !memcmp(req_buff + 8, "AMCC", 4)) {
414 close();
2127e193 415#if defined(_WIN32) || defined(__CYGWIN__)
bed94269 416 set_err(EINVAL, "AMCC/3ware controller, please try changing device to %s,N", get_dev_name());
2127e193 417#else
bed94269
GI
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());
2127e193 420#endif
bed94269
GI
421 return this;
422 }
2127e193 423
bed94269
GI
424 // Marvell ?
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());
427 close();
428 smart_device_auto_ptr newdev(
429 new legacy_marvell_device(smi(), get_dev_name(), get_req_type()),
430 this
431 );
432 newdev->open(); // TODO: Can possibly pass open fd
433 delete this;
434 return newdev.release();
435 }
2127e193 436
bed94269
GI
437 // SAT or USB ?
438 {
439 smart_device * newdev = smi()->autodetect_sat_device(this, req_buff, len);
2127e193
GI
440 if (newdev)
441 // NOTE: 'this' is now owned by '*newdev'
442 return newdev;
443 }
2127e193
GI
444
445 // Nothing special found
446 return this;
447}
448
449
450/////////////////////////////////////////////////////////////////////////////
451/// Implement platform interface with old functions.
452
453class legacy_smart_interface
454: public /*implements*/ smart_interface
455{
456public:
457#ifdef HAVE_GET_OS_VERSION_STR
54965743 458 virtual std::string get_os_version_str();
2127e193
GI
459#endif
460
54965743 461 virtual std::string get_app_examples(const char * appname);
2127e193
GI
462
463 virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
464 const char * pattern = 0);
465
466protected:
467 virtual ata_device * get_ata_device(const char * name, const char * type);
468
469 virtual scsi_device * get_scsi_device(const char * name, const char * type);
470
471 virtual smart_device * autodetect_smart_device(const char * name);
472
473 virtual smart_device * get_custom_smart_device(const char * name, const char * type);
474
54965743 475 virtual std::string get_valid_custom_dev_types_str();
2127e193
GI
476};
477
478
479//////////////////////////////////////////////////////////////////////
480
481#ifdef HAVE_GET_OS_VERSION_STR
54965743 482std::string legacy_smart_interface::get_os_version_str()
2127e193
GI
483{
484 return ::get_os_version_str();
485}
486#endif
487
54965743 488std::string legacy_smart_interface::get_app_examples(const char * appname)
2127e193
GI
489{
490 if (!strcmp(appname, "smartctl"))
491 ::print_smartctl_examples(); // this prints to stdout ...
54965743 492 return ""; // ... so don't print again.
2127e193
GI
493}
494
495ata_device * legacy_smart_interface::get_ata_device(const char * name, const char * type)
496{
497 return new legacy_ata_device(this, name, type);
498}
499
500scsi_device * legacy_smart_interface::get_scsi_device(const char * name, const char * type)
501{
502 return new legacy_scsi_device(this, name, type);
503}
504
505
506smart_device * legacy_smart_interface::autodetect_smart_device(const char * name)
507{
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, "");
511 }
512 // TODO: Test autodetect device here
513 return 0;
514}
515
516
517static void free_devnames(char * * devnames, int numdevs)
518{
e9583e0c 519 static const char version[] = "$Id: dev_legacy.cpp 3098 2010-04-30 17:35:35Z chrfranke $";
2127e193
GI
520 for (int i = 0; i < numdevs; i++)
521 FreeNonZero(devnames[i], -1,__LINE__, version);
522 FreeNonZero(devnames, (sizeof (char*) * numdevs),__LINE__, version);
523}
524
525bool legacy_smart_interface::scan_smart_devices(smart_device_list & devlist,
526 const char * type, const char * pattern /*= 0*/)
527{
528 if (pattern) {
529 set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");
530 return false;
531 }
532
533 // Make namelists
534 char * * atanames = 0; int numata = 0;
535 if (!type || !strcmp(type, "ata")) {
536 numata = ::make_device_names(&atanames, "ATA");
537 if (numata < 0) {
538 set_err(ENOMEM);
539 return false;
540 }
541 }
542
543 char * * scsinames = 0; int numscsi = 0;
544 if (!type || !strcmp(type, "scsi")) {
545 numscsi = ::make_device_names(&scsinames, "SCSI");
546 if (numscsi < 0) {
547 free_devnames(atanames, numata);
548 set_err(ENOMEM);
549 return false;
550 }
551 }
552
553 // Add to devlist
554 int i;
555 if (type==NULL)
556 type="";
557 for (i = 0; i < numata; i++) {
558 ata_device * atadev = get_ata_device(atanames[i], type);
559 if (atadev)
bed94269 560 devlist.push_back(atadev);
2127e193
GI
561 }
562 free_devnames(atanames, numata);
563
564 for (i = 0; i < numscsi; i++) {
565 scsi_device * scsidev = get_scsi_device(scsinames[i], type);
566 if (scsidev)
bed94269 567 devlist.push_back(scsidev);
2127e193
GI
568 }
569 free_devnames(scsinames, numscsi);
570 return true;
571}
572
573
574smart_device * legacy_smart_interface::get_custom_smart_device(const char * name, const char * type)
575{
576 // Marvell ?
577 if (!strcmp(type, "marvell"))
578 return new legacy_marvell_device(this, name, type);
579
580 // 3Ware ?
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");
585 return 0;
586 }
587 if (!(0 <= disknum && disknum <= 127)) {
588 set_err(EINVAL, "Option -d 3ware,N (N=%d) must have 0 <= N <= 127", disknum);
589 return 0;
590 }
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);
595 }
596
597 // Areca?
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");
602 return 0;
603 }
604 if (!(1 <= disknum && disknum <= 24)) {
605 set_err(EINVAL, "Option -d areca,N (N=%d) must have 1 <= N <= 24", disknum);
606 return 0;
607 }
608 return new legacy_areca_device(this, name, disknum);
609 }
610
611 // Highpoint ?
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");
618 return 0;
619 }
620 if (!(1 <= controller && controller <= 8)) {
621 set_err(EINVAL, "Option '-d hpt,L/M/N' invalid controller id L supplied");
622 return 0;
623 }
624 if (!(1 <= channel && channel <= 8)) {
625 set_err(EINVAL, "Option '-d hpt,L/M/N' invalid channel number M supplied");
626 return 0;
627 }
628 if (!(1 <= disknum && disknum <= 15)) {
629 set_err(EINVAL, "Option '-d hpt,L/M/N' invalid pmport number N supplied");
630 return 0;
631 }
632 return new legacy_highpoint_device(this, name, controller, channel, disknum);
633 }
634
635 // CCISS ?
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");
640 return 0;
641 }
e9583e0c
GI
642 if (!(0 <= disknum && disknum <= 127)) {
643 set_err(EINVAL, "Option -d cciss,N (N=%d) must have 0 <= N <= 127", disknum);
2127e193
GI
644 return 0;
645 }
646 return new legacy_cciss_device(this, name, disknum);
647 }
648
649 return 0;
650}
651
54965743 652std::string legacy_smart_interface::get_valid_custom_dev_types_str()
2127e193
GI
653{
654 return "marvell, areca,N, 3ware,N, hpt,L/M/N, cciss,N";
655}
656
657} // namespace
658
659
660/////////////////////////////////////////////////////////////////////////////
661/// Initialize platform interface and register with smi()
662
663void smart_interface::init()
664{
665 static os::legacy_smart_interface the_interface;
666 smart_interface::set(&the_interface);
667}