seti $printhelp = $printhelp
seti $help = $help
if $help != 0
  echo 'This will scan the drive using the read verify command.'
  echo 'Reads are done in blocks (clusters) of 256 sectors.'
  echo 'This means that all warnings and errors show the position'
  echo ' of the first sector of the block, NOT the actual problem sector.'
  echo 'If the required variables below are not set on the command line,'
  echo ' then you will be propted to enter them.'
  echo 'This requires the number variable startsector to be set.'
  echo 'This requires the number variable endsector to be set.'
  echo 'If endsector is set to 0, it will default to the last addressable sector.'
  echo 'This uses the identify device command to determine the last sector.'
  echo 'This requires the number variable threshold to be set.'
  echo 'Threshold is time in milliseconds.'
  echo 'If a read exceeds the threshold, a warning will be given.'
  echo 'If threshold is set to 0, it will default to 150ms.'
  echo 'This requires the number variable softtimeout to be set.'
  echo 'Softtimeout is the time in milliseconds to wait before giving up on a command.'
  echo 'If softtimeout is set to 0, it will default to 10000ms.'
  echo 'This requires the number variable hardtimeout to be set.'
  echo 'Hardtimeout is the time to wait after a softreset before doing a hardreset.'
  echo 'If hardtimeout is set to 0, it will default to 10000ms.'
  echo 'This requires the string variable logfile to be set.'
  echo 'Logfile will capture all the warnings and errors.'
  echo 'This requires the string variable rateslog to be set.'
  echo 'Rateslog will capture timing info that can be ploted.'
  echo 'Rateslog captures the low, high, and average over 256 block reads.'
  echo 'If logfile and/or ratesfile are blank no log will be produced.'

  echo 'Example using defaults:'
  echo 'hddsupertool -t /dev/sda -f ata_scan_device threshold=0 softtimeout=0 startsector=0 endsector=0 logfile=="" rateslog==""'
  if $printhelp = 1
    exit 0
  endif
  echo 'Hit ENTER to continue...'
  userinput $choicestring
  previousscript
endif

include good_subroutines

sets $null = ""
seti $using_menu = $using_menu


seti $ask = 0
while 1 = 1
  variablecheck $startsector
  if $error_level < 16
    seti $ask = 1
  elseif $using_menu = 1
    seti $ask = 1
  endif
  if $ask = 1
    echo "Enter start sector, or leave blank for first:"
    userinput $choicestring
    if $choicestring != $null
      seti $startsector = $choicestring 0
      break
    else
      seti $startsector = 0
      break
    endif
  else
    break
  endif
done

seti $ask = 0
while 1 = 1
  variablecheck $endsector
  if $error_level < 16
    seti $ask = 1
  elseif $using_menu = 1
    seti $ask = 1
  endif
  if $ask = 1
    echo "Enter end sector, or leave blank for last:"
    userinput $choicestring
    if $choicestring != $null
      seti $endsector = $choicestring 0
      break
    else
      seti $endsector = 0
      break
    endif
  else
    break
  endif
done

seti $ask = 0
while 1 = 1
  variablecheck $threshold
  if $error_level < 16
    seti $ask = 1
  elseif $using_menu = 1
    seti $ask = 1
  endif
  if $ask = 1
    echo "Enter warning threshold in ms, or leave blank for default(150):"
    userinput $choicestring
    if $choicestring != $null
      seti $threshold = $choicestring 0
      break
    else
      seti $threshold = 150
      break
    endif
  else
    break
  endif
done
if $threshold = 0
  seti $threshold = 150
endif
seti $threshold = $threshold * 1000

seti $ask = 0
while 1 = 1
  variablecheck $softtimeout
  if $error_level < 16
    seti $ask = 1
  elseif $using_menu = 1
    seti $ask = 1
  endif
  if $ask = 1
    echo "Enter softtimeout in ms, or leave blank for default(10000ms):"
    userinput $choicestring
    if $choicestring != $null
      seti $softtimeout = $choicestring 0
      break
    else
      seti $softtimeout = 10000
      break
    endif
  else
    break
  endif
done
if $softtimeout = 0
  seti $softtimeout = 10000
endif
seti $softtimeout = $softtimeout * 1000
softtimeout $softtimeout

seti $ask = 0
while 1 = 1
  variablecheck $hardtimeout
  if $error_level < 16
    seti $ask = 1
  elseif $using_menu = 1
    seti $ask = 1
  endif
  if $ask = 1
    echo "Enter hardtimeout in ms, or leave blank for default(10000ms):"
    userinput $choicestring
    if $choicestring != $null
      seti $hardtimeout = $choicestring 0
      break
    else
      seti $hardtimeout = 10000
      break
    endif
  else
    break
  endif
done
if $hardtimeout = 0
  seti $hardtimeout = 10000
endif
seti $hardtimeout = $hardtimeout * 1000
hardtimeout $hardtimeout

seti $ask = 0
variablecheck $logfile
if $error_level < 16
  seti $ask = 1
elseif $using_menu = 1
  seti $ask = 1
endif
if $ask = 1
  echo "Enter logfile name, or leave blank for no log:"
  userinput $choicestring
  sets $logfile = $choicestring
endif

seti $ask = 0
variablecheck $rateslog
if $error_level < 16
  seti $ask = 1
elseif $using_menu = 1
  seti $ask = 1
endif
if $ask = 1
  echo "Enter optional rates logfile name, or leave blank for no log:"
  userinput $choicestring
  sets $rateslog = $choicestring
endif

# get the size of the drive using identify device command
buffersize 512
setreadpio
ata28cmd 0 0 0 0 0 0xa0 0xec
# check if command failed
seti $command_failed = 0
gosub check_command_status
if $command_failed = 1
  echo "Identify device command failed!"
  gosub show_sense_data
  gosub show_ata_return_status
  previousscript
endif
# check if drive supports extended commands
seti $tempnum = buffer 167
seti $extended = $tempnum & 4
# total addressable sectors
if $extended > 0
  seti $addressable = buffer 200 qw
else
  seti $addressable = buffer 120 dw
endif

if $endsector = 0
  seti $endsector = $addressable
endif


buffersize 0
setreadpio
seti $blocksize = 256
seti $logchunk = 256
seti $currentposition = $startsector
seti $previousposition = $currentposition
seti $blockposition = $currentposition
seti $processedsectors = 0
seti $totalsectors = $endsector - $startsector
seti $logcounter = 0
seti $accumulatedtime = 0
seti $1ms = 0
seti $3ms = 0
seti $10ms = 0
seti $30ms = 0
seti $100ms = 0
seti $300ms = 0
seti $over300ms = 0
seti $errorcount = 0
seti $highvalue = 0
seti $lowvalue = 1000000000

if $logfile != $null
  deletefile $logfile
  sets $line = "# warning log " $date
  writelog $logfile $line
  sets $line = "# cluster size is " $blocksize
  writelog $logfile $line
endif

if $rateslog != $null
  deletefile $rateslog
  sets $line = "# position  low  high  avg"
  writelog $rateslog $line
endif

gettime
seti $displaytime = $time
seti $redodisplay = 1
echo " "
echo " "
echo " "
echo " "
echo " "
echo " "
echo " "
echo " "
echo " "
echo " "
echo " "
echo " "
echo " "
echo " "
gettime
seti $programstart = $time
while $currentposition < $endsector
  seti $sector = $currentposition

  if $extended > 0
    seti $features = 0
    seti $count = $blocksize
    seti $LBAlow = $sector & 0xffff
    seti $temp = $sector > 16
    seti $LBAmid = $temp & 0xffff
    seti $temp = $sector > 32
    seti $LBAhigh = $temp & 0xffff
    seti $device = 0xe0
    seti $command = 0x42
    gettime
    seti $startreadtime = $time
    ata48cmd $features $count $LBAhigh $LBAmid $LBAlow $device $command
    gettime
    seti $endreadtime = $time

  else
    seti $features = 0
    seti $count = $blocksize & 0xff
    seti $LBAlow = $sector & 0xff
    seti $temp = $sector > 8
    seti $LBAmid = $temp & 0xff
    seti $temp = $sector > 16
    seti $LBAhigh = $temp & 0xff
    seti $temp = $sector > 24
    seti $LBAdevice = $temp & 0x0f
    seti $device = 0xe0
    seti $device = $device | $LBAdevice
    seti $command = 0x40
    gettime
    seti $startreadtime = $time
    ata28cmd $features $count $LBAlow $LBAmid $LBAhigh $device $command
    gettime
    seti $endreadtime = $time
  endif

  # check if command failed
  seti $command_failed = 0
  gosub check_command_status
  if $command_failed = 1
    seti $devicefault = $ata_return_status & 0x20
  endif

  seti $readtime = $endreadtime - $startreadtime
  if $command_failed = 1
    seti $errorcount = $errorcount + 1
  elseif $readtime > 300000
    seti $over300ms = $over300ms + 1
  elseif $readtime > 100000
    seti $300ms = $300ms + 1
  elseif $readtime > 30000
    seti $100ms = $100ms + 1
  elseif $readtime > 10000
    seti $30ms = $30ms + 1
  elseif $readtime > 3000
    seti $10ms = $10ms + 1
  elseif $readtime > 1000
    seti $3ms = $3ms + 1
  else
    seti $1ms = $1ms + 1
  endif


  if $readtime < $lowvalue
    seti $lowvalue = $readtime
  endif
  if $readtime > $highvalue
    seti $highvalue = $readtime
  endif

  seti $showerror = 0
  if $command_failed = 1
    seti $showerror = 1
  elseif $readtime > $threshold
    seti $showerror = 1
  endif

  if $showerror > 0
    upline
    upline
    upline
    upline
    upline
    upline
    upline
    upline
    upline
    upline
    upline
    upline
    upline
    seti $adjustedtime = $readtime / 1000
    if $command_failed = 1
      hex
      sets $errorstring = "e=0x" $ata_return_error " s=0x" $ata_return_status
      decimal
      if $direct_mode = 1
        seti $command_status = $command_status & 0xfff0
        if $command_status = 0x20
          sets $status = "BUSY"
        elseif $command_status = 0x30
          sets $status = "TIMEOUT/RESET"
        elseif $command_status = 0x40
          sets $status = "TIMEOUT/RESET/BUSY"
        elseif $command_status = 0x50
          sets $status = "TIMEOUT"
        elseif $command_status = 0x60
          sets $status = "NO DRQ"
        elseif $command_status = 0x70
          sets $status = "WRONG DEVICE"
        elseif $command_status > 0x7f
          hex
          sets $status = "OTHER/0x" $command_status
          decimal
        else
          sets $status = ""
        endif
      else
        sets $status = ""
      endif
      sets $outputline = "ERROR " $status " " $currentposition "  " $adjustedtime "ms " $errorstring
    else
      sets $outputline = "WARNING " $currentposition "  " $adjustedtime "ms "
    endif
    echo $outputline
    echo $null
    echo $null
    echo $null
    echo $null
    echo $null
    echo $null
    echo $null
    echo $null
    echo $null
    echo $null
    echo $null
    echo $null
    echo $null
    seti $redodisplay = 1
    if $logfile != $null
      writelog $logfile $outputline
    endif
  endif


  seti $elapsedtime = $endreadtime - $displaytime
  if $elapsedtime > 1000000
    seti $redodisplay = 1
  endif
  if $redodisplay = 1
    seti $sectorstransferred = $currentposition - $previousposition
    seti $previousposition = $currentposition
    seti $bytestransferred = $sectorstransferred * 512
    seti $adjustedbytes = $bytestransferred * 1000000
    seti $currentspeed = $adjustedbytes / $elapsedtime
    seti $currentspeed = $currentspeed / 1000
    gosub displayoutput
    seti $displaytime = $endreadtime
  endif
  seti $redodisplay = 0


  seti $accumulatedtime = $accumulatedtime + $readtime
  seti $logcounter = $logcounter + 1
  if $logcounter = $logchunk
    seti $average = $accumulatedtime / $logchunk

    if $rateslog != $null
      sets $line = $blockposition "  " $lowvalue "  " $highvalue "  "  $average
      writelog $rateslog $line
    else
      #echo "low=" $lowvalue "  high=" $highvalue "  average="  $average
    endif

    seti $highvalue = 0
    seti $lowvalue = 1000000000
    seti $accumulatedtime = 0
    seti $logcounter = 0
    seti $blockposition = $currentposition + $blocksize
  endif

  if $devicefault > 0
    echo "Device fault detected!"
    echo "Current position= " $currentposition
    gosub show_ata_return_status
    seti $devicefault = 0
    echo "Power cycle the drive and hit enter to continue"
    userinput $dummy
    # do a hard reset to set the drive back up for access
    hardreset
  endif

  seti $currentposition = $currentposition + $blocksize
  seti $processedsectors = $processedsectors + $blocksize
  # check if last read is less than blocksize
  seti $partial = $currentposition + $blocksize
  if $partial > $endsector
    if $currentposition < $endsector
      seti $blocksize = $endsector - $currentposition
    endif
  endif
done

if $devicefault = 0
  gosub displayoutput
endif


previousscript
end



subroutine displayoutput
    upline
    upline
    upline
    upline
    upline
    upline
    upline
    upline
    upline
    upline
    upline
    upline
    upline
    echo "                                                  "
    gettime
    seti $totaltime = $time - $programstart
    seti $totalseconds = $totaltime / 1000000
    seti $remainder = $totaltime % 1000000
    seti $totalminutes = $totalseconds / 60
    seti $seconds = $totalseconds % 60
    seti $hours = $totalminutes / 60
    seti $minutes = $totalminutes % 60
    echo "scantime h:m:s = " $hours ":" $minutes ":" $seconds "." $remainder
    seti $positionpercent = $processedsectors * 100
    seti $percentcomplete = $positionpercent / $totalsectors
    seti $remainder = $positionpercent % $totalsectors
    seti $remainder = $remainder * 100
    seti $percentremainder = $remainder / $totalsectors
    if $percentremainder < 10
      echo $percentcomplete ".0" $percentremainder "%  complete    "
    else
      echo $percentcomplete "." $percentremainder "%  complete    "
    endif
    echo "position= " $currentposition "                           "
    echo "speed= " $currentspeed " kB/s                           "
    echo "<1ms=" $1ms "                           "
    echo "1-3ms=" $3ms "                           "
    echo "3-10ms=" $10ms "                          "
    echo "10-30ms=" $30ms "                           "
    echo "30-100ms=" $100ms "                           "
    echo "100-300ms=" $300ms "                           "
    echo ">300ms=" $over300ms "                           "
    echo "errors=" $errorcount "                           "
endsubroutine


