From b69d2483bfee18b1277e1dcff27f047c99da3329 Mon Sep 17 00:00:00 2001 From: chrfranke <chrfranke@4ea69e1a-61f1-4043-bf83-b5c94c648137> Date: Mon, 23 Mar 2009 21:59:31 +0000 Subject: [PATCH] Add option '-d usbsunplus' for drives behind SunplusIT USB bridges. git-svn-id: https://smartmontools.svn.sourceforge.net/svnroot/smartmontools/trunk@2769 4ea69e1a-61f1-4043-bf83-b5c94c648137 --- sm5/CHANGELOG | 7 +- sm5/scsiata.cpp | 232 ++++++++++++++++++++++++++++++++++++++-------- sm5/smartctl.8.in | 11 ++- 3 files changed, 205 insertions(+), 45 deletions(-) diff --git a/sm5/CHANGELOG b/sm5/CHANGELOG index 4cd89cef7..25e42f964 100644 --- a/sm5/CHANGELOG +++ b/sm5/CHANGELOG @@ -1,6 +1,6 @@ CHANGELOG for smartmontools -$Id: CHANGELOG,v 1.790 2009/03/22 17:17:39 chrfranke Exp $ +$Id: CHANGELOG,v 1.791 2009/03/23 21:59:31 chrfranke Exp $ The most recent version of this file is: http://smartmontools.cvs.sourceforge.net/smartmontools/sm5/CHANGELOG?view=markup @@ -41,6 +41,11 @@ NOTES FOR FUTURE RELEASES: see TODO file. <DEVELOPERS: ADDITIONS TO THE CHANGE LOG GO JUST BELOW HERE, PLEASE> + [CF] Add experimental option '-d usbsunplus' for drives behind + SunplusIT USB bridges. Tested on WinXP with SPIF215(?) in + TrekStor DataStation maxi m.u.. Many thanks to SunplusIT + tech support for providing the required information. + [CF] Windows: Provide a non-console version of smartctl.exe as smartctl-nc.exe. This prevents that a new console is opened when smartctl is run from a GUI program with diff --git a/sm5/scsiata.cpp b/sm5/scsiata.cpp index a5f735478..394c6b7d1 100644 --- a/sm5/scsiata.cpp +++ b/sm5/scsiata.cpp @@ -51,7 +51,7 @@ #include "dev_ata_cmd_set.h" // ata_device_with_command_set #include "dev_tunnelled.h" // tunnelled_device<> -const char *scsiata_c_cvsid="$Id: scsiata.cpp,v 1.28 2009/03/17 19:53:14 chrfranke Exp $" +const char *scsiata_c_cvsid="$Id: scsiata.cpp,v 1.29 2009/03/23 21:59:31 chrfranke Exp $" CONFIG_H_CVSID EXTERN_H_CVSID INT64_H_CVSID SCSICMDS_H_CVSID SCSIATA_H_CVSID UTILITY_H_CVSID; /* for passing global control variables */ @@ -458,6 +458,39 @@ const unsigned char * sg_scsi_sense_desc_find(const unsigned char * sensep, } +// Call scsi_pass_through and check sense. +// TODO: Provide as member function of class scsi_device (?) +static bool scsi_pass_through_and_check(scsi_device * scsidev, scsi_cmnd_io * iop, + const char * msg = "") +{ + // Provide sense buffer + unsigned char sense[32] = {0, }; + iop->sensep = sense; + iop->max_sense_len = sizeof(sense); + iop->timeout = SCSI_TIMEOUT_DEFAULT; + + // Run cmd + if (!scsidev->scsi_pass_through(iop)) { + if (con->reportscsiioctl > 0) + pout("%sscsi_pass_through() failed, errno=%d [%s]\n", + msg, scsidev->get_errno(), scsidev->get_errmsg()); + return false; + } + + // Check sense + scsi_sense_disect sinfo; + scsi_do_sense_disect(iop, &sinfo); + int err = scsiSimpleSenseFilter(&sinfo); + if (err) { + if (con->reportscsiioctl > 0) + pout("%sscsi error: %s\n", msg, scsiErrString(err)); + return scsidev->set_err(EIO, "scsi error %s", scsiErrString(err)); + } + + return true; +} + + ///////////////////////////////////////////////////////////////////////////// namespace sat { @@ -925,28 +958,10 @@ bool usbjmicron_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & ou io_hdr.cmnd = cdb; io_hdr.cmnd_len = sizeof(cdb); - unsigned char sense[32] = {0, }; - io_hdr.sensep = sense; - io_hdr.max_sense_len = sizeof(sense); - io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; - scsi_device * scsidev = get_tunnel_dev(); - if (!scsidev->scsi_pass_through(&io_hdr)) { - if (con->reportscsiioctl > 0) - pout("usbjmicron_device::ata_pass_through: scsi_pass_through() failed, " - "errno=%d [%s]\n", scsidev->get_errno(), scsidev->get_errmsg()); + if (!scsi_pass_through_and_check(scsidev, &io_hdr, + "usbjmicron_device::ata_pass_through: ")) return set_err(scsidev->get_err()); - } - - scsi_sense_disect sinfo; - scsi_do_sense_disect(&io_hdr, &sinfo); - int err = scsiSimpleSenseFilter(&sinfo); - if (err) { - if (con->reportscsiioctl > 0) - pout("usbjmicron_device::ata_pass_through: scsi error: %s\n", - scsiErrString(err)); - return set_err(EIO, "scsi error %s", scsiErrString(err)); - } if (in.out_needed.is_set()) { if (is_smart_status) { @@ -1010,27 +1025,159 @@ bool usbjmicron_device::get_registers(unsigned short addr, io_hdr.cmnd = cdb; io_hdr.cmnd_len = sizeof(cdb); - unsigned char sense[32] = {0, }; - io_hdr.sensep = sense; - io_hdr.max_sense_len = sizeof(sense); - io_hdr.timeout = SCSI_TIMEOUT_DEFAULT; - scsi_device * scsidev = get_tunnel_dev(); - if (!scsidev->scsi_pass_through(&io_hdr)) { - if (con->reportscsiioctl > 0) - pout("usbjmicron_device::get_registers: scsi_pass_through failed, " - "errno=%d [%s]\n", scsidev->get_errno(), scsidev->get_errmsg()); + if (!scsi_pass_through_and_check(scsidev, &io_hdr, + "usbjmicron_device::get_registers: ")) return set_err(scsidev->get_err()); + + return true; +} + + +///////////////////////////////////////////////////////////////////////////// + +/// SunplusIT USB Bridge support. + +class usbsunplus_device +: public tunnelled_device< + /*implements*/ ata_device, + /*by tunnelling through a*/ scsi_device + > +{ +public: + usbsunplus_device(smart_interface * intf, scsi_device * scsidev, + const char * req_type); + + virtual ~usbsunplus_device() throw(); + + virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); +}; + + +usbsunplus_device::usbsunplus_device(smart_interface * intf, scsi_device * scsidev, + const char * req_type) +: smart_device(intf, scsidev->get_dev_name(), "usbsunplus", req_type), + tunnelled_device<ata_device, scsi_device>(scsidev) +{ + set_info().info_name = strprintf("%s [USB Sunplus]", scsidev->get_info_name()); +} + +usbsunplus_device::~usbsunplus_device() throw() +{ +} + +bool usbsunplus_device::ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out) +{ + if (!ata_cmd_is_ok(in, + true, // data_out_support + false, // !multi_sector_support + true) // ata_48bit_support + ) + return false; + + scsi_cmnd_io io_hdr; + unsigned char cdb[12]; + + if (in.in_regs.is_48bit_cmd()) { + // Set "previous" registers + memset(&io_hdr, 0, sizeof(io_hdr)); + io_hdr.dxfer_dir = DXFER_NONE; + + cdb[ 0] = 0xf8; + cdb[ 1] = 0x00; + cdb[ 2] = 0x23; // Subcommand: Pass through presetting + cdb[ 3] = 0x00; + cdb[ 4] = 0x00; + cdb[ 5] = in.in_regs.prev.features; + cdb[ 6] = in.in_regs.prev.sector_count; + cdb[ 7] = in.in_regs.prev.lba_low; + cdb[ 8] = in.in_regs.prev.lba_mid; + cdb[ 9] = in.in_regs.prev.lba_high; + cdb[10] = 0x00; + cdb[11] = 0x00; + + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + + scsi_device * scsidev = get_tunnel_dev(); + if (!scsi_pass_through_and_check(scsidev, &io_hdr, + "usbsunplus_device::scsi_pass_through (presetting): ")) + return set_err(scsidev->get_err()); } - scsi_sense_disect sinfo; - scsi_do_sense_disect(&io_hdr, &sinfo); - int err = scsiSimpleSenseFilter(&sinfo); - if (err) { - if (con->reportscsiioctl > 0) - pout("usbjmicron_device::get_registers: scsi error: %s\n", - scsiErrString(err)); - return set_err(EIO, "scsi error %s", scsiErrString(err)); + // Run Pass through command + memset(&io_hdr, 0, sizeof(io_hdr)); + unsigned char protocol; + switch (in.direction) { + case ata_cmd_in::no_data: + io_hdr.dxfer_dir = DXFER_NONE; + protocol = 0x00; + break; + case ata_cmd_in::data_in: + io_hdr.dxfer_dir = DXFER_FROM_DEVICE; + io_hdr.dxfer_len = in.size; + io_hdr.dxferp = (unsigned char *)in.buffer; + memset(in.buffer, 0, in.size); + protocol = 0x10; + break; + case ata_cmd_in::data_out: + io_hdr.dxfer_dir = DXFER_TO_DEVICE; + io_hdr.dxfer_len = in.size; + io_hdr.dxferp = (unsigned char *)in.buffer; + protocol = 0x11; + break; + default: + return set_err(EINVAL); + } + + cdb[ 0] = 0xf8; + cdb[ 1] = 0x00; + cdb[ 2] = 0x22; // Subcommand: Pass through + cdb[ 3] = protocol; + cdb[ 4] = (unsigned char)(io_hdr.dxfer_len >> 9); + cdb[ 5] = in.in_regs.features; + cdb[ 6] = in.in_regs.sector_count; + cdb[ 7] = in.in_regs.lba_low; + cdb[ 8] = in.in_regs.lba_mid; + cdb[ 9] = in.in_regs.lba_high; + cdb[10] = in.in_regs.device | 0xa0; + cdb[11] = in.in_regs.command; + + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + + scsi_device * scsidev = get_tunnel_dev(); + if (!scsi_pass_through_and_check(scsidev, &io_hdr, + "usbsunplus_device::scsi_pass_through: ")) + // Returns sense key 0x03 (medium error) on ATA command error + return set_err(scsidev->get_err()); + + if (in.out_needed.is_set()) { + // Read ATA output registers + unsigned char regbuf[8] = {0, }; + memset(&io_hdr, 0, sizeof(io_hdr)); + io_hdr.dxfer_dir = DXFER_FROM_DEVICE; + io_hdr.dxfer_len = sizeof(regbuf); + io_hdr.dxferp = regbuf; + + cdb[ 0] = 0xf8; + cdb[ 1] = 0x00; + cdb[ 2] = 0x21; // Subcommand: Get status + memset(cdb+3, 0, sizeof(cdb)-3); + io_hdr.cmnd = cdb; + io_hdr.cmnd_len = sizeof(cdb); + + if (!scsi_pass_through_and_check(scsidev, &io_hdr, + "usbsunplus_device::scsi_pass_through (get registers): ")) + return set_err(scsidev->get_err()); + + out.out_regs.error = regbuf[1]; + out.out_regs.sector_count = regbuf[2]; + out.out_regs.lba_low = regbuf[3]; + out.out_regs.lba_mid = regbuf[4]; + out.out_regs.lba_high = regbuf[5]; + out.out_regs.device = regbuf[6]; + out.out_regs.status = regbuf[7]; } return true; @@ -1080,6 +1227,10 @@ ata_device * smart_interface::get_sat_device(const char * type, scsi_device * sc return new usbjmicron_device(this, scsidev, type, port); } + else if (!strcmp(type, "usbsunplus")) { + return new usbsunplus_device(this, scsidev, type); + } + else { set_err(EINVAL, "Unknown USB device type '%s'", type); return 0; @@ -1143,8 +1294,9 @@ struct usb_id_entry { }; const char d_sat[] = "sat"; -const char d_jmicron[] = "usbjmicron"; const char d_cypress[] = "usbcypress"; +const char d_jmicron[] = "usbjmicron"; +const char d_sunplus[] = "usbsunplus"; const char d_unsup[] = "unsupported"; // Map USB IDs -> '-d type' string @@ -1155,7 +1307,7 @@ const usb_id_entry usb_ids[] = { { 0x059f, 0x0651, -1, d_unsup }, // LaCie hard disk (FA Porsche design) { 0x059f, 0x1018, -1, d_sat }, // LaCie hard disk (Neil Poulton design) { 0x0bc2, 0x3001, -1, d_sat }, // Seagate FreeAgent Desk - { 0x0c0b, 0xb159, 0x0103, d_unsup }, // Dura Micro ? + { 0x0c0b, 0xb159, 0x0103, d_sunplus }, // Dura Micro (Sunplus USB-bridge) { 0x0d49, 0x7310, 0x0125, d_sat }, // Maxtor OneTouch 4 //{ 0x0d49, -1, -1, d_sat }, // Maxtor Basics Desktop { 0x1058, 0x1001, 0x0104, d_sat }, // WD Elements Desktop diff --git a/sm5/smartctl.8.in b/sm5/smartctl.8.in index 917ea2a06..000e5bbc6 100644 --- a/sm5/smartctl.8.in +++ b/sm5/smartctl.8.in @@ -1,7 +1,7 @@ .ig Copyright (C) 2002-8 Bruce Allen <smartmontools-support@lists.sourceforge.net> - $Id: smartctl.8.in,v 1.121 2009/03/14 16:14:10 chrfranke Exp $ + $Id: smartctl.8.in,v 1.122 2009/03/23 21:59:31 chrfranke Exp $ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free @@ -214,8 +214,8 @@ use the exit status of \fBsmartctl\fP (see RETURN VALUES below). .B \-d TYPE, \-\-device=TYPE Specifies the type of the device. The valid arguments to this option are \fIata\fP, \fIscsi\fP, \fIsat\fP, \fImarvell\fP, \fI3ware,N\fP, -\fIareca,N\fP, \fIusbcypress\fP, \fIusbjmicron\fP, \fIcciss,N\fP, -\fIhpt,L/M\fP (or \fIhpt,L/M/N\fP), and \fItest\fP. +\fIareca,N\fP, \fIusbcypress\fP, \fIusbjmicron\fP, \fIusbsunplus\fP, +\fIcciss,N\fP, \fIhpt,L/M\fP (or \fIhpt,L/M/N\fP), and \fItest\fP. If this option is not used then \fBsmartctl\fP will attempt to guess the device type from the device name or from controller type info @@ -244,6 +244,9 @@ specified by \'\-d usbjmicron,PORT\' where PORT is 0 (master) or 1 (slave). If no PORT is specified, it is auto-detected. If both ports are connected, 0 takes precedence. +[NEW EXPERIMENTAL SMARTCTL FEATURE] The \'usbsunplus\' device type is for +SATA disks that are behind a SunplusIT USB to SATA bridge. + Under Linux, to look at SATA disks behind Marvell SATA controllers (using Marvell's \'linuxIAL\' driver rather than libata driver) use \'\-d marvell\'. Such controllers show up as Marvell Technology Group Ltd. SATA I or II controllers @@ -1666,7 +1669,7 @@ these documents may be found in the References section of the .SH CVS ID OF THIS PAGE: -$Id: smartctl.8.in,v 1.121 2009/03/14 16:14:10 chrfranke Exp $ +$Id: smartctl.8.in,v 1.122 2009/03/23 21:59:31 chrfranke Exp $ .\" Local Variables: .\" mode: nroff .\" End: -- GitLab