/*
TAPEINFO is a Linux utility to dump the inquiry and log pages of the
Hewlett-Packard C1533A tape drive.

Copyright 1995 Eric L. Smith

TAPEINFO is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License version 2 as published by the Free
Software Foundation.  Note that I am not granting permission to redistribute
or modify TAPEINFO under the terms of any later version of the General Public
License.

This program is distributed in the hope that it will be useful (or at least
amusing), but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
Public License for more details.

You should have received a copy of the GNU General Public License along with
this program (in the file "COPYING"); if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

$Header: /usr/home/kolwynia/eric/tapeinfo/RCS/logsense.c,v 1.5 1995/09/11 18:20:50 eric Exp eric $
*/

#include <stdio.h>

#include "tapeinfo.h"
#include "scsicmd.h"
#include "logsense.h"


typedef struct
{
  int code;
  char *name;
} log_parameter_s;


typedef struct
{
  int page_num;
  char *page_name;
  void (* decode)(FILE *f, unsigned char page, unsigned char *buf, unsigned long len);
  log_parameter_s *parms;
} log_page_s;


extern log_page_s log_page_info [];


log_page_s *find_log_page_info (unsigned char page)
{
  log_page_s *info;
  for (info = & log_page_info [0]; info->page_num != -1; info++)
    if (page == info->page_num)
      return (info);
  return (NULL);
}


void print_log_param_name (FILE *f, log_parameter_s *parms, unsigned short code)
{
  log_parameter_s *parm;
  if (parms)
    for (parm = parms; parm->code != -1; parm++)
      {
	if (parm->code == code)
	  {
	    fprintf (f, "%s", parm->name);
	    return;
	  }
      }
  fprintf (f, "unknown code %04x", code);
}


log_parameter_s error_counter_parms [] =
{
  { 0x0003, "total errors corrected (soft errors)" },
  { 0x0004, "total times correction algorithm processed" },
  { 0x0005, "total groups processed" },
  { 0x0006, "total errors uncorrected (hard errors)" },
  { -1, NULL }
};

log_parameter_s tape_log_parms [] =
{
  { 0x0001, "total number of groups written" },
  { 0x0002, "current RAW retries" },
  { 0x0003, "current number of groups read" },
  { 0x0004, "current C3 ECC retries" },
  { 0x0005, "previous number of groups written" },
  { 0x0006, "previous RAW retries" },
  { 0x0007, "previous number of groups read" },
  { 0x0008, "previous C3 ECC retries" },
  { 0x0009, "total number of groups written" },
  { 0x000a, "total RAW retries" },
  { 0x000b, "total number of groups read" },
  { 0x000c, "total C3 ECC retries" },
  { 0x000d, "load count" },
  { -1, NULL }
};

log_parameter_s tape_capacity_log_parms [] =
{
  { 0x0001, "remaining capacity, partition 0 (kilobytes)" },
  { 0x0002, "remaining capacity, partition 1 (kilobytes)" },
  { 0x0003, "maximum capacity, partition 0 (kilobytes)" },
  { 0x0004, "maximum capacity, partition 1 (kilobytes)" },
  { -1, NULL }
};

log_parameter_s frame_error_counter_parms [] =
{
  { 0x0001, "frames read or written" },
  { 0x0002, "main data C1 block errors  - positive tracks" },
  { 0x0003, "main data C1 block errors  - negative tracks" },
  { 0x0004, "sub area 0 C1 block errors - positive tracks" },
  { 0x0005, "sub area 1 C1 block errors - positive tracks" },
  { 0x0006, "sub area 0 C1 block errors - negative tracks" },
  { 0x0007, "sub area 1 C1 block errors - negative tracks" },
  { 0x0008, "total retry count" },
  { 0x0009, "read C2 uncorrectable block" },
  { -1, NULL }
};


log_parameter_s drive_counter_parms [] =
{
  { 0x0001, "loads" },
  { 0x0002, "write drive errors" },
  { 0x0003, "read drive errors" },
  { -1, NULL }
};


log_parameter_s mechanism_counter_parms [] =
{
  { 0x0001, "faulty 12V" },
  { 0x0003, "mode sensor fault" },
  { 0x0004, "tension fault" },
  { 0x0005, "bad diameter" },
  { 0x0006, "capstan stalled" },
  { 0x0007, "drum lock lost" },
  { 0x0008, "drum stalled" },
  { 0x0009, "drum Dref lost" },
  { 0x000a, "drum PG lost" },
  { 0x000b, "supply reel stuck unthreading" },
  { 0x000c, "supply reel stuck capstan mode" },
  { 0x000d, "capstan clean slip" },
  { 0x000e, "take-up reel stuck capstan mode" },
  { 0x000f, "RLS stuck RL mode" },
  { 0x0010, "RAM test fail" },
  { 0x0011, "RAM check fail" },
  { 0x0012, "supply reel brake fail" },
  { 0x0013, "take-up reel brake fail" },
  { 0x0014, "signal processor test" },
  { -1, NULL }
};


log_parameter_s data_compression_transfer_parms [] =
{
  { 0x0001, "number of entities written" },
  { 0x0002, "number of entities read" },
  { 0x0003, "number of records written" },
  { 0x0004, "number of records read" },
  { 0x0005, "kilobytes to data compression" },
  { 0x0006, "kilobytes from data compression" },
  { 0x0007, "kilobytes to tape" },
  { 0x0008, "kilobytes from tape" },
  { 0x0009, "logical entity size" },
  { 0x000a, "physical entity size" },
  { 0x000b, "uncompressed entities" },
  { -1, NULL }
};


void decode_general_parms (FILE *f, unsigned char page, unsigned char *buf, unsigned long len)
{
  log_page_s *info;
  int i, j;
  int code;

  info = find_log_page_info (page);

  for (i = 4; i < len; i += (4 + buf [i+3]))
    {
      code = (buf [i] << 8) | buf [i+1];
      print_log_param_name (f, info->parms, code);
      fprintf (f, ": 0x");
      for (j = 0; j < buf [i+3]; j++)
	fprintf (f, "%02x", buf [i+4+j]);
      fprintf (f, "\n");
    }
}


void decode_trace_parms (FILE *f, unsigned char page, unsigned char *buf, unsigned long len)
{
  log_page_s *info;

  info = find_log_page_info (page);

  hex_dump (f, buf, len);
}


log_page_s log_page_info [] =
{
  { 0x00, "list of supported pages", NULL, NULL },
  { 0x02, "write error counter", decode_general_parms, error_counter_parms },
  { 0x03, "read error counter", decode_general_parms,  error_counter_parms },
  { 0x30, "tape log", decode_general_parms, tape_log_parms },
  { 0x31, "tape capacity log", decode_general_parms, tape_capacity_log_parms },
  { 0x32, "buffer trace log", decode_trace_parms, NULL },
  { 0x33, "device trace log", decode_trace_parms, NULL },
  { 0x34, "write frame error counters", decode_general_parms, frame_error_counter_parms },
  { 0x35, "read frame error counters",  decode_general_parms, frame_error_counter_parms },
  { 0x36, "bad group log", NULL, NULL },
  { 0x37, "drive counters", decode_general_parms, drive_counter_parms },
  { 0x38, "mechanism counters", decode_general_parms, mechanism_counter_parms },
  { 0x39, "data compression transfer log", decode_general_parms, data_compression_transfer_parms },
  { 0x3a, "data compression trace log", NULL, NULL },
  { 0x3b, "channel trace log", NULL, NULL },
  { -1, NULL, NULL, NULL }
};


void print_log_sense_page (FILE *f, int fd, unsigned char page)
{
  unsigned long count, len;
  int i;
  log_page_s *info;

  unsigned char cmd [] = { T_LOG_SENSE,
			    0x00,  /* LUN */
			    0x40,  /* current cumulative values, page 0 */
			    0x00,  /* reserved */
			    0x00,  /* reserved */
			    0x00,  /* reserved */
			    0x00,  /* reserved */
			    BUFFER_SIZE / 256,  /* allocation length */
			    BUFFER_SIZE % 256,
			    0x00   /* control */
			};

  info = find_log_page_info (page);

  fprintf (f, "Log Sense page %02x", page);
  if (info && info->page_name)
    fprintf (f, ": %s", info->page_name);
  fprintf (f, "\n");

  cmd [2] |= page;
  count = scsi_command (fd, cmd, sizeof (cmd), buf2, BUFFER_SIZE);
  len = ((buf2 [2] << 8) | buf2 [3]) + 4;

  if (info && info->decode)
    (info->decode) (f, page, buf2, len);
  else
    hex_dump (f, buf2, len);
}


void print_log_sense_pages (FILE *f, int fd, int all)
{
  unsigned long count, len;
  int i;
  int page;
  log_page_s *page_info;

  unsigned char cmd [] = { T_LOG_SENSE,
			    0x00,  /* LUN */
			    0x40,  /* current cumulative values, page 0 */
			    0x00,  /* reserved */
			    0x00,  /* reserved */
			    0x00,  /* reserved */
			    0x00,  /* reserved */
			    BUFFER_SIZE / 256,  /* allocation length */
			    BUFFER_SIZE % 256,
			    0x00   /* control */
			};

  count = scsi_command (fd, cmd, sizeof (cmd), buf1, BUFFER_SIZE);
  len = ((buf1 [2] << 8) | buf1 [3]) + 4;

  if (! all)
    fprintf (f, "Available log sense pages:\n");
  for (i = 0; i < (len - 4); i++)
    {
      page = buf1 [i + 4];
      if (all)
	{
	  print_log_sense_page (f, fd, page);
	  fprintf (f, "\n");
	}
      else
	{
	  fprintf (f, "%02x", page);
	  page_info = find_log_page_info (page);
	  if (page_info)
	    fprintf (f, ": %s", page_info -> page_name);
	  fprintf (f, "\n");
	}
    }
}
