]> git.proxmox.com Git - mirror_smartmontools-debian.git/blob - dev_legacy.cpp
8b39d3fc6b248eb131e15e7fa1b8468516fbec9d
[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 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 2915 2009-09-18 21:17:37Z 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 smart_device * newdev = 0;
412 try {
413 // 3ware ?
414 if (!memcmp(req_buff + 8, "3ware", 5) || !memcmp(req_buff + 8, "AMCC", 4)) {
415 close();
416 #if defined(_WIN32) || defined(__CYGWIN__)
417 set_err(EINVAL, "AMCC/3ware controller, please try changing device to %s,N", get_dev_name());
418 #else
419 set_err(EINVAL, "AMCC/3ware controller, please try adding '-d 3ware,N',\n"
420 "you may need to replace %s with /dev/twaN or /dev/tweN", get_dev_name());
421 #endif
422 return this;
423 }
424
425 // Marvell ?
426 if (len >= 42 && !memcmp(req_buff + 36, "MVSATA", 6)) { // TODO: Linux-specific?
427 //pout("Device %s: using '-d marvell' for ATA disk with Marvell driver\n", get_dev_name());
428 close();
429 newdev = new legacy_marvell_device(smi(), get_dev_name(), get_req_type());
430 newdev->open(); // TODO: Can possibly pass open fd
431 delete this;
432 return newdev;
433 }
434
435 // SAT or USB ?
436 newdev = smi()->autodetect_sat_device(this, req_buff, len);
437 if (newdev)
438 // NOTE: 'this' is now owned by '*newdev'
439 return newdev;
440 }
441 catch (...) {
442 // Cleanup if exception occurs after newdev was allocated
443 delete newdev;
444 throw;
445 }
446
447 // Nothing special found
448 return this;
449 }
450
451
452 /////////////////////////////////////////////////////////////////////////////
453 /// Implement platform interface with old functions.
454
455 class legacy_smart_interface
456 : public /*implements*/ smart_interface
457 {
458 public:
459 #ifdef HAVE_GET_OS_VERSION_STR
460 virtual std::string get_os_version_str();
461 #endif
462
463 virtual std::string get_app_examples(const char * appname);
464
465 virtual bool scan_smart_devices(smart_device_list & devlist, const char * type,
466 const char * pattern = 0);
467
468 protected:
469 virtual ata_device * get_ata_device(const char * name, const char * type);
470
471 virtual scsi_device * get_scsi_device(const char * name, const char * type);
472
473 virtual smart_device * autodetect_smart_device(const char * name);
474
475 virtual smart_device * get_custom_smart_device(const char * name, const char * type);
476
477 virtual std::string get_valid_custom_dev_types_str();
478 };
479
480
481 //////////////////////////////////////////////////////////////////////
482
483 #ifdef HAVE_GET_OS_VERSION_STR
484 std::string legacy_smart_interface::get_os_version_str()
485 {
486 return ::get_os_version_str();
487 }
488 #endif
489
490 std::string legacy_smart_interface::get_app_examples(const char * appname)
491 {
492 if (!strcmp(appname, "smartctl"))
493 ::print_smartctl_examples(); // this prints to stdout ...
494 return ""; // ... so don't print again.
495 }
496
497 ata_device * legacy_smart_interface::get_ata_device(const char * name, const char * type)
498 {
499 return new legacy_ata_device(this, name, type);
500 }
501
502 scsi_device * legacy_smart_interface::get_scsi_device(const char * name, const char * type)
503 {
504 return new legacy_scsi_device(this, name, type);
505 }
506
507
508 smart_device * legacy_smart_interface::autodetect_smart_device(const char * name)
509 {
510 switch (::guess_device_type(name)) {
511 case CONTROLLER_ATA : return new legacy_ata_device(this, name, "");
512 case CONTROLLER_SCSI: return new legacy_scsi_device(this, name, "");
513 }
514 // TODO: Test autodetect device here
515 return 0;
516 }
517
518
519 static void free_devnames(char * * devnames, int numdevs)
520 {
521 static const char version[] = "$Id: dev_legacy.cpp 2915 2009-09-18 21:17:37Z chrfranke $";
522 for (int i = 0; i < numdevs; i++)
523 FreeNonZero(devnames[i], -1,__LINE__, version);
524 FreeNonZero(devnames, (sizeof (char*) * numdevs),__LINE__, version);
525 }
526
527 bool legacy_smart_interface::scan_smart_devices(smart_device_list & devlist,
528 const char * type, const char * pattern /*= 0*/)
529 {
530 if (pattern) {
531 set_err(EINVAL, "DEVICESCAN with pattern not implemented yet");
532 return false;
533 }
534
535 // Make namelists
536 char * * atanames = 0; int numata = 0;
537 if (!type || !strcmp(type, "ata")) {
538 numata = ::make_device_names(&atanames, "ATA");
539 if (numata < 0) {
540 set_err(ENOMEM);
541 return false;
542 }
543 }
544
545 char * * scsinames = 0; int numscsi = 0;
546 if (!type || !strcmp(type, "scsi")) {
547 numscsi = ::make_device_names(&scsinames, "SCSI");
548 if (numscsi < 0) {
549 free_devnames(atanames, numata);
550 set_err(ENOMEM);
551 return false;
552 }
553 }
554
555 // Add to devlist
556 int i;
557 if (type==NULL)
558 type="";
559 for (i = 0; i < numata; i++) {
560 ata_device * atadev = get_ata_device(atanames[i], type);
561 if (atadev)
562 devlist.add(atadev);
563 }
564 free_devnames(atanames, numata);
565
566 for (i = 0; i < numscsi; i++) {
567 scsi_device * scsidev = get_scsi_device(scsinames[i], type);
568 if (scsidev)
569 devlist.add(scsidev);
570 }
571 free_devnames(scsinames, numscsi);
572 return true;
573 }
574
575
576 smart_device * legacy_smart_interface::get_custom_smart_device(const char * name, const char * type)
577 {
578 // Marvell ?
579 if (!strcmp(type, "marvell"))
580 return new legacy_marvell_device(this, name, type);
581
582 // 3Ware ?
583 int disknum = -1, n1 = -1, n2 = -1;
584 if (sscanf(type, "3ware,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) {
585 if (n2 != (int)strlen(type)) {
586 set_err(EINVAL, "Option -d 3ware,N requires N to be a non-negative integer");
587 return 0;
588 }
589 if (!(0 <= disknum && disknum <= 127)) {
590 set_err(EINVAL, "Option -d 3ware,N (N=%d) must have 0 <= N <= 127", disknum);
591 return 0;
592 }
593 int contr = ::guess_device_type(name);
594 if (contr != CONTROLLER_3WARE_9000_CHAR && contr != CONTROLLER_3WARE_678K_CHAR)
595 contr = CONTROLLER_3WARE_678K;
596 return new legacy_escalade_device(this, name, contr, disknum);
597 }
598
599 // Areca?
600 disknum = n1 = n2 = -1;
601 if (sscanf(type, "areca,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) {
602 if (n2 != (int)strlen(type)) {
603 set_err(EINVAL, "Option -d areca,N requires N to be a non-negative integer");
604 return 0;
605 }
606 if (!(1 <= disknum && disknum <= 24)) {
607 set_err(EINVAL, "Option -d areca,N (N=%d) must have 1 <= N <= 24", disknum);
608 return 0;
609 }
610 return new legacy_areca_device(this, name, disknum);
611 }
612
613 // Highpoint ?
614 int controller = -1, channel = -1; disknum = 1;
615 n1 = n2 = -1; int n3 = -1;
616 if (sscanf(type, "hpt,%n%d/%d%n/%d%n", &n1, &controller, &channel, &n2, &disknum, &n3) >= 2 || n1 == 4) {
617 int len = strlen(type);
618 if (!(n2 == len || n3 == len)) {
619 set_err(EINVAL, "Option '-d hpt,L/M/N' supports 2-3 items");
620 return 0;
621 }
622 if (!(1 <= controller && controller <= 8)) {
623 set_err(EINVAL, "Option '-d hpt,L/M/N' invalid controller id L supplied");
624 return 0;
625 }
626 if (!(1 <= channel && channel <= 8)) {
627 set_err(EINVAL, "Option '-d hpt,L/M/N' invalid channel number M supplied");
628 return 0;
629 }
630 if (!(1 <= disknum && disknum <= 15)) {
631 set_err(EINVAL, "Option '-d hpt,L/M/N' invalid pmport number N supplied");
632 return 0;
633 }
634 return new legacy_highpoint_device(this, name, controller, channel, disknum);
635 }
636
637 // CCISS ?
638 disknum = n1 = n2 = -1;
639 if (sscanf(type, "cciss,%n%d%n", &n1, &disknum, &n2) == 1 || n1 == 6) {
640 if (n2 != (int)strlen(type)) {
641 set_err(EINVAL, "Option -d cciss,N requires N to be a non-negative integer");
642 return 0;
643 }
644 if (!(0 <= disknum && disknum <= 15)) {
645 set_err(EINVAL, "Option -d cciss,N (N=%d) must have 0 <= N <= 15", disknum);
646 return 0;
647 }
648 return new legacy_cciss_device(this, name, disknum);
649 }
650
651 return 0;
652 }
653
654 std::string legacy_smart_interface::get_valid_custom_dev_types_str()
655 {
656 return "marvell, areca,N, 3ware,N, hpt,L/M/N, cciss,N";
657 }
658
659 } // namespace
660
661
662 /////////////////////////////////////////////////////////////////////////////
663 /// Initialize platform interface and register with smi()
664
665 void smart_interface::init()
666 {
667 static os::legacy_smart_interface the_interface;
668 smart_interface::set(&the_interface);
669 }