diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..cfe806e --- /dev/null +++ b/.gitattributes @@ -0,0 +1,9 @@ +Changelog text eol=lf +Changelog.* text eol=lf +LICENSE.* text eol=lf +Makefile text eol=lf +Makefile.* text eol=lf +README text eol=lf +README.* text eol=lf +*.c text eol=lf +*.h text eol=lf diff --git a/Changelog b/Changelog new file mode 100644 index 0000000..c78d19a --- /dev/null +++ b/Changelog @@ -0,0 +1,232 @@ +hdparm-6.9 + - added -s flag to control power-up in standby + (thanks to chrfranke) + - make --Istdin more robust + - added -I recognition of SMART Command Transport (SCT) + (thanks to chrfranke). + - fix X2 over-reporting of -T results + - add udma 3/4/5 modes to the -i results +hdparm-6.8 + - improve parsing/operation of --Istdin function +hdparm-6.7 + - don't default to "-v" when only new "--" longopts are used. + - calculate integrity word if not correct + - remove used code/parameter from identify() + - fix "(null)" strings from the "Drive conforms to" line of -I + - tidied up usage of prefix vars in Makefile + - fix bug in -C implementation + - new -H flag for (Hitachi) drive temperature +hdparm-6.6 + - fix build for Redhat/Fedora systems. + - disable security commands when building on Redhat/Fedora, + as they lack the required data structures in the system headers. +hdparm-6.5 + - fix -I bugs from version 6.4. +hdparm-6.4 + - Makefile updates from Mikkel Krautz. + - manpage updates / corrections. + - fixed bug in -C code. + - major updates to bring -I information up to current specs. +hdparm-6.3 + - added reporting of ATA revision numbers > 7 +hdparm-6.2 + - eliminated short form (-F) of --security-freeze flag + - completely revamped ATA Security Commands + (now they actually work sometimes) + courtesy of Leonard den Ottolander (a BIG thank-you!) +hdparm-6.1 + - fix BLKGETSIZE bug introduced in 6.0 + - man page clarification for -M (courtesy Stephen Gran) +hdparm-6.0 + - correct usage of BLKGETSIZE64 (returns bytes instead of sectors) + - Added ATA Security switches (Benjamin Benz bbe[AT]heise[DOT]de) +hdparm-5.9 + - fix security-mode display (Franz Lehner) + - fixed -W bug (Stephan Gran) +hdparm-5.8 + - added HDIO_SET_WCACHE support to -W + - fixed double byteswap for big endian + - fixed %lld warnings on 64-bit architectures + - "hdparm -h" now goes to stdout instead of stderr +hdparm-5.7 + - fixed output of NULs in -I string fields + - fixed big-endian -I crashes from 5.6 + - removed remains of defunct major-number checks + - added support for BLKGETSIZE64 ioctl internally + - added --direct flag to use O_DIRECT on device open + - renamed -Istdin to --Istdin + - renamed -Istdout to --Istdout + - allow other flags to follow any of: -I, --Istdin, --Istdout +hdparm-5.6 + - fixed reversed device names in idectl script + - renamed readahead variables to avoid new glibc conflicts + - added -Istdout flag to dump IDENTIFY data as hex + - removed MAJOR number checks to open up hdpar for SATA-SCSI drivers +hdparm-5.5 + - added limited support for SCSI(-controlled) CDROM/optical drives + - incorporated various -I clean-up patches from Maciej W. Rozycki + - removed wordswap from capacity calculation: latest kenels already do it + - fixed slight issues with ATA Revision display + - added debian scripts from Stephan Gran +hdparm-5.4 + - fixed 2.5.67 compile error (LVM_BLK_MAJOR) + - first attempt to support BIG_ENDIAN in identify.c + - converted -T and -t to use minimum timing intervals (2 and 3 seconds) + - fix device size overflow issue with -t + - hdparm -I: fixed device size output for devices with reversed endian. + - hdparm -i: added explanation for "*" + - slight formatting change for -Tt outputs + - fixed "(illegal value)" formatting + - added CCISS_MAJOR + - incorporated RedHat-8.0 and other patches +hdparm-5.3 + - get rid of malloc() + - fix(?) BIG_ENDIAN problems + - fix -tT to work on very small drives + - fix -p output for parameters >= 100 +hdparm-5.2 + - compile fixes for latest 2.5.xx kernels + - moved -s from CFLAGS to LDFLAGS + - fixed manpage formatting error from 5.1 + - -i was broken due to non-compatible changes in 2.5.xx + - the fix for -i now breaks hdparm -i for really old kernels +hdparm-5.1 + - fixed segfault from -i on older drives +hdparm-5.0 + - updated -I to most recent ATA6 draft standard + - added -Istdin to process input from /proc/ide/hd*/identify + - CFLAGS Makefile fix from Kevin P. Fleming + - -X mode names from Martin + - tweaked #include's for Slackware + - eliminate bogus "udma10" from -I output + - formatting fixes for -I from various people + - clean compile fixes from Steven Augart + - allow -d parameters other than 0/1 for special uses + - removed busstate, acoustic, and nowerr from "-v" subset + - support for Pacific Digital ADMA-100i +hdparm-4.9 + - fixed compile error with some kernels +hdparm-4.8 + - updated -Q to take queue depth as parameter value +hdparm-4.7 + - add -z option for BLKRRPART ("re-read partition table") + - add -Q option to turn on/off tagged queuing + - add -M option for acoustic feature set + - allow ANY value for the parameter to '-p' (user beware!) + - applied APM fixes from Gildas Bazin +hdparm-4.6 + - fix version numbers, update DMA notes in manpage +hdparm-4.5 + - ENDIAN tidyup, GETGEO fixes, Makefile fixes, mostly courtesy of Maciej W. Rozycki +hdparm-4.4 + - add -b option, courtesy of Tim Hockin +hdparm-4.3 + - display most fields as unsigned rather than signed values +hdparm-4.2 + - completely new format/implementation of "-I" with much more detail + - code cleanups for newer libs/compilers +hdparm-4.1 + - merge some changes from Andre + - code cleanups from Neil Macvicar and others + - improved output from -i for newer features + - incorporate RedHat-7.0 patches +hdparm-4.0 + - no such release +hdparm-3.9 + - added IDE_MAJOR[6-9] + - nuked the LBAsects display (again!) +hdparm-3.8 + - fix display of drive capacity (Stephane Eranian ) + - new -E "set cdrom speed" option (Matthias Oster ) + - new -R and -U "(un)register hwif" options (Christian Lademann ) + - new contrib subdirectory with contributed scripts from users. + - support for display of newer PIO and DMA/UDMA modes +hdparm-3.7 + - use O_NONBLOCK for open(), to handle drives with media removed + - make "HDIO_DRIVE_CMD failed" messages slightly more verbose + - get rid of "HDIO_GET_MULTCOUNT failed" from -i on non-disks +hdparm-3.6 + - added new -V (version) option + - removed confusing LBA "remapping" geometry calculation + - small fix to "-I" + - Courtesy of Andrzej Krzysztofowicz : + - added support for old XT drives + - transfer display in kB/s rather than in MB/s for slow disks + - fixed -v for SCSI disks + - added -L to lock/unlock door of removeable harddisks + - added udma modes 3,4,5.. + - updated Makefile to use "destdir" prefix -- Ytiddo +hdparm-3.5 + - fixed compilation under older kernels + - fixed udma mode info under older kernels +hdparm-3.4 + - added udma mode info + - added support for SCSI_DISK_MAJOR{0-7} + - fix -h (help) to show -y instead of -Y for "standby" + - fix display of drive SerialNo: 20 chars instead of just 8 + - modify -C -y -Y to each try both possible command codes + - add interpretations for use of -X to set UltraDMA modes + - add -D for enable/disable drive defect-mgmt +hdparm-3.3 + - add -C, -y, and -Y flags for IDE power management +hdparm-3.2 + - force BLKFLSBUF after -T or -t -- kmg@barco.com + - fix minor -T/-t mixup in manpage -- hankedr@mail.auburn.edu +hdparm-3.1 + - permit "-p" with values larger than 5 (for cmd640 readahead) + - updated version number on manpage (was way out of date!) +hdparm-3.0 + - always touch each sector in read_big_block() -- Jon Burgess + - move cache timings to new -T option -- piercarl@sabi.demon.co.uk +hdparm-2.9: + - updated author's email address +hdparm-2.8: + - fixed compilation against older kernels + - changed "KB" to "kB" + - support for "-t" on "md" devices (from boris@xtalk.msk.su) + - removed "Estimating raw driver speed" message from "-t" + because it is likely very incorrect on faster systems + - added "-I" to re-issue IDENTIFY command to drive +hdparm-2.7: + - fixed .lsm file + - fixed "hdparm -c" (broken in 2.6) (kudos to clive@epos.demon.co.uk) +hdparm-2.6: + - tidied up manpage + - added support for HDIO_SET_PIO_MODE (kernel 1.3.61+) + - reduced codesize by combining fprintf's in help +hdparm-2.5: + - fixed -i display of eight character fwrev field + - rearranged output of -v +hdparm-2.4: + - added flag to turn on/off "using_dma" flag (kernel 1.3.22+) + - added warnings about CMD-640B and RZ1000 bugs to manpage ("-u1") + - cleaned up output from -c, added text interpretations + - added -c functionality to -v + - removed -a functionality from -v for scsi drives + - added -n flag for ignoring the "write error" bit from a drive + - moved binary from /usr/local/bin to /usr/local/sbin + - added support for 3rd/4th IDE interfaces +hdparm-2.3: + - added -c flag for controlling 32-bit chipset features with 1.2.9+ + - fixed error messages when -t used on SCSI drives + - fixed date on man page to read 1995 rather than 1994 (late change) +hdparm-2.2: + - fixed a memory problem in my PC, and now BLKFLSBUF seems safe again + - fixed "help" line for "-v" +hdparm-2.1: + - fixed formatting of DMA info line + - added "(DANGEROUS)" to -u,-X,-W options in "hdparm -h" + - changed order in which settings are applied: placed standby last +hdparm-2.0: + - added this file to the distribution + - added -f and -q flags to hdparm.c and hdparm.8 + - added -N to gcc in makefile + - changed installation paths to /usr/local/* in makefile + - removed meaningless CPU% measures + - removed -s and -x flags + - added new -AKPSWXZ flags using new HDIO_DRIVE_CMD ioctl + - removed BLKFLSBUF ioctl from everywhere except -t + (there may be a kernel bug in the implementation of BLKFLSBUF + when used on an active (mounted rw) filesystem). + - most features now require (E)IDE driver v2.6 or higher + (ide-2.6.patch.65+.gz) diff --git a/LICENSE.TXT b/LICENSE.TXT new file mode 100644 index 0000000..280a1c0 --- /dev/null +++ b/LICENSE.TXT @@ -0,0 +1,9 @@ +BSD-Style Open Source License: + +You may freely use, modify, and redistribute the hdparm program, +as either binary or source, or both. + +The only condition is that my name and copyright notice +remain in the source code as-is. + +Mark Lord (mlord@pobox.com) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d860ffe --- /dev/null +++ b/Makefile @@ -0,0 +1,44 @@ +# Makefile for hdparm + +# DESTDIR is for non root installs (eg packages, NFS) only! +DESTDIR = + +binprefix = +manprefix = /usr +exec_prefix = $(binprefix)/ +sbindir = $(exec_prefix)sbin +mandir = $(manprefix)/share/man +oldmandir = $(manprefix)/man + +ifndef CC +CC = gcc +endif +CFLAGS := -O2 -W -Wall -Wbad-function-cast -Wcast-align -Wpointer-arith -Wcast-qual -Wshadow -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -fkeep-inline-functions -Wwrite-strings -Waggregate-return -Wnested-externs -Wtrigraphs $(CFLAGS) + + +LDFLAGS = -s +INSTALL = install +INSTALL_DATA = $(INSTALL) -m 644 +INSTALL_DIR = $(INSTALL) -m 755 -d +INSTALL_PROGRAM = $(INSTALL) + +all: hdparm + +hdparm: hdparm.o identify.o hdparm.h + $(CC) $(LDFLAGS) -o hdparm hdparm.o identify.o + +install: all hdparm.8 + if [ ! -z $(DESTDIR) ]; then $(INSTALL_DIR) $(DESTDIR) ; fi + if [ ! -z $(DESTDIR)$(sbindir) ]; then $(INSTALL_DIR) $(DESTDIR)$(sbindir) ; fi + if [ ! -z $(DESTDIR)$(mandir) ]; then $(INSTALL_DIR) $(DESTDIR)$(mandir) ; fi + if [ ! -z $(DESTDIR)$(mandir)/man8/ ]; then $(INSTALL_DIR) $(DESTDIR)$(mandir)/man8/ ; fi + if [ -f $(DESTDIR)$(sbindir)/hdparm ]; then rm -f $(DESTDIR)$(sbindir)/hdparm ; fi + if [ -f $(DESTDIR)$(mandir)/man8/hdparm.8 ]; then rm -f $(DESTDIR)$(mandir)/man8/hdparm.8 ;\ + elif [ -f $(DESTDIR)$(oldmandir)/man8/hdparm.8 ]; then rm -f $(DESTDIR)$(oldmandir)/man8/hdparm.8 ; fi + $(INSTALL_PROGRAM) -D hdparm $(DESTDIR)$(sbindir)/hdparm + if [ -d $(DESTDIR)$(mandir) ]; then $(INSTALL_DATA) -D hdparm.8 $(DESTDIR)$(mandir)/man8/hdparm.8 ;\ + elif [ -d $(DESTDIR)$(oldmandir) ]; then $(INSTALL_DATA) -D hdparm.8 $(DESTDIR)$(oldmandir)/man8/hdparm.8 ; fi + +clean: + rm -f hdparm *.o core + diff --git a/Makefile.cygwin b/Makefile.cygwin new file mode 100644 index 0000000..14c9789 --- /dev/null +++ b/Makefile.cygwin @@ -0,0 +1,48 @@ +# Makefile for hdparm for Cygwin + +DESTDIR = + +binprefix = /usr +manprefix = /usr +exec_prefix = $(binprefix)/ +sbindir = $(exec_prefix)sbin +mandir = $(manprefix)/share/man +oldmandir = $(manprefix)/man + +CC = gcc +CFLAGS = -O2 -W -Wall + +LDFLAGS = -s +INSTALL = install +INSTALL_DATA = $(INSTALL) -m 644 +INSTALL_DIR = $(INSTALL) -m 755 -d +INSTALL_PROGRAM = $(INSTALL) + +all: hdparm + +hdparm.o: hdparm.h fs.h hdreg.h rawio.h shm.h version.h + +identify.o: hdparm.h + +rawio.o: rawio.h fs.h hdreg.h + +shm.o: shm.h + +hdparm: hdparm.o identify.o rawio.o shm.o + $(CC) $(LDFLAGS) -o $@ hdparm.o identify.o rawio.o shm.o + +install: all hdparm.8 + if [ ! -z $(DESTDIR) ]; then $(INSTALL_DIR) $(DESTDIR) ; fi + if [ ! -z $(DESTDIR)$(sbindir) ]; then $(INSTALL_DIR) $(DESTDIR)$(sbindir) ; fi + if [ ! -z $(DESTDIR)$(mandir) ]; then $(INSTALL_DIR) $(DESTDIR)$(mandir) ; fi + if [ ! -z $(DESTDIR)$(mandir)/man8/ ]; then $(INSTALL_DIR) $(DESTDIR)$(mandir)/man8/ ; fi + if [ -f $(DESTDIR)$(sbindir)/hdparm ]; then rm -f $(DESTDIR)$(sbindir)/hdparm ; fi + if [ -f $(DESTDIR)$(mandir)/man8/hdparm.8 ]; then rm -f $(DESTDIR)$(mandir)/man8/hdparm.8 ;\ + elif [ -f $(DESTDIR)$(oldmandir)/man8/hdparm.8 ]; then rm -f $(DESTDIR)$(oldmandir)/man8/hdparm.8 ; fi + $(INSTALL_PROGRAM) -D hdparm $(DESTDIR)$(sbindir)/hdparm + if [ -d $(DESTDIR)$(mandir) ]; then $(INSTALL_DATA) -D hdparm.8 $(DESTDIR)$(mandir)/man8/hdparm.8 ;\ + elif [ -d $(DESTDIR)$(oldmandir) ]; then $(INSTALL_DATA) -D hdparm.8 $(DESTDIR)$(oldmandir)/man8/hdparm.8 ; fi + +clean: + rm -f hdparm.exe *.o + diff --git a/Makefile.mingw32 b/Makefile.mingw32 new file mode 100644 index 0000000..ae0c1d7 --- /dev/null +++ b/Makefile.mingw32 @@ -0,0 +1,39 @@ +# Mingw-w64 win32 Makefile + +CC = i686-w64-mingw32-gcc +CFLAGS = -O2 -Wall -Wextra -ffunction-sections -fdata-sections -static +LDFLAGS = -s -Wl,--gc-sections + + +all: hdparm.exe + +hdparm.exe: hdparm.o + $(CC) -o hdparm.exe hdparm.o identify.o rawio.o shm.o timer.o $(LDFLAGS) + +hdparm.o: identify.o rawio.o shm.o timer.o + $(CC) $(CFLAGS) -o hdparm.o -c hdparm.c + +identify.o: + $(CC) $(CFLAGS) -o identify.o -c identify.c + +rawio.o: + $(CC) $(CFLAGS) -o rawio.o -c ./win32/rawio.c + +shm.o: + $(CC) $(CFLAGS) -o shm.o -c ./win32/shm.c + +timer.o: + $(CC) $(CFLAGS) -o timer.o -c ./win32/timer.c + + +doc: hdparm.8.html hdparm.8.txt + +hdparm.8.html: hdparm.8 + man2html hdparm.8 | sed 1d > hdparm.8.html + +hdparm.8.txt: hdparm.8 + groff -man -Tascii -P'-bcou' < hdparm.8 > hdparm.8.txt + + +clean: + rm -f hdparm.exe *.o hdparm.8.html hdparm.8.txt diff --git a/README.acoustic b/README.acoustic new file mode 100644 index 0000000..ef22cbe --- /dev/null +++ b/README.acoustic @@ -0,0 +1,54 @@ +Automatic Acoustic Management +============================= + +BIG BIG WARNING: THIS FEATURE IS EXPERIMENTAL AND NOT WELL TESTED. USE AT YOUR OWN RISK!! +I HAVE ONLY TESTED IT WITH MY OWN HARDDRIVES AND HAD NO PROBLEMS USING IT. BUT AS I AM +NO IDE EXPERT AND THIS IS ONLY BASED ON WHAT I HAVE READ IN THE KERNEL SOURCES I GIVE +NO GUARANTEE FOR ANYTHING!! + +Most modern harddisk drives have the ability to speed down the head movements +to reduce their noise output. The possible values are between 0 and 254. 128 +is the most quiet (and therefore slowest) setting and 254 the fastest (and loudest). +Some drives have only two levels (quiet / fast), while others may have different +levels realized between 128 and 254. + +To be able to use this with hdparm, you will need a current kernel with the -ac patches +applied. Maybe the kernel of your favorite distribution will already include this (SuSE +has it in their current kernel, for example). +Just try to compile hdparm, type "hdparm" and have a look for "-M" in the output. If it +doesn't appear, your kernel most likely doesn't support it. + +You can get the acoustic setting by typing + +hdparm -M /dev/hda + +To set the most quiet mode use + +hdparm -M 128 /dev/hda + +For the fastest setting use + +hdparm -M 254 /dev/hda + +Now test different values and try to hear the difference. :-) + +Not all disk drives support this setting - and the speed impacts may also vary between +different manufacturers and models. + +If you see lines like + +kernel: hdb: task_no_data_intr: status=0x51 { DriveReady SeekComplete Error } +kernel: hdb: task_no_data_intr: error=0x04 { DriveStatusError } + +in your syslog, then your harddisk will most likely not support Acoustic Management. + +Perhaps you also wanna have a look on the IBM Feature Tool, available at +http://www.storage.ibm.com/hdd/support/download.htm. + +I would welcome any feedback to + + +Gernot + + + diff --git a/README.md b/README.md new file mode 100644 index 0000000..6fa5f59 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +### HDPARM for Windows diff --git a/contrib/README b/contrib/README new file mode 100644 index 0000000..9011248 --- /dev/null +++ b/contrib/README @@ -0,0 +1,19 @@ +These files were contributed by various users. + +===================================================================== + +Dear Mark, + +attached I send you a small patch for hdparm-3.5 to make use of the +ioctls HDIO_UNREGISTER_HWIF and HDIO_SCAN_HWIF found in newer kernels. + +With this patched version I can make my IBM ThinkPad 600E recognize +'hot-swapped' ide-devices in its UltraBay II - slot. Together with +tpctl-0.7.0 and the two scripts 'idectl' and 'ultrabayd' it works +really nice. + +Christian Lademann + +Attached: idectl, ultrabayd + +===================================================================== diff --git a/contrib/idectl b/contrib/idectl new file mode 100644 index 0000000..c6f0c3f --- /dev/null +++ b/contrib/idectl @@ -0,0 +1,45 @@ +#!/bin/sh + +HDPARM=/sbin/hdparm +MAX_IDE_NR=1 + +IDE_IO_0=0x1f0 +IDE_IO_1=0x170 + +USE_IDE_DEV_0=/dev/hda +USE_IDE_DEV_1=/dev/hdc + +usage () { + if [ $# -gt 0 ]; then + echo $* >&2 + echo + fi + + echo "usage: $0 ide-channel-nr [off|on|rescan]" 2>&1 + exit 1 +} + +IDE_NR=$1 +MODE=$2 + +do_register=0 +do_unregister=0 + + +if [ ! "$IDE_NR" ] || [ $IDE_NR -lt 0 ] || [ $IDE_NR -gt $MAX_IDE_NR ]; then + usage "Unrecognized IDE-channel number" +fi + +case "$MODE" in +on ) do_register=1 ;; +off ) do_unregister=1 ;; +rescan ) do_unregister=1; do_register=1 ;; +* ) usage "Unrecognized command" ;; +esac + +eval "IDE_IO=\$IDE_IO_$IDE_NR" +eval "USE_IDE_DEV=\$USE_IDE_DEV_$IDE_NR" + +[ $do_unregister -eq 1 ] && eval "$HDPARM -U $IDE_NR $USE_IDE_DEV > /dev/null" +[ $do_register -eq 1 ] && eval "$HDPARM -R $IDE_IO 0 0 $USE_IDE_DEV > /dev/null" + diff --git a/contrib/ultrabayd b/contrib/ultrabayd new file mode 100644 index 0000000..136ce94 --- /dev/null +++ b/contrib/ultrabayd @@ -0,0 +1,68 @@ +#!/bin/sh + +VERSION="0.1.000" + +TPCTL="/usr/bin/tpctl" +IDECTL="/sbin/idectl" + +INTERVAL=5 + +case "$1" in +-V ) + echo "$0: Version $VERSION" + exit 0 +;; + +-daemon ) + o_d_type="" + o_d_id="" + + while true; do + d_type="" + d_type_n="" + d_id="" + d_id_n="" + + while read w1 w2 w3 wn; do + case "$w1" in + device ) + case "$w2" in + type: ) d_type=$w3; d_type_n="$wn";; + ID: ) d_id=$w3; d_id_n="$wn";; + esac + ;; + esac + + if [ "$d_type" ] && [ "$d_id" ]; then + break; + fi + done <<- END + `eval $TPCTL -iU` + END + + if [ "$d_type" != "$o_d_type" ] || [ "$d_id" != "$o_d_id" ]; then + echo -n "\07" > /dev/tty1 + + o_d_type="$d_type" + o_d_id="$d_id" + + case "$d_type" in + 0x10 ) + eval "$IDECTL 1 rescan" + ;; + + * ) + eval "$IDECTL 1 off" + ;; + esac + fi + + sleep $INTERVAL + done +;; + +* ) + exec nohup $0 -daemon 2>&1 > /dev/null & +;; +esac + diff --git a/debian/hdparm.conf b/debian/hdparm.conf new file mode 100644 index 0000000..8ae62df --- /dev/null +++ b/debian/hdparm.conf @@ -0,0 +1,123 @@ +## This is the default configuration for hdparm for Debian. It is a +## rather simple script, so please follow the following guidelines :) +## Any line that begins with a comment is ignored - add as many as you +## like. Note that an in-line comment is not supported. If a line +## consists of whitespace only (tabs, spaces, carriage return), it will be +## ignored, so you can space control fields as you like. ANYTHING ELSE +## IS PARSED!! This means that lines with stray characters or lines that +## use non # comment characters will be interpreted by the initscript. +## This has probably minor, but potentially serious, side effects for your +## hard drives, so please follow the guidelines. Patches to improve +## flexibilty welcome. Please read /usr/share/doc/hdparm/README.Debian for +## notes about known issues, especially if you have an MD array. +## +## Note that if the init script causes boot problems, you can pass 'nohdparm' +## on the kernel command line, and the script will not be run. +## +## Uncommenting the options below will cause them to be added to the DEFAULT +## string which is prepended to options listed in the blocks below. +## +## If an option is listed twice, the second instance replaces the first. +## +## /sbin/hdparm is not run unless a block of the form: +## DEV { +## option +## option +## ... +## } +## exists. This blocks will cause /sbin/hdparm OPTIONS DEV to be run. +## Where OPTIONS is the concatenation of all options previously defined +## outside of a block and all options defined with in the block. + +# -q be quiet +quiet +# -a sector count for filesystem read-ahead +#read_ahead_sect = 12 +# -A disable/enable the IDE drive's read-lookahead feature +#lookahead = on +# -b bus state +#bus = on +# -B apm setting +#apm = 255 +# -c enable (E)IDE 32-bit I/O support - can be any of 0,1,3 +#io32_support = 1 +# -d disable/enable the "using_dma" flag for this drive +#dma = off +# -D enable/disable the on-drive defect management +#defect_mana = off +# -E cdrom speed +#cd_speed = 16 +# -k disable/enable the "keep_settings_over_reset" flag for this drive +#keep_settings_over_reset = off +# -K disable/enable the drive's "keep_features_over_reset" flag +#keep_features_over_reset = on +# -m sector count for multiple sector I/O +#mult_sect_io = 32 +# -P maximum sector count for the drive's internal prefetch mechanism +#prefetch_sect = 12 +# -r read-only flag for device +#read_only = off +# -S standby (spindown) timeout for the drive +#spindown_time = 24 +# -u interrupt-unmask flag for the drive +#interrupt_unmask = on +# -W Disable/enable the IDE drive's write-caching feature +#write_cache = off +# -X IDE transfer mode for newer (E)IDE/ATA2 drives +#transfer_mode = 34 +# -y force to immediately enter the standby mode +#standby +# -Y force to immediately enter the sleep mode +#sleep +# -Z Disable the power-saving function of certain Seagate drives +#disable_seagate +# -M Set the acoustic management properties of a drive +#acoustic_management +# -p Set the chipset PIO mode +# chipset_pio_mode + +# Root file systems. Please see README.Debian for details +# ROOTFS = /dev/hda + +## New note - you can use straight hdparm commands in this config file +## as well - the set up is ugly, but it keeps backwards compatibility +## Additionally, it should be noted that any blocks that begin with +## the keyword 'command_line' are not run until after the root filesystem +## is mounted. This is done to avoid running blocks twice. If you need +## to run hdparm to set parameters for your root disk, please use the +## standard format. + +#Samples follow: +#First three are good for devfs systems, fourth one for systems that do +#not use devfs. The fifth example uses straight hdparm command line +#syntax. Any of the blocks that use command line syntax must begin with +#the keyword 'command_line', and no attempt is made to validate syntax. +#It is provided for those more comfortable with hdparm syntax. + +#/dev/discs/disc0/disc { +# mult_sect_io = 16 +# write_cache = off +# spindown_time = 240 +#} + +#/dev/discs/disc1/disc { +# mult_sect_io = 32 +# spindown_time = 36 +# write_cache = off +#} + +#/dev/cdroms/cdrom0 { +# dma = on +# interrupt_unmask = on +# io32_support = 0 +#} + +#/dev/hda { +# mult_sect_io = 16 +# write_cache = off +# dma = on +#} + +#command_line { +# hdparm -q -m16 -q -W0 -q -d1 /dev/hda +#} diff --git a/debian/hdparm.init b/debian/hdparm.init new file mode 100644 index 0000000..79fe4fb --- /dev/null +++ b/debian/hdparm.init @@ -0,0 +1,277 @@ +#!/bin/sh + +set -e + +. /lib/lsb/init-functions +. /etc/default/rcS + +#In certian cases you may wish to run this script twice. Once at S07 +#and once later in the boot process. If you do this call /etc/init.d/hdparm +#again from rcS.d with a name such as S27hdparm.second. +# +#See /usr/share/doc/hdparm/README.Debian for more details. + +case "$0" in + *hdparm) + FIRST=yes + ;; + *) + FIRST=no + ;; +esac + +MYNAME="$0" + +report() +{ + echo "${MYNAME}: $*" +} + +report_error() +{ + echo "${MYNAME}: Error: $*" >&2 +} + +report_error_and_exit() +{ + report_error "$*. Exiting." + exit 1 +} + +case $1 in + start|restart|reload|force-reload) + ;; + stop) + exit 0 + ;; + *) + log_success_msg "Usage: $0 {stop|start|restart|reload|force-reload}" + exit 1 + ;; +esac + +if grep -w -q "nohdparm" /proc/cmdline ; then + log_success_msg "Skipping setup of disc parameters as specified..." + exit 0 +fi + +raidstat=OK +if [ -e /proc/mdstat ]; then + if grep -iq resync /proc/mdstat; then + raidstat=RESYNC + fi +elif [ -e /proc/rd/status ]; then + raidstat=`cat /proc/rd/status` +fi + +if ! [ "$raidstat" = 'OK' ]; then + log_warning_msg "RAID status not OK. Exiting." + exit 0 +fi + +log_begin_msg "Setting disc parameters..." + +DISC= +DEFAULT= +OPTIONS= +DEF_QUIET= +OPT_QUIET= + +# set_option() adds $1 to the $OPTIONS list if in a disk stanza +# and adds $1 to the $DEFAULT list if not in a disk stanza +# +# the block beginning: +# if test x${i%${i#??}} != x${1%${1#??}}; then +# checks to see if $1 is already in the list and +# if so removes the first instance + +set_option() +{ + if test -n "$DISC"; then + NEW_OPT= + for i in $OPTIONS; do + if test x${i%${i#??}} != x${1%${1#??}}; then + NEW_OPT="$NEW_OPT $i" + else + NEW_OPT=${NEW_OPT%-q} + fi + done + OPTIONS="$NEW_OPT $OPT_QUIET $1" + else + NEW_DEF= + for i in $DEFAULT; do + if test x${i%${i#??}} != x${1%${1#??}}; then + NEW_DEF="$NEW_DEF $i" + else + NEW_DEF=${NEW_DEF%-q} + fi + done + DEFAULT="$NEW_DEF $DEF_QUIET $1" + fi +} + +eval_value() +{ + case $1 in + off|0) + set_option "$2"0 + ;; + on|1) + set_option "$2"1 + ;; + *) + return 1 + ;; + esac +} + +# Get blocks as far as the drive's write cache. +/bin/sync + +# Set options for a group of disks in /etc/default/hdparm +[ -e /etc/default/hdparm ] && . /etc/default/hdparm + +if [ -n "$harddisks" -a -n "$hdparm_opts" ]; then + for drive in $harddisks; do + /sbin/hdparm -q -f $drive + hdparm -q $hdparm_opts -q $drive + [ "$VERBOSE" != no ] && log_success_msg "Found enabled disk: $drive" + done +fi + +egrep -v '^[[:space:]]*(#|$)' /etc/hdparm.conf | while read KEY SEP VALUE; do + if [ "$NEXT_LINE" != 'go' ]; then + case $SEP in + '{') + case $KEY in + command_line) + NEXT_LINE=go + unset DISC + unset OPTIONS + unset OPT_QUIET + ;; + *) + DISC=$KEY + OPTIONS=$DEFAULT + OPT_QUIET=$DEF_QUIET + WAS_RUN=0 + ;; + esac + ;; + =) + case $KEY in + read_ahead_sect) + set_option -a$VALUE + ;; + lookahead) + eval_value $VALUE -A + ;; + bus) + eval_value $VALUE -b + ;; + apm) + set_option -B$VALUE + ;; + io32_support) + set_option -c$VALUE + ;; + dma) + eval_value $VALUE -d + ;; + defect_mana) + eval_value $VALUE -D + ;; + cd_speed) + set_option -E$VALUE + ;; + mult_sect_io) + set_option -m$VALUE + ;; + prefetch_sect) + set_option -P$VALUE + ;; + read_only) + eval_value $VALUE -r + ;; + spindown_time) + set_option -S$VALUE + ;; + interrupt_unmask) + eval_value $VALUE -u + ;; + write_cache) + eval_value $VALUE -W + ;; + transfer_mode) + set_option -X$VALUE + ;; + acoustic_management) + set_option -M$VALUE + ;; + keep_settings_over_reset) + eval_value $VALUE -k + ;; + keep_features_over_reset) + eval_value $VALUE -K + ;; + chipset_pio_mode) + set_option -p$VALUE + ;; + *) + log_failure_msg "Unknown option $KEY!" + exit 1 + ;; + esac + ;; + "") + case $KEY in + }) + if [ -z "$DISC" ]; then + if [ "$WAS_RUN" != 1 ]; then + log_failure_msg "No disk enabled. Exiting..." + exit 1 + fi + fi + if [ -n "$OPTIONS" ]; then + # Flush the drive's internal write cache to the disk. + /sbin/hdparm -q -f $DISC + + /sbin/hdparm $OPTIONS $DISC + [ "$VERBOSE" != no ] && log_success_msg "Found enabled disk: $DISC" + fi + ;; + quiet) + if [ -n "$DISC" ]; then + OPT_QUIET=-q + else + DEF_QUIET=-q + fi + ;; + standby) + set_option -y + ;; + sleep) + set_option -Y + ;; + disable_seagate) + set_option -Z + ;; + *) + log_failure_msg "Unknown option $KEY!" + exit 1 + ;; + esac + ;; + *) + log_failure_msg "Unknown separator $SEP!" + exit 1 + ;; + esac +else + $KEY $SEP $VALUE + NEXT_LINE=no-go + WAS_RUN=1 +fi +done + +log_end_msg 0 diff --git a/hdparm-sysconfig b/hdparm-sysconfig new file mode 100644 index 0000000..5a3e78d --- /dev/null +++ b/hdparm-sysconfig @@ -0,0 +1,37 @@ +# This file was adapted from Mandrake Linux 8.1 +# +# These options are used to tune the hard drives - +# read the hdparm man page for more information + +# Set this to 1 to enable DMA. This might cause some +# data corruption on certain chipset / hard drive +# combinations. This is used with the "-d" option + +# USE_DMA=1 + +# Multiple sector I/O. a feature of most modern IDE hard drives, +# permitting the transfer of multiple sectors per I/O interrupt, +# rather than the usual one sector per interrupt. When this feature +# is enabled, it typically reduces operating system overhead for disk +# I/O by 30-50%. On many systems, it also provides increased non-DMA +# data throughput of anywhere from 5% to 50%. Some drives, however +# (most notably the older WD Caviar series), seem to run slower with +# multiple mode enabled. Under rare circumstances, such failures can +# result in # massive filesystem corruption. USE WITH CAUTION AND BACKUP. +# This is the sector count for multiple sector I/O - the "-m" option +# +# MULTIPLE_IO=8 + +# (E)IDE 32-bit I/O support (to interface card) +# +# EIDE_32BIT=1 + +# Enable drive read-lookahead +# +# LOOKAHEAD=1 + +# Add extra parameters here if wanted +# Other flags you might want to experiment with are -u1, -A1 and -W1 +# See the hdparm manpage (man hdparm) for details and more options. +# +EXTRA_PARAMS= diff --git a/hdparm.8 b/hdparm.8 new file mode 100644 index 0000000..9d870b1 --- /dev/null +++ b/hdparm.8 @@ -0,0 +1,594 @@ +.TH HDPARM 8 "October 2006" "Version 6.9" + +.SH NAME +hdparm \- get/set hard disk parameters +.SH SYNOPSIS +.B hdparm +[ flags ] [device] .. +.SH DESCRIPTION +.BI hdparm +provides a command line interface to various hard disk ioctls +supported by the stock Linux ATA/IDE device driver subsystem. +Some options may work correctly only with the latest kernels. +For best results, compile hdparm with the include files from the latest kernel source code. +.SH OPTIONS +When no flags are given, +.I -acdgkmnru +is assumed. +.TP +.I -a +Get/set sector count for filesystem (software) read-ahead. +This is used to improve performance in sequential reads of large files, +by prefetching additional +blocks in anticipation of them being needed by the running task. +Many IDE drives also have a separate built-in read-ahead function, +which augments this filesystem (software) read-ahead function. +.TP +.I -A +Disable/enable the IDE drive\'s read-lookahead feature (usually ON by default). +Usage: +.B -A0 +(disable) or +.B -A1 +(enable). +.TP +.I -b +Get/set bus state. +.TP +.I -B +Set Advanced Power Management feature, if the drive supports it. A low value +means aggressive power management and a high value means better performance. A value of 255 will disable apm on the drive. +.TP +.I -c +Query/enable (E)IDE 32-bit I/O support. A numeric parameter can be +used to enable/disable 32-bit I/O support: +Currently supported values include +.I 0 +to disable 32-bit I/O support, +.I 1 +to enable 32-bit data transfers, and +.I 3 +to enable 32-bit data transfers with a special +.I sync +sequence required by many chipsets. The value +.I 3 +works with nearly all +32-bit IDE chipsets, but incurs slightly more overhead. +Note that "32-bit" refers to data transfers across a PCI or VLB bus to the +interface card only; all (E)IDE drives still have only a 16-bit connection +over the ribbon cable from the interface card. +.TP +.I -C +Check the current IDE power mode status, which will always be one of +.B unknown +(drive does not support this command), +.B active/idle +(normal operation), +.B standby +(low power mode, drive has spun down), +or +.B sleeping +(lowest power mode, drive is completely shut down). +The +.B -S, -y, -Y, +and +.B -Z +flags can be used to manipulate the IDE power modes. +.TP +.I -d +Disable/enable the "using_dma" flag for this drive. This option now works +with most combinations of drives and PCI interfaces which support DMA +and which are known to the kernel IDE driver. +It is also a good idea to use the appropriate +.I -X +option in combination with +.I -d1 +to ensure that the drive itself is programmed for the correct DMA mode, +although most BIOSs should do this for you at boot time. +Using DMA nearly always gives the best performance, +with fast I/O throughput and low CPU usage. +But there are at least a few configurations of chipsets and drives +for which DMA does not make much of a difference, or may even slow +things down (on really messed up hardware!). Your mileage may vary. +.TP +.I --direct +Use the kernel O_DIRECT flag when performing a +.I -t +timing test. This bypasses the page cache, causing the reads +to go directly from the drive into hdparm's buffers, using so-called +"raw" I/O. In many cases, this can produce results that appear +much faster than the usual page cache method, giving a better indication +of raw device and driver performance. +.TP +.I -D +Enable/disable the on-drive defect management feature, +whereby the drive firmware tries to automatically manage +defective sectors by relocating them to "spare" sectors +reserved by the factory for such. +.TP +.I -E +Set cdrom speed. This is NOT necessary for regular operation, +as the drive will automatically switch speeds on its own. +But if you want to play with it, just supply a speed number +after the option, usually a number like 2 or 4. +.TP +.I -f +Sync and flush the buffer cache for the device on exit. +This operation is also performed as part of the +.I -t +and +.I -T +timings. +.TP +.I -g +Display the drive geometry (cylinders, heads, sectors), +the size (in sectors) of the device, +and the starting offset (in sectors) of the device from +the beginning of the drive. +.TP +.I -h +Display terse usage information (help). +.TP +.I -i +Display the identification info that was obtained from the drive at +.I boot time, +if available. +This is a feature of modern IDE drives, +and may not be supported by older devices. +The data returned may or may not be current, depending on activity +since booting the system. +However, the current multiple sector mode count is always shown. +For a more detailed interpretation of the identification info, +refer to +.I AT Attachment Interface for Disk Drives +(ANSI ASC X3T9.2 working draft, revision 4a, April 19/93). +.TP +.I -I +Request identification info directly from the drive, +which is displayed in a new expanded format with considerably +more detail than with the older +.I -i +flag. +.TP +.I --Istdin +This is a special "no seatbelts" variation on the +.B -I +option, +which accepts a drive identification block as standard input +instead of using a /dev/hd* parameter. +The format of this block must be +.I exactly +the same as that found in the /proc/ide/*/hd*/identify "files", +or that produced by the +.B --Istdout +option described below. +This variation is designed for use with collected "libraries" of drive +identification information, and can also be used on ATAPI +drives which may give media errors with the standard mechanism. +.TP +.I --Istdout +This option simply dumps the identify data in hex to stdout, +in a format similar to that from /proc/, and suitable for +later use with the +.I --Istdin +option. +.TP +.I -k +Get/set the keep_settings_over_reset flag for the drive. +When this flag is set, the driver will preserve the +.I -dmu +options over a soft reset, (as done during the error recovery sequence). +This flag defaults to off, +to prevent drive reset loops which could be caused by combinations of +.I -dmu +settings. The +.I -k +flag should therefore only be set after one has achieved confidence in +correct system operation with a chosen set of configuration settings. +In practice, all that is typically necessary to test a configuration +(prior to using -k) is to verify that the drive can be read/written, +and that no error logs (kernel messages) are generated in the process +(look in /var/adm/messages on most systems). +.TP +.I -K +Set the drive\'s keep_features_over_reset flag. Setting this enables +the drive to retain the settings for +.I -APSWXZ +over a soft reset (as done during the error recovery sequence). +Not all drives support this feature. +.TP +.I -L +Set the drive\'s doorlock flag. Setting this to +.B 1 +will lock the door mechanism of some removable hard drives +(eg. Syquest, ZIP, Jazz..), and setting it to +.B 0 +will unlock the door mechanism. Normally, Linux +maintains the door locking mechanism automatically, depending on drive usage +(locked whenever a filesystem is mounted). But on system shutdown, this can +be a nuisance if the root partition is on a removeable disk, since the root +partition is left mounted (read-only) after shutdown. So, by using this +command to unlock the door +.B after +the root filesystem is remounted read-only, one can then remove the cartridge +from the drive after shutdown. +.TP +.I -m +Get/set sector count for multiple sector I/O on the drive. A setting of +.B 0 +disables this feature. Multiple sector mode (aka IDE Block Mode), is a feature +of most modern IDE hard drives, permitting the transfer of multiple sectors per +I/O interrupt, rather than the usual one sector per interrupt. When this +feature is enabled, it typically reduces operating system overhead for disk +I/O by 30-50%. On many systems, it also provides increased data throughput +of anywhere from 5% to 50%. Some drives, however +(most notably the WD Caviar series), +seem to run slower with multiple mode enabled. Your mileage may vary. +Most drives support the minimum settings of +2, 4, 8, or 16 (sectors). Larger settings may also be possible, depending on +the drive. A setting of 16 or 32 seems optimal on many systems. +Western Digital recommends lower settings of 4 to 8 on many of their drives, +due tiny (32kB) drive buffers and non-optimized buffering algorithms. +The +.B -i +flag can be used to find the maximum setting supported by an installed drive +(look for MaxMultSect in the output). +Some drives claim to support multiple mode, but lose data at some settings. +Under rare circumstances, such failures can result in +.B massive filesystem corruption. +.TP +.I -M +Get/set Automatic Acoustic Management (AAM) setting. Most modern harddisk drives +have the ability to speed down the head movements to reduce their noise output. +The possible values are between 0 and 254. 128 is the most quiet (and therefore +slowest) setting and 254 the fastest (and loudest). Some drives have only two +levels (quiet / fast), while others may have different levels between 128 and 254. +At the moment, most drives only support 3 options, off, quiet, and fast. +These have been assigned the values 0, 128, and 254 at present, respectively, +but integer space has been incorporated for future expansion, should this change. +.TP +.I -n +Get or set the "ignore write errors" flag in the driver. +Do NOT play with this without grokking the driver source code first. +.TP +.I -p +Attempt to reprogram the IDE interface chipset for the specified PIO mode, +or attempt to auto-tune for the "best" PIO mode supported by the drive. +This feature is supported in the kernel for only a few "known" chipsets, +and even then the support is iffy at best. Some IDE chipsets are unable +to alter the PIO mode for a single drive, in which case this flag may cause +the PIO mode for +.I both +drives to be set. Many IDE chipsets support either fewer or more than the +standard six (0 to 5) PIO modes, so the exact speed setting that is actually +implemented will vary by chipset/driver sophistication. +.I Use with extreme caution! +This feature includes zero protection for the unwary, +and an unsuccessful outcome may result in +.I severe filesystem corruption! +.TP +.I -P +Set the maximum sector count for the drive\'s internal prefetch mechanism. +Not all drives support this feature. +.TP +.I -q +Handle the next flag quietly, suppressing normal output. This is useful +for reducing screen clutter when running from system startup scripts. +Not applicable to the +.I -i +or +.I -v +or +.I -t +or +.I -T +flags. +.TP +.I -Q +Set tagged queue depth (1 or greater), or turn tagged queuing off (0). +This only works with the newer 2.5.xx (or later) kernels, and only with +the few drives that currently support it. +.TP +.I -r +Get/set read-only flag for the device. When set, Linux disallows write operations on the device. +.TP +.I -R +Register an IDE interface. +.B Dangerous. +See the +.B -U +option for more information. +.TP +.I -s +Enable/disable the power-on in standby feature, if supported by +the drive. If enabled, the drive is powered-up in the +.B standby +mode to allow the controller to sequence the spin-up of devices. +This feature is usually disabled and the drive is powered-up in the +.B active +mode (see -C above). +Note that a drive may also allow to enable this feature by a jumper. +Some SATA drives support the control of this feature by pin 11 of +the SATA power connector. In these cases, this command may be +unsupported or may have no effect. +.TP +.I -S +Set the standby (spindown) timeout for the drive. This value is used +by the drive to determine how long to wait (with no disk activity) +before turning off the spindle motor to save power. Under such +circumstances, the drive may take as long as 30 seconds to respond to +a subsequent disk access, though most drives are much quicker. The +encoding of the timeout value is somewhat peculiar. A value of zero +means "timeouts are disabled": the device will not automatically enter +standby mode. Values from 1 to 240 specify multiples of 5 seconds, +yielding timeouts from 5 seconds to 20 minutes. Values from 241 to +251 specify from 1 to 11 units of 30 minutes, yielding timeouts from +30 minutes to 5.5 hours. A value of 252 signifies a timeout of 21 +minutes. A value of 253 sets a vendor-defined timeout period between 8 +and 12 hours, and the value 254 is reserved. 255 is interpreted as 21 +minutes plus 15 seconds. Note that some older drives may have very +different interpretations of these values. +.TP +.I -T +Perform timings of cache reads for benchmark and comparison purposes. +For meaningful results, this operation should be repeated 2-3 times +on an otherwise inactive system (no other active processes) with at +least a couple of megabytes of free memory. This displays the speed +of reading directly from the Linux buffer cache without disk access. +This measurement is essentially an indication of the throughput of the +processor, cache, and memory of the system under test. +The cache read testing time can be specified by an optional numeric +parameter. The default is 2 seconds. +.TP +.I -t +Perform timings of device reads for benchmark and comparison purposes. +For meaningful results, this operation should be repeated 2-3 times on +an otherwise inactive system (no other active processes) with at least a +couple of megabytes of free memory. This displays the speed of reading +through the buffer cache to the disk without any prior caching of data. +This measurement is an indication of how fast the drive can sustain +sequential data reads under Linux, without any filesystem overhead. To +ensure accurate measurements, the buffer cache is flushed during the +processing of +.I -t +using the BLKFLSBUF ioctl. +The maximum read testing time can be specified by an optional numeric +parameter. The default is 3 seconds. +.TP +.I -u +Get/set interrupt-unmask flag for the drive. A setting of +.B 1 +permits the +driver to unmask other interrupts during processing of a disk interrupt, +which greatly improves Linux\'s responsiveness and eliminates "serial port +overrun" errors. +.B Use this feature with caution: +some drive/controller combinations do +not tolerate the increased I/O latencies possible when this feature is enabled, +resulting in +.B massive filesystem corruption. +In particular, +.B CMD-640B +and +.B RZ1000 +(E)IDE interfaces can be +.B unreliable +(due to a hardware flaw) when this option is used with kernel versions earlier +than 2.0.13. Disabling the +.B IDE prefetch +feature of these interfaces (usually a BIOS/CMOS setting) +provides a safe fix for the problem for use with earlier kernels. +.TP +.I -U +Un-register an IDE interface. +.B Dangerous. +The companion for the +.B -R +option. +Intended for use with hardware made specifically for hot-swapping (very rare!). +Use with knowledge and +.B extreme caution +as this can easily hang or damage your system. +The hdparm source distribution includes a \'contrib\' directory with +some user-donated scripts for hot-swapping on the UltraBay of a ThinkPad 600E. +Use at your own risk. +.TP +.I -v +Display all settings, except -i (same as -acdgkmnru for IDE, -gr for SCSI or +-adgr for XT). This is also the default behaviour when no flags are specified. +.TP +.I -w +Perform a device reset (DANGEROUS). Do NOT use this option. +It exists for unlikely situations where a reboot might otherwise be +required to get a confused drive back into a useable state. +.TP +.I -W +Disable/enable the IDE drive\'s write-caching feature +(default state is undeterminable; manufacturer/model specific). +.TP +.I -x +Tristate device for hotswap (DANGEROUS). +.TP +.I -X +Set the IDE transfer mode for newer (E)IDE/ATA drives. +This is typically used in combination with +.I -d1 +when enabling DMA to/from a drive on a supported interface chipset, where +.I -X mdma2 +is used to select multiword DMA mode2 transfers and +.I -X sdma1 +is used to select simple mode 1 DMA transfers. +With systems which support UltraDMA burst timings, +.I -X udma2 +is used to select UltraDMA mode2 transfers (you\'ll need to prepare +the chipset for UltraDMA beforehand). +Apart from that, use of this flag is +.I seldom necessary +since most/all modern IDE drives default to their fastest PIO transfer mode +at power-on. Fiddling with this can be both needless and risky. +On drives which support alternate transfer modes, +.I -X +can be used to switch the mode of the drive +.I only. +Prior to changing the transfer mode, the IDE interface should be jumpered +or programmed (see +.I -p +flag) +for the new mode setting to prevent loss and/or corruption of data. +.I Use this with extreme caution! +For the PIO (Programmed Input/Output) +transfer modes used by Linux, this value is simply the desired +PIO mode number plus 8. +Thus, a value of 09 sets PIO mode1, 10 enables PIO mode2, +and 11 selects PIO mode3. +Setting 00 restores the drive\'s "default" PIO mode, and 01 disables IORDY. +For multiword DMA, the value used is the desired DMA mode number +plus 32. for UltraDMA, the value is the desired UltraDMA mode number +plus 64. +.TP +.I -y +Force an IDE drive to immediately enter the low power consumption +.B standby +mode, usually causing it to spin down. +The current power mode status can be checked using the +.B -C +flag. +.TP +.I -Y +Force an IDE drive to immediately enter the lowest power consumption +.B sleep +mode, causing it to shut down completely. A hard or soft reset +is required before the drive can be accessed again +(the Linux IDE driver will automatically handle issuing a reset if/when needed). +The current power mode status can be checked using the +.B -C +flag. +.TP +.I -z +Force a kernel re-read of the partition table of the specified device(s). +.TP +.I -Z +Disable the automatic power-saving function of certain Seagate drives +(ST3xxx models?), to prevent them from idling/spinning-down +at inconvenient times. +.TP +.I -H +Read the temperature from some (mostly Hitachi) drives. +Also reports if the temperature is within operating condition range +(this may not be reliable). Does not cause the drive to spin up if idle. +.TP +.SH ATA Security Feature Set +.PP +These switches are DANGEROUS to experiment with, and might not work with every +kernel. +.B USE AT YOUR OWN RISK. +.TP +.I --security-help +Display terse usage info for all of the --security-* flags. +.TP +.I --security-freeze +Freeze the drive\'s security settings. +The drive does not accept any security commands until next power-on reset. +Use this function in combination with --security-unlock to protect drive +from any attempt to set a new password. Can be used standalone, too. +.TP +.I --security-unlock PWD +Unlock the drive, using password PWD. +Password is given as an ASCII string and is padded with NULs to reach 32 bytes. +The applicable drive password is selected with the --user-master switch. +.B THIS FEATURE IS EXPERIMENTAL AND NOT WELL TESTED. USE AT YOUR OWN RISK. +.TP +.I --security-set-pass PWD +Lock the drive, using password PWD (Set Password) (DANGEROUS). +Password is given as an ASCII string and is padded with NULs to reach 32 bytes. +The applicable drive password is selected with the --user-master switch and the +applicable security mode with the --security-mode switch. +.B THIS FEATURE IS EXPERIMENTAL AND NOT WELL TESTED. USE AT YOUR OWN RISK. +.TP +.I --security-disable PWD +Disable drive locking, using password PWD. +Password is given as an ASCII string and is padded with NULs to reach 32 bytes. +The applicable drive password is selected with the --user-master switch. +.B THIS FEATURE IS EXPERIMENTAL AND NOT WELL TESTED. USE AT YOUR OWN RISK. +.TP +.I --security-erase PWD +Erase (locked) drive, using password PWD (DANGEROUS). +Password is given as an ASCII string and is padded with NULs to reach 32 bytes. +The applicable drive password is selected with the --user-master switch. +.B THIS FEATURE IS EXPERIMENTAL AND NOT WELL TESTED. USE AT YOUR OWN RISK. +.TP +.I --security-erase-enhanced PWD +Enhanced erase (locked) drive, using password PWD (DANGEROUS). +Password is given as an ASCII string and is padded with NULs to reach 32 bytes. +The applicable drive password is selected with the --user-master switch. +.B THIS FEATURE IS EXPERIMENTAL AND NOT WELL TESTED. USE AT YOUR OWN RISK. +.TP +.I --user-master USER +Specifies which password (user/master) to select. +.B Defaults to master. +Only useful in combination with --security-unlock, --security-set-pass, +--security-disable, --security-erase or --security-erase-enhanced. + u user password + m master password + +.B THIS FEATURE IS EXPERIMENTAL AND NOT WELL TESTED. USE AT YOUR OWN RISK. +.TP +.I --security-mode MODE +Specifies which security mode (high/maximum) to set. +.B Defaults to high. +Only useful in combination with --security-set-pass. + h high security + m maximum security + +.B THIS FEATURE IS EXPERIMENTAL AND NOT WELL TESTED. USE AT YOUR OWN RISK. +.SH BUGS +As noted above, the +.B -m sectcount +and +.B -u 1 +options should be used with caution at first, preferably on a +read-only filesystem. Most drives work well with these features, but +a few drive/controller combinations are not 100% compatible. Filesystem +corruption may result. Backup everything before experimenting! +.PP +Some options (eg. -r for SCSI) may not work with old kernels as +necessary ioctl()\'s were not supported. +.PP +Although this utility is intended primarily for use with (E)IDE hard disk +devices, several of the options are also valid (and permitted) for use with +SCSI hard disk devices and MFM/RLL hard disks with XT interfaces. +.PP +The Linux kernel up until 2.6.12 (and probably later) doesn\'t handle the +security unlock and disable commands gracefully and will segfault and in some +cases even panic. The security commands however might indeed have been executed +by the drive. This poor kernel behaviour makes the PIO data security commands +rather useless at the moment. +.PP +Note that the "security erase" and "security disable" commands have been +implemented as two consecutive PIO data commands and will not succeed on a +locked drive because the second command will not be issued after the segfault. +See the code for hints how patch it to work around this problem. Despite the +segfault it is often still possible to run two instances of hdparm +consecutively and issue the two necessary commands that way. +.SH AUTHOR +.B hdparm +has been written by Mark Lord , the original primary +developer and maintainer of the (E)IDE driver for Linux, +with suggestions from many netfolk. +.PP +The disable Seagate auto-powersaving code +is courtesy of Tomi Leppikangas(tomilepp@paju.oulu.fi). +.PP +Security freeze command by Benjamin Benz , 2005. +.PP +PIO data out security commands by Leonard den Ottolander +, 2005. +Parts by Benjamin Benz and others. +.PP +Windows version by Christian Franke , 2006. +.SH SEE ALSO +.B http://www.t13.org/ +Technical Committee T13 ATA Attachment. +.PP +.B http://www.serialata.org/ +Serial ATA International Organization. diff --git a/hdparm.c b/hdparm.c new file mode 100644 index 0000000..a834915 --- /dev/null +++ b/hdparm.c @@ -0,0 +1,2278 @@ +/* hdparm.c - Command line interface to get/set hard disk parameters */ +/* - by Mark Lord (C) 1994-2005 -- freely distributable */ +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef _WIN32 +#include +#include +#endif +#if !defined(_WIN32) && !defined(__CYGWIN__) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#else +#ifndef __CYGWIN__ +#include "win32/timer.h" +#endif +#include "win32/fs.h" +#include "win32/hdreg.h" +#include "win32/shm.h" +#include "win32/rawio.h" +#include "win32/version.h" +#define __le16_to_cpus(x) ((void)(x)) +#endif + +#include "hdparm.h" + +extern const char *minor_str[]; + +#ifndef VERSION +#define VERSION "v6.9" +#endif + +#undef DO_FLUSHCACHE /* under construction: force cache flush on -W0 */ + +#ifndef O_DIRECT +#define O_DIRECT 040000 /* direct disk access, not easily obtained from headers */ +#endif + +#if !defined(_WIN32) && !defined(__CYGWIN__) +#ifndef CDROM_SELECT_SPEED /* already defined in 2.3.xx kernels and above */ +#define CDROM_SELECT_SPEED 0x5322 +#endif +#endif + +#ifndef BLKGETSIZE64 +#define BLKGETSIZE64 _IOR(0x12,114,size_t) +#endif + +#define TIMING_BUF_MB 2 +#define TIMING_BUF_BYTES (TIMING_BUF_MB * 1024 * 1024) + +char *progname; +static int verbose = 0, noisy = 1, quiet = 0; +static int flagcount = 0, do_flush = 0; +static int do_ctimings, do_timings = 0; +static double ctimings = 0.0, timings = 0.0; + +#ifdef HDIO_GET_IDENTITY +static int get_identity = 0; +#endif +#ifdef HDIO_GETGEO +static int get_geom = 0; +#endif + +#ifdef BLKRASET +static unsigned long set_fsreadahead= 0, get_fsreadahead= 0, fsreadahead= 0; +#endif +#ifdef BLKROSET +static unsigned long set_readonly = 0, get_readonly = 0, readonly = 0; +#endif +#ifdef HDIO_SET_UNMASKINTR +static unsigned long set_unmask = 0, get_unmask = 0, unmask = 0; +#endif +#ifdef HDIO_SET_MULTCOUNT +static unsigned long set_mult = 0, get_mult = 0, mult = 0; +#endif +#ifdef HDIO_SET_DMA +static unsigned long set_dma = 0, get_dma = 0, dma = 0; +#endif +#ifdef HDIO_GET_QDMA +static unsigned long set_dma_q = 0, get_dma_q = 0, dma_q = 0; +#endif +#ifdef HDIO_SET_NOWERR +static unsigned long set_nowerr = 0, get_nowerr = 0, nowerr = 0; +#endif +#ifdef HDIO_GET_KEEPSETTINGS +static unsigned long set_keep = 0, get_keep = 0, keep = 0; +#endif +#ifdef HDIO_SET_32BIT +static unsigned long set_io32bit = 0, get_io32bit = 0, io32bit = 0; +#endif +#ifdef HDIO_SET_PIO_MODE +static unsigned long set_piomode = 0, noisy_piomode= 0; +int piomode = 0; +#endif +#ifdef HDIO_DRIVE_CMD +static unsigned long set_dkeep = 0, get_dkeep = 0, dkeep = 0; +static unsigned long set_standby = 0, get_standby = 0, standby_requested= 0; +static unsigned long set_xfermode = 0, get_xfermode = 0; +static int xfermode_requested= 0; +static unsigned long set_lookahead= 0, get_lookahead= 0, lookahead= 0; +static unsigned long set_prefetch = 0, get_prefetch = 0, prefetch = 0; +static unsigned long set_defects = 0, get_defects = 0, defects = 0; +static unsigned long set_wcache = 0, get_wcache = 0, wcache = 0; +static unsigned long set_doorlock = 0, get_doorlock = 0, doorlock = 0; +static unsigned long set_seagate = 0, get_seagate = 0; +static unsigned long set_standbynow = 0, get_standbynow = 0; +static unsigned long set_sleepnow = 0, get_sleepnow = 0; +static unsigned long set_powerup_in_standby = 0, get_powerup_in_standby = 0, powerup_in_standby = 0; +static unsigned long get_hitachi_temp = 0; + +static unsigned long set_freeze = 0; +#ifndef WIN_SECURITY_FREEZE_LOCK + #define WIN_SECURITY_FREEZE_LOCK 0xF5 +#endif + +#ifdef IDE_DRIVE_TASK_NO_DATA +static unsigned long security_master = 1, security_mode = 0; +static unsigned long enhanced_erase = 0; +static unsigned long set_security = 0; + +#ifndef WIN_SECURITY_SET_PASS + #define WIN_SECURITY_SET_PASS 0xF1 + #define WIN_SECURITY_UNLOCK 0xF2 + #define WIN_SECURITY_ERASE_PREPARE 0xF3 + #define WIN_SECURITY_ERASE_UNIT 0xF4 + #define WIN_SECURITY_DISABLE 0xF6 +#endif +static unsigned int security_command = WIN_SECURITY_UNLOCK; + +static char security_password[33]; +#endif // IDE_DRIVE_TASK_NO_DATA + +static unsigned long get_powermode = 0; +static unsigned long set_apmmode = 0, get_apmmode= 0, apmmode = 0; +#endif // HDIO_DRIVE_CMD +#ifdef CDROM_SELECT_SPEED +static unsigned long set_cdromspeed = 0, cdromspeed = 0; +#endif /* CDROM_SELECT_SPEED */ +#ifdef HDIO_DRIVE_CMD +static int get_IDentity = 0; +#endif +#ifdef HDIO_UNREGISTER_HWIF +static int unregister_hwif = 0; +static int hwif = 0; +#endif +#ifdef HDIO_SCAN_HWIF +static int scan_hwif = 0; +static int hwif_data = 0; +static int hwif_ctrl = 0; +static int hwif_irq = 0; +#endif +#ifdef HDIO_GET_BUSSTATE +static int set_busstate = 0, get_busstate = 0, busstate = 0; +#endif +#ifdef BLKRRPART +static int reread_partn = 0; +#endif +#if defined(HDIO_GET_ACOUSTIC) || defined(HDIO_DRIVE_CMD) +static int set_acoustic = 0, get_acoustic = 0, acoustic = 0; +#endif + +#ifdef HDIO_DRIVE_RESET +static int perform_reset = 0; +#endif /* HDIO_DRIVE_RESET */ +#ifdef HDIO_TRISTATE_HWIF +static int perform_tristate = 0, tristate = 0; +#endif /* HDIO_TRISTATE_HWIF */ + +#ifdef O_NONBLOCK +static int open_flags = O_RDONLY|O_NONBLOCK; +#else +static int open_flags = O_RDONLY; +#endif + +#ifdef HDIO_GET_IDENTITY +// Historically, if there was no HDIO_OBSOLETE_IDENTITY, then +// then the HDIO_GET_IDENTITY only returned 142 bytes. +// Otherwise, HDIO_OBSOLETE_IDENTITY returns 142 bytes, +// and HDIO_GET_IDENTITY returns 512 bytes. But the latest +// 2.5.xx kernels no longer define HDIO_OBSOLETE_IDENTITY +// (which they should, but they should just return -EINVAL). +// +// So.. we must now assume that HDIO_GET_IDENTITY returns 512 bytes. +// On a really old system, it will not, and we will be confused. +// Too bad, really. + +const char *cfg_str[] = +{ "", " HardSect", " SoftSect", " NotMFM", + " HdSw>15uSec", " SpinMotCtl", " Fixed", " Removeable", + " DTR<=5Mbs", " DTR>5Mbs", " DTR>10Mbs", " RotSpdTol>.5%", + " dStbOff", " TrkOff", " FmtGapReq", " nonMagnetic" +}; + +const char *SlowMedFast[] = {"slow", "medium", "fast", "eide", "ata"}; +const char *BuffType[] = {"unknown", "1Sect", "DualPort", "DualPortCache"}; + +#define YN(b) (((b)==0)?"no":"yes") + +static void dmpstr (const char *prefix, unsigned int i, const char *s[], unsigned int maxi) +{ + if (i > maxi) + printf("%s%u", prefix, i); + else + printf("%s%s", prefix, s[i]); +} + +static void dump_identity (const struct hd_driveid *id) +{ + int i; + char pmodes[64] = {0,}, dmodes[128]={0,}, umodes[128]={0,}; + const unsigned short int *id_regs= (const void*) id; + unsigned long capacity; + + printf("\n Model=%.40s, FwRev=%.8s, SerialNo=%.20s", + id->model, id->fw_rev, id->serial_no); + printf("\n Config={"); + for (i=0; i<=15; i++) { + if (id->config & (1<cyls, id->heads, id->sectors, + id->track_bytes, id->sector_bytes, id->ecc_bytes); + dmpstr(" BuffType=",id->buf_type,BuffType,3); + printf(", BuffSize=%ukB, MaxMultSect=%u", id->buf_size/2, id->max_multsect); + if (id->max_multsect) { + printf(", MultSect="); + if (!(id->multsect_valid&1)) + printf("?%u?", id->multsect); + else if (id->multsect) + printf("%u", id->multsect); + else + printf("off"); + } + putchar('\n'); + if (id->tPIO <= 5) { + strcat(pmodes, "pio0 "); + if (id->tPIO >= 1) strcat(pmodes, "pio1 "); + if (id->tPIO >= 2) strcat(pmodes, "pio2 "); + } + if (!(id->field_valid&1)) + printf(" (maybe):"); + capacity = (id->cur_capacity1 << 16) | id->cur_capacity0; + printf(" CurCHS=%u/%u/%u, CurSects=%lu", id->cur_cyls, id->cur_heads, id->cur_sectors, capacity); + printf(", LBA=%s", YN(id->capability&2)); + if (id->capability&2) + printf(", LBAsects=%u", id->lba_capacity); + + if (id->capability&1) { + if (id->dma_1word | id->dma_mword) { + if (id->dma_1word & 0x100) strcat(dmodes,"*"); + if (id->dma_1word & 1) strcat(dmodes,"sdma0 "); + if (id->dma_1word & 0x200) strcat(dmodes,"*"); + if (id->dma_1word & 2) strcat(dmodes,"sdma1 "); + if (id->dma_1word & 0x400) strcat(dmodes,"*"); + if (id->dma_1word & 4) strcat(dmodes,"sdma2 "); + if (id->dma_1word & 0xf800) strcat(dmodes,"*"); + if (id->dma_1word & 0xf8) strcat(dmodes,"sdma? "); + if (id->dma_mword & 0x100) strcat(dmodes,"*"); + if (id->dma_mword & 1) strcat(dmodes,"mdma0 "); + if (id->dma_mword & 0x200) strcat(dmodes,"*"); + if (id->dma_mword & 2) strcat(dmodes,"mdma1 "); + if (id->dma_mword & 0x400) strcat(dmodes,"*"); + if (id->dma_mword & 4) strcat(dmodes,"mdma2 "); + if (id->dma_mword & 0xf800) strcat(dmodes,"*"); + if (id->dma_mword & 0xf8) strcat(dmodes,"mdma? "); + } + } + printf("\n IORDY="); + if (id->capability&8) + printf((id->capability&4) ? "on/off" : "yes"); + else + printf("no"); + if ((id->capability&8) || (id->field_valid&2)) { + if (id->field_valid&2) { + printf(", tPIO={min:%u,w/IORDY:%u}", id->eide_pio, id->eide_pio_iordy); + if (id->eide_pio_modes & 1) strcat(pmodes, "pio3 "); + if (id->eide_pio_modes & 2) strcat(pmodes, "pio4 "); + if (id->eide_pio_modes &~3) strcat(pmodes, "pio? "); + } + if (id->field_valid&4) { + if (id->dma_ultra & 0x100) strcat(umodes,"*"); + if (id->dma_ultra & 0x001) strcat(umodes,"udma0 "); + if (id->dma_ultra & 0x200) strcat(umodes,"*"); + if (id->dma_ultra & 0x002) strcat(umodes,"udma1 "); + if (id->dma_ultra & 0x400) strcat(umodes,"*"); + if (id->dma_ultra & 0x004) strcat(umodes,"udma2 "); + if (id->dma_ultra & 0x800) strcat(umodes,"*"); + if (id->dma_ultra & 0x008) strcat(umodes,"udma3 "); + if (id->dma_ultra & 0x1000) strcat(umodes,"*"); + if (id->dma_ultra & 0x010) strcat(umodes,"udma4 "); + if (id->dma_ultra & 0x2000) strcat(umodes,"*"); + if (id->dma_ultra & 0x020) strcat(umodes,"udma5 "); + if (id->dma_ultra & 0x4000) strcat(umodes,"*"); + if (id->dma_ultra & 0x0040) strcat(umodes,"udma6 "); + if (id->dma_ultra & 0x8000) strcat(umodes,"*"); + if (id->dma_ultra & 0x0080) strcat(umodes,"udma7 "); + } + } + if ((id->capability&1) && (id->field_valid&2)) + printf(", tDMA={min:%u,rec:%u}", id->eide_dma_min, id->eide_dma_time); + printf("\n PIO modes: %s", pmodes); + if (*dmodes) + printf("\n DMA modes: %s", dmodes); + if (*umodes) + printf("\n UDMA modes: %s", umodes); + + printf("\n AdvancedPM=%s",YN(id_regs[83]&8)); + if (id_regs[83] & 8) { + if (!(id_regs[86]&8)) + printf(": disabled (255)"); + else if ((id_regs[91]&0xFF00)!=0x4000) + printf(": unknown setting"); + else + printf(": mode=0x%02X (%u)",id_regs[91]&0xFF,id_regs[91]&0xFF); + } + if (id_regs[82]&0x20) + printf(" WriteCache=%s",(id_regs[85]&0x20) ? "enabled" : "disabled"); +#ifdef __NEW_HD_DRIVE_ID + if (id->minor_rev_num || id->major_rev_num) { + printf("\n Drive conforms to: "); + if (id->minor_rev_num <= 31) + printf("%s: ", minor_str[id->minor_rev_num]); + else + printf("unknown: "); + if (id->major_rev_num != 0x0000 && /* NOVAL_0 */ + id->major_rev_num != 0xFFFF) { /* NOVAL_1 */ + /* through ATA/ATAPI-7 is currently defined-- + * increase this value as further specs are + * standardized (though we can guess safely to 15) + */ + for (i=0; i <= 7; i++) { + if (id->major_rev_num & (1<= elapsed) /* more than 1MB/s */ + printf("%3u MB in %5.2f seconds = %6.2f MB/sec\n", + total_MB, elapsed, + total_MB / elapsed); + else + printf("%3u MB in %5.2f seconds = %6.2f kB/sec\n", + total_MB, elapsed, + total_MB / elapsed * 1024); + + flush_buffer_cache(fd); + sleep(1); +quit: + if (-1 == shmdt(buf)) + perror ("could not detach sharedmem buf"); +} + +static int do_blkgetsize (int fd, __ull *blksize64) +{ + int rc; + unsigned int blksize32 = 0; + + if (0 == ioctl(fd, BLKGETSIZE64, blksize64)) { // returns bytes + *blksize64 /= 512; + return 0; + } + rc = ioctl(fd, BLKGETSIZE, &blksize32); // returns sectors + if (rc) + perror(" BLKGETSIZE failed"); + *blksize64 = blksize32; + return rc; +} + +void time_device (int fd) +{ + char *buf; + double elapsed; + struct itimerval e1, e2; + int shmid; + unsigned int max_iterations = 1024, total_MB, iterations; + + // + // get device size + // + if (do_ctimings || do_timings) { + __ull blksize; + if (0 == do_blkgetsize(fd, &blksize)) + max_iterations = (unsigned)(blksize / (2 * 1024) / TIMING_BUF_MB); + } + + if ((shmid = shmget(IPC_PRIVATE, TIMING_BUF_BYTES, 0600)) == -1) { + perror ("could not allocate sharedmem buf"); + return; + } + if (shmctl(shmid, SHM_LOCK, NULL) == -1) { + perror ("could not lock sharedmem buf"); + (void) shmctl(shmid, IPC_RMID, NULL); + return; + } + if ((buf = shmat(shmid, (char *) 0, 0)) == (char *) -1) { + perror ("could not attach sharedmem buf"); + (void) shmctl(shmid, IPC_RMID, NULL); + return; + } + if (shmctl(shmid, IPC_RMID, NULL) == -1) + perror ("shmctl(,IPC_RMID,) failed"); + + /* Clear out the device request queues & give them time to complete */ + sync(); + sleep(3); + + printf(" Timing %s disk reads: ", (open_flags & O_DIRECT) ? "O_DIRECT" : "buffered"); + fflush(stdout); + + /* + * getitimer() is used rather than gettimeofday() because + * it is much more consistent (on my machine, at least). + */ + { + const struct itimerval tv = {{1000,0},{1000,0}}; + setitimer(ITIMER_REAL, &tv, NULL); + } + + /* Now do the timings for real */ + iterations = 0; + getitimer(ITIMER_REAL, &e1); + do { + ++iterations; + if (read_big_block (fd, buf)) + goto quit; + getitimer(ITIMER_REAL, &e2); + elapsed = (e1.it_value.tv_sec - e2.it_value.tv_sec) + + ((e1.it_value.tv_usec - e2.it_value.tv_usec) / 1000000.0); + } while (elapsed < timings && iterations < max_iterations); + + total_MB = iterations * TIMING_BUF_MB; + if ((total_MB / elapsed) > 1.0) /* more than 1MB/s */ + printf("%3u MB in %5.2f seconds = %6.2f MB/sec\n", + total_MB, elapsed, total_MB / elapsed); + else + printf("%3u MB in %5.2f seconds = %6.2f kB/sec\n", + total_MB, elapsed, total_MB / elapsed * 1024); +quit: + if (-1 == shmdt(buf)) + perror ("could not detach sharedmem buf"); +} + +static void on_off (unsigned int value) +{ + printf(value ? " (on)\n" : " (off)\n"); +} + +#ifdef HDIO_GET_BUSSTATE +static void bus_state_value (unsigned int value) +{ + const char *string; + + switch (value) { + case BUSSTATE_ON: + string = " (on)\n"; + break; + case BUSSTATE_OFF: + string = " (off)\n"; + break; + case BUSSTATE_TRISTATE: + string = " (tristate)\n"; + break; + default: + string = " (unknown: %d)\n"; + break; + } + printf(string, value); +} +#endif + +#ifdef HDIO_DRIVE_CMD +static void interpret_standby (unsigned int standby) +{ + printf(" ("); + switch(standby) { + case 0: printf("off"); + break; + case 252: printf("21 minutes"); + break; + case 253: printf("vendor-specific"); + break; + case 254: printf("?reserved"); + break; + case 255: printf("21 minutes + 15 seconds"); + break; + default: + if (standby <= 240) { + unsigned int secs = standby * 5; + unsigned int mins = secs / 60; + secs %= 60; + if (mins) printf("%u minutes", mins); + if (mins && secs) printf(" + "); + if (secs) printf("%u seconds", secs); + } else if (standby <= 251) { + unsigned int mins = (standby - 240) * 30; + unsigned int hrs = mins / 60; + mins %= 60; + if (hrs) printf("%u hours", hrs); + if (hrs && mins) printf(" + "); + if (mins) printf("%u minutes", mins); + } else + printf("illegal value"); + break; + } + printf(")\n"); +} + +struct xfermode_entry { + int val; + const char *name; +}; + +static const struct xfermode_entry xfermode_table[] = { + { 8, "pio0" }, + { 9, "pio1" }, + { 10, "pio2" }, + { 11, "pio3" }, + { 12, "pio4" }, + { 13, "pio5" }, + { 14, "pio6" }, + { 15, "pio7" }, + { 16, "sdma0" }, + { 17, "sdma1" }, + { 18, "sdma2" }, + { 19, "sdma3" }, + { 20, "sdma4" }, + { 21, "sdma5" }, + { 22, "sdma6" }, + { 23, "sdma7" }, + { 32, "mdma0" }, + { 33, "mdma1" }, + { 34, "mdma2" }, + { 35, "mdma3" }, + { 36, "mdma4" }, + { 37, "mdma5" }, + { 38, "mdma6" }, + { 39, "mdma7" }, + { 64, "udma0" }, + { 65, "udma1" }, + { 66, "udma2" }, + { 67, "udma3" }, + { 68, "udma4" }, + { 69, "udma5" }, + { 70, "udma6" }, + { 71, "udma7" }, + { 0, NULL } +}; + +static int translate_xfermode(char * name) +{ + const struct xfermode_entry *tmp; + char *endptr; + int val = -1; + + for (tmp = xfermode_table; tmp->name != NULL; ++tmp) { + if (!strcmp(name, tmp->name)) + return tmp->val; + } + val = strtol(name, &endptr, 10); + if (*endptr == '\0') + return val; + return -1; +} + +static void interpret_xfermode (unsigned int xfermode) +{ + printf(" ("); + switch(xfermode) { + case 0: printf("default PIO mode"); + break; + case 1: printf("default PIO mode, disable IORDY"); + break; + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: printf("PIO flow control mode%u", xfermode-8); + break; + case 16: + case 17: + case 18: + case 19: + case 20: + case 21: + case 22: + case 23: printf("singleword DMA mode%u", xfermode-16); + break; + case 32: + case 33: + case 34: + case 35: + case 36: + case 37: + case 38: + case 39: printf("multiword DMA mode%u", xfermode-32); + break; + case 64: + case 65: + case 66: + case 67: + case 68: + case 69: + case 70: + case 71: printf("UltraDMA mode%u", xfermode-64); + break; + default: + printf("unknown, probably not valid"); + break; + } + printf(")\n"); +} +#endif /* HDIO_DRIVE_CMD */ + +#ifndef VXVM_MAJOR +#define VXVM_MAJOR 199 +#endif + +#ifndef CCISS_MAJOR +#define CCISS_MAJOR 104 +#endif + +void process_dev (char *devname) +{ + int fd; + static long parm, multcount; +#ifndef HDIO_DRIVE_CMD + int force_operation = 0; +#endif + + fd = open (devname, open_flags); + if (fd < 0) { + perror(devname); + exit(errno); + } + if (!quiet) + printf("\n%s:\n", devname); + +#ifdef BLKRASET + if (set_fsreadahead) { + if (get_fsreadahead) + printf(" setting fs readahead to %ld\n", fsreadahead); + if (ioctl(fd, BLKRASET, fsreadahead)) + perror(" BLKRASET failed"); + } +#endif +#ifdef HDIO_UNREGISTER_HWIF + if (unregister_hwif) { + printf(" attempting to unregister hwif#%u\n", hwif); + if (ioctl(fd, HDIO_UNREGISTER_HWIF, hwif)) + perror(" HDIO_UNREGISTER_HWIF failed"); + } +#endif +#ifdef HDIO_SCAN_HWIF + if (scan_hwif) { + int args[3]; + printf(" attempting to scan hwif (0x%x, 0x%x, %u)\n", hwif_data, hwif_ctrl, hwif_irq); + args[0] = hwif_data; + args[1] = hwif_ctrl; + args[2] = hwif_irq; + if (ioctl(fd, HDIO_SCAN_HWIF, args)) + perror(" HDIO_SCAN_HWIF failed"); + } +#endif +#ifdef HDIO_SET_PIO_MODE + if (set_piomode) { + if (noisy_piomode) { + if (piomode == 255) + printf(" attempting to auto-tune PIO mode\n"); + else if (piomode < 100) + printf(" attempting to set PIO mode to %d\n", piomode); + else if (piomode < 200) + printf(" attempting to set MDMA mode to %d\n", (piomode-100)); + else + printf(" attempting to set UDMA mode to %d\n", (piomode-200)); + } + if (ioctl(fd, HDIO_SET_PIO_MODE, piomode)) + perror(" HDIO_SET_PIO_MODE failed"); + } +#endif +#ifdef HDIO_SET_32BIT + if (set_io32bit) { + if (get_io32bit) + printf(" setting 32-bit IO_support flag to %ld\n", io32bit); + if (ioctl(fd, HDIO_SET_32BIT, io32bit)) + perror(" HDIO_SET_32BIT failed"); + } +#endif +#ifdef HDIO_SET_MULTCOUNT + if (set_mult) { + if (get_mult) + printf(" setting multcount to %ld\n", mult); + if (ioctl(fd, HDIO_SET_MULTCOUNT, mult)) + perror(" HDIO_SET_MULTCOUNT failed"); +#ifndef HDIO_DRIVE_CMD + else force_operation = 1; +#endif + } +#endif +#ifdef BLKROSET + if (set_readonly) { + if (get_readonly) { + printf(" setting readonly to %ld", readonly); + on_off(readonly); + } + if (ioctl(fd, BLKROSET, &readonly)) + perror(" BLKROSET failed"); + } +#endif +#ifdef HDIO_SET_UNMASKINTR + if (set_unmask) { + if (get_unmask) { + printf(" setting unmaskirq to %ld", unmask); + on_off(unmask); + } + if (ioctl(fd, HDIO_SET_UNMASKINTR, unmask)) + perror(" HDIO_SET_UNMASKINTR failed"); + } +#endif +#ifdef HDIO_SET_DMA + if (set_dma) { + if (get_dma) { + printf(" setting using_dma to %ld", dma); + on_off(dma); + } + if (ioctl(fd, HDIO_SET_DMA, dma)) + perror(" HDIO_SET_DMA failed"); + } +#endif +#ifdef HDIO_SET_QDMA + if (set_dma_q) { + if (get_dma_q) { + printf(" setting DMA queue_depth to %ld", dma_q); + on_off(dma_q); + } + if (ioctl(fd, HDIO_SET_QDMA, dma_q)) + perror(" HDIO_SET_QDMA failed"); + } +#endif +#ifdef HDIO_SET_NOWERR + if (set_nowerr) { + if (get_nowerr) { + printf(" setting nowerr to %ld", nowerr); + on_off(nowerr); + } + if (ioctl(fd, HDIO_SET_NOWERR, nowerr)) + perror(" HDIO_SET_NOWERR failed"); + } +#endif +#ifdef HDIO_GET_KEEPSETTINGS + if (set_keep) { + if (get_keep) { + printf(" setting keep_settings to %ld", keep); + on_off(keep); + } + if (ioctl(fd, HDIO_SET_KEEPSETTINGS, keep)) + perror(" HDIO_SET_KEEPSETTINGS failed"); + } +#endif /* HDIO_GET_KEEPSETTINGS */ +#ifdef HDIO_DRIVE_CMD + if (set_doorlock) { + unsigned char args[4] = {0,0,0,0}; + args[0] = doorlock ? WIN_DOORLOCK : WIN_DOORUNLOCK; + if (get_doorlock) { + printf(" setting drive doorlock to %ld", doorlock); + on_off(doorlock); + } + if (ioctl(fd, HDIO_DRIVE_CMD, &args)) + perror(" HDIO_DRIVE_CMD(doorlock) failed"); + } + if (set_dkeep) { + /* lock/unlock the drive's "feature" settings */ + unsigned char args[4] = {WIN_SETFEATURES,0,0,0}; + if (get_dkeep) { + printf(" setting drive keep features to %ld", dkeep); + on_off(dkeep); + } + args[2] = dkeep ? 0x66 : 0xcc; + if (ioctl(fd, HDIO_DRIVE_CMD, &args)) + perror(" HDIO_DRIVE_CMD(keepsettings) failed"); + } + if (set_defects) { + unsigned char args[4] = {WIN_SETFEATURES,0,0x04,0}; + args[2] = defects ? 0x04 : 0x84; + if (get_defects) + printf(" setting drive defect management to %ld\n", defects); + if (ioctl(fd, HDIO_DRIVE_CMD, &args)) + perror(" HDIO_DRIVE_CMD(defectmgmt) failed"); + } + if (set_prefetch) { + unsigned char args[4] = {WIN_SETFEATURES,0,0xab,0}; + args[1] = (char)prefetch; + if (get_prefetch) + printf(" setting drive prefetch to %ld\n", prefetch); + if (ioctl(fd, HDIO_DRIVE_CMD, &args)) + perror(" HDIO_DRIVE_CMD(setprefetch) failed"); + } + if (set_xfermode) { + unsigned char args[4] = {WIN_SETFEATURES,0,3,0}; + args[1] = xfermode_requested; + if (get_xfermode) { + printf(" setting xfermode to %d", xfermode_requested); + interpret_xfermode(xfermode_requested); + } + if (ioctl(fd, HDIO_DRIVE_CMD, &args)) + perror(" HDIO_DRIVE_CMD(setxfermode) failed"); + } + if (set_lookahead) { + unsigned char args[4] = {WIN_SETFEATURES,0,0,0}; + args[2] = lookahead ? 0xaa : 0x55; + if (get_lookahead) { + printf(" setting drive read-lookahead to %ld", lookahead); + on_off(lookahead); + } + if (ioctl(fd, HDIO_DRIVE_CMD, &args)) + perror(" HDIO_DRIVE_CMD(setreadahead) failed"); + } + if (set_powerup_in_standby) { + unsigned char args[4] = {WIN_SETFEATURES,0,0,0}; + args[2] = powerup_in_standby ? 0x06 : 0x86; + if (get_powerup_in_standby) { + printf(" setting power-up in standby to %ld", powerup_in_standby); + on_off(powerup_in_standby); + } + if (ioctl(fd, HDIO_DRIVE_CMD, &args)) + perror(" HDIO_DRIVE_CMD(powerup_in_standby) failed"); + } + if (set_apmmode) { + unsigned char args[4] = {WIN_SETFEATURES,0,0,0}; + if (apmmode<1) apmmode=1; + if (apmmode>255) apmmode=255; + if (get_apmmode) + printf(" setting Advanced Power Management level to"); + if (apmmode==255) { + /* disable Advanced Power Management */ + args[2] = 0x85; /* feature register */ + if (get_apmmode) printf(" disabled\n"); + } else { + /* set Advanced Power Management mode */ + args[2] = 0x05; /* feature register */ + args[1] = (unsigned char)apmmode; /* sector count register */ + if (get_apmmode) printf(" 0x%02lX (%ld)\n",apmmode,apmmode); + } + if (ioctl(fd, HDIO_DRIVE_CMD, &args)) + perror(" HDIO_DRIVE_CMD failed"); + } +#endif +#ifdef CDROM_SELECT_SPEED + if (set_cdromspeed) { + printf ("setting cdrom speed to %ld\n", cdromspeed); + if (ioctl (fd, CDROM_SELECT_SPEED, cdromspeed)) + perror(" CDROM_SELECT_SPEED failed"); + } +#endif +#ifdef HDIO_DRIVE_CMD + if (set_wcache) { +#ifdef DO_FLUSHCACHE +#ifndef WIN_FLUSHCACHE +#define WIN_FLUSHCACHE 0xe7 +#endif + unsigned char flushcache[4] = {WIN_FLUSHCACHE,0,0,0}; +#endif /* DO_FLUSHCACHE */ + unsigned char args[4] = {WIN_SETFEATURES,0,0,0}; + args[2] = wcache ? 0x02 : 0x82; + if (get_wcache) { + printf(" setting drive write-caching to %ld", wcache); + on_off(wcache); + } +#ifdef HDIO_SET_WCACHE + if (ioctl(fd, HDIO_SET_WCACHE, wcache)) +#endif + if (ioctl(fd, HDIO_DRIVE_CMD, &args)) + perror(" HDIO_DRIVE_CMD(setcache) failed"); +#ifdef DO_FLUSHCACHE + if (!wcache && ioctl(fd, HDIO_DRIVE_CMD, &flushcache)) + perror (" HDIO_DRIVE_CMD(flushcache) failed"); +#endif /* DO_FLUSHCACHE */ + } + if (set_standbynow) { +#ifndef WIN_STANDBYNOW1 +#define WIN_STANDBYNOW1 0xE0 +#endif +#ifndef WIN_STANDBYNOW2 +#define WIN_STANDBYNOW2 0x94 +#endif + unsigned char args1[4] = {WIN_STANDBYNOW1,0,0,0}; + unsigned char args2[4] = {WIN_STANDBYNOW2,0,0,0}; + if (get_standbynow) + printf(" issuing standby command\n"); + if (ioctl(fd, HDIO_DRIVE_CMD, &args1) + && ioctl(fd, HDIO_DRIVE_CMD, &args2)) + perror(" HDIO_DRIVE_CMD(standby) failed"); + } + if (set_sleepnow) { +#ifndef WIN_SLEEPNOW1 +#define WIN_SLEEPNOW1 0xE6 +#endif +#ifndef WIN_SLEEPNOW2 +#define WIN_SLEEPNOW2 0x99 +#endif + unsigned char args1[4] = {WIN_SLEEPNOW1,0,0,0}; + unsigned char args2[4] = {WIN_SLEEPNOW2,0,0,0}; + if (get_sleepnow) + printf(" issuing sleep command\n"); + if (ioctl(fd, HDIO_DRIVE_CMD, &args1) + && ioctl(fd, HDIO_DRIVE_CMD, &args2)) + perror(" HDIO_DRIVE_CMD(sleep) failed"); + } +#endif +#if defined(WIN_SECURITY_SET_PASS) && defined(IDE_DRIVE_TASK_NO_DATA) + if (set_security) { +#ifndef TASKFILE_OUT +#define TASKFILE_OUT 4 +#endif +#ifndef IDE_DRIVE_TASK_OUT +#define IDE_DRIVE_TASK_OUT 3 +#endif +#ifndef HDIO_DRIVE_TASKFILE +#define HDIO_DRIVE_TASKFILE 0x031d +#endif + int err; + const char *description; + struct request_s { + ide_task_request_t req __attribute__((packed)); + char data[512] __attribute__((packed)); + } request; + memset(&request, 0, sizeof(request)); + ((task_struct_t *)(&request.req.io_ports))->command = security_command; + request.req.data_phase = TASKFILE_OUT; + request.req.req_cmd = IDE_DRIVE_TASK_OUT; /* IN? */ + request.req.out_size = 512; + request.data[0] = (security_master & 0x01); + memcpy(&request.data[2], security_password, 32); + + /* Not setting any out_flags causes a segfault and most + of the times a kernel panic */ + request.req.out_flags.b.status_command = 1; + + switch (security_command) { + case WIN_SECURITY_UNLOCK: + description = "SECURITY_UNLOCK"; + break; + case WIN_SECURITY_SET_PASS: + description = "SECURITY_SET_PASS"; + request.data[1] = (security_mode & 0x01); + if (security_master) { + /* master password revision code */ + request.data[34] = 0x11; + request.data[35] = 0xff; + } + break; + case WIN_SECURITY_DISABLE: + description = "SECURITY_DISABLE"; + break; + case WIN_SECURITY_ERASE_UNIT: + description = "SECURITY_ERASE"; + request.data[0] |= (enhanced_erase & 0x02); + break; + default: + description = "NOTHING"; + } + + printf(" Issuing %s command, password=\"%s\", user=%s", + description, security_password, + request.data[0] ? "master" : "user"); + if (security_command == WIN_SECURITY_SET_PASS) + printf(", mode=%s", request.data[1] ? "max" : "high"); + printf("\n"); +/* Since the Linux kernel until at least 2.6.12 segfaults on the first command + when issued on a locked drive the actual erase is never issued. + One could patch the code to issue separate commands for erase prepare and + erase to erase a locked drive. +*/ + if (security_command == WIN_SECURITY_ERASE_UNIT) { + unsigned char args[4] = {WIN_SECURITY_ERASE_PREPARE,0,0,0}; + if (ioctl(fd, HDIO_DRIVE_CMD, &args)) + perror(" HDIO_DRIVE_CMD(erase prepare) failed"); + else + if ((ioctl(fd, HDIO_DRIVE_TASKFILE, &request))) { + err = errno; + perror("Problem issuing erase command"); + if (err != 0) + printf("Error: %d\n", err); + if (err == EINVAL) + printf("You need to configure your kernel with CONFIG_IDE_TASK_IOCTL.\n"); + } +/* We would like to issue these commands consecutively, but since the Linux +kernel until at least 2.6.12 segfaults on each command issued the second will +never be executed. +By disabling the below code block one is at least able to issue the commands +consecutively in two runs (assuming the segfault isn't followed by an oops. +*/ + } else if (security_command == WIN_SECURITY_DISABLE) { + /* First attempt an unlock */ + ((task_struct_t *)(&request.req.io_ports))->command = WIN_SECURITY_UNLOCK; + if ((ioctl(fd, HDIO_DRIVE_TASKFILE, &request))) { + err = errno; + perror("Problem issuing unlock command"); + if (err != 0) + printf("Error: %d\n", err); + if (err == EINVAL) + printf("You need to configure your kernel with CONFIG_IDE_TASK_IOCTL.\n"); + } else { + /* Then the security disable */ + ((task_struct_t *)(&request.req.io_ports))->command = security_command; + if ((ioctl(fd, HDIO_DRIVE_TASKFILE, &request))) { + err = errno; + perror("Problem issuing security disable command"); + if (err != 0) + printf("Error: %d\n", err); + if (err == EINVAL) + printf("You need to configure your kernel with CONFIG_IDE_TASK_IOCTL.\n"); + } + } + } else if ((ioctl(fd, HDIO_DRIVE_TASKFILE, &request))) { + err = errno; + perror("Problem issuing security command"); + if (err != 0) + printf("Error: %d\n", err); + if (err == EINVAL) + printf("You need to configure your kernel with CONFIG_IDE_TASK_IOCTL.\n"); + } + } +#endif // defined(WIN_SECURITY_SET_PASS) && defined(IDE_DRIVE_TASK_NO_DATA) +#if defined(WIN_SECURITY_FREEZE_LOCK) && defined(HDIO_DRIVE_CMD) + if (set_freeze) { + unsigned char args[4] = {WIN_SECURITY_FREEZE_LOCK,0,0,0}; + printf(" issuing Security Freeze command\n"); + if (ioctl(fd, HDIO_DRIVE_CMD, &args)) + perror(" HDIO_DRIVE_CMD(freeze) failed"); + } +#endif // defined(WIN_SECURITY_FREEZE_LOCK) && defined(HDIO_DRIVE_CMD) +#ifdef HDIO_DRIVE_CMD + if (set_seagate) { + unsigned char args[4] = {0xfb,0,0,0}; + if (get_seagate) + printf(" disabling Seagate auto powersaving mode\n"); + if (ioctl(fd, HDIO_DRIVE_CMD, &args)) + perror(" HDIO_DRIVE_CMD(seagatepwrsave) failed"); + } + if (set_standby) { + unsigned char args[4] = {WIN_SETIDLE1,(unsigned char)standby_requested,0,0}; + if (get_standby) { + printf(" setting standby to %lu", standby_requested); + interpret_standby(standby_requested); + } + if (ioctl(fd, HDIO_DRIVE_CMD, &args)) + perror(" HDIO_DRIVE_CMD(setidle1) failed"); + } + if (get_hitachi_temp) { + unsigned char args[4] = {0xf0,0,0x01,0}; /* "Sense Condition", vendor-specific */ + if (ioctl(fd, HDIO_DRIVE_CMD, &args)) + perror(" HDIO_DRIVE_CMD(hitachisensecondition) failed"); + else { + printf(" drive temperature (celsius) is: "); + if (args[2]==0) + printf("under -20"); + else if (args[2]==0xFF) + printf("over 107"); + else + printf("%d", args[2]/2-20); + printf("\n drive temperature in range: %s\n", YN(!(args[1]&0x10)) ); + } + } +#endif + if (!flagcount) + verbose = 1; + +#ifdef HDIO_GET_MULTCOUNT + if (verbose || get_mult || get_identity) { + multcount = -1; + if (ioctl(fd, HDIO_GET_MULTCOUNT, &multcount)) { + if (get_mult) + perror(" HDIO_GET_MULTCOUNT failed"); + } else if (verbose | get_mult) { + printf(" multcount = %2ld", multcount); + on_off(multcount); + } + } +#endif +#ifdef HDIO_GET_32BIT + if (verbose || get_io32bit) { + if (0 == ioctl(fd, HDIO_GET_32BIT, &parm)) { + printf(" IO_support =%3ld (", parm); + switch (parm) { + case 0: printf("default "); + case 2: printf("16-bit)\n"); + break; + case 1: printf("32-bit)\n"); + break; + case 3: printf("32-bit w/sync)\n"); + break; + case 8: printf("Request-Queue-Bypass)\n"); + break; + default:printf("\?\?\?)\n"); + } + } + } +#endif +#ifdef HDIO_GET_UNMASKINTR + if (verbose || get_unmask) { + if (0 == ioctl(fd, HDIO_GET_UNMASKINTR, &parm)) { + printf(" unmaskirq = %2ld", parm); + on_off(parm); + } + } +#endif +#ifdef HDIO_GET_DMA + if (verbose || get_dma) { + if (0 == ioctl(fd, HDIO_GET_DMA, &parm)) { + printf(" using_dma = %2ld", parm); + if (parm == 8) + printf(" (DMA-Assisted-PIO)\n"); + else + on_off(parm); + } + } +#endif +#ifdef HDIO_GET_QDMA + if (get_dma_q) { + if(ioctl(fd, HDIO_GET_QDMA, &parm)) { + perror(" HDIO_GET_QDMA failed"); + } else { + printf(" queue_depth = %2ld", parm); + on_off(parm); + } + } +#endif +#ifdef HDIO_GET_KEEPSETTINGS + if (verbose || get_keep) { + if (0 == ioctl(fd, HDIO_GET_KEEPSETTINGS, &parm)) { + printf(" keepsettings = %2ld", parm); + on_off(parm); + } + } +#endif /* HDIO_GET_KEEPSETTINGS */ + +#ifdef HDIO_SET_NOWERR + if (get_nowerr) { + if (ioctl(fd, HDIO_GET_NOWERR, &parm)) + perror(" HDIO_GET_NOWERR failed"); + else { + printf(" nowerr = %2ld", parm); + on_off(parm); + } + } +#endif +#ifdef BLKROGET + if (verbose || get_readonly) { + if (ioctl(fd, BLKROGET, &parm)) + perror(" BLKROGET failed"); + else { + printf(" readonly = %2ld", parm); + on_off(parm); + } + } +#endif +#ifdef BLKRAGET + if (verbose || get_fsreadahead) { + if (ioctl(fd, BLKRAGET, &parm)) + perror(" BLKRAGET failed"); + else { + printf(" readahead = %2ld", parm); + on_off(parm); + } + } +#endif +#ifdef HDIO_GETGEO + if (verbose || get_geom) { + __ull blksize; + static const char msg[] = " geometry = %u/%u/%u, sectors = %lld, start = %ld\n"; +// Note to self: when compiled 32-bit (AMD,Mips64), the userspace version of this struct +// is going to be 32-bits smaller than the kernel representation.. random stack corruption! + static struct hd_geometry g; +#ifdef HDIO_GETGEO_BIG + static struct hd_big_geometry bg; +#endif + + if (0 == do_blkgetsize(fd, &blksize)) { +#ifdef HDIO_GETGEO_BIG + if (!ioctl(fd, HDIO_GETGEO_BIG, &bg)) + printf(msg, bg.cylinders, bg.heads, bg.sectors, blksize, bg.start); + else +#endif + if (ioctl(fd, HDIO_GETGEO, &g)) + perror(" HDIO_GETGEO failed"); + else + printf(msg, g.cylinders, g.heads, g.sectors, blksize, g.start); + } + } +#endif +#ifdef HDIO_DRIVE_CMD + if (get_powermode) { +#ifndef WIN_CHECKPOWERMODE1 +#define WIN_CHECKPOWERMODE1 0xE5 +#endif +#ifndef WIN_CHECKPOWERMODE2 +#define WIN_CHECKPOWERMODE2 0x98 +#endif + unsigned char args[4] = {WIN_CHECKPOWERMODE1,0,0,0}; + const char *state; + if (ioctl(fd, HDIO_DRIVE_CMD, &args) + && (args[0] = WIN_CHECKPOWERMODE2) /* (single =) try again with 0x98 */ + && ioctl(fd, HDIO_DRIVE_CMD, &args)) { + if (errno != EIO || args[0] != 0 || args[1] != 0) + state = "unknown"; + else + state = "sleeping"; + } else { + state = (args[2] == 255) ? "active/idle" : "standby"; + } + printf(" drive state is: %s\n", state); + } +#endif +#ifdef HDIO_DRIVE_RESET + if (perform_reset) { + if (ioctl(fd, HDIO_DRIVE_RESET, NULL)) + perror(" HDIO_DRIVE_RESET failed"); + } +#endif /* HDIO_DRIVE_RESET */ +#ifdef HDIO_TRISTATE_HWIF + if (perform_tristate) { + unsigned char args[4] = {0,tristate,0,0}; + if (ioctl(fd, HDIO_TRISTATE_HWIF, &args)) + perror(" HDIO_TRISTATE_HWIF failed"); + } +#endif /* HDIO_TRISTATE_HWIF */ +#ifdef HDIO_GET_IDENTITY + if (get_identity) { + static struct hd_driveid id; + + if (!ioctl(fd, HDIO_GET_IDENTITY, &id)) { + if (multcount != -1) { + id.multsect = (unsigned char)multcount; + id.multsect_valid |= 1; + } else + id.multsect_valid &= ~1; + dump_identity(&id); + } else if (errno == -ENOMSG) + printf(" no identification info available\n"); + else + perror(" HDIO_GET_IDENTITY failed"); + } +#endif +#ifdef HDIO_DRIVE_CMD + if (get_IDentity) { + __u16 *id; + unsigned char args[4+512] = {WIN_IDENTIFY,0,0,1,}; // FIXME? + unsigned i; + if (ioctl(fd, HDIO_DRIVE_CMD, &args)) { + args[0] = WIN_PIDENTIFY; + if (ioctl(fd, HDIO_DRIVE_CMD, &args)) { + perror(" HDIO_DRIVE_CMD(identify) failed"); + goto identify_abort; + } + } + id = (__u16 *)&args[4]; + if (get_IDentity == 2) { + for (i = 0; i < (256/8); ++i) { + printf("%04x %04x %04x %04x %04x %04x %04x %04x\n", id[0], id[1], id[2], id[3], id[4], id[5], id[6], id[7]); + id += 8; + } + } else { + for(i = 0; i < 0x100; ++i) { + __le16_to_cpus(&id[i]); + } + identify((void *)id); + } +identify_abort: ; + } +#endif +#ifdef HDIO_SET_BUSSTATE + if (set_busstate) { + if (get_busstate) { + printf(" setting bus state to %d", busstate); + bus_state_value(busstate); + } + if (ioctl(fd, HDIO_SET_BUSSTATE, busstate)) + perror(" HDIO_SET_BUSSTATE failed"); + } +#endif +#ifdef HDIO_GET_BUSSTATE + if (get_busstate) { + if (ioctl(fd, HDIO_GET_BUSSTATE, &parm)) + perror(" HDIO_GET_BUSSTATE failed"); + else { + printf(" busstate = %2ld", parm); + bus_state_value(parm); + } + } +#endif +#ifdef BLKRRPART + if (reread_partn) { + if (ioctl(fd, BLKRRPART, NULL)) { + perror(" BLKRRPART failed"); + } + } +#endif +#ifdef HDIO_DRIVE_CMD + if (set_acoustic) { + if (get_acoustic) { + printf(" setting acoustic management to %d\n", acoustic); + } +#if 0 + if (ioctl(fd, HDIO_SET_ACOUSTIC, acoustic)) + perror(" HDIO_SET_ACOUSTIC failed"); +#else +{ + unsigned char args[4] = {WIN_SETFEATURES,0,0,0}; + args[1] = acoustic; + args[2] = acoustic ? 0x42 : 0xc2; + if (ioctl(fd, HDIO_DRIVE_CMD, &args)) + perror(" HDIO_DRIVE_CMD:ACOUSTIC failed"); +} +#endif + } + #endif /* HDIO_SET_ACOUSTIC */ + #ifdef HDIO_GET_ACOUSTIC + if (get_acoustic) { + // FIXME: use Word94 from IDENTIFY for this value + if (ioctl(fd, HDIO_GET_ACOUSTIC, &parm)) { + perror(" HDIO_GET_ACOUSTIC failed"); + } else { + printf(" acoustic = %2ld (128=quiet ... 254=fast)\n", parm); + } + } + #endif /* HDIO_GET_ACOUSTIC */ + + if (do_ctimings) + time_cache (fd); + if (do_timings) + time_device (fd); + if (do_flush) + flush_buffer_cache (fd); + close (fd); +} + +void usage_error (int out) +{ + FILE *desc; + int ret; + + if (out == 0) { + desc = stdout; + ret = 0; + } else { + desc = stderr; + ret = 1; + } + + fprintf(desc,"\n%s - get/set hard disk parameters - version %s\n\n", progname, VERSION); + fprintf(desc,"Usage: %s [options] [device] ..\n\n", progname); +#if defined(_WIN32) || defined(__CYGWIN__) + fprintf(desc,"Device:\n" + " /dev/hd[a-z] harddisk 0,1,...\n" + " /dev/sd[a-z] harddisk 0,1,...\n" + " /dev/scd[0-9] cd-rom 0,1,...\n\n"); +#endif + fprintf(desc,"Options:\n" +#ifdef BLKRASET + " -a get/set fs readahead\n" +#endif +#ifdef HDIO_DRIVE_CMD + " -A set drive read-lookahead flag (0/1)\n" +#endif +#ifdef HDIO_GET_BUSSTATE + " -b get/set bus state (0 == off, 1 == on, 2 == tristate)\n" +#endif +#ifdef HDIO_DRIVE_CMD + " -B set Advanced Power Management setting (1-255)\n" +#endif +#ifdef HDIO_SET_32BIT + " -c get/set IDE 32-bit IO setting\n" +#endif +#ifdef HDIO_DRIVE_CMD + " -C check IDE power mode status\n" +#endif +#ifdef HDIO_SET_DMA + " -d get/set using_dma flag\n" +#endif +#if defined(_WIN32) || defined(__CYGWIN__) + " --debug enable debugging output\n" +#endif + " --direct use O_DIRECT to bypass page cache for timings\n" +#ifdef HDIO_DRIVE_CMD + " -D enable/disable drive defect management\n" +#endif +#ifdef CDROM_SELECT_SPEED + " -E set cd-rom drive speed\n" +#endif + " -f flush buffer cache for device on exit\n" +#ifdef HDIO_GETGEO + " -g display drive geometry\n" +#endif + " -h display terse usage information\n" +#ifdef HDIO_DRIVE_CMD + " -H read temperature from drive (Hitachi only)\n" +#endif +#ifdef HDIO_GET_IDENTITY + " -i display drive identification\n" +#endif +#ifdef HDIO_DRIVE_CMD + " -I detailed/current information directly from drive\n" +#endif + " --Istdin read identify data from stdin as ASCII hex\n" +#ifdef HDIO_DRIVE_CMD + " --Istdout write identify data to stdout as ASCII hex\n" +#endif +#ifdef HDIO_GET_KEEPSETTINGS + " -k get/set keep_settings_over_reset flag (0/1)\n" +#endif +#ifdef HDIO_DRIVE_CMD + " -K set drive keep_features_over_reset flag (0/1)\n" + " -L set drive doorlock (0/1) (removable harddisks only)\n" +#endif +#ifdef HDIO_GET_ACOUSTIC + " -M get/set acoustic management (0-254, 128: quiet, 254: fast) (EXPERIMENTAL)\n" +#endif /* HDIO_GET_ACOUSTIC */ +#ifdef HDIO_SET_MULTCOUNT + " -m get/set multiple sector count\n" +#endif +#ifdef HDIO_SET_NOWERR + " -n get/set ignore-write-errors flag (0/1)\n" +#endif +#ifdef HDIO_SET_PIO_MODE + " -p set PIO mode on IDE interface chipset (0,1,2,3,4,...)\n" +#endif +#ifdef HDIO_DRIVE_CMD + " -P set drive prefetch count\n" +#endif + " -q change next setting quietly\n" +#ifdef HDIO_GET_QDMA + " -Q get/set DMA tagged-queuing depth (if supported)\n" +#endif +#ifdef BLKROSET + " -r get/set device readonly flag (DANGEROUS to set)\n" +#endif +#ifdef HDIO_SCAN_HWIF + " -R register an IDE interface (DANGEROUS)\n" +#endif +#ifdef HDIO_DRIVE_CMD + " -s set power-up in standby flag (0/1)\n" + " -S set standby (spindown) timeout\n" +#endif + " -t perform device read timings\n" + " -T perform cache read timings\n" +#ifdef HDIO_SET_UNMASKINTR + " -u get/set unmaskirq flag (0/1)\n" +#endif +#ifdef HDIO_UNREGISTER_HWIF + " -U un-register an IDE interface (DANGEROUS)\n" +#endif + " -v defaults; same as -" +#ifdef HDIO_SET_MULTCOUNT + "m" +#endif +#ifdef HDIO_SET_32BIT + "c" +#endif +#ifdef HDIO_SET_UNMASKINTR + "u" +#endif +#ifdef HDIO_SET_DMA + "d" +#endif +#ifdef HDIO_GET_KEEPSETTINGS + "k" +#endif +#ifdef BLKROSET + "r" +#endif +#ifdef BLKRASET + "a" +#endif +#ifdef HDIO_GETGEO + "g" +#endif + " for IDE drives\n" + " -V display program version and exit immediately\n" +#ifdef HDIO_DRIVE_RESET + " -w perform device reset (DANGEROUS)\n" +#endif +#ifdef HDIO_DRIVE_CMD + " -W set drive write-caching flag (0/1) (DANGEROUS)\n" +#endif +#ifdef HDIO_TRISTATE_HWIF + " -x tristate device for hotswap (0/1) (DANGEROUS)\n" +#endif +#ifdef HDIO_DRIVE_CMD + " -X set IDE xfer mode (DANGEROUS)\n" + " -y put IDE drive in standby mode\n" + " -Y put IDE drive to sleep\n" + " -Z disable Seagate auto-powersaving mode\n" +#endif +#ifdef BLKRRPART + " -z re-read partition table\n" +#endif +#if defined(WIN_SECURITY_SET_PASS) && defined(IDE_DRIVE_TASK_NO_DATA) + " --security-help display help for ATA security commands\n" +#elif defined(WIN_SECURITY_FREEZE_LOCK) && defined(HDIO_DRIVE_CMD) + " --security-freeze Freeze security settings until reset\n" + " (remaining ATA security commands were unavailable at build time)\n" +#else + " (ATA security commands were unavailable at build time)\n" +#endif + "\n"); + exit(ret); +} + +#if defined(WIN_SECURITY_SET_PASS) && defined(IDE_DRIVE_TASK_NO_DATA) +static void security_help (void) +{ + printf("\n" + "ATA Security Commands:\n" + " --security-freeze Freeze security settings until reset\n\n" + " The remainder of these are VERY DANGEROUS and can KILL your drive!\n" + " Due to bugs in most Linux kernels, use of these commands may even\n" + " trigger kernel segfaults or worse. EXPERIMENT AT YOUR OWN RISK!\n\n" + " --security-unlock PWD Unlock drive, using password PWD\n" + " --security-set-pass PWD Lock drive, using password PWD (DANGEROUS)\n" + " Use 'NULL' as the password to set empty password\n" + " Drive gets locked when user password is selected\n" + " --security-disable PWD Disable drive locking, using password PWD\n" + " --security-erase PWD Erase (locked) drive using password PWD (DANGEROUS)\n" + " (VERY VERY DANGEROUS -- DO NOT USE!!)\n" + " --security-erase-enhanced PWD\n" + " Enhanced-erase a (locked) drive, using password PWD\n" + " (VERY VERY DANGEROUS -- DO NOT USE!!)\n" + " --security-mode MODE Select security level (high/maximum) (default high)\n" + " h high security\n" + " m maximum security\n" + " --user-master USER Select user/master password (default master)\n" + " u user\n" + " m master\n\n" + ); +} +#endif + +#define GET_NUMBER(flag,num) num = 0; \ + if (!*p && argc && isdigit(**argv)) \ + p = *argv++, --argc; \ + while (isdigit(*p)) { \ + flag = 1; \ + num = (num * 10) + (*p++ - '0'); \ + } + +#define GET_STRING(flag, num) tmpstr = name; \ + tmpstr[0] = '\0'; \ + if (!*p && argc && isalnum(**argv)) \ + p = *argv++, --argc; \ + while (isalnum(*p) && (tmpstr - name) < 31) { \ + tmpstr[0] = *p++; \ + tmpstr[1] = '\0'; \ + ++tmpstr; \ + } \ + num = translate_xfermode(name); \ + if (num == -1) \ + flag = 0; \ + else \ + flag = 1; + +#define GET_ASCII_PASSWORD(flag, pwd) tmpstr = pwd; \ + memset(&pwd,0,sizeof(pwd)); \ + if (!*p && argc && isgraph(**argv)) \ + p = *argv++, --argc; \ + while ((tmpstr - pwd) < 32) { \ + if (!isgraph(*p)) { \ + if (*p > 0) { \ + flag = 0; \ + printf("Abort: Password contains non-printable characters!"); \ + } else flag = 1; \ + break; \ + } else {\ + sscanf(p,"%c",(unsigned char *) &tmpstr[0]); \ + p = p+1; \ + ++tmpstr; \ + } \ + } + +static int fromhex (unsigned char c) +{ + if (c >= '0' && c <= '9') + return (c - '0'); + if (c >= 'a' && c <= 'f') + return 10 + (c - 'a'); + if (c >= 'A' && c <= 'F') + return 10 + (c - 'A'); + fprintf(stderr, "bad char: '%c' 0x%02x\n", c, c); + exit(-1); +} + +static int ishex (char c) +{ + return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')); +} + +static int identify_from_stdin (void) +{ + unsigned short sbuf[512]; + int err, wc = 0; + + do { + int digit; + char d[4]; + + if (ishex(d[digit=0] = getchar()) + && ishex(d[++digit] = getchar()) + && ishex(d[++digit] = getchar()) + && ishex(d[++digit] = getchar())) { + sbuf[wc] = (fromhex(d[0]) << 12) | (fromhex(d[1]) << 8) | (fromhex(d[2]) << 4) | fromhex(d[3]); + __le16_to_cpus((__u16 *)(&sbuf[wc])); + ++wc; + } else if (d[digit] == EOF) { + goto eof; + } else if (wc == 0) { + /* skip over leading lines of cruft */ + while (d[digit] != '\n') { + if (d[digit] == EOF) + goto eof; + d[digit=0] = getchar(); + }; + } + } while (wc < 256); + putchar('\n'); + identify(sbuf); + return 0; +eof: + err = errno; + fprintf(stderr, "read only %u/256 IDENTIFY words from stdin: %s\n", wc, strerror(err)); + exit(err); +} + +int main(int argc, char **argv) +{ + char c, *p, *p1; + char *tmpstr; + char name[32]; +#ifdef HDIO_GET_QDMA + int neg; +#endif + +#ifndef _WIN32 + if ((progname = (char *) strrchr(*argv, '/')) == NULL) +#else + if ((progname = (char *) strrchr(*argv, '\\')) == NULL) +#endif + progname = *argv; + else + progname++; + ++argv; + + if (!--argc) + usage_error(1); + while (argc--) { + p = *argv++; + if (*p == '-') { + if (0 == strcmp(p, "--direct")) { + open_flags |= O_DIRECT; + ++flagcount; + continue; + } +#if defined(_WIN32) || defined(__CYGWIN__) + if (0 == strcmp(p, "--debug")) { + win32_debug++; + ++flagcount; + continue; + } +#endif + if (0 == strcmp(p, "--Istdin")) { + identify_from_stdin(); + ++flagcount; + continue; + } +#ifdef HDIO_DRIVE_CMD + if (0 == strcmp(p, "--Istdout")) { + get_IDentity = 2; + ++flagcount; + continue; + } +#endif + if (!*++p) + usage_error(1); + while ((c = *p++)) { + ++flagcount; + switch (c) { + case 'V': + fprintf(stdout, "%s %s\n", progname, VERSION); + exit(0); + break; + case 'v': + verbose = 1; + break; +#ifdef HDIO_DRIVE_CMD + case 'I': + get_IDentity = 1; + break; +#endif +#ifdef HDIO_GET_IDENTITY + case 'i': + get_identity = 1; + break; +#endif +#ifdef HDIO_GETGEO + case 'g': + get_geom = 1; + break; +#endif + case 'f': + do_flush = 1; + break; + case 'q': + quiet = 1; + noisy = 0; + break; +#ifdef HDIO_SET_UNMASKINTR + case 'u': + get_unmask = noisy; + noisy = 1; + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + if (*p == '0' || *p == '1') { + set_unmask = 1; + unmask = *p++ - '0'; + } + break; +#endif +#ifdef HDIO_SET_DMA + case 'd': + get_dma = noisy; + noisy = 1; + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + if (*p >= '0' && *p <= '9') { + set_dma = 1; + dma = *p++ - '0'; + } + break; +#endif +#ifdef HDIO_SET_NOWERR + case 'n': + get_nowerr = noisy; + noisy = 1; + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + if (*p == '0' || *p == '1') { + set_nowerr = 1; + nowerr = *p++ - '0'; + } + break; +#endif +#ifdef HDIO_SET_PIO_MODE + case 'p': + noisy_piomode = noisy; + noisy = 1; + GET_STRING(set_piomode,piomode); + break; +#endif +#ifdef BLKROGET + case 'r': + get_readonly = noisy; + noisy = 1; + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + if (*p == '0' || *p == '1') { + set_readonly = 1; + readonly = *p++ - '0'; + } + break; +#endif +#ifdef HDIO_SET_MULTCOUNT + case 'm': + get_mult = noisy; + noisy = 1; + GET_NUMBER(set_mult,mult); + break; +#endif +#ifdef HDIO_SET_32BIT + case 'c': + get_io32bit = noisy; + noisy = 1; + GET_NUMBER(set_io32bit,io32bit); + break; +#endif +#ifdef HDIO_DRIVE_CMD + case 's': + get_powerup_in_standby = noisy; + noisy = 1; + GET_NUMBER(set_powerup_in_standby,powerup_in_standby); + if (!set_powerup_in_standby) + fprintf(stderr, "-s: missing value\n"); + break; + + case 'S': + get_standby = noisy; + noisy = 1; + GET_NUMBER(set_standby,standby_requested); + if (!set_standby) + fprintf(stderr, "-S: missing value\n"); + break; + + case 'D': + get_defects = noisy; + noisy = 1; + GET_NUMBER(set_defects,defects); + if (!set_defects) + fprintf(stderr, "-D: missing value\n"); + break; +#endif +#ifdef CDROM_SELECT_SPEED + case 'E': + set_cdromspeed = 1; + GET_NUMBER(set_cdromspeed,cdromspeed); + if (!set_cdromspeed) + fprintf (stderr, "-s: missing value\n"); + break; +#endif /* CDROM_SELECT_SPEED */ +#ifdef HDIO_DRIVE_CMD + case 'P': + get_prefetch = noisy; + noisy = 1; + GET_NUMBER(set_prefetch,prefetch); + if (!set_prefetch) + fprintf(stderr, "-P: missing value\n"); + break; + + case 'X': + get_xfermode = noisy; + noisy = 1; + GET_STRING(set_xfermode,xfermode_requested); + if (!set_xfermode) + fprintf(stderr, "-X: missing value\n"); + break; + + case 'K': + get_dkeep = noisy; + noisy = 1; + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + if (*p == '0' || *p == '1') { + set_dkeep = 1; + dkeep = *p++ - '0'; + } else + fprintf(stderr, "-K: missing value (0/1)\n"); + break; + + case 'A': + get_lookahead = noisy; + noisy = 1; + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + if (*p == '0' || *p == '1') { + set_lookahead = 1; + lookahead = *p++ - '0'; + } else + fprintf(stderr, "-A: missing value (0/1)\n"); + break; + + case 'L': + get_doorlock = noisy; + noisy = 1; + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + if (*p == '0' || *p == '1') { + set_doorlock = 1; + doorlock = *p++ - '0'; + } else + fprintf(stderr, "-L: missing value (0/1)\n"); + break; + + case 'W': + get_wcache = noisy; + noisy = 1; + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + if (*p == '0' || *p == '1') { + set_wcache = 1; + wcache = *p++ - '0'; + } else + fprintf(stderr, "-W: missing value (0/1)\n"); + break; + + case 'C': + get_powermode = noisy; + noisy = 1; + break; + + case 'y': + get_standbynow = noisy; + noisy = 1; + set_standbynow = 1; + break; + + case 'Y': + get_sleepnow = noisy; + noisy = 1; + set_sleepnow = 1; + break; +#endif /* HDIO_DRIVE_CMD */ +#ifdef BLKRRPART + case 'z': + reread_partn = 1; + break; +#endif +#ifdef HDIO_DRIVE_CMD + case 'Z': + get_seagate = noisy; + noisy = 1; + set_seagate = 1; + break; + case 'H': + get_hitachi_temp = noisy; + noisy = 1; + break; +#endif /* HDIO_DRIVE_CMD */ +#ifdef HDIO_GET_KEEPSETTINGS + case 'k': + get_keep = noisy; + noisy = 1; + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + if (*p == '0' || *p == '1') { + set_keep = 1; + keep = *p++ - '0'; + } + break; +#endif /* HDIO_GET_KEEPSETTINGS */ +#ifdef HDIO_UNREGISTER_HWIF + case 'U': + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + + if(! p) { + fprintf(stderr, "expected hwif_nr\n"); + exit(1); + } + + sscanf(p++, "%i", &hwif); + + unregister_hwif = 1; + break; +#endif /* HDIO_UNREGISTER_HWIF */ +#ifdef HDIO_SCAN_HWIF + case 'R': + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + + if(! p) { + fprintf(stderr, "expected hwif_data\n"); + exit(1); + } + + sscanf(p++, "%i", &hwif_data); + + if (argc && isdigit(**argv)) + p = *argv++, --argc; + else { + fprintf(stderr, "expected hwif_ctrl\n"); + exit(1); + } + + sscanf(p, "%i", &hwif_ctrl); + + if (argc && isdigit(**argv)) + p = *argv++, --argc; + else { + fprintf(stderr, "expected hwif_irq\n"); + exit(1); + } + + sscanf(p, "%i", &hwif_irq); + + *p = '\0'; + + scan_hwif = 1; + break; +#endif /* HDIO_SCAN_HWIF */ +#ifdef HDIO_GET_QDMA + case 'Q': + get_dma_q = noisy; + noisy = 1; + neg = 0; +#ifdef HDIO_SET_QDMA + GET_NUMBER(set_dma_q, dma_q); + if (neg) + dma_q = -dma_q; +#endif + break; +#endif +#ifdef HDIO_DRIVE_RESET + case 'w': + perform_reset = 1; + break; +#endif /* HDIO_DRIVE_RESET */ +#ifdef HDIO_TRISTATE_HWIF + case 'x': + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + if (*p == '0' || *p == '1') { + perform_tristate = 1; + tristate = *p++ - '0'; + } else + fprintf(stderr, "-x: missing value (0/1)\n"); + break; + +#endif /* HDIO_TRISTATE_HWIF */ +#ifdef BLKRASET + case 'a': + get_fsreadahead = noisy; + noisy = 1; + GET_NUMBER(set_fsreadahead,fsreadahead); + break; +#endif +#ifdef HDIO_DRIVE_CMD + case 'B': + get_apmmode = noisy; + noisy = 1; + GET_NUMBER(set_apmmode,apmmode); + if (!set_apmmode) + printf("-B: missing value (1-255)\n"); + break; +#endif /* HDIO_DRIVE_CMD */ + case 't': + GET_NUMBER(do_timings,timings); + if (!do_timings) + timings = 3.0; + do_timings = 1; + do_flush = 1; + break; + case 'T': + GET_NUMBER(do_ctimings,ctimings); + if (!do_ctimings) + ctimings = 2.0; + do_ctimings = 1; + do_flush = 1; + break; +#ifdef HDIO_GET_BUSSTATE + case 'b': + get_busstate = noisy; + noisy = 1; + if (!*p && argc && isdigit(**argv)) + p = *argv++, --argc; + switch (*p) { + case '0': + case '1': + case '2': + set_busstate = 1; + busstate = *p++ - '0'; + break; + default: + break; + } + break; +#endif +#ifdef HDIO_GET_ACOUSTIC + case 'M': + get_acoustic = noisy; /* are we noisy? */ + noisy = 1; + GET_NUMBER(set_acoustic,acoustic); + break; +#endif /* HDIO_GET_ACOUSTIC */ + case 'h': + usage_error(0); + break; +#if defined(WIN_SECURITY_FREEZE_LOCK) && defined(HDIO_DRIVE_CMD) + case '-': + p1 = p; //Save Switch-String + while (isgraph(*p)) + p++; //Move P forward + if (0 == strcmp(p1, "security-freeze")) { + set_freeze = 1; + } +#if defined(WIN_SECURITY_SET_PASS) && defined(IDE_DRIVE_TASK_NO_DATA) + else if (0 == strcmp(p1, "security-help")) { + security_help(); + if (!argc) + exit(0); + } else if (0 == strcmp(p1, "security-unlock")) { + open_flags = O_RDWR; + set_security = 1; + security_command = WIN_SECURITY_UNLOCK; + GET_ASCII_PASSWORD(set_security,security_password); + } else if (0 == strcmp(p1, "security-set-pass")) { + open_flags=O_RDWR; + set_security = 1; + security_command = WIN_SECURITY_SET_PASS; + GET_ASCII_PASSWORD(set_security,security_password); + if (strcmp(security_password, "NULL") == 0) + memset(&security_password, 0, sizeof(security_password)); + } else if (0 == strcmp(p1, "security-disable")) { + open_flags = O_RDWR; + set_security = 1; + security_command = WIN_SECURITY_DISABLE; + GET_ASCII_PASSWORD(set_security,security_password); + } else if (0 == strcmp(p1, "security-erase")) { + open_flags = O_RDWR; + set_security = 1; + security_command = WIN_SECURITY_ERASE_UNIT; + GET_ASCII_PASSWORD(set_security,security_password); + } else if (0 == strcmp(p1, "security-erase-enhanced")) { + open_flags = O_RDWR; + set_security = 1; + enhanced_erase = 1; + security_command = WIN_SECURITY_ERASE_UNIT; + GET_ASCII_PASSWORD(set_security,security_password); + } else if (0 == strcmp(p1, "security-mode")) { + if (!*p && argc && isalpha(**argv)) + p = *argv++, --argc; + if (*p == 'm') /* max */ + security_mode = 1; + ++p; + } else if (0 == strcmp(p1, "user-master")) { + if (!*p && argc && isalpha(**argv)) + p = *argv++, --argc; + if (*p == 'u') /* user */ + security_master = 0; + ++p; + } +#endif // defined(WIN_SECURITY_SET_PASS) && defined(IDE_DRIVE_TASK_NO_DATA) + else { + usage_error(1); + } + break; +#endif // defined(WIN_SECURITY_FREEZE_LOCK) && defined(HDIO_DRIVE_CMD) + default: + usage_error(1); + } + } + if (!argc) + usage_error(1); + } else { + process_dev (p); + } + } + exit (0); +} diff --git a/hdparm.h b/hdparm.h new file mode 100644 index 0000000..04a82c7 --- /dev/null +++ b/hdparm.h @@ -0,0 +1,41 @@ +/* Some prototypes for extern functions. */ + +#if !defined(_WIN32) && !defined(__CYGWIN__) +#include /* for __u16 */ +typedef unsigned long long __ull; +#else +typedef unsigned char __u8; +typedef unsigned short __u16; +typedef unsigned long __u32; +#ifndef _MSC_VER +typedef unsigned long long __u64; +typedef unsigned long long __ull; +#else +typedef unsigned __int64 __u64; +typedef unsigned __int64 __ull; +#endif +#endif + + +#if !defined(__GNUC__) && !defined(__attribute__) +#define __attribute__(x) /* if not using GCC, turn off the __attribute__ + compiler-advice feature. */ +#endif + +/* identify() is the only extern function used across two source files. The + others, though, were declared in hdparm.c with global scope; since other + functions in that file have static (file) scope, I assume the difference is + intentional. */ +extern void identify (__u16 *id_supplied); + +extern void usage_error(int out) __attribute__((noreturn)); +extern int main(int argc, char **argv) __attribute__((noreturn)); +extern void flush_buffer_cache (int fd); +extern int seek_to_zero (int fd); +extern int read_big_block (int fd, char *buf); +extern void time_cache (int fd); +extern void time_device (int fd); +extern void no_scsi (void); +extern void no_xt (void); +extern void process_dev (char *devname); + diff --git a/hdparm.lsm b/hdparm.lsm new file mode 100644 index 0000000..526ac4e --- /dev/null +++ b/hdparm.lsm @@ -0,0 +1,62 @@ +Begin4 +Title: hdparm +Version: 6.9 +Entered-date: 2006-10-25 +Description: hdparm - get/set hard disk parameters for Linux IDE drives. + v6.9 --Istdin fix, new -s flag, new SCT reporting, fixed -T x2 error.. + v6.8 improve parsing/operation of --Istdin function + v6.7 misc fixes, new -H flag, fixed -C flag + v6.6 fix build on Redhat/Fedora + v6.5 fix -I bugs introduced in v6.4 + v6.4 major update for -I, bug fix for -C + v6.3 report ATA revisions > 7 + v6.2 major rework of ATA Security Commands + v6.1 bug fix for BLKGETSIZE + v6.0 bug fix for BLKGETSIZE64; new ATA Security Commands + v5.9 bug fix: -W1/-W0 now work again + v5.8 minor fixes + v5.7 bug fixes, --direct flag (O_DIRECT), other enhancements + v5.6 cleanups, new "-Istdout" flag, removed MAJOR-nr restrictions + v5.5 added debian scripts, minor fixes + v5.4 lots of fixes/updates, new timing measurement code + v5.3 endian fixes, other stuff + v5.2 compile fixes for 2.5.xx + v5.1 fixed segfault in "-i" on older drives + v5.0 lots of updates and new features + v4.9 fixed compile error with 2.5.xx kernels + v4.8 changed -Q to allow specifying queue depth + v4.7 added -z, -Q, -M flags; expanded parm range for -p + v4.6 manpage updates, version number corrections + v4.5 cleanups, mostly courtesy of Maciej W. Rozycki + v4.4 added -b option get/set bus state + v4.3 use unsigned format for most stuff + v4.2 lots of updates, rewritten -I option + v4.0 no such version ever released + v3.9 support for ide[6-9], more cosmetics + v3.8 added -E, -R, and -U options; cosmetic fixes + v3.7 added UDMA mode display + v3.6 mostly cosmetic fixes, xt & ide4+ide5 support + v3.5 fixed udma display/compile for older kernels + v3.4 mostly cosmetic updates + v3.3 add -C, -y, -Y flags for power management + v3.2 flush buffer cache after -t or -T + v3.1 add support for "-p[6789]" + v3.0 move cache timings to new -T option + v2.9 update author's email addr; document -I option + v2.8 some fixes; removed "Estimating raw driver speed" from -t + v2.7 fixed "hdparm -c" (broke in 2.6); fixed .lsm file + v2.6 added "-p" flag to set IDE interface chipset PIO modes + v2.5 cosmetic fixes, manpage clarifications + v2.4 added support for -d (DMA) flag + v2.3 added runtime flags for 32-bit mode; fixed -t for SCSI. + v2.0 adds zillions of features for the new (E)IDE driver +Keywords: ide eide ata atapi performance kernel cd cdrom disk device driver +Author: mlord@pobox.com (Mark Lord) +Maintained-by: mlord@pobox.com (Mark Lord) +Primary-site: http://sourceforge.net/projects/hdparm/ +Alternate-site: http://www.ibiblio.org/pub/Linux/system/hardware + 47K hdparm-6.9.tar.gz + 1K hdparm.lsm +Platforms: Linux, kernels 2.2 through 2.6 +Copying-policy: BSD License +End diff --git a/identify.c b/identify.c new file mode 100644 index 0000000..051557a --- /dev/null +++ b/identify.c @@ -0,0 +1,1186 @@ +#include +#include +#include +#include +#if !defined(_WIN32) && !defined(__CYGWIN__) +#include +#include + +#if __BYTE_ORDER == __BIG_ENDIAN +#define __USE_XOPEN +#endif + +#include +#endif +#include "hdparm.h" + +/* device types */ +/* ------------ */ +#define NO_DEV 0xffff +#define ATA_DEV 0x0000 +#define ATAPI_DEV 0x0001 + +/* word definitions */ +/* ---------------- */ +#define GEN_CONFIG 0 /* general configuration */ +#define LCYLS 1 /* number of logical cylinders */ +#define CONFIG 2 /* specific configuration */ +#define LHEADS 3 /* number of logical heads */ +#define TRACK_BYTES 4 /* number of bytes/track (ATA-1) */ +#define SECT_BYTES 5 /* number of bytes/sector (ATA-1) */ +#define LSECTS 6 /* number of logical sectors/track */ +#define START_SERIAL 10 /* ASCII serial number */ +#define LENGTH_SERIAL 10 /* 10 words (20 bytes or characters) */ +#define BUF_TYPE 20 /* buffer type (ATA-1) */ +#define BUF_SIZE 21 /* buffer size (ATA-1) */ +#define RW_LONG 22 /* extra bytes in R/W LONG cmd ( < ATA-4)*/ +#define START_FW_REV 23 /* ASCII firmware revision */ +#define LENGTH_FW_REV 4 /* 4 words (8 bytes or characters) */ +#define START_MODEL 27 /* ASCII model number */ +#define LENGTH_MODEL 20 /* 20 words (40 bytes or characters) */ +#define SECTOR_XFER_MAX 47 /* r/w multiple: max sectors xfered */ +#define DWORD_IO 48 /* can do double-word IO (ATA-1 only) */ +#define CAPAB_0 49 /* capabilities */ +#define CAPAB_1 50 +#define PIO_MODE 51 /* max PIO mode supported (obsolete)*/ +#define DMA_MODE 52 /* max Singleword DMA mode supported (obs)*/ +#define WHATS_VALID 53 /* what fields are valid */ +#define LCYLS_CUR 54 /* current logical cylinders */ +#define LHEADS_CUR 55 /* current logical heads */ +#define LSECTS_CUR 56 /* current logical sectors/track */ +#define CAPACITY_LSB 57 /* current capacity in sectors */ +#define CAPACITY_MSB 58 +#define SECTOR_XFER_CUR 59 /* r/w multiple: current sectors xfered */ +#define LBA_SECTS_LSB 60 /* LBA: total number of user */ +#define LBA_SECTS_MSB 61 /* addressable sectors */ +#define SINGLE_DMA 62 /* singleword DMA modes */ +#define MULTI_DMA 63 /* multiword DMA modes */ +#define ADV_PIO_MODES 64 /* advanced PIO modes supported */ + /* multiword DMA xfer cycle time: */ +#define DMA_TIME_MIN 65 /* - minimum */ +#define DMA_TIME_NORM 66 /* - manufacturer's recommended */ + /* minimum PIO xfer cycle time: */ +#define PIO_NO_FLOW 67 /* - without flow control */ +#define PIO_FLOW 68 /* - with IORDY flow control */ +#define PKT_REL 71 /* typical #ns from PKT cmd to bus rel */ +#define SVC_NBSY 72 /* typical #ns from SERVICE cmd to !BSY */ +#define CDR_MAJOR 73 /* CD ROM: major version number */ +#define CDR_MINOR 74 /* CD ROM: minor version number */ +#define QUEUE_DEPTH 75 /* queue depth */ +#define SATA_CAP_0 76 /* Serial ATA Capabilities */ +#define SATA_RESERVED_77 77 /* reserved for future Serial ATA definition */ +#define SATA_SUPP_0 78 /* Serial ATA features supported */ +#define SATA_EN_0 79 /* Serial ATA features enabled */ +#define MAJOR 80 /* major version number */ +#define MINOR 81 /* minor version number */ +#define CMDS_SUPP_0 82 /* command/feature set(s) supported */ +#define CMDS_SUPP_1 83 +#define CMDS_SUPP_2 84 +#define CMDS_EN_0 85 /* command/feature set(s) enabled */ +#define CMDS_EN_1 86 +#define CMDS_EN_2 87 +#define ULTRA_DMA 88 /* ultra DMA modes */ + /* time to complete security erase */ +#define ERASE_TIME 89 /* - ordinary */ +#define ENH_ERASE_TIME 90 /* - enhanced */ +#define ADV_PWR 91 /* current advanced power management level + in low byte, 0x40 in high byte. */ +#define PSWD_CODE 92 /* master password revision code */ +#define HWRST_RSLT 93 /* hardware reset result */ +#define ACOUSTIC 94 /* acoustic mgmt values ( >= ATA-6) */ +#define LBA_LSB 100 /* LBA: maximum. Currently only 48 */ +#define LBA_MID 101 /* bits are used, but addr 103 */ +#define LBA_48_MSB 102 /* has been reserved for LBA in */ +#define LBA_64_MSB 103 /* the future. */ +#define CMDS_SUPP_3 119 +#define CMDS_EN_3 120 +#define RM_STAT 127 /* removable media status notification feature set support */ +#define SECU_STATUS 128 /* security status */ +#define CFA_PWR_MODE 160 /* CFA power mode 1 */ +#define START_MEDIA 176 /* media serial number */ +#define LENGTH_MEDIA 20 /* 20 words (40 bytes or characters)*/ +#define START_MANUF 196 /* media manufacturer I.D. */ +#define LENGTH_MANUF 10 /* 10 words (20 bytes or characters) */ +#define SCT_SUPP 206 /* SMART command transport (SCT) support */ +#define TRANSPORT_MAJOR 222 /* PATA vs. SATA etc.. */ +#define TRANSPORT_MINOR 223 /* minor revision number */ +#define INTEGRITY 255 /* integrity word */ + +/* bit definitions within the words */ +/* -------------------------------- */ + +/* many words are considered valid if bit 15 is 0 and bit 14 is 1 */ +#define VALID 0xc000 +#define VALID_VAL 0x4000 +/* many words are considered invalid if they are either all-0 or all-1 */ +#define NOVAL_0 0x0000 +#define NOVAL_1 0xffff + +/* word 0: gen_config */ +#define NOT_ATA 0x8000 +#define NOT_ATAPI 0x4000 /* (check only if bit 15 == 1) */ +#define MEDIA_REMOVABLE 0x0080 +#define DRIVE_NOT_REMOVABLE 0x0040 /* bit obsoleted in ATA 6 */ +#define INCOMPLETE 0x0004 +#define CFA_SUPPORT_VAL 0x848a /* 848a=CFA feature set support */ +#define DRQ_RESPONSE_TIME 0x0060 +#define DRQ_3MS_VAL 0x0000 +#define DRQ_INTR_VAL 0x0020 +#define DRQ_50US_VAL 0x0040 +#define PKT_SIZE_SUPPORTED 0x0003 +#define PKT_SIZE_12_VAL 0x0000 +#define PKT_SIZE_16_VAL 0x0001 +#define EQPT_TYPE 0x1f00 +#define SHIFT_EQPT 8 + +#define CDROM 0x0005 +const char *pkt_str[] = { + "Direct-access device", /* word 0, bits 12-8 = 00 */ + "Sequential-access device", /* word 0, bits 12-8 = 01 */ + "Printer", /* word 0, bits 12-8 = 02 */ + "Processor", /* word 0, bits 12-8 = 03 */ + "Write-once device", /* word 0, bits 12-8 = 04 */ + "CD-ROM", /* word 0, bits 12-8 = 05 */ + "Scanner", /* word 0, bits 12-8 = 06 */ + "Optical memory", /* word 0, bits 12-8 = 07 */ + "Medium changer", /* word 0, bits 12-8 = 08 */ + "Communications device", /* word 0, bits 12-8 = 09 */ + "ACS-IT8 device", /* word 0, bits 12-8 = 0a */ + "ACS-IT8 device", /* word 0, bits 12-8 = 0b */ + "Array controller", /* word 0, bits 12-8 = 0c */ + "Enclosure services", /* word 0, bits 12-8 = 0d */ + "Reduced block command device", /* word 0, bits 12-8 = 0e */ + "Optical card reader/writer", /* word 0, bits 12-8 = 0f */ + "", /* word 0, bits 12-8 = 10 */ + "", /* word 0, bits 12-8 = 11 */ + "", /* word 0, bits 12-8 = 12 */ + "", /* word 0, bits 12-8 = 13 */ + "", /* word 0, bits 12-8 = 14 */ + "", /* word 0, bits 12-8 = 15 */ + "", /* word 0, bits 12-8 = 16 */ + "", /* word 0, bits 12-8 = 17 */ + "", /* word 0, bits 12-8 = 18 */ + "", /* word 0, bits 12-8 = 19 */ + "", /* word 0, bits 12-8 = 1a */ + "", /* word 0, bits 12-8 = 1b */ + "", /* word 0, bits 12-8 = 1c */ + "", /* word 0, bits 12-8 = 1d */ + "", /* word 0, bits 12-8 = 1e */ + "Unknown", /* word 0, bits 12-8 = 1f */ +}; +const char *ata1_cfg_str[] = { /* word 0 in ATA-1 mode */ + "reserved", /* bit 0 */ + "hard sectored", /* bit 1 */ + "soft sectored", /* bit 2 */ + "not MFM encoded ", /* bit 3 */ + "head switch time > 15us", /* bit 4 */ + "spindle motor control option", /* bit 5 */ + "fixed drive", /* bit 6 */ + "removable drive", /* bit 7 */ + "disk xfer rate <= 5Mbs", /* bit 8 */ + "disk xfer rate > 5Mbs, <= 10Mbs", /* bit 9 */ + "disk xfer rate > 5Mbs", /* bit 10 */ + "rotational speed tol.", /* bit 11 */ + "data strobe offset option", /* bit 12 */ + "track offset option", /* bit 13 */ + "format speed tolerance gap reqd", /* bit 14 */ + "ATAPI" /* bit 14 */ +}; + +/* word 1: number of logical cylinders */ +#define LCYLS_MAX 0x3fff /* maximum allowable value */ + +/* word 2: specific configureation + * (a) require SET FEATURES to spin-up + * (b) require spin-up to fully reply to IDENTIFY DEVICE + */ +#define STBY_NID_VAL 0x37c8 /* (a) and (b) */ +#define STBY_ID_VAL 0x738c /* (a) and not (b) */ +#define PWRD_NID_VAL 0x8c73 /* not (a) and (b) */ +#define PWRD_ID_VAL 0xc837 /* not (a) and not (b) */ + +/* words 47 & 59: sector_xfer_max & sector_xfer_cur */ +#define SECTOR_XFER 0x00ff /* sectors xfered on r/w multiple cmds*/ +#define MULTIPLE_SETTING_VALID 0x0100 /* 1=multiple sector setting is valid */ + +/* word 49: capabilities 0 */ +#define STD_STBY 0x2000 /* 1=standard values supported (ATA); + 0=vendor specific values */ +#define IORDY_SUP 0x0800 /* 1=support; 0=may be supported */ +#define IORDY_OFF 0x0400 /* 1=may be disabled */ +#define LBA_SUP 0x0200 /* 1=Logical Block Address support */ +#define DMA_SUP 0x0100 /* 1=Direct Memory Access support */ +#define DMA_IL_SUP 0x8000 /* 1=interleaved DMA support (ATAPI) */ +#define CMD_Q_SUP 0x4000 /* 1=command queuing support (ATAPI) */ +#define OVLP_SUP 0x2000 /* 1=overlap operation support (ATAPI) */ +#define SWRST_REQ 0x1000 /* 1=ATA SW reset required (ATAPI, obsolete */ + +/* word 50: capabilities 1 */ +#define MIN_STANDBY_TIMER 0x0001 /* 1=device specific standby timer value minimum */ + +/* words 51 & 52: PIO & DMA cycle times */ +#define MODE 0xff00 /* the mode is in the MSBs */ + +/* word 53: whats_valid */ +#define OK_W88 0x0004 /* the ultra_dma info is valid */ +#define OK_W64_70 0x0002 /* see above for word descriptions */ +#define OK_W54_58 0x0001 /* current cyl, head, sector, cap. info valid */ + +/*word 63,88: dma_mode, ultra_dma_mode*/ +#define MODE_MAX 7 /* bit definitions force udma <=7 (when + * udma >=8 comes out it'll have to be + * defined in a new dma_mode word!) */ + +/* word 64: PIO transfer modes */ +#define PIO_SUP 0x00ff /* only bits 0 & 1 are used so far, */ +#define PIO_MODE_MAX 8 /* but all 8 bits are defined */ + +/* word 75: queue_depth */ +#define DEPTH_BITS 0x001f /* bits used for queue depth */ + +/* words 80-81: version numbers */ +/* NOVAL_0 or NOVAL_1 means device does not report version */ + +/* word 81: minor version number */ +#define MINOR_MAX 0x22 +const char *minor_str[MINOR_MAX+2] = { /* word 81 value: */ + "Unspecified", /* 0x0000 */ + "ATA-1 X3T9.2 781D prior to revision 4", /* 0x0001 */ + "ATA-1 published, ANSI X3.221-1994", /* 0x0002 */ + "ATA-1 X3T9.2 781D revision 4", /* 0x0003 */ + "ATA-2 published, ANSI X3.279-1996", /* 0x0004 */ + "ATA-2 X3T10 948D prior to revision 2k", /* 0x0005 */ + "ATA-3 X3T10 2008D revision 1", /* 0x0006 */ + "ATA-2 X3T10 948D revision 2k", /* 0x0007 */ + "ATA-3 X3T10 2008D revision 0", /* 0x0008 */ + "ATA-2 X3T10 948D revision 3", /* 0x0009 */ + "ATA-3 published, ANSI X3.298-199x", /* 0x000a */ + "ATA-3 X3T10 2008D revision 6", /* 0x000b */ + "ATA-3 X3T13 2008D revision 7 and 7a", /* 0x000c */ + "ATA/ATAPI-4 X3T13 1153D revision 6", /* 0x000d */ + "ATA/ATAPI-4 T13 1153D revision 13", /* 0x000e */ + "ATA/ATAPI-4 X3T13 1153D revision 7", /* 0x000f */ + "ATA/ATAPI-4 T13 1153D revision 18", /* 0x0010 */ + "ATA/ATAPI-4 T13 1153D revision 15", /* 0x0011 */ + "ATA/ATAPI-4 published, ANSI INCITS 317-1998", /* 0x0012 */ + "ATA/ATAPI-5 T13 1321D revision 3", + "ATA/ATAPI-4 T13 1153D revision 14", /* 0x0014 */ + "ATA/ATAPI-5 T13 1321D revision 1", /* 0x0015 */ + "ATA/ATAPI-5 published, ANSI INCITS 340-2000", /* 0x0016 */ + "ATA/ATAPI-4 T13 1153D revision 17", /* 0x0017 */ + "ATA/ATAPI-6 T13 1410D revision 0", /* 0x0018 */ + "ATA/ATAPI-6 T13 1410D revision 3a", /* 0x0019 */ + "ATA/ATAPI-7 T13 1532D revision 1", /* 0x001a */ + "ATA/ATAPI-6 T13 1410D revision 2", /* 0x001b */ + "ATA/ATAPI-6 T13 1410D revision 1", /* 0x001c */ + "ATA/ATAPI-7 published, ANSI INCITS 397-2005", /* 0x001d */ + "ATA/ATAPI-7 T13 1532D revision 0", /* 0x001e */ + "Reserved", /* 0x001f */ + "Reserved", /* 0x0020 */ + "ATA/ATAPI-7 T13 1532D revision 4a", /* 0x0021 */ + "ATA/ATAPI-6 published, ANSI INCITS 361-2002", /* 0x0022 */ + "Reserved", /* 0x0023-0xfffe*/ +}; +const char actual_ver[MINOR_MAX+2] = { + /* word 81 value: */ + 0, /* 0x0000 WARNING: */ + 1, /* 0x0001 WARNING: */ + 1, /* 0x0002 WARNING: */ + 1, /* 0x0003 WARNING: */ + 2, /* 0x0004 WARNING: This array */ + 2, /* 0x0005 WARNING: corresponds */ + 3, /* 0x0006 WARNING: *exactly* */ + 2, /* 0x0007 WARNING: to the ATA/ */ + 3, /* 0x0008 WARNING: ATAPI version */ + 2, /* 0x0009 WARNING: listed in */ + 3, /* 0x000a WARNING: the */ + 3, /* 0x000b WARNING: minor_str */ + 3, /* 0x000c WARNING: array */ + 4, /* 0x000d WARNING: above. */ + 4, /* 0x000e WARNING: */ + 4, /* 0x000f WARNING: if you change */ + 4, /* 0x0010 WARNING: that one, */ + 4, /* 0x0011 WARNING: change this one */ + 4, /* 0x0012 WARNING: too!!! */ + 5, /* 0x0013 WARNING: */ + 4, /* 0x0014 WARNING: */ + 5, /* 0x0015 WARNING: */ + 5, /* 0x0016 WARNING: */ + 4, /* 0x0017 WARNING: */ + 6, /* 0x0018 WARNING: */ + 6, /* 0x0019 WARNING: */ + 7, /* 0x001a WARNING: */ + 6, /* 0x001b WARNING: */ + 6, /* 0x001c WARNING: */ + 7, /* 0x001d WARNING: */ + 7, /* 0x001e WARNING: */ + 0, /* 0x001f WARNING: */ + 0, /* 0x0020 WARNING: */ + 7, /* 0x0021 WARNING: */ + 6, /* 0x0022 WARNING: */ + 0 /* 0x0023-0xfffe */ +}; + +/* words 82-84: cmds/feats supported */ +#define CMDS_W82 0x77ff /* word 82: defined command locations*/ +#define CMDS_W83 0x3fff /* word 83: defined command locations*/ +#define CMDS_W84 0x27ff /* word 84: defined command locations*/ +#define SUPPORT_48_BIT 0x0400 +#define NUM_CMD_FEAT_STR 48 + +static const char unknown[8] = "obsolete"; +//static const char unknown[8] = "unknown"; +#define unknown "unknown-" + +static const char *feat_0_str[16] = { + "obsolete 82[15]", /* word 82 bit 15: obsolete */ + "NOP cmd", /* word 82 bit 14 */ + "READ_BUFFER command", /* word 82 bit 13 */ + "WRITE_BUFFER command", /* word 82 bit 12 */ + "WRITE_VERIFY command", /* word 82 bit 11: obsolete */ + "Host Protected Area feature set", /* word 82 bit 10 */ + "DEVICE_RESET command", /* word 82 bit 9 */ + "SERVICE interrupt", /* word 82 bit 8 */ + "Release interrupt", /* word 82 bit 7 */ + "Look-ahead", /* word 82 bit 6 */ + "Write cache", /* word 82 bit 5 */ + "PACKET command feature set", /* word 82 bit 4 */ + "Power Management feature set", /* word 82 bit 3 */ + "Removable Media feature set", /* word 82 bit 2 */ + "Security Mode feature set", /* word 82 bit 1 */ + "SMART feature set" /* word 82 bit 0 */ +}; +static const char *feat_1_str[16] = { + NULL, /* word 83 bit 15: !valid bit */ + NULL, /* word 83 bit 14: valid bit */ + "FLUSH_CACHE_EXT", /* word 83 bit 13 */ + "Mandatory FLUSH_CACHE", /* word 83 bit 12 */ + "Device Configuration Overlay feature set", /* word 83 bit 11 */ + "48-bit Address feature set", /* word 83 bit 10 */ + "Automatic Acoustic Management feature set", /* word 83 bit 9 */ + "SET_MAX security extension", /* word 83 bit 8 */ + "Address Offset Reserved Area Boot", /* word 83 bit 7 */ + "SET_FEATURES required to spinup after power up",/* word 83 bit 6 */ + "Power-Up In Standby feature set", /* word 83 bit 5 */ + "Removable Media Status Notification feature set",/* word 83 bit 4 */ + "Advanced Power Management feature set", /* word 83 bit 3 */ + "CFA feature set", /* word 83 bit 2 */ + "READ/WRITE_DMA_QUEUED", /* word 83 bit 1 */ + "DOWNLOAD_MICROCODE" /* word 83 bit 0 */ +}; +static const char *feat_2_str[16] = { + NULL, /* word 84 bit 15: !valid bit */ + NULL, /* word 84 bit 14: valid bit */ + "IDLE_IMMEDIATE with UNLOAD", /* word 84 bit 13 */ + "unknown 84[12]", /* word 84 bit 12 */ + "unknown 84[11]", /* word 84 bit 11 */ + "URG for WRITE_STREAM[_DMA]_EXT", /* word 84 bit 10 */ + "URG for READ_STREAM[_DMA]_EXT", /* word 84 bit 9 */ + "64-bit World wide name", /* word 84 bit 8 */ + "WRITE_DMA_QUEUED_FUA_EXT", /* word 84 bit 7 */ + "WRITE_{DMA|MULTIPLE}_FUA_EXT", /* word 84 bit 6 */ + "General Purpose Logging feature set", /* word 84 bit 5 */ + "Media Card Pass-Through", /* word 84 bit 4 */ + "Media Card Pass Through Command feature set", /* word 84 bit 3 */ + "Media serial number", /* word 84 bit 2 */ + "SMART self-test", /* word 84 bit 1 */ + "SMART error logging" /* word 84 bit 0 */ +}; +static const char *feat_3_str[16] = { + NULL, /* word 119 bit 15: !valid bit */ + NULL, /* word 119 bit 14: valid bit */ + "unknown 119[13]", /* word 119 bit 13 */ + "unknown 119[12]", /* word 119 bit 12 */ + "unknown 119[11]", /* word 119 bit 11 */ + "unknown 119[10]", /* word 119 bit 10 */ + "unknown 119[9]", /* word 119 bit 9 */ + "unknown 119[8]", /* word 119 bit 8 */ + "unknown 119[7]", /* word 119 bit 7 */ + "unknown 119[6]", /* word 119 bit 6 */ + "unknown 119[5]", /* word 119 bit 5 */ + "Segmented DOWNLOAD_MICROCODE", /* word 119 bit 4 */ + "{READ,WRITE}_DMA_EXT_GPL commands", /* word 119 bit 3 */ + "WRITE_UNCORRECTABLE command", /* word 119 bit 2 */ + "Write-Read-Verify feature set", /* word 119 bit 1 */ + "unknown 119[0]" /* word 119 bit 0: reserved for DT2014 */ +}; +static const char *cap_sata0_str[16] = { + "unknown 76[15]", /* word 76 bit 15 */ + "unknown 76[14]", /* word 76 bit 14 */ + "unknown 76[13]", /* word 76 bit 13 */ + "unknown 76[12]", /* word 76 bit 12 */ + "unknown 76[11]", /* word 76 bit 11 */ + "Phy event counters", /* word 76 bit 10 */ + "Host-initiated interface power management", /* word 76 bit 9 */ + "Native Command Queueing (NCQ)", /* word 76 bit 8 */ + "unknown 76[7]", /* word 76 bit 7 */ + "unknown 76[6]", /* word 76 bit 6 */ + "unknown 76[5]", /* word 76 bit 5 */ + "unknown 76[4]", /* word 76 bit 4 */ + "unknown 76[3]", /* word 76 bit 3 */ + "SATA-II signaling speed (3.0Gb/s)", /* word 76 bit 2 */ + "SATA-I signaling speed (1.5Gb/s)", /* word 76 bit 1 */ + "unknown 76[0]" /* word 76 bit 0 */ +}; +static const char *feat_sata0_str[16] = { + "unknown 78[15]", /* word 78 bit 15 */ + "unknown 78[14]", /* word 78 bit 14 */ + "unknown 78[13]", /* word 78 bit 13 */ + "unknown 78[12]", /* word 78 bit 12 */ + "unknown 78[11]", /* word 78 bit 11 */ + "unknown 78[10]", /* word 78 bit 10 */ + "unknown 78[9]", /* word 78 bit 9 */ + "unknown 78[8]", /* word 78 bit 8 */ + "unknown 78[7]", /* word 78 bit 7 */ + "Software settings preservation", /* word 78 bit 6 */ + "unknown 78[5]", /* word 78 bit 5 */ + "In-order data delivery", /* word 78 bit 4 */ + "Device-initiated interface power management", /* word 78 bit 3 */ + "DMA Setup Auto-Activate optimization", /* word 78 bit 2 */ + "Non-Zero buffer offsets in DMA Setup FIS", /* word 78 bit 1 */ + "unknown 78[0]" /* word 78 bit 0 */ +}; + +/* words 85-87: cmds/feats enabled */ +/* use cmd_feat_str[] to display what commands and features have + * been enabled with words 85-87 + */ + +/* words 89, 90, SECU ERASE TIME */ +#define ERASE_BITS 0x00ff + +/* word 92: master password revision */ +/* NOVAL_0 or NOVAL_1 means no support for master password revision */ + +/* word 93: hw reset result */ +#define CBLID 0x2000 /* CBLID status */ +#define RST0 0x0001 /* 1=reset to device #0 */ +#define DEV_DET 0x0006 /* how device num determined */ +#define JUMPER_VAL 0x0002 /* device num determined by jumper */ +#define CSEL_VAL 0x0004 /* device num determined by CSEL_VAL */ + +/* word 127: removable media status notification feature set support */ +#define RM_STAT_BITS 0x0003 +#define RM_STAT_SUP 0x0001 + +/* word 128: security */ +#define SECU_ENABLED 0x0002 +#define SECU_LEVEL 0x0100 /* was 0x0010 */ +#define NUM_SECU_STR 6 +const char *secu_str[] = { + "supported", /* word 128, bit 0 */ + "enabled", /* word 128, bit 1 */ + "locked", /* word 128, bit 2 */ + "frozen", /* word 128, bit 3 */ + "expired: security count", /* word 128, bit 4 */ + "supported: enhanced erase" /* word 128, bit 5 */ +}; + +/* word 160: CFA power mode */ +#define VALID_W160 0x8000 /* 1=word valid */ +#define PWR_MODE_REQ 0x2000 /* 1=CFA power mode req'd by some cmds*/ +#define PWR_MODE_OFF 0x1000 /* 1=CFA power moded disabled */ +#define MAX_AMPS 0x0fff /* value = max current in ma */ + +/* word 206: SMART command transport (SCT) */ +static const char *feat_sct_str[16] = { + "unknown 206[15]", /* word 206 bit 15 */ + "unknown 206[14]", /* word 206 bit 14 */ + "unknown 206[13]", /* word 206 bit 13 */ + "unknown 206[12]", /* word 206 bit 12 */ + "unknown 206[11]", /* word 206 bit 11 */ + "unknown 206[10]", /* word 206 bit 10 */ + "unknown 206[9]", /* word 206 bit 9 */ + "unknown 206[8]", /* word 206 bit 8 */ + "unknown 206[7]", /* word 206 bit 7 */ + "unknown 206[6]", /* word 206 bit 6 */ + "SCT Data Tables (AC5)", /* word 206 bit 5 */ + "SCT Features Control (AC4)", /* word 206 bit 4 */ + "SCT Error Recovery Control (AC3)", /* word 206 bit 3 */ + "SCT LBA Segment Access (AC2)", /* word 206 bit 2 */ + "SCT Long Sector Access (AC1)", /* word 206 bit 1 */ + "SMART Command Transport (SCT) feature set" /* word 206 bit 0 */ +}; + +/* word 255: integrity */ +#define SIG 0x00ff /* signature location */ +#define SIG_VAL 0x00A5 /* signature value */ + +__u8 mode_loop(__u16 mode_sup, __u16 mode_sel, int cc, __u8 *have_mode); +void print_ascii(__u16 *p, __u8 length); + +#ifdef _MSC_VER +#define abs(x) ((__int64)(x) >= 0 ? (__int64)(x) : -(__int64)(x)) +#endif + + +// Given a known-supported ATA major revision, +// return the lowest possible supported ATA revision. +// Each new revision seems to (sometimes) obsolete one +// of the bits corresponding to an older revision. +static __u16 min_ata_std (__u16 major) +{ + if (major <= 4) // up to ata4, no obsolete bits + return 1; + if (major == 5) // ata5 obsoleted the ata1 bit + return 2; + if (major <= 7) // ata6,7 obsoleted the ata2 bit + return 3; + return 4; // ata8 obsoleted the ata3 bit +} + +static void print_features (__u16 supported, __u16 enabled, const char *names[]) +{ + int i; + for (i = 0; i < 16; ++i) { + __u16 mask = 1 << i; + if ((supported & mask) && names[15 - i]) + printf("\t %c\t%s\n", (enabled & mask) ? '*' : ' ', names[15 - i]); + } +} + +static int print_transport_type(__u16 major, __u16 minor) +{ + unsigned int ttype, subtype, transport = 0; + + if (major == 0x0000 || major == 0xffff) + return transport; + printf("Transport: "); + ttype = major >> 12; + subtype = major & 0xfff; + transport = ttype; + switch (ttype) { + case 0: + printf("Parallel"); + if (subtype & 1) + printf(", ATA8-APT"); + break; + case 1: + printf("Serial"); + if (subtype & 0xf) { + if (subtype & 1) + printf(", ATA8-AST"); + if (subtype & 2) + printf(", SATA 1.0a"); + if (subtype & 4) + printf(", SATA II Extensions"); + if (subtype & 8) + printf(", SATA Rev 2.5"); + } + break; + default: + printf("0x%04x", major); + break; + } + if (minor != 0x0000 && minor != 0xffff) { + printf("; Revision: "); + switch (minor) { + case 0x21: + printf("ATA8-AST T13 Project D1697 Revision 0b"); + break; + default: + printf("0x%04x", minor); + } + } + putchar('\n'); + return transport; +} + +/* our main() routine: */ +void identify (__u16 *id_supplied) +{ + + __u16 val[256], ii, jj, kk; + __u16 like_std = 1, std = 0, min_std = 0xffff; + __u16 dev = NO_DEV, eqpt = NO_DEV; + __u8 have_mode = 0, err_dma = 0; + __u8 chksum = 0; + __u32 ll, mm, nn; + __u64 bb, bbbig; /* (:) */ + int transport; + + memcpy(val, id_supplied, sizeof(val)); + + /* calculate checksum over all bytes */ + for(ii = GEN_CONFIG; ii<=INTEGRITY; ii++) { + chksum += val[ii] + (val[ii] >> 8); + } + + /* check if we recognise the device type */ + printf("\n"); + + if(!(val[GEN_CONFIG] & NOT_ATA)) { + dev = ATA_DEV; + printf("ATA device, with "); + } else if(val[GEN_CONFIG]==CFA_SUPPORT_VAL) { + dev = ATA_DEV; + like_std = 4; + printf("CompactFlash ATA device, with "); + } else if(!(val[GEN_CONFIG] & NOT_ATAPI)) { + dev = ATAPI_DEV; + eqpt = (val[GEN_CONFIG] & EQPT_TYPE) >> SHIFT_EQPT; + printf("ATAPI %s, with ", pkt_str[eqpt]); + like_std = 3; + } else { + printf("Unknown device type:\n\tbits 15&14 of general configuration word 0 both set to 1.\n"); + exit(EINVAL); + } + if(!(val[GEN_CONFIG] & MEDIA_REMOVABLE)) + printf("non-"); + printf("removable media\n"); + + + /* Info from the specific configuration word says whether or not the + * ID command completed correctly. It is only defined, however in + * ATA/ATAPI-5 & 6; it is reserved (value theoretically 0) in prior + * standards. Since the values allowed for this word are extremely + * specific, it should be safe to check it now, even though we don't + * know yet what standard this device is using. + */ + if((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==STBY_ID_VAL) || + (val[CONFIG]==PWRD_NID_VAL) || (val[CONFIG]==PWRD_ID_VAL) ) { + like_std = 5; + if((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==STBY_ID_VAL)) + printf("powers-up in standby; SET FEATURES subcmd spins-up.\n"); + if(((val[CONFIG]==STBY_NID_VAL) || (val[CONFIG]==PWRD_NID_VAL)) && + (val[GEN_CONFIG] & INCOMPLETE)) + printf("\n\tWARNING: ID response incomplete.\n\tWARNING: Following data may be incorrect.\n\n"); + } + + /* output the model and serial numbers and the fw revision */ + if(val[START_MODEL]) { + printf("\t%-20s","Model Number:"); + print_ascii(&val[START_MODEL], LENGTH_MODEL); + } + if(val[START_SERIAL]) { + printf("\t%-20s","Serial Number:"); + print_ascii( &val[START_SERIAL], LENGTH_SERIAL); + } + if(val[START_FW_REV]) { + printf("\t%-20s","Firmware Revision:"); + print_ascii(&val[START_FW_REV], LENGTH_FW_REV); + } + if(val[START_MEDIA]) { + printf("\t%-20s","Media Serial Num:"); + print_ascii(&val[START_MEDIA], LENGTH_MEDIA); + } + if(val[START_MANUF]) { + printf("\t%-20s","Media Manufacturer:"); + print_ascii(&val[START_MANUF], LENGTH_MANUF); + } + + transport = print_transport_type(val[TRANSPORT_MAJOR], val[TRANSPORT_MINOR]); + + /* major & minor standards version number (Note: these words were not + * defined until ATA-3 & the CDROM std uses different words.) */ + printf("Standards:"); + if(eqpt != CDROM) { + //printf("major=%04x minor=%04x\n", val[MAJOR], val[MINOR]); + const char * used = 0; + if(val[MINOR] && (val[MINOR] <= MINOR_MAX)) { + if(like_std < 3) + like_std = 3; + std = actual_ver[val[MINOR]]; + if (std) + used = minor_str[val[MINOR]]; + } else { + /* check for recent ATA-8 revision codes (not added to + * actual_ver/minor_str to avoid large sparse tables) */ + switch (val[MINOR]) { + case 0x0027: used = "ATA-8-ACS revision 3c"; break; + case 0x0033: used = "ATA-8-ACS revision 3e"; break; + case 0x0042: used = "ATA-8-ACS revision 3f"; break; + case 0x0052: used = "ATA-8-ACS revision 3b"; break; + case 0x0107: used = "ATA-8-ACS revision 2d"; break; + } + if (used) + std = 8; + } + if (used) + printf("\n\tUsed: %s ", used); + else if (val[MINOR] >= 0x001f) /* first "reserved" value possibly later used by ATA-8 */ + printf("\n\tUsed: unknown (minor revision code 0x%04x) ", val[MINOR]); + + /* looks like when they up-issue the std, they obsolete one; + * thus, only the newest 4 issues need be supported. + * (That's what "kk" and "min_std" are all about) */ + if(val[MAJOR] && (val[MAJOR] != NOVAL_1)) { + printf("\n\tSupported: "); + jj = val[MAJOR] << 1; + kk = min_ata_std(like_std); + for(ii = 14; ii > kk; ii--) { + if(jj & 0x8000) { + printf("%u ", ii); + if (ii > like_std) { + like_std = ii; + kk = min_ata_std(like_std); + } + if (min_std > ii) + min_std = ii; + } + jj <<= 1; + } + if(like_std < 3) + like_std = 3; + } + /* Figure out what standard the device is using if it hasn't told + * us. If we know the std, check if the device is using any of + * the words from the next level up. It happens. + */ + if(like_std < std) like_std = std; + if(((std == 7) || (!std && (like_std < 8))) && + (val[SCT_SUPP] & 0x1)) { + like_std = 8; + } else if(((std == 5) || (!std && (like_std < 6))) && + ( (((val[CMDS_SUPP_1] & VALID) == VALID_VAL) && + ((val[CMDS_SUPP_1] & CMDS_W83) > 0x00ff)) || + (((val[CMDS_SUPP_2] & VALID) == VALID_VAL) && + (val[CMDS_SUPP_2] & CMDS_W84) ) ) ) { + like_std = 6; + } else if(((std == 4) || (!std && (like_std < 5))) && + ((((val[INTEGRITY] & SIG) == SIG_VAL) && !chksum) || + ((val[HWRST_RSLT] & VALID) == VALID_VAL) || + (((val[CMDS_SUPP_1] & VALID) == VALID_VAL) && + ((val[CMDS_SUPP_1] & CMDS_W83) > 0x001f)) ) ) { + like_std = 5; + } else if(((std == 3) || (!std && (like_std < 4))) && + ((((val[CMDS_SUPP_1] & VALID) == VALID_VAL) && + (((val[CMDS_SUPP_1] & CMDS_W83) > 0x0000) || + ((val[CMDS_SUPP_0] & CMDS_W82) > 0x000f))) || + ((val[CAPAB_1] & VALID) == VALID_VAL) || + ((val[WHATS_VALID] & OK_W88) && val[ULTRA_DMA]) || + ((val[RM_STAT] & RM_STAT_BITS) == RM_STAT_SUP) ) ) { + like_std = 4; + } else if(((std == 2) || (!std && (like_std < 3))) && + ((val[CMDS_SUPP_1] & VALID) == VALID_VAL) ) { + like_std = 3; + } else if(((std == 1) || (!std && (like_std < 2))) && + ((val[CAPAB_0] & (IORDY_SUP | IORDY_OFF)) || + (val[WHATS_VALID] & OK_W64_70)) ) { + like_std = 2; + } + if(!std) { + printf("\n\tLikely used: %u\n",like_std); + } else if(like_std > std) { + printf("& some of %u\n",like_std); + } else printf("\n"); + } else { + /* TBD: do CDROM stuff more thoroughly. For now... */ + kk = 0; + if(val[CDR_MINOR] == 9) { + kk = 1; + printf("\n\tUsed: ATAPI for CD-ROMs, SFF-8020i, r2.5"); + } + if(val[CDR_MAJOR] && (val[CDR_MAJOR] != NOVAL_1)) { + kk = 1; + printf("\n\tSupported: CD-ROM ATAPI"); + jj = val[CDR_MAJOR] >> 1; + for(ii = 1; ii <15; ii++) { + if(jj & 0x0001) { + printf("-%u ", ii); + } + jj >>= 1; + } + } + if(!kk) printf("\n\tLikely used CD-ROM ATAPI-1\n"); + else printf("\n"); + /* the cdrom stuff is more like ATA-2 than anything else, so: */ + like_std = 2; + } + + if(min_std == 0xffff) + min_std = like_std > 4 ? like_std - 3 : 1; + + printf("Configuration:\n"); + /* more info from the general configuration word */ + if((eqpt != CDROM) && (like_std == 1)) { + jj = val[GEN_CONFIG] >> 1; + for(ii = 1; ii < 15; ii++) { + if(jj & 0x0001) printf("\t%s\n",ata1_cfg_str[ii]); + jj >>=1; + } + } + if(dev == ATAPI_DEV) { + printf("\tDRQ response: "); /* Data Request (DRQ) */ + switch(val[GEN_CONFIG] & DRQ_RESPONSE_TIME) { + case DRQ_3MS_VAL : printf("3ms.\n"); break; + case DRQ_INTR_VAL : printf("<=10ms with INTRQ\n"); break; + case DRQ_50US_VAL : printf("50us.\n"); break; + default : printf("unknown.\n"); break; + } + printf("\tPacket size: "); + switch(val[GEN_CONFIG] & PKT_SIZE_SUPPORTED) { + case PKT_SIZE_12_VAL : printf("12 bytes\n"); break; + case PKT_SIZE_16_VAL : printf("16 bytes\n"); break; + default : printf("Unknown\n"); break; + } + } else { + /* addressing...CHS? See section 6.2 of ATA specs 4 or 5 */ + ll = 0; mm = 0; bb = 0; bbbig = 0; + if (val[CAPAB_0] & LBA_SUP) + ll = (__u32)val[LBA_SECTS_MSB] << 16 | val[LBA_SECTS_LSB]; + if ( (ll > 0x00FBFC10) && (!val[LCYLS])) { + printf("\tCHS addressing not supported\n"); + } else { + jj = val[WHATS_VALID] & OK_W54_58; + printf("\tLogical\t\tmax\tcurrent\n"); + printf("\tcylinders\t%u\t%u\n",val[LCYLS],jj?val[LCYLS_CUR]:0); + printf("\theads\t\t%u\t%u\n",val[LHEADS],jj?val[LHEADS_CUR]:0); + printf("\tsectors/track\t%u\t%u\n",val[LSECTS],jj?val[LSECTS_CUR]:0); + if(jj) + bb = (__u64)val[LCYLS_CUR] * val[LHEADS_CUR] * val[LSECTS_CUR]; + else + bb = (__u64)val[LCYLS] * val[LHEADS] * val[LSECTS]; + printf("\t--\n"); + if((min_std == 1) && (val[TRACK_BYTES] || val[SECT_BYTES])) { + printf("\tbytes/track: %u",val[TRACK_BYTES]); + printf("\tbytes/sector: %u\n",val[SECT_BYTES]); + } + if(jj) { + mm = (__u32)val[CAPACITY_MSB] << 16 | val[CAPACITY_LSB]; + /* ATA-1 is ambiguous on ordering of words 57 & 58 */ + if(like_std < 3) { + nn = (__u32)val[CAPACITY_LSB] << 16 | val[CAPACITY_MSB]; + /* check Endian of capacity bytes */ + if(abs(mm - bb) > abs(nn - bb)) + mm = nn; + } + printf("\tCHS current addressable sectors:%11u\n",(unsigned)mm); + } + } + if (val[CAPAB_0] & LBA_SUP) { + /* LBA addressing */ + printf("\tLBA user addressable sectors:%11u\n",(unsigned)ll); + if( ((val[CMDS_SUPP_1] & VALID) == VALID_VAL) && + (val[CMDS_SUPP_1] & SUPPORT_48_BIT) ) { + bbbig = (__u64)val[LBA_64_MSB] << 48 | + (__u64)val[LBA_48_MSB] << 32 | + (__u64)val[LBA_MID] << 16 | + val[LBA_LSB] ; + printf("\tLBA48 user addressable sectors:%11llu\n", (__ull)bbbig); + } + } + if (!bbbig) bbbig = (__u64)(ll>mm ? ll : mm); /* # 512 byte blocks */ + if (!bbbig) bbbig = bb; + printf("\tdevice size with M = 1024*1024: %11llu MBytes\n", (__ull)(bbbig>>11)); + bbbig = (bbbig<<9)/1000000; + printf("\tdevice size with M = 1000*1000: %11llu MBytes ", (__ull)bbbig); + if(bbbig > 1000) printf("(%llu GB)\n", (__ull)(bbbig/1000)); + else printf("\n"); + + } + + /* hw support of commands (capabilities) */ + printf("Capabilities:\n"); + printf("\t"); + if(dev == ATAPI_DEV) { + if(eqpt != CDROM) { + if(val[CAPAB_0] & CMD_Q_SUP) printf("Cmd queuing, "); + } + if(val[CAPAB_0] & OVLP_SUP) printf("Cmd overlap, "); + } + if(val[CAPAB_0] & LBA_SUP) printf("LBA, "); + if(like_std != 1) { + printf("IORDY"); + if(!(val[CAPAB_0] & IORDY_SUP)) printf("(may be)"); + if(val[CAPAB_0] & IORDY_OFF) printf("(can"); + else printf("(cannot"); + printf(" be disabled)"); + } else { + printf("IORDY not likely"); + } + printf("\n"); + if((like_std == 1) && val[BUF_TYPE]) { + kk = val[BUF_TYPE]; + printf("\tBuffer type: %04x: ",kk); + if (kk < 2) printf("single port, single-sector"); + else printf("dual port, multi-sector"); + if (kk > 2) printf(" with read caching ability"); + printf("\n"); + } + jj = 0; + if((min_std == 1) && (val[BUF_SIZE] && (val[BUF_SIZE] != NOVAL_1))) { + printf("\tBuffer size: %.1fkB",(float)val[BUF_SIZE]/2); + jj = 1; + } + if((min_std < 4) && (val[RW_LONG])) { + printf("\tbytes avail on r/w long: %u",val[RW_LONG]); + jj = 1; + } + if((eqpt != CDROM) && (like_std > 3)) { + int has_queuing = 0; + if (transport == 1 || (val[SATA_CAP_0] && val[SATA_CAP_0] != 0xffff)) { + if (val[SATA_CAP_0] & 0x0100) + has_queuing = 1; // SATA NCQ + } + if ((val[CMDS_SUPP_1] & VALID) == VALID_VAL && val[CMDS_SUPP_1] & 2) { + has_queuing = 1; // TCQ + } + if (has_queuing) { + printf("\tQueue depth: %u",(val[QUEUE_DEPTH] & DEPTH_BITS)+1); + jj = 1; + } + } + if(jj) printf("\n"); + if(dev == ATA_DEV) { + if(like_std == 1) { + printf("\tCan"); + if(!val[DWORD_IO]) printf("not"); + printf(" perform double-word IO\n"); + } else { + printf("\tStandby timer values: spec'd by "); + if(val[CAPAB_0] & STD_STBY) printf("Standard"); + else printf("Vendor"); + if((like_std > 3) && ((val[CAPAB_1] & VALID) == VALID_VAL)) { + if(val[CAPAB_1] & MIN_STANDBY_TIMER) printf(", with "); + else printf(", no "); + printf("device specific minimum\n"); + } else printf("\n"); + } + printf("\tR/W multiple sector transfer: "); + if((like_std < 3) && !(val[SECTOR_XFER_MAX] & SECTOR_XFER)) { + printf("not supported\n"); + } else { + printf("Max = %u\t",val[SECTOR_XFER_MAX] & SECTOR_XFER); + printf("Current = "); + if(val[SECTOR_XFER_CUR] & MULTIPLE_SETTING_VALID) + printf("%u\n",val[SECTOR_XFER_CUR] & SECTOR_XFER); + else printf("?\n"); + } + if((like_std > 3) && (val[CMDS_SUPP_1] & 0x0008)) { + /* We print out elsewhere whether the APM feature is enabled or + not. If it's not enabled, let's not repeat the info; just print + nothing here. */ + printf("\tAdvanced power management level: "); + if ( (val[ADV_PWR] & 0xFF00) == 0x4000 ) { + __u8 apm_level = val[ADV_PWR] & 0x00FF; + + printf("%u (0x%x)\n", apm_level, apm_level); + } else { + printf("unknown setting (0x%04x)\n", val[ADV_PWR]); + } + } + if(like_std > 5) { + if(val[ACOUSTIC]) { + printf("\tRecommended acoustic management value: %u, current value: %u\n", (val[ACOUSTIC] >> 8) & 0x00ff, val[ACOUSTIC] & 0x00ff); + } + } + } else { /* ATAPI */ + if(eqpt != CDROM) { + if(val[CAPAB_0] & SWRST_REQ) printf("\tATA sw reset required\n"); + } + if(val[PKT_REL] || val[SVC_NBSY]) { + printf("\tOverlap support:"); + if(val[PKT_REL]) printf(" %uus to release bus.",val[PKT_REL]); + if(val[SVC_NBSY]) printf(" %uus to clear BSY after SERVICE cmd.",val[SVC_NBSY]); + printf("\n"); + } + } + + /* DMA stuff. Check that only one DMA mode is selected. */ + printf("\tDMA: "); + if(!(val[CAPAB_0] & DMA_SUP)) { + printf("not supported\n"); + } else { + if(val[DMA_MODE] && !val[SINGLE_DMA] && !val[MULTI_DMA]) { + printf("sdma%u",(val[DMA_MODE] & MODE) >> 8); + } else { + if(val[SINGLE_DMA]) { + jj = val[SINGLE_DMA]; kk = val[SINGLE_DMA] >> 8; + err_dma += mode_loop(jj,kk,'s',&have_mode); + } + if(val[MULTI_DMA]) { + jj = val[MULTI_DMA]; kk = val[MULTI_DMA] >> 8; + err_dma += mode_loop(jj,kk,'m',&have_mode); + } + if((val[WHATS_VALID] & OK_W88) && val[ULTRA_DMA]) { + jj = val[ULTRA_DMA]; kk = val[ULTRA_DMA] >> 8; + err_dma += mode_loop(jj,kk,'u',&have_mode); + } + if(err_dma || !have_mode) + printf("(?)"); + } + printf("\n"); + + if((dev == ATAPI_DEV) && (eqpt != CDROM) && (val[CAPAB_0] & DMA_IL_SUP)) + printf("\t Interleaved DMA support\n"); + + if((val[WHATS_VALID] & OK_W64_70) && + (val[DMA_TIME_MIN] || val[DMA_TIME_NORM])) { + printf("\t Cycle time:"); + if(val[DMA_TIME_MIN]) + printf(" min=%uns",val[DMA_TIME_MIN]); + if(val[DMA_TIME_NORM]) + printf(" recommended=%uns",val[DMA_TIME_NORM]); + printf("\n"); + } + } + + /* Programmed IO stuff */ + printf("\tPIO: "); + /* If a drive supports mode n (e.g. 3), it also supports all modes less + * than n (e.g. 3, 2, 1 and 0). Print all the modes. */ + if((val[WHATS_VALID] & OK_W64_70) && (val[ADV_PIO_MODES] & PIO_SUP)) { + jj = ((val[ADV_PIO_MODES] & PIO_SUP) << 3) | 0x0007; + for(ii = 0; ii <= PIO_MODE_MAX ; ii++) { + if(jj & 0x0001) + printf("pio%d ",ii); + jj >>=1; + } + printf("\n"); + } else if(((min_std < 5) || (eqpt == CDROM)) && ((val[PIO_MODE]>>8) <= 2)) { + for(ii = 0; ii <= val[PIO_MODE]>>8; ii++) { + printf("pio%d ",ii); + } + printf("\n"); + } else printf("unknown\n"); + if(val[WHATS_VALID] & OK_W64_70) { + if(val[PIO_NO_FLOW] || val[PIO_FLOW]) { + printf("\t Cycle time:"); + if(val[PIO_NO_FLOW]) + printf(" no flow control=%uns", val[PIO_NO_FLOW]); + if(val[PIO_FLOW]) + printf(" IORDY flow control=%uns", val[PIO_FLOW]); + printf("\n"); + } + } + + if((val[CMDS_SUPP_1] & VALID) == VALID_VAL){ + printf("Commands/features:\n\tEnabled\tSupported:\n"); + print_features(val[CMDS_SUPP_0] & 0x7fff, val[CMDS_EN_0], feat_0_str); + if( (val[CMDS_SUPP_1] & VALID) == VALID_VAL) + print_features(val[CMDS_SUPP_1] & 0x3fff, val[CMDS_EN_1], feat_1_str); + if( (val[CMDS_SUPP_2] & VALID) == VALID_VAL + && (val[CMDS_EN_2] & VALID) == VALID_VAL) + print_features(val[CMDS_SUPP_2] & 0x3fff, val[CMDS_EN_2], feat_2_str); + if( (val[CMDS_SUPP_1] & VALID) == VALID_VAL + && (val[CMDS_EN_1] & 0x8000) == 0x8000 + && (val[CMDS_SUPP_3] & VALID) == VALID_VAL + && (val[CMDS_EN_3] & VALID) == VALID_VAL) + print_features(val[CMDS_SUPP_3] & 0x3fff, val[CMDS_EN_3], feat_3_str); + if (transport == 1 || (val[SATA_CAP_0] && val[SATA_CAP_0] != 0xffff)) + print_features(val[SATA_CAP_0], val[SATA_CAP_0], cap_sata0_str); + if (transport == 1 || (val[SATA_SUPP_0] && val[SATA_SUPP_0] != 0xffff)) + print_features(val[SATA_SUPP_0], val[SATA_EN_0], feat_sata0_str); + if (val[SCT_SUPP] & 0x1) + print_features(val[SCT_SUPP], val[SCT_SUPP] & 0x3f, feat_sct_str); + } + if((val[RM_STAT] & RM_STAT_BITS) == RM_STAT_SUP) + printf("\tRemovable Media Status Notification feature set supported\n"); + + /* security */ + if((eqpt != CDROM) && (like_std > 3) && + (val[SECU_STATUS] || val[ERASE_TIME] || val[ENH_ERASE_TIME])) { + printf("Security: \n"); + if(val[PSWD_CODE] && (val[PSWD_CODE] != NOVAL_1)) + printf("\tMaster password revision code = %u\n",val[PSWD_CODE]); + jj = val[SECU_STATUS]; + if(jj) { + for(ii = 0; ii < NUM_SECU_STR; ii++) { + if(!(jj & 0x0001)) printf("\tnot\t"); + else printf("\t\t"); + printf("%s\n",secu_str[ii]); + jj >>=1; + } + if(val[SECU_STATUS] & SECU_ENABLED) { + printf("\tSecurity level "); + if(val[SECU_STATUS] & SECU_LEVEL) printf("maximum\n"); + else printf("high\n"); + } + } + jj = val[ERASE_TIME] & ERASE_BITS; + kk = val[ENH_ERASE_TIME] & ERASE_BITS; + if(jj || kk) { + printf("\t"); + if(jj) printf("%umin for SECURITY ERASE UNIT. ", jj==ERASE_BITS ? 508 : jj<<1); + if(kk) printf("%umin for ENHANCED SECURITY ERASE UNIT.", kk==ERASE_BITS ? 508 : kk<<1); + printf("\n"); + } + } + + /* reset result */ + if((val[HWRST_RSLT] & VALID) == VALID_VAL) { + printf("HW reset results:\n"); + if(val[HWRST_RSLT] & CBLID) printf("\tCBLID- above Vih\n"); + else printf("\tCBLID- below Vih\n"); + if(val[HWRST_RSLT] & RST0) { + printf("\tDevice num = 0"); + jj = val[HWRST_RSLT]; + } else { + printf("\tDevice num = 1"); + jj = val[HWRST_RSLT] >> 8; + } + if((jj & DEV_DET) == JUMPER_VAL) + printf(" determined by the jumper"); + else if((jj & DEV_DET) == CSEL_VAL) + printf(" determined by CSEL"); + printf("\n"); + } + + /* more stuff from std 5 */ + if((like_std > 4) && (eqpt != CDROM)) { + if(val[CFA_PWR_MODE] & VALID_W160) { + printf("CFA power mode 1:\n\t"); + if(val[CFA_PWR_MODE] & PWR_MODE_OFF) printf("dis"); + else printf("en"); + printf("abled"); + if(val[CFA_PWR_MODE] & PWR_MODE_REQ) + printf(" and required by some commands"); + printf("\n"); + if(val[CFA_PWR_MODE] & MAX_AMPS) + printf("\tMaximum current = %uma\n",val[CFA_PWR_MODE] & MAX_AMPS); + } + if((val[INTEGRITY] & SIG) == SIG_VAL) { + printf("Checksum: %scorrect", chksum ? "in" : ""); + if (chksum) + printf(" (0x%02x), expected 0x%02x\n", chksum, 0x100 - chksum); + putchar('\n'); + } else { + printf("\tIntegrity word not set (found 0x%04x, expected 0x%02x%02x)\n", + val[INTEGRITY], 0x100 - chksum, SIG_VAL); + } + } +} + +__u8 mode_loop(__u16 mode_sup, __u16 mode_sel, int cc, __u8 *have_mode) { + __u16 ii; + __u8 err_dma = 0; + for(ii = 0; ii <= MODE_MAX; ii++) { + if(mode_sel & 0x0001) { + printf("*%cdma%u ",cc,ii); + if(*have_mode) err_dma = 1; + *have_mode = 1; + } else if(mode_sup & 0x0001) { + printf("%cdma%u ",cc,ii); + } + mode_sup >>=1; mode_sel >>=1; + } + return err_dma; +} + +void print_ascii(__u16 *p, __u8 length) { + __u8 ii; + char cl; + + /* find first non-space & print it */ + for(ii = 0; ii< length; ii++) { + if(((char) 0x00ff&((*p)>>8)) != ' ') break; + if((cl = (char) 0x00ff&(*p)) != ' ') { + if(cl != '\0') printf("%c",cl); + p++; ii++; + break; + } + p++; + } + /* print the rest */ + for(; ii< length; ii++) { + unsigned char c; + /* some older devices have NULLs */ + c = (*p) >> 8; + if (c) putchar(c); + c = (unsigned char)(*p); + if (c) putchar(c); + p++; + } + printf("\n"); +} diff --git a/win32/Changelog.win32 b/win32/Changelog.win32 new file mode 100644 index 0000000..a4afcba --- /dev/null +++ b/win32/Changelog.win32 @@ -0,0 +1,23 @@ +hdparm-6.9-20070228 + - Cygwin port (patch 1668007) + - added /dev/sd[a-z] device names +hdparm-6.9-20070224 + - added identify of recent ATA-8 minor revisions (patch 1667900) + - added optional test duration argument to -t and -T (patch 1593726) +hdparm-6.9-20061030 + - merged with hdparm-6.9 + - added SCT feature set (patch 1577615) + - added '-s' to control power-up in standby (patch 1579209) + - fixed .TP in man page (patch 1586652) + - fixed parameter passing of SET_FEATURES commands (-B, -M, -S) + - added get acoustic (-M) + - added --direct +hdparm-6.6-20060901 + - IDE+SMART ioctl for Win2000, XP +#include // offsetof() +#include +#include +#include + +#define WIN32_LEAN_AND_MEAN +#include +#if _MSC_VER >= 1400 +#define _WIN32_WINNT 0x0502 +#include +#endif + +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + +///////////////////////////////////////////////////////////////////////////// + +#ifndef IOCTL_STORAGE_RESET_DEVICE +#define IOCTL_STORAGE_RESET_DEVICE 0x2d5004 +#endif + + +#ifndef IOCTL_DISK_GET_DRIVE_GEOMETRY +#define IOCTL_DISK_GET_DRIVE_GEOMETRY 0x070000 + +typedef enum _MEDIA_TYPE { + Unknown +} MEDIA_TYPE; + +typedef struct _DISK_GEOMETRY { + LARGE_INTEGER Cylinders; + MEDIA_TYPE MediaType; + DWORD TracksPerCylinder; + DWORD SectorsPerTrack; + DWORD BytesPerSector; +} DISK_GEOMETRY; + +#endif // IOCTL_DISK_GET_DRIVE_GEOMETRY + +typedef char ASSERT_SIZEOF_DISK_GEOMETRY[sizeof(DISK_GEOMETRY) == 24]; + + +#ifndef IOCTL_DISK_GET_LENGTH_INFO +#define IOCTL_DISK_GET_LENGTH_INFO 0x07405c + +typedef struct _GET_LENGTH_INFORMATION { + LARGE_INTEGER Length; +} GET_LENGTH_INFORMATION; + +#endif // IOCTL_DISK_GET_LENGTH_INFO + +typedef char ASSERT_SIZEOF_GET_LENGTH_INFORMATION[sizeof(GET_LENGTH_INFORMATION) == 8]; + + +#ifndef SMART_RCV_DRIVE_DATA + +typedef struct _IDEREGS { + UCHAR bFeaturesReg; + UCHAR bSectorCountReg; + UCHAR bSectorNumberReg; + UCHAR bCylLowReg; + UCHAR bCylHighReg; + UCHAR bDriveHeadReg; + UCHAR bCommandReg; + UCHAR bReserved; +} IDEREGS; + +#endif // SMART_RCV_DRIVE_DATA + +typedef char ASSERT_SIZEOF_IDEREGS[sizeof(IDEREGS) == 8]; + + +#ifndef IOCTL_IDE_PASS_THROUGH +#define IOCTL_IDE_PASS_THROUGH 0x04d028 +#endif + +#pragma pack(1) + +typedef struct _ATA_PASS_THROUGH { + IDEREGS IdeReg; + ULONG DataBufferSize; + UCHAR DataBuffer[1]; +} ATA_PASS_THROUGH; + +#pragma pack() + +typedef char ASSERT_SIZEOF_ATA_PASS_THROUGH[sizeof(ATA_PASS_THROUGH) == 12+1]; + + +#ifndef IOCTL_ATA_PASS_THROUGH +#define IOCTL_ATA_PASS_THROUGH 0x04d02c + +typedef struct _ATA_PASS_THROUGH_EX { + USHORT Length; + USHORT AtaFlags; + UCHAR PathId; + UCHAR TargetId; + UCHAR Lun; + UCHAR ReservedAsUchar; + ULONG DataTransferLength; + ULONG TimeOutValue; + ULONG ReservedAsUlong; + ULONG/*_PTR*/ DataBufferOffset; + UCHAR PreviousTaskFile[8]; + UCHAR CurrentTaskFile[8]; +} ATA_PASS_THROUGH_EX; + +typedef char ASSERT_SIZEOF_ATA_PASS_THROUGH_EX[sizeof(ATA_PASS_THROUGH_EX) == 40]; + +#define ATA_FLAGS_DRDY_REQUIRED 0x01 +#define ATA_FLAGS_DATA_IN 0x02 +#define ATA_FLAGS_DATA_OUT 0x04 +#define ATA_FLAGS_48BIT_COMMAND 0x08 + +#endif //�IOCTL_ATA_PASS_THROUGH + +#ifndef SMART_RCV_DRIVE_DATA +#define SMART_RCV_DRIVE_DATA 0x07c088 + +#pragma pack(1) + +typedef struct _SENDCMDINPARAMS { + ULONG cBufferSize; + IDEREGS irDriveRegs; + UCHAR bDriveNumber; + UCHAR bReserved[3]; + ULONG dwReserved[4]; + UCHAR bBuffer[1]; +} SENDCMDINPARAMS; + +typedef struct _DRIVERSTATUS { + UCHAR bDriverError; + UCHAR bIDEError; + UCHAR bReserved[2]; + ULONG dwReserved[2]; +} DRIVERSTATUS; + +typedef struct _SENDCMDOUTPARAMS { + ULONG cBufferSize; + DRIVERSTATUS DriverStatus; + UCHAR bBuffer[1]; +} SENDCMDOUTPARAMS; + + +#pragma pack() +#endif // SMART_RCV_DRIVE_DATA + +typedef char ASSERT_SIZEOF_SENDCMDINPARAMS [sizeof(SENDCMDINPARAMS) == 32+1]; +typedef char ASSERT_SIZEOF_SENDCMDOUTPARAMS[sizeof(SENDCMDOUTPARAMS) == 16+1]; + + +///////////////////////////////////////////////////////////////////////////// + +int win32_debug; + +static void print_ide_regs(const IDEREGS * ri, const IDEREGS * ro) +{ + if (ri) + printf(" In : CMD=0x%02x, FR=0x%02x, SC=0x%02x, SN=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n", + ri->bCommandReg, ri->bFeaturesReg, ri->bSectorCountReg, ri->bSectorNumberReg, + ri->bCylLowReg, ri->bCylHighReg, ri->bDriveHeadReg); + if (ro) + printf(" Out: STS=0x%02x,ERR=0x%02x, SC=0x%02x, SN=0x%02x, CL=0x%02x, CH=0x%02x, SEL=0x%02x\n", + ro->bCommandReg, ro->bFeaturesReg, ro->bSectorCountReg, ro->bSectorNumberReg, + ro->bCylLowReg, ro->bCylHighReg, ro->bDriveHeadReg); +} + + +///////////////////////////////////////////////////////////////////////////// + +static int ide_pass_through(HANDLE hdevice, IDEREGS * regs, void * data, unsigned datasize) +{ + unsigned int size = sizeof(ATA_PASS_THROUGH)-1 + datasize; + ATA_PASS_THROUGH * buf = (ATA_PASS_THROUGH *)VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE); + DWORD num_out; + const unsigned char magic = 0xcf; + + if (!buf) { + errno = ENOMEM; + return -1; + } + + buf->IdeReg = *regs; + buf->DataBufferSize = datasize; + if (datasize) + buf->DataBuffer[0] = magic; + + if (!DeviceIoControl(hdevice, IOCTL_IDE_PASS_THROUGH, + buf, size, buf, size, &num_out, NULL)) { + long err = GetLastError(); + if (win32_debug) { + printf(" IOCTL_IDE_PASS_THROUGH failed, Error=%ld\n", err); + print_ide_regs(regs, NULL); + } + VirtualFree(buf, 0, MEM_RELEASE); + errno = ENOSYS; + return -1; + } + + if (buf->IdeReg.bCommandReg/*Status*/ & 0x01) { + if (win32_debug) { + printf(" IOCTL_IDE_PASS_THROUGH command failed:\n"); + print_ide_regs(regs, &buf->IdeReg); + } + VirtualFree(buf, 0, MEM_RELEASE); + errno = EIO; + return -1; + } + + if (datasize) { + if (!(num_out == size && buf->DataBuffer[0] != magic)) { + if (win32_debug) { + printf(" IOCTL_IDE_PASS_THROUGH output data missing (%lu, %lu)\n", + num_out, buf->DataBufferSize); + print_ide_regs(regs, &buf->IdeReg); + } + VirtualFree(buf, 0, MEM_RELEASE); + errno = EIO; + return -1; + } + memcpy(data, buf->DataBuffer, datasize); + } + + if (win32_debug) { + printf(" IOCTL_IDE_PASS_THROUGH succeeded, bytes returned: %lu (buffer %lu)\n", + num_out, buf->DataBufferSize); + print_ide_regs(regs, &buf->IdeReg); + } + *regs = buf->IdeReg; + + VirtualFree(buf, 0, MEM_RELEASE); + return 0; +} + + +///////////////////////////////////////////////////////////////////////////// + +static int ata_pass_through(HANDLE hdevice, IDEREGS * regs, void * data, int datasize) +{ + typedef struct { + ATA_PASS_THROUGH_EX apt; + ULONG Filler; + UCHAR ucDataBuf[512]; + } ATA_PASS_THROUGH_EX_WITH_BUFFERS; + + ATA_PASS_THROUGH_EX_WITH_BUFFERS ab; + IDEREGS * ctfregs; + unsigned int size; + DWORD num_out; + const unsigned char magic = 0xcf; + + memset(&ab, 0, sizeof(ab)); + ab.apt.Length = sizeof(ATA_PASS_THROUGH_EX); + //ab.apt.PathId = 0; + //ab.apt.TargetId = 0; + //ab.apt.Lun = 0; + ab.apt.TimeOutValue = 10; + size = offsetof(ATA_PASS_THROUGH_EX_WITH_BUFFERS, ucDataBuf); + ab.apt.DataBufferOffset = size; + + if (datasize) { + if (!(data && 0 <= datasize && datasize <= (int)sizeof(ab.ucDataBuf))) { + errno = EINVAL; + return -1; + } + ab.apt.AtaFlags = ATA_FLAGS_DATA_IN; + ab.apt.DataTransferLength = datasize; + size += datasize; + ab.ucDataBuf[0] = magic; + } + else { + //ab.apt.AtaFlags = 0; + //ab.apt.DataTransferLength = 0; + } + + ctfregs = (IDEREGS *)ab.apt.CurrentTaskFile; + *ctfregs = *regs; + + if (!DeviceIoControl(hdevice, IOCTL_ATA_PASS_THROUGH, + &ab, size, &ab, size, &num_out, NULL)) { + long err = GetLastError(); + if (win32_debug) { + printf(" IOCTL_ATA_PASS_THROUGH failed, Error=%ld\n", err); + print_ide_regs(regs, NULL); + } + errno = ENOSYS; + return -1; + } + + if (ctfregs->bCommandReg/*Status*/ & 0x01) { + if (win32_debug) { + printf(" IOCTL_ATA_PASS_THROUGH command failed:\n"); + print_ide_regs(regs, ctfregs); + } + *regs = *ctfregs; + errno = EIO; + return -1; + } + + if (datasize) + memcpy(data, ab.ucDataBuf, datasize); + + if (win32_debug) { + printf(" IOCTL_ATA_PASS_THROUGH succeeded, bytes returned: %lu\n", num_out); + print_ide_regs(regs, ctfregs); + } + *regs = *ctfregs; + + return 0; +} + + +///////////////////////////////////////////////////////////////////////////// + +static int smart_rcv_drive_data(HANDLE hdevice, IDEREGS * regs, void * data, unsigned datasize) +{ + SENDCMDINPARAMS inpar; + unsigned char outbuf[sizeof(SENDCMDOUTPARAMS)-1 + 512]; + const SENDCMDOUTPARAMS * outpar; + DWORD num_out; + + memset(&inpar, 0, sizeof(inpar)); + inpar.irDriveRegs = *regs; + inpar.irDriveRegs.bDriveHeadReg = 0xA0; + //inpar.bDriveNumber = 0; + inpar.cBufferSize = 512; + + if (datasize != 512) { + errno = EINVAL; + return -1; + } + + memset(&outbuf, 0, sizeof(outbuf)); + + if (!DeviceIoControl(hdevice, SMART_RCV_DRIVE_DATA, &inpar, sizeof(SENDCMDINPARAMS)-1, + outbuf, sizeof(SENDCMDOUTPARAMS)-1 + 512, &num_out, NULL)) { + long err = GetLastError(); + if (win32_debug) { + printf(" SMART_RCV_DRIVE_DATA failed, Error=%ld\n", err); + print_ide_regs(regs, NULL); + } + errno = ENOSYS; + return -1; + } + + outpar = (const SENDCMDOUTPARAMS *)outbuf; + + if (outpar->DriverStatus.bDriverError || outpar->DriverStatus.bIDEError) { + if (win32_debug) { + printf(" SMART_RCV_DRIVE_DATA failed, DriverError=0x%02x, IDEError=0x%02x\n", + outpar->DriverStatus.bDriverError, outpar->DriverStatus.bIDEError); + print_ide_regs(regs, NULL); + } + errno = (!outpar->DriverStatus.bIDEError ? ENOSYS : EIO); + return -1; + } + + memcpy(data, outpar->bBuffer, 512); + + if (win32_debug) { + printf(" SMART_RCV_DRIVE_DATA suceeded, bytes returned: %lu (buffer %lu)\n", + num_out, outpar->cBufferSize); + print_ide_regs(regs, NULL); + } + + return 0; +} + + +///////////////////////////////////////////////////////////////////////////// + +static int try_ata_pass_through(HANDLE hdevice, IDEREGS * regs, void * data, int datasize) +{ + static char avail = 0x7; + int rc; + if (avail & 0x1) { + rc = ata_pass_through(hdevice, regs, data, datasize); + if (rc >= 0 || errno != ENOSYS) + return rc; + avail &= ~0x1; + } + if ((avail & 0x2) && datasize >= 0) { + rc = ide_pass_through(hdevice, regs, data, datasize); + if (rc >= 0 || errno != ENOSYS) + return rc; + avail &= ~0x2; + } + if ((avail & 0x4) && regs->bCommandReg == WIN_IDENTIFY) { + rc = smart_rcv_drive_data(hdevice, regs, data, datasize); + if (rc >= 0 || errno != ENOSYS) + return rc; + avail &= ~0x4; + } + if (win32_debug) { + printf(" No ATA PASS THROUGH I/O control available\n"); + print_ide_regs(regs, NULL); + } + errno = ENOSYS; + return -1; +} + + +///////////////////////////////////////////////////////////////////////////// + +static int get_disk_geometry(HANDLE h, DISK_GEOMETRY * geo) +{ + DWORD num_out; + if (!DeviceIoControl(h, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, geo, sizeof(*geo), &num_out, NULL)) { + long err = GetLastError(); + if (win32_debug) + printf(" IOCTL_DISK_GET_DRIVE_GEOMETRY failed, Error=%ld\n", err); + errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO); + return -1; + } + if (win32_debug) + printf(" IOCTL_DISK_GET_DRIVE_GEOMETRY succeeded, bytes returned: %lu\n", num_out); + return 0; +} + +static __int64 get_disk_length(HANDLE h) +{ + DWORD num_out; + GET_LENGTH_INFORMATION li; + if (!DeviceIoControl(h, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &li, sizeof(li), &num_out, NULL)) { + long err = GetLastError(); + if (win32_debug) + printf(" IOCTL_DISK_GET_LENGTH_INFO failed, Error=%ld\n", err); + errno = (err == ERROR_INVALID_FUNCTION ? ENOSYS : EIO); + return -1; + } + if (win32_debug) + printf(" IOCTL_DISK_GET_LENGTH_INFO succeeded, bytes returned: %lu\n", num_out); + return li.Length.QuadPart; +} + + +///////////////////////////////////////////////////////////////////////////// + +static int reset_device(HANDLE h) +{ + DWORD num_out; + if (!DeviceIoControl(h, IOCTL_STORAGE_RESET_DEVICE, NULL, 0, NULL, 0, &num_out, NULL)) { + long err = GetLastError(); + if (win32_debug) + printf(" IOCTL_STORAGE_RESET_DEVICE failed, Error=%ld\n", err); + errno = (err == ERROR_INVALID_FUNCTION || err == ERROR_NOT_SUPPORTED ? ENOSYS : EIO); + return -1; + } + if (win32_debug) + printf(" IOCTL_STORAGE_RESET_DEVICE succeeded\n"); + return 0; +} + + +///////////////////////////////////////////////////////////////////////////// + +static char is_cd = 0; + +int win32_open(const char * name, unsigned flags, unsigned perm) +{ + int len; + char drv[1+1] = ""; + unsigned cdno = ~0; + int n1 = -1; + char path[50]; + HANDLE h; + DWORD crflags; + (void)perm; + + if (!strncmp("/dev/", name, 5)) + name += 5; + len = strlen(name); + if (sscanf(name, "%*[hs]d%1[a-z]%n", drv, &n1) == 1 && n1 == len) { + sprintf(path, "\\\\.\\PhysicalDrive%d", drv[0] - 'a'); + is_cd = 0; + } + else if (sscanf(name, "scd%u%n", &cdno, (n1=-1, &n1)) == 1 && n1 == len && cdno <= 15) { + sprintf(path, "\\\\.\\CdRom%u", cdno); + is_cd = 1; + } + else { + errno = EINVAL; + return -1; + } + + crflags = (flags & O_DIRECT ? FILE_FLAG_NO_BUFFERING : 0); + if ((h = CreateFileA(path, + GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, OPEN_EXISTING, crflags, 0)) == INVALID_HANDLE_VALUE) { + long err = GetLastError(); + if (win32_debug) + printf("%s: cannot open, Error=%ld\n", path, err); + if (err == ERROR_FILE_NOT_FOUND) + errno = ENOENT; + else if (err == ERROR_ACCESS_DENIED) + errno = EACCES; + else + errno = EIO; + return -1; + } + + if (win32_debug) + printf("%s: successfully opened\n", path); + + return (int)h; +} + + +int win32_close(int fd) +{ + CloseHandle((HANDLE)fd); + return 0; +} + + +int win32_read(int fd, char * buf, int size) +{ + DWORD num_read; + if (!ReadFile((HANDLE)fd, buf, size, &num_read, NULL)) { + errno = EIO; + return -1; + } + return num_read; +} + + +long win32_lseek(int fd, long offset, int where) +{ + DWORD pos; + if (where != SEEK_SET) { + errno = EINVAL; + return -1; + } + pos = SetFilePointer((HANDLE)fd, offset, 0, FILE_BEGIN); + if (pos == INVALID_SET_FILE_POINTER) { + errno = EIO; + return -1; + } + return pos; +} + + +static void fix_id_string(unsigned char * s, int n) +{ + int i; + for (i = 0; i < n-1; i+=2) { + unsigned char c = s[i]; s[i] = s[i+1]; s[i+1] = c; + } + for (i = n-1; i > 0 && s[i] == ' '; i--) + s[i] = 0; +} + + +int win32_ioctl(int fd, int code, void * arg) +{ + int rc = 0; + + switch (code) { + +#ifdef BLKGETSIZE + case BLKGETSIZE: + case BLKGETSIZE64: + { + __int64 size = get_disk_length((HANDLE)fd); + if (size < 0 && errno == ENOSYS) { + DISK_GEOMETRY dg; + rc = get_disk_geometry((HANDLE)fd, &dg); + if (rc) + break; + size = dg.Cylinders.QuadPart * dg.TracksPerCylinder * dg.SectorsPerTrack * dg.BytesPerSector; + } + if (code == BLKGETSIZE) + *(unsigned *)arg = (unsigned)(size >> 9); + else + *(unsigned __int64 *)arg = size; + } + break; +#endif + +#ifdef HDIO_GETGEO + case HDIO_GETGEO: + case HDIO_GETGEO_BIG: + { + DISK_GEOMETRY dg; + rc = get_disk_geometry((HANDLE)fd, &dg); + if (rc) + break; + if (code == HDIO_GETGEO) { + struct hd_geometry * gp = (struct hd_geometry *)arg; + gp->cylinders = (unsigned short)(dg.Cylinders.LowPart <= 0xffff ? dg.Cylinders.LowPart : 0xffff); + gp->heads = (unsigned char)(dg.TracksPerCylinder <= 0xff ? dg.TracksPerCylinder : 0xff); + gp->sectors = (unsigned char)(dg.SectorsPerTrack <= 0xff ? dg.SectorsPerTrack : 0xff); + gp->start = 0; + } + else { + struct hd_big_geometry * gp = (struct hd_big_geometry *)arg; + gp->cylinders = dg.Cylinders.LowPart; + gp->heads = (unsigned char)(dg.TracksPerCylinder <= 0xff ? dg.TracksPerCylinder : 0xff); + gp->sectors = (unsigned char)(dg.SectorsPerTrack <= 0xff ? dg.SectorsPerTrack : 0xff); + gp->start = 0; + } + } + break; +#endif + +#ifdef HDIO_GET_IDENTITY + case HDIO_GET_IDENTITY: + if (!arg) // Flush + break; + { + struct hd_driveid * id = (struct hd_driveid *)arg; + IDEREGS regs = {0,0,0,0,0,0,0,0}; + regs.bCommandReg = (!is_cd ? WIN_IDENTIFY : WIN_PIDENTIFY); + regs.bSectorCountReg = 1; + rc = try_ata_pass_through((HANDLE)fd, ®s, id, 512); + if (rc) + break; + fix_id_string(id->model, sizeof(id->model)); + fix_id_string(id->fw_rev, sizeof(id->fw_rev)); + fix_id_string(id->serial_no, sizeof(id->serial_no)); + } + break; +#endif + +#ifdef HDIO_GET_ACOUSTIC + case HDIO_GET_ACOUSTIC: + if (!arg) // Flush + break; + { + struct hd_driveid id; + IDEREGS regs = {0,0,0,0,0,0,0,0}; + memset(&id, 0, sizeof(id)); + regs.bCommandReg = (!is_cd ? WIN_IDENTIFY : WIN_PIDENTIFY); + regs.bSectorCountReg = 1; + rc = try_ata_pass_through((HANDLE)fd, ®s, &id, 512); + if (rc) + break; + *(long *)arg = (id.words94_125[0] & 0xff); + } + break; +#endif + +#ifdef HDIO_DRIVE_RESET + case HDIO_DRIVE_RESET: + rc = reset_device((HANDLE)fd); + break; +#endif + +#ifdef HDIO_DRIVE_CMD + case HDIO_DRIVE_CMD: + if (!arg) // Flush + break; + { + // input: + // [0]: COMMAND + // [1]: SECTOR NUMBER (SMART) or SECTOR COUNT (other) + // [2]: FEATURE + // [3]: SECTOR COUNT (transfer size) + // output: + // [0]: STATUS + // [1]: ERROR + // [2]: SECTOR COUNT + // [3]: (undefined?) + // [4...]: data + unsigned char * idebuf = (unsigned char *)arg; + IDEREGS regs = {0,0,0,0,0,0,0,0}; + regs.bCommandReg = idebuf[0]; + regs.bFeaturesReg = idebuf[2]; + if (idebuf[3]) + regs.bSectorCountReg = idebuf[3]; + else + regs.bSectorCountReg = idebuf[1]; + rc = try_ata_pass_through((HANDLE)fd, ®s, idebuf+4, idebuf[3] * 512); + idebuf[0] = regs.bCommandReg; // STS + idebuf[1] = regs.bFeaturesReg; // ERR + idebuf[2] = regs.bSectorCountReg; + } + break; +#endif + + default: + errno = ENOSYS; + rc = -1; + break; + } + + return rc; +} diff --git a/win32/rawio.h b/win32/rawio.h new file mode 100644 index 0000000..1ef72fb --- /dev/null +++ b/win32/rawio.h @@ -0,0 +1,30 @@ +/* win32/rawio.h - ioctl() emulation module for hdparm for Windows */ +/* - by Christian Franke (C) 2006-7 -- freely distributable */ + +int win32_open(const char * name, unsigned flags, unsigned perm); +int win32_close(int fd); +int win32_read(int fd, char * buf, int size); +long win32_lseek(int fd, long offset, int where); +int win32_ioctl(int fd, int code, void * arg); + +extern int win32_debug; + +#ifndef O_DIRECT +#define O_DIRECT 040000 +#endif + +#ifndef ENOMSG +#define ENOMSG 100 +#endif + +#ifndef RAWIO_INTERNAL + +#define open(name, flags) win32_open(name, flags, 0666) +#define close(fd) win32_close(fd) +#define read(fd, buf, size) win32_read(fd, buf, size) +#define lseek(fd, offset, where) win32_lseek(fd, offset, where) +#define ioctl(fd, code, arg) win32_ioctl(fd, code, (void*)(arg)) +#define fsync(fd) ((void)0) +#define sync() ((void)0) + +#endif diff --git a/win32/shm.c b/win32/shm.c new file mode 100644 index 0000000..427a538 --- /dev/null +++ b/win32/shm.c @@ -0,0 +1,41 @@ +/* win32/shm.c - shm*() emulation module for hdparm for Windows */ +/* - by Christian Franke (C) 2006-7 -- freely distributable */ + +#include "shm.h" + +#include + +#define WIN32_LEAN_AND_MEAN +#include + + +int shmget(int key, int size, int flags) +{ + char * addr; + (void)key; (void)flags; + addr = VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE); + if (!addr) { + errno = ENOMEM; return -1; + } + return (int)addr; +} + +int shmctl(int id, int cmd, void *arg) +{ + (void)id; (void)cmd; (void)arg; + return 0; +} + +void * shmat(int id, void *addr, int flags) +{ + (void)addr; (void)flags; + return (void *)id; +} + +int shmdt(void * addr) +{ + if (!VirtualFree(addr, 0, MEM_RELEASE)) { + errno = EINVAL; return -1; + } + return 0; +} diff --git a/win32/shm.h b/win32/shm.h new file mode 100644 index 0000000..c895530 --- /dev/null +++ b/win32/shm.h @@ -0,0 +1,12 @@ +/* win32/shm.h - shm*() emulation module for hdparm for Windows */ +/* - by Christian Franke (C) 2006 -- freely distributable */ + +#define IPC_PRIVATE 0 +#define IPC_RMID 1 +#define SHM_LOCK 2 + +int shmget(int key, int size, int flags); +int shmctl(int id, int cmd, void *arg); +void * shmat(int id, void *addr, int flags); +int shmdt(void * addr); + diff --git a/win32/timer.c b/win32/timer.c new file mode 100644 index 0000000..76bfee6 --- /dev/null +++ b/win32/timer.c @@ -0,0 +1,42 @@ +/* win32/timer.c - sleep/itimer emulation module for hdparm for Windows */ +/* - by Christian Franke (C) 2006-7 -- freely distributable */ + +#include "timer.h" + +#define WIN32_LEAN_AND_MEAN +#include + +void sleep(int seconds) +{ + Sleep(seconds * 1000L); +} + + +static struct itimerval start_timerval; +static __int64 start_walltime; + +int getitimer(int which, struct itimerval * val) +{ + FILETIME ft; __int64 wallnow, timernow, elapsed; + (void)which; + GetSystemTimeAsFileTime(&ft); + wallnow = (((__int64)ft.dwHighDateTime << 32) | ft.dwLowDateTime); + elapsed = (wallnow - start_walltime) / 10; + timernow = (__int64)start_timerval.it_value.tv_sec * 1000000 + start_timerval.it_value.tv_usec - elapsed; + if (timernow < 0) + timernow = 0; + val->it_value.tv_sec = (long)(timernow / 1000000); + val->it_value.tv_usec = (long)(timernow % 1000000); + val->it_interval = start_timerval.it_interval; + return 0; +} + +int setitimer(int which, const struct itimerval *val, struct itimerval *valout) +{ + FILETIME ft; + (void)which; (void)valout; + start_timerval = *val; + GetSystemTimeAsFileTime(&ft); + start_walltime = (((__int64)ft.dwHighDateTime << 32) | ft.dwLowDateTime); + return 0; +} diff --git a/win32/timer.h b/win32/timer.h new file mode 100644 index 0000000..480b83b --- /dev/null +++ b/win32/timer.h @@ -0,0 +1,20 @@ +/* win32/timer.h - sleep/itimer emulation module for hdparm for Windows */ +/* - by Christian Franke (C) 2006 -- freely distributable */ + +void sleep(int seconds); + +#define ITIMER_REAL 0 + +struct timeval { + long tv_sec; + long tv_usec; +}; + +struct itimerval { + struct timeval it_interval; + struct timeval it_value; +}; + +int getitimer(int which, struct itimerval * val); +int setitimer(int which, const struct itimerval * val, struct itimerval * valout); + diff --git a/win32/version.h b/win32/version.h new file mode 100644 index 0000000..7d07dde --- /dev/null +++ b/win32/version.h @@ -0,0 +1,12 @@ +/* hdparm for Windows version */ +#define VERSION_MAJOR "v6.9" +#define VERSION_MINOR "20070228" + +#ifdef __CYGWIN__ +#define PLATFORM "Cygwin" +#else +#define PLATFORM "Win32" +#endif + +#define VERSION VERSION_MAJOR"-"VERSION_MINOR" ("PLATFORM")" +