]> git.proxmox.com Git - mirror_smartmontools-debian.git/blob - dev_legacy.cpp
Merge commit 'upstream/5.38+svn2993'
[mirror_smartmontools-debian.git] / dev_legacy.cpp
1 /*
2 * dev_legacy.cpp
3 *
4 * Home page of code is: http://smartmontools.sourceforge.net
5 *
6 * Copyright (C) 2008-9 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 "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
27 const char * dev_legacy_cpp_cvsid = "$Id: dev_legacy.cpp 2973 2009-10-26 22:38:19Z chrfranke $"
28 DEV_INTERFACE_H_CVSID;
29
30 extern smartmonctrl * con; // con->reportscsiioctl
31
32 /////////////////////////////////////////////////////////////////////////////
33
34 // Legacy interface declarations (now commented out globally):
35
36 // from utility.h:
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);
43 #endif
44
45 // from atacmds.h:
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);
53 #endif
54
55 // from scsicmds.h:
56 int do_scsi_cmnd_io(int dev_fd, struct scsi_cmnd_io * iop, int report);
57
58 // from smartctl.h:
59 void print_smartctl_examples();
60
61 /////////////////////////////////////////////////////////////////////////////
62
63 namespace os { // No need to publish anything, name provided for Doxygen
64
65 /////////////////////////////////////////////////////////////////////////////
66 /// Implement shared open/close routines with old functions.
67
68 class legacy_smart_device
69 : virtual public /*implements*/ smart_device
70 {
71 public:
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
84 protected:
85 /// Return filedesc for derived classes.
86 int get_fd() const
87 { return m_fd; }
88
89 private:
90 int m_fd; ///< filedesc, -1 if not open.
91 const char * m_mode; ///< Mode string for deviceopen().
92 };
93
94
95 legacy_smart_device::~legacy_smart_device() throw()
96 {
97 if (m_fd >= 0)
98 ::deviceclose(m_fd);
99 }
100
101 bool legacy_smart_device::is_open() const
102 {
103 return (m_fd >= 0);
104 }
105
106 bool 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
116 bool 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
129 class legacy_ata_device
130 : public /*implements*/ ata_device_with_command_set,
131 public /*extends*/ legacy_smart_device
132 {
133 public:
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
140 protected:
141 virtual int ata_command_interface(smart_command_set command, int select, char * data);
142 };
143
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")
147 {
148 }
149
150 int 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
156 bool 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
166 class legacy_escalade_device
167 : public /*implements*/ ata_device_with_command_set,
168 public /*extends*/ legacy_smart_device
169 {
170 public:
171 legacy_escalade_device(smart_interface * intf, const char * dev_name,
172 int escalade_type, int disknum);
173
174 protected:
175 virtual int ata_command_interface(smart_command_set command, int select, char * data);
176
177 private:
178 int m_escalade_type; ///< Type string for escalade_command_interface().
179 int m_disknum; ///< Disk number.
180 };
181
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"),
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
194 int 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
203 class legacy_areca_device
204 : public /*implements*/ ata_device_with_command_set,
205 public /*extends*/ legacy_smart_device
206 {
207 public:
208 legacy_areca_device(smart_interface * intf, const char * dev_name, int disknum);
209
210 protected:
211 virtual int ata_command_interface(smart_command_set command, int select, char * data);
212
213 private:
214 int m_disknum; ///< Disk number.
215 };
216
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"),
220 m_disknum(disknum)
221 {
222 set_info().info_name = strprintf("%s [areca_%02d]", dev_name, disknum);
223 }
224
225 int 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
234 class legacy_marvell_device
235 : public /*implements*/ ata_device_with_command_set,
236 public /*extends*/ legacy_smart_device
237 {
238 public:
239 legacy_marvell_device(smart_interface * intf, const char * dev_name, const char * req_type);
240
241 protected:
242 virtual int ata_command_interface(smart_command_set command, int select, char * data);
243 };
244
245
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")
250 {
251 }
252
253 int 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
262 class legacy_highpoint_device
263 : public /*implements*/ ata_device_with_command_set,
264 public /*extends*/ legacy_smart_device
265 {
266 public:
267 legacy_highpoint_device(smart_interface * intf, const char * dev_name,
268 unsigned char controller, unsigned char channel, unsigned char port);
269
270 protected:
271 virtual int ata_command_interface(smart_command_set command, int select, char * data);
272
273 private:
274 unsigned char m_hpt_data[3]; ///< controller/channel/port
275 };
276
277
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")
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
287 int 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
301 class legacy_scsi_device
302 : public /*implements*/ scsi_device,
303 public /*extends*/ legacy_smart_device
304 {
305 public:
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
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")
317 {
318 }
319
320 bool 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
337 class legacy_cciss_device
338 : public /*implements*/ scsi_device,
339 public /*extends*/ legacy_smart_device
340 {
341 public:
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
346 private:
347 unsigned char m_disknum; ///< Disk number.
348 };
349
350
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"),
355 m_disknum(disknum)
356 {
357 set_info().info_name = strprintf("%s [cciss_disk_%02d]", dev_name, disknum);
358 }
359
360 bool 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
378 smart_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)
408 return this;
409
410 // Use INQUIRY to detect type
411
412 // 3ware ?
413 if (!memcmp(req_buff + 8, "3ware", 5) || !memcmp(req_buff + 8, "AMCC", 4)) {
414 close();
415 #if defined(_WIN32) || defined(__CYGWIN__)
416 set_err(EINVAL, "AMCC/3ware controller, please try changing device to %s,N", get_dev_name());
417 #else
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());
420 #endif
421 return this;
422 }
423
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 }
436
437 // SAT or USB ?
438 {
439 smart_device * newdev = smi()->autodetect_sat_device(this, req_buff, len);
440 if (newdev)
441 // NOTE: 'this' is now owned by '*newdev'
442 return newdev;
443 }
444
445 // Nothing special found
446 return this;
447 }
448
449
450 /////////////////////////////////////////////////////////////////////////////
451 /// Implement platform interface with old functions.
452
453 class legacy_smart_interface
454 : public /*implements*/ smart_interface
455 {
456 public:
457 #ifdef HAVE_GET_OS_VERSION_STR
458 virtual std::string get_os_version_str();
459 #endif
460
461 virtual std::string get_app_examples(const char * appname);
462
463 virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
464 const char * pattern = 0);
465
466 protected:
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
475 virtual std::string get_valid_custom_dev_types_str();
476 };
477
478
479 //////////////////////////////////////////////////////////////////////
480
481 #ifdef HAVE_GET_OS_VERSION_STR
482 std::string legacy_smart_interface::get_os_version_str()
483 {
484 return ::get_os_version_str();
485 }
486 #endif
487
488 std::string legacy_smart_interface::get_app_examples(const char * appname)
489 {
490 if (!strcmp(appname, "smartctl"))
491 ::print_smartctl_examples(); // this prints to stdout ...
492 return ""; // ... so don't print again.
493 }
494
495 ata_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
500 scsi_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
506 smart_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
517 static void free_devnames(char * * devnames, int numdevs)
518 {
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);
523 }
524
525 bool 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)
560 devlist.push_back(atadev);
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)
567 devlist.push_back(scsidev);
568 }
569 free_devnames(scsinames, numscsi);
570 return true;
571 }
572
573
574 smart_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 }
642 if (!(0 <= disknum && disknum <= 15)) {
643 set_err(EINVAL, "Option -d cciss,N (N=%d) must have 0 <= N <= 15", disknum);
644 return 0;
645 }
646 return new legacy_cciss_device(this, name, disknum);
647 }
648
649 return 0;
650 }
651
652 std::string legacy_smart_interface::get_valid_custom_dev_types_str()
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
663 void smart_interface::init()
664 {
665 static os::legacy_smart_interface the_interface;
666 smart_interface::set(&the_interface);
667 }