diff --git a/sm5/CHANGELOG b/sm5/CHANGELOG index 80e65893f61806bee7df9759e182abff6c649c2d..35fff0236e92b8775f5a5b59bd82625b8970f82d 100644 --- a/sm5/CHANGELOG +++ b/sm5/CHANGELOG @@ -1,6 +1,6 @@ CHANGELOG for smartmontools -$Id: CHANGELOG,v 1.774 2009/02/23 22:59:04 manfred99 Exp $ +$Id: CHANGELOG,v 1.775 2009/02/27 22:42:51 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 usbjmicron[,PORT]' for drives + behind JMicron USB bridges. Tested on WinXP with JM20336 in + AixCase AIX-ESU35CD. Many thanks to JMicron tech support + for providing the required information. + [MS] knowndrives.cpp update: Add WD Caviar Green 8MB and 32MB cache variants, stretch to 2TB. diff --git a/sm5/dev_interface.h b/sm5/dev_interface.h index 35e7afd80eb69fb4476cbc9e3d0c80bf2306a050..95efc2a5846b7f1f06c41a7ff04c279101a38b28 100644 --- a/sm5/dev_interface.h +++ b/sm5/dev_interface.h @@ -18,7 +18,7 @@ #ifndef DEV_INTERFACE_H #define DEV_INTERFACE_H -#define DEV_INTERFACE_H_CVSID "$Id: dev_interface.h,v 1.7 2008/09/29 19:13:49 chrfranke Exp $\n" +#define DEV_INTERFACE_H_CVSID "$Id: dev_interface.h,v 1.8 2009/02/27 22:42:52 chrfranke Exp $\n" #include <stdarg.h> #include <string> @@ -351,6 +351,11 @@ struct ata_in_regs_48bit bool is_48bit_cmd() const { return prev.is_set(); } + /// Return true if 48-bit command with any nonzero high byte + bool is_real_48bit_cmd() const + { return ( prev.features || prev.sector_count + || prev.lba_low || prev.lba_mid || prev.lba_high); } + ata_in_regs_48bit(); }; diff --git a/sm5/scsiata.cpp b/sm5/scsiata.cpp index eeff3641aea83c5ecdaf0ceeafa201dc999c3bc2..691e18d9d58a38b9fef2cce7f076b3ee2d31067a 100644 --- a/sm5/scsiata.cpp +++ b/sm5/scsiata.cpp @@ -50,7 +50,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.21 2009/01/30 18:50:24 chrfranke Exp $" +const char *scsiata_c_cvsid="$Id: scsiata.cpp,v 1.22 2009/02/27 22:42:52 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 */ @@ -783,6 +783,209 @@ static int has_usbcypress_pass_through(ata_device * atadev, const char *manufact } #endif +///////////////////////////////////////////////////////////////////////////// + +/// JMicron USB Bridge support. + +class usbjmicron_device +: public tunnelled_device< + /*implements*/ ata_device, + /*by tunnelling through a*/ scsi_device + > +{ +public: + usbjmicron_device(smart_interface * intf, scsi_device * scsidev, + const char * req_type, int port); + + virtual ~usbjmicron_device() throw(); + + virtual bool open(); + + virtual bool ata_pass_through(const ata_cmd_in & in, ata_cmd_out & out); + +private: + bool get_registers(unsigned short addr, unsigned char * buf, unsigned short size); + + int m_port; +}; + + +usbjmicron_device::usbjmicron_device(smart_interface * intf, scsi_device * scsidev, + const char * req_type, int port) +: smart_device(intf, scsidev->get_dev_name(), "usbjmicron", req_type), + tunnelled_device<ata_device, scsi_device>(scsidev), + m_port(port) +{ + set_info().info_name = strprintf("%s [USB JMicron]", scsidev->get_info_name()); +} + +usbjmicron_device::~usbjmicron_device() throw() +{ +} + + +bool usbjmicron_device::open() +{ + // Open USB first + if (!tunnelled_device<ata_device, scsi_device>::open()) + return false; + + // Detect port if not specified + if (m_port < 0) { + unsigned char regbuf[2] = {0, 0}; + if (!get_registers(0x720f, regbuf, sizeof(regbuf))) { + close(); + return false; + } + + if (regbuf[0] & 0x04) + m_port = 0; + else if (regbuf[0] & 0x40) + m_port = 1; + else { + close(); + return set_err(ENODEV, "No device connected"); + } + } + + return true; +} + + +bool usbjmicron_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 (limited, see below) + ) + return false; + + // Support 48-bit commands with zero high bytes + if (in.in_regs.is_48bit_cmd()) { + if (in.in_regs.is_real_48bit_cmd() || in.out_needed.is_set()) + return set_err(ENOSYS, "48-bit ATA commands not fully supported"); + } + + if (m_port < 0) + return set_err(EIO, "Unknown JMicron port"); + + scsi_cmnd_io io_hdr; + memset(&io_hdr, 0, sizeof(io_hdr)); + + bool rwbit = true; + switch (in.direction) { + case ata_cmd_in::no_data: + io_hdr.dxfer_dir = DXFER_NONE; + 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); + 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; + rwbit = false; + break; + default: + return set_err(EINVAL); + } + + // Build pass through command + unsigned char cdb[12]; + cdb[ 0] = 0xdf; + cdb[ 1] = (rwbit ? 0x10 : 0x00); + cdb[ 2] = 0x00; + cdb[ 3] = (unsigned char)(io_hdr.dxfer_len >> 8); + cdb[ 4] = (unsigned char)(io_hdr.dxfer_len ); + 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 | (m_port == 0 ? 0xa0 : 0xb0); + cdb[11] = in.in_regs.command; + + 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()); + return set_err(scsidev->get_err()); + } + + if (in.out_needed.is_set()) { + // Read ATA output registers + // NOTE: There is a small race condition here! + unsigned char regbuf[16] = {0, }; + if (!get_registers((m_port == 0 ? 0x8000 : 0x9000), regbuf, sizeof(regbuf))) + return false; + + out.out_regs.sector_count = regbuf[ 0]; + out.out_regs.lba_mid = regbuf[ 4]; + out.out_regs.lba_low = regbuf[ 6]; + out.out_regs.device = regbuf[ 9]; + out.out_regs.lba_high = regbuf[10]; + out.out_regs.error = regbuf[13]; + out.out_regs.status = regbuf[14]; + } + + return true; +} + +bool usbjmicron_device::get_registers(unsigned short addr, + unsigned char * buf, unsigned short size) +{ + unsigned char cdb[12]; + cdb[ 0] = 0xdf; + cdb[ 1] = 0x10; + cdb[ 2] = 0x00; + cdb[ 3] = (unsigned char)(size >> 8); + cdb[ 4] = (unsigned char)(size ); + cdb[ 5] = 0x00; + cdb[ 6] = (unsigned char)(addr >> 8); + cdb[ 7] = (unsigned char)(addr ); + cdb[ 8] = 0x00; + cdb[ 9] = 0x00; + cdb[10] = 0x00; + cdb[11] = 0xfd; + + scsi_cmnd_io io_hdr; + memset(&io_hdr, 0, sizeof(io_hdr)); + io_hdr.dxfer_dir = DXFER_FROM_DEVICE; + io_hdr.dxfer_len = size; + io_hdr.dxferp = buf; + 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()); + return set_err(scsidev->get_err()); + } + + return true; +} + + } // namespace using namespace sat; @@ -803,6 +1006,7 @@ ata_device * smart_interface::get_sat_device(const char * type, scsi_device * sc } return new sat_device(this, scsidev, type, ptlen); } + else if (!strncmp(type, "usbcypress", 10)) { unsigned signature = 0x24; int n1 = -1, n2 = -1; if (!(((sscanf(type, "usbcypress%n,0x%x%n", &n1, &signature, &n2) == 1 && n2 == (int)strlen(type)) || n1 == (int)strlen(type)) @@ -813,6 +1017,18 @@ ata_device * smart_interface::get_sat_device(const char * type, scsi_device * sc } return new usbcypress_device(this, scsidev, type, signature); } + + else if (!strncmp(type, "usbjmicron", 10)) { + int port = -1, n1 = -1, n2 = -1; + if (!( (sscanf(type, "usbjmicron%n,%d%n", &n1, &port, &n2) == 1 + && n2 == (int)strlen(type) && 0 <= port && port <= 1) + || n1 == (int)strlen(type))) { + set_err(EINVAL, "Option '-d usbmicron,<n>' requires <n> to be 0 or 1"); + return 0; + } + return new usbjmicron_device(this, scsidev, type, port); + } + else { set_err(EINVAL, "Unknown USB device type '%s'", type); return 0; diff --git a/sm5/smartctl.8.in b/sm5/smartctl.8.in index c29fce3afd303a8813bf7f7373914838d3163334..9b6d1574248d1dc35904612aded447506d3b31fb 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.119 2009/02/06 22:33:05 chrfranke Exp $ + $Id: smartctl.8.in,v 1.120 2009/02/27 22:42:52 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 @@ -213,7 +213,8 @@ use the exit status of \fBsmartctl\fP (see RETURN VALUES below). .TP .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, \fIcciss,N\fP, and +are \fIata\fP, \fIscsi\fP, \fIsat\fP, \fImarvell\fP, \fI3ware,N\fP, +\fIareca,N\fP, \fIusbcypress\fP, \fIusbjmicron\fP, \fIcciss,N\fP, and \fIhpt,L/M\fP (or \fIhpt,L/M/N\fP). If this option is not used then \fBsmartctl\fP will attempt to guess the device type from the device name. @@ -230,6 +231,12 @@ The default scsi operation code is 0x24, but although it can be overridden with \'\-d usbcypress,0xn\', where n is the scsi operation code, you're running the risk of damage to the device or filesystems on it. +[NEW EXPERIMENTAL SMARTCTL FEATURE] The \'usbjmicron\' device type is for +SATA disks that are behind a JMicron USB to SATA bridge. The port can be +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. + 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 @@ -1652,7 +1659,7 @@ these documents may be found in the References section of the .SH CVS ID OF THIS PAGE: -$Id: smartctl.8.in,v 1.119 2009/02/06 22:33:05 chrfranke Exp $ +$Id: smartctl.8.in,v 1.120 2009/02/27 22:42:52 chrfranke Exp $ .\" Local Variables: .\" mode: nroff .\" End: