/* Copyright (c) 1996-2004, Adaptec Corporation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 * - Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 * - Neither the name of the Adaptec Corporation nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

/****************************************************************************
*
* Created:  7/17/98
*
*****************************************************************************
*
* File Name:		ListDev.cpp
* Module:
* Contributors:		Lee Page
* Description:		This command lists all
* Version Control:
*
* $Revision: 97 $
* $NoKeywords: $
* $Log: $
*****************************************************************************/

/*** INCLUDES ***/
#include "command.hpp"
#include "listdev.hpp"
#include "rustring.h"
#include "ctlr_map.hpp"
#include "status.hpp"
#include "rscenum.h"
#include "flashmem.h"
//#include "eventlog.hpp"
#include <stdio.h>
#include <ctype.h>
#include "dpt_eata.h"
/*** CONSTANTS ***/
const int BUF_LEN = 256;
extern char* EventStrings[];
extern void GetBatteryEventString(uSHORT, char *);
#define CACHE_MODE_LEVELS 2
// These aren't used [ks]
//#define DefaultSmorSignature "DPT Signature Unavailable"
//#define DefaultBiosSignature DefaultSmorSignature
//#define DefaultBadMemoryAddress 0
extern int FID_RESET_OUTBUFF;
extern uSHORT PHYS_LIST_SIZE;
extern dpt_sig_S mysig;
extern int DptLegacy;
//DEBUG CODE:HITACHI
#define NV_HIDE_CACHE  0x80
bool showCache =	true;
bool showvendor	= false;				

/*** TYPES ***/
/*** STATIC DATA ***/
/*** EXTERNAL DATA ***/
/*** MACROS ***/
/*** PROTOTYPES ***/
/*** FUNCTIONS ***/
List_Device::List_Device(
	list_Type		what,
 	SCSI_Address	&address_to_List
	): list_What( what ),
	obj_Root( address_to_List )
	{
	ENTER( "List_Device::List_Device(" );

	EXIT();
	}

List_Device::List_Device( list_Type what ): list_What( what ),
	obj_Root( -1 )	// this is bogus, it won't be used because they aren't listing
					// devices hanging off a device
	{
	ENTER( "List_Device::List_Device( list_Type what ): list_What( what )," );

	EXIT();
	}

List_Device::List_Device( const List_Device &right ):
	list_What( right.list_What ),
	obj_Root( right.obj_Root )
	{
	ENTER( "List_Device::List_Device( const List_Device &right ):" );
	EXIT();
	}

List_Device::~List_Device()
	{
	ENTER( "List_Device::~List_Device()" );
	EXIT();
	}

Command::Dpt_Error	List_Device::execute( String_List **output )
	{
	ENTER( "Command::Dpt_Error	List_Device::execute( String_List **output )" );
	Dpt_Error	err;
   printDummyStr = false;
   char temp [100];
   char temp2 [sizeof(dpt_sig_S)];
   dpt_sig_S engSig;
	// allocate this object once for all functions.
	*output	= new String_List();
   if ((list_What == LIST_VERSION) ||
       (list_What == LIST_CONTROLLERS) ||
       (list_What == LIST_BATTERY))
   	Init_Engine(1); // scan hbas only
   else
   	Init_Engine();

	switch( list_What )
		{
		case LIST_ARRAY:
			// get visibles
			err = List_Log_Devices( **output, GET_ARRAY);
			break;

		case LIST_LOGICALS:
			// get visibles
			err = List_Log_Devices( **output, GET_RAID);
			break;

  		case LIST_CACHE:
			// list write cache mode
			err = List_LSU_Devices( **output);
			break;

  		case LIST_VERSION:
			// list version - controller info plus bios/smor
			err = List_Devices( **output);
			break;

		case LIST_PHYSICALS:
			err = List_Devices( **output);
			break;

        case LIST_INQUIRY_VENDOR:
			showvendor = true; 
			
		case LIST_INQUIRY:
			err = List_Inquiry( **output);
			break;

		case LIST_CONTROLLERS:
			err = List_Devices( **output);
			break;

		case LIST_HOT_SPARES:
			// get hiddens
			err = List_Log_Devices( **output, GET_RAID_HOT_SPARE);
			break;
		case LIST_ARRAYNAME:
			// get visibles
			err = List_Log_Devices( **output, GET_ARRAYNAME);
			break;

		case LIST_TAGS:
			err = List_Tags(**output);
			break;

		case LIST_ALL_VENDOR:
			showvendor = true; 
		case LIST_ALL:
         printDummyStr = true;
         // show dptutil version and date and description
         if (DptLegacy)
			   (*output)->add_Item (EventStrings[STR_DPTUTIL_ALL_CAPS]);
         else
			   (*output)->add_Item (EventStrings[STR_RAIDUTIL_ALL_CAPS]);
         sprintf (temp, EventStrings[STR_VERSION_DATE],
             mysig.dsVersion, mysig.dsRevision,
             mysig.dsSubRevision, mysig.dsMonth,
             mysig.dsDay, mysig.dsYear + 1980, mysig.dsDescription);
			(*output)->add_Item (temp);
			(*output)->add_Item ("\n");
         // show engine version and date and description
         err = engine->Send( MSG_GET_ENGINE_SIG);
			engine->Extract (temp2, sizeof(dpt_sig_S));
         memcpy (&engSig, temp2, sizeof(dpt_sig_S));
         

		 if (DptLegacy)
			   (*output)->add_Item (EventStrings[STR_DPT_ENGINE]);
         else
			   (*output)->add_Item (EventStrings[STR_ADPT_ENGINE]);
         sprintf (temp, EventStrings[STR_VERSION_DATE],
             engSig.dsVersion, engSig.dsRevision,
             engSig.dsSubRevision, engSig.dsMonth,
             engSig.dsDay, engSig.dsYear + 1980, engSig.dsDescription);
			(*output)->add_Item (temp);
			(*output)->add_Item ("\n\n");
         // show controller
         list_What = LIST_CONTROLLERS;
			err = List_Devices( **output);
			(*output)->add_Item( "\n" );
         // show physical
         list_What = LIST_PHYSICALS;
			err = List_Devices( **output);
			(*output)->add_Item( "\n" );
         // show arrays
         list_What = LIST_ARRAY;
			err = List_Log_Devices( **output, GET_ARRAY);
			(*output)->add_Item( "\n" );
         // show hot spares
         list_What = LIST_HOT_SPARES;
			err = List_Log_Devices( **output, GET_RAID_HOT_SPARE);
			(*output)->add_Item( "\n" );
			// kmc - Adding speed support listing.
         list_What = LIST_SPEED;
			err = List_Device_Speeds( **output );	
			(*output)->add_Item( "\n" );
         // show cache
         list_What = LIST_CACHE;
			err = List_LSU_Devices( **output);
			(*output)->add_Item( "\n" );
			// list version - controller info plus bios/smor
         list_What = LIST_VERSION;
			err = List_Devices( **output);
			(*output)->add_Item( "\n" );
			// list battery info
         list_What = LIST_BATTERY;
			err = List_Devices( **output);
			(*output)->add_Item( "\n" );
			// list inquiry
         list_What = LIST_INQUIRY;
			err = List_Inquiry( **output);
			(*output)->add_Item( "\n" );
         // add next here
			err = Dpt_Error::DPT_CMD_ERR_NO_ERROR;
			break;

		case LIST_SPEED:
			err = List_Device_Speeds( **output );
			(*output)->add_Item( "\n" );
			err = Dpt_Error::DPT_CMD_ERR_NO_ERROR;
		break;

		case LIST_REDIRECT:
			err	= List_Log_Devices( **output, GET_RAID_REDIRECT);
        break;

		case LIST_BATTERY:
			err = List_Devices( **output);
        break;

		default:
			err	= Dpt_Error::DPT_CMD_ERR_INVALID_LIST_TYPE;
			break;
		}

	if( err.Is_Error() )
		{
        char buf[2];
        buf[0] = TABULATION_PLACE_HOLDER;
        buf[1] = '\0';
        (**output).add_Item(buf);
		(**output).add_Item (EventStrings[STR_FAILURE]);
		(**output).add_Item( (char *) err );
		}
	else
		{
		if( (**output).get_Num_Items() == 0 )
			{
			(**output).add_Item(EventStrings[STR_NO_ITEMS_FOUND]);
			(**output).add_Item( "\n" );
			}
		}

	EXIT();
	return( err );
	}

// kmc - added method to print the negogiated xfer speed of the devices.
Command::Dpt_Error List_Device::List_Device_Speeds (String_List& output)
	{
	ENTER( "Command::Dpt_Error		List_Device::List_Device_Speeds(" );
	int dev_Index;
	bool more_Devs_Left	= true;
	Dpt_Error err;
	DPT_TAG_T		this_Objs_Tag;
	bool first_time_through = true;

   for (dev_Index = 0; more_Devs_Left && !err.Is_Error(); dev_Index++)
    {
		get_Devs_Type	devs_Type;

		this_Objs_Tag = Get_Dev_by_Index( obj_Root.hba, dev_Index, &more_Devs_Left, &devs_Type );

		if (more_Devs_Left)
      {
         if (first_time_through == true)
         {
            output.add_Item(EventStrings[STR_LIST_DEV_SPEED_HEADER]);
            output.add_Item( STR_DEV_DIVIDING_LINE );
            first_time_through = false;
    		}
         // print out speed of all SCSI physical devices -kds
         if (devs_Type != GET_SCSI_HBA)
         {
            bool trashValid;
            SCSI_Address objAddr = DPT_Tag_to_Address (this_Objs_Tag, &trashValid);
            hba_Tag = Get_HBA_by_Index (objAddr.hba, &trashValid);
            err |= List_This_Devices_Speed (objAddr.bus, output, this_Objs_Tag);
         }
		}
   }

    if ( first_time_through == true )
    {
        err |= Dpt_Error::DPT_CMD_ERR_CANT_FIND_COMPONENT;
    }

	EXIT();
	return( err );
	}

Command::Dpt_Error List_Device::List_This_Devices_Speed( 
                     int dev_chan, String_List &output,
							DPT_TAG_T this_Objs_Tag)
{
	ENTER( "Command::Dpt_Error	List_Device::List_This_Devices_Speed(" );
	DPT_TAG_T currTag = engine->hbaInfo_P->myTag;
	Dpt_Error err;
   
	uSHORT speed;

	engine->Reset();
	err = engine->Send( MSG_GET_INFO, this_Objs_Tag );

	if (!err.Is_Error())
	{
      // Print the device addrs
		dptCaddr_S *addr_P = &engine->devInfo_P->addr;
		DPTControllerMap map;
		char * String = map.getTargetString(
		  addr_P->hba, addr_P->chan, addr_P->id, addr_P->lun );
		output.add_Item( String );
		delete [] String;
  		char speedString[16];
		sprintf(speedString, "%s", "?");
   		char widthString[16];
		widthString[0] = '\0';		

		engine->Reset ();
		engine->Send( MSG_GET_INFO, currTag );			

		// Added by Qi Zhu, for SATA
		int SataController = 0; 
		dptChanInfo2_S ChanInfo2[5];

		if (engine->Send(MSG_GET_CHAN_INFO, currTag)== MSG_RTN_COMPLETED)
		{	
			uLONG NumChanInfo2=0;

			engine->Extract ((char *)&NumChanInfo2, sizeof(NumChanInfo2));
			engine->Extract ((char *)ChanInfo2, sizeof(ChanInfo2));
			
			if((NumChanInfo2)&&(ChanInfo2[0].pc13Flags4 & FLG_CHAN2_SATA))
			{
				SataController = 1;
			}
		}			

		if(SataController)
		{
			engine->Reset();
			err = engine->Send( MSG_GET_INFO, this_Objs_Tag );
			int SataSpeed;

			//
			// Calculate the transfer speed in MBS. We had to put the speed
			// into the log page and set the wide bit because the field
			// isn't big enough so by setting wide we could cut the value in
			// half and it would fit. The busSpeed field comes back in MHz. Since
			// wide is set we multiply this value by two to get Megabytes/sec. We 
			// will have to kludge here for those odd speeds (33,45, and 133) that 
			// get truncated in the engine when converting from Hz to MHz.
			//		
			SataSpeed = engine->devInfo_P->busSpeed * 2;
			if((SataSpeed == 32)||(SataSpeed == 44)||(SataSpeed == 132))
			{
				++SataSpeed;
			}
        
			//
			// Now set up the type
			//
			switch(SataSpeed)
			{
				case 150:
					strcpy (speedString, EventStrings[STR_SATA1500]);
                    break;
				case 300:
					strcpy (speedString, EventStrings[STR_SATA3000]);
					break;
				case 600:
					strcpy (speedString, EventStrings[STR_SATA6000]);
					break;
				//
				// If we don't recognize it, just call it SATA
				//
				default:
					strcpy (speedString, EventStrings[STR_SATA]);
					break;
			} //switch(engine->devInfo_P->busSpeed)
		} //else if(SataController)

		else {			
			if (engine->hbaInfo_P->chanInfo[0].flags & FLG_CHAN_UDMA)
			{
				engine->Reset();
				err = engine->Send( MSG_GET_INFO, this_Objs_Tag );
				int AtaSpeed;

				switch(engine->devInfo_P->udmaModeSupported)
				{
					case UDMA_MODE_33 :
						AtaSpeed = 33;
						break;
					case UDMA_MODE_45 :
						AtaSpeed = 45;
						break;
					case UDMA_MODE_66 :
						AtaSpeed = 66;
						break;
					case UDMA_MODE_100 :
						AtaSpeed = 100;
						break;
					case UDMA_MODE_133 :
						AtaSpeed = 133;
						break;
					//
					// For older engines that didn't support the udmaModeSelected field
					// or new modes we don't support yet, the MHz still comes back in the
					// busSpeed field. UDMA is wide so we multiply this value by two to
					// get Megabytes/sec. We will have to kludge here for those odd speeds 
					// (33 and 45) that get truncated in the engine when converting from
					// Hz to MHz.
					//
					default:
						AtaSpeed = engine->devInfo_P->busSpeed * 2;
						if((AtaSpeed == 32)||(AtaSpeed == 44)||(AtaSpeed == 132))
						{
							++AtaSpeed;
						}
						break;
				} //switch(gInfo_P->udmaModeSelected)

				//
				// Set up the Ultra mode string
				//
				switch(AtaSpeed)
				{
					case 33 :
						strcpy (speedString, EventStrings[STR_ATA33]);
						break;
					case 45 :
						strcpy (speedString, EventStrings[STR_ATA45]);
						AtaSpeed = 45;
						break;
					case 66 :
						strcpy (speedString, EventStrings[STR_ATA66]);
						AtaSpeed = 66;
						break;
					case 100 :
						strcpy (speedString, EventStrings[STR_ATA100]);
						AtaSpeed = 100;
						break;
					case 133 :
						strcpy (speedString, EventStrings[STR_ATA133]);
						AtaSpeed = 133;
						break; 	
					//
					// If we don't recognize it, just call it ATA
					//
					default:
						strcpy (speedString, EventStrings[STR_ATA]);
						break;
				} //switch(AtaSpeed)
				output.add_Item (speedString);
			} //else if(UdmaController)

			else
			{
				// Get the MAXIMUM speed here.
				// As the max speed, we are going to take the LOWER
				// of the max speeds as reported by the NVRAM and the HW
				// SO ... get both then choose the lower to report
		
				NvramMegaHertz = 0; // unknown
				HwMegaHertz = 0; // unknown
				uSHORT slowerSpeed;
				
				err = GetSpeedFromNvram (this_Objs_Tag);
				err = GetSpeedFromHW (dev_chan);
				if (NvramMegaHertz == 0) // if unknown, use Hw...
					slowerSpeed = HwMegaHertz;
				else
					slowerSpeed = (NvramMegaHertz < HwMegaHertz ? 
							NvramMegaHertz : HwMegaHertz);
				engine->Reset ();
	
		
				if (engine->Send(MSG_GET_CHAN_INFO, currTag)== MSG_RTN_COMPLETED)
				{
					dptChanInfo2_S ChanInfo2[5];
					uLONG NumChanInfo2;

					engine->Extract ((char *)&NumChanInfo2, sizeof(NumChanInfo2));
					engine->Extract ((char *)ChanInfo2, sizeof(ChanInfo2));
			
					if((NumChanInfo2)&&(ChanInfo2[0].pc13Flags4 & FLG_CHAN2_ULTR160))
					{
						slowerSpeed = 320;
					}
				}			
				if (HwMegaHertz == 0xffff) // if fibre
					strcpy (speedString, EventStrings[STR_FIBRE]);
				else
				{
			 
					switch (slowerSpeed)
					{
						case 0:
							strcpy (speedString, EventStrings[STR_UNKNOWN]);
						break;
						case 1:
							strcpy (speedString, EventStrings[STR_ASYNC]);
						break;
						case 20:
							strcpy (speedString, EventStrings[STR_ULTRA]);
						break;
						case 40:
							strcpy (speedString, EventStrings[STR_ULTRA2]);
						break;
						case 80:
							strcpy (speedString, EventStrings[STR_ULTRA3]);
						break;
						case 160:
							strcpy (speedString, EventStrings[STR_ULTRA160]);
						break;
						case 320:
							strcpy (speedString, EventStrings[STR_ULTRA320]);
						break;
						default:
							sprintf(speedString, EventStrings[STR_FILLIN_MHZ], slowerSpeed);
						break;
					}
				}
			}
		}
	output.add_Item (speedString);
   	// Get the NEGOTIATED speed and width bit here.
   	engine->Reset ();
   	err = engine->Send (MSG_GET_INFO, this_Objs_Tag);

	if (!err.Is_Error() && engine->devInfo_P
#if (defined(FLG_DEV_SPEED_VALID))
			&& ( engine->devInfo_P->flags3 & FLG_DEV_SPEED_VALID )
#endif
// kds - need this line?
         && (engine->devInfo_P->objType != DPT_SCSI_PROCESSOR)
	   )
		{
   		speed = engine->devInfo_P->busSpeed;	
   		if ( engine->devInfo_P->flags3 & FLG_DEV_FIBRE )
        {
			sprintf(speedString, EventStrings[STR_FILLIN_GHZ], speed / 1000);
		   	strcpy( widthString, EventStrings[STR_FIBRE]);
        }
        else
        {
   	   		if (speed != 0)
	   		{
         		// Do some math based on the device width/type to get the xfer rate in MB/Second.
         		// If we are a wide device...
         		if (engine->devInfo_P->flags3 & FLG_DEV_WIDE16)
		        {
      		   		// Compute the MB/Second rate for a wide device, multiplying
      				// by 2 to double the width pass-thru.
         			sprintf (speedString, EventStrings[STR_FILLIN_MB_PER_SEC], (speed * 2));
	   			}
         		else if (engine->devInfo_P->flags3 & FLG_DEV_FIBRE)
		        {
      		   		// Compute the MB/Second rate for fibre, we'll get back 1000 MHz or 2000 MHz 
		      		// Dividing my 10 gives us the MB/Second rate.
         			sprintf (speedString, EventStrings[STR_FILLIN_MB_PER_SEC], (speed / 10));
	   			}
         		else
		        {
      		   		// Compute the MB/Second rate.
      				sprintf (speedString, EventStrings[STR_FILLIN_MB_PER_SEC], speed);
   				}
            }
   	   		else
	   		{
		   		strcpy(speedString, EventStrings[STR_ASYNC]);
   			}

            // print the width
   	   		if ( engine->devInfo_P->flags3 & FLG_DEV_WIDE16 )
	   		{
		   		strcpy( widthString, EventStrings[STR_WIDE]);
   			}
   	   		else 				
	   		{
		   		strcpy( widthString, EventStrings[STR_NARROW]);
				}
			}
      }
      else
      {
		   // if we can't get the speed then report a ? 
   		sprintf(speedString, "%s", "?");
	  }
   	  output.add_Item( speedString );
	  output.add_Item( widthString );
      output.add_Item( "\n" );
   }

	EXIT();
	return( err );
}

Command::Dpt_Error List_Device::GetSpeedFromNvram (
                   DPT_TAG_T this_Objs_Tag)
{
	ENTER( "Command::Dpt_Error	List_Device::(GetSpeedFromNvram(");
	Dpt_Error err;

	// get the NVRAM page
	engine->Reset();
	engine->Insert ((uCHAR)0x2E);	// NVRAM page
	engine->Insert ((uCHAR)0x40);	// Interpret and current

	err = engine->Send (MSG_GET_MODE_PAGE, hba_Tag);

	uCHAR speeds[4];
	speeds[0] = (uCHAR) -1;
	speeds[1] = (uCHAR) -1;
	speeds[2] = (uCHAR) -1;
	speeds[3] = (uCHAR) -1;

	if (!err.Is_Error())
	{
		dptNVRAM_S NVRAM;

		if ((engine->Extract ((uCHAR *) &NVRAM, 4) != 0)
		 && (engine->Extract ((uCHAR *) &NVRAM, sizeof (NVRAM)) != 0 ))
		{
			speeds[0] = NVRAM.getXfrRate(0);
			speeds[1] = NVRAM.getXfrRate(1);
			speeds[2] = NVRAM.getXfrRate(2);
			speeds[3] = NVRAM.getXfrRate(3);
		}
	}

	engine->Reset();
	err = engine->Send( MSG_GET_INFO, this_Objs_Tag );

	if (!err.Is_Error())
	{
		dptCaddr_S *addr_P = &engine->devInfo_P->addr;
		uSHORT speed = speeds[addr_P->chan];

		switch (speed)
		{
			case 0:
            NvramMegaHertz = 10;
				break;
			case 1:
            NvramMegaHertz = 8;
				break;
			case 2:
            NvramMegaHertz = 5;
				break;
			case 3:
            NvramMegaHertz = 1; // async
				break;
			case 4:
            NvramMegaHertz = 20;
				break;
			case 5:
            NvramMegaHertz = 40;
				break;
			case 6:
            NvramMegaHertz = 80;
				break;
			case 7:
            NvramMegaHertz = 160;
				break;
			case 8:
            NvramMegaHertz = 320;
				break;
			default:
            NvramMegaHertz = 0; // unknown
				break;
		}
   }
   return err;
}

Command::Dpt_Error List_Device::GetSpeedFromHW (
                   int dev_chan)
{
	ENTER( "Command::Dpt_Error	List_Device::(GetSpeedFromHW(");
	Dpt_Error err;

	engine->Reset();
	engine->Insert ((uCHAR)0x33);	// HW page
	engine->Insert ((uCHAR)0x01);	// Read, do not clear
	err = engine->Send (MSG_GET_LOG_PAGE, hba_Tag);
	char temp_Buf[BUF_LEN];
	temp_Buf[0] = '\0';
   uSHORT pageLen;
	if (!err.Is_Error())
	{
		engine->Extract (temp_Buf, 4);
      pageLen = (((temp_Buf[2] & 0xff) * 256) + 
                  (temp_Buf[3] & 0xff));
      // search for Parameter Code 0x0d
      uSHORT index = 4;
      uSHORT parmLen;
      bool foundD = false;
      while (index < pageLen && !foundD)
      {
         engine->Extract (temp_Buf, 4);
         if (temp_Buf[1] == 0x0d)
         {
            parmLen = temp_Buf[3] & 0xff;
    			engine->Extract (temp_Buf, parmLen);
            if (temp_Buf[0] == dev_chan)
            foundD = true;
         }
         else
         {
            parmLen = temp_Buf[3] & 0xff;
            engine->Skip (parmLen);
            index += parmLen;
         }
      }
   	temp_Buf[parmLen] = '\0';
      // if fibre
      if (temp_Buf[2] & 0x01)
         HwMegaHertz = 0xffff;
      else
         HwMegaHertz = temp_Buf[3];
	}
   return err;
}


Command::Dpt_Error List_Device::List_Log_Devices(
							String_List		&output,
							get_Devs_Type	devs_Type)
{
	ENTER( "Command::Dpt_Error		List_Device::List_Log_Devices(" );
	int			dev_Index;
	Dpt_Error	err;
	bool		more_Devs_Left	= true;
	bool		retrieve_Hiddens	= false;
	bool		first_time_through	= true;

	switch( devs_Type )
	{
		case GET_RAID_HOT_SPARE:
//		case GET_RAID_REDIRECT:
			retrieve_Hiddens = true;
			break;
	}

	for( dev_Index = 0; more_Devs_Left; dev_Index++ )
	{
		DPT_TAG_T this_Objs_Tag;

		this_Objs_Tag = Get_Log_Dev_by_Index( dev_Index, 
            retrieve_Hiddens, &more_Devs_Left, obj_Root.hba);

		// for REDIRECT, make sure the type is correct, otherwise the
		// header is printed and no devices
		if ((devs_Type == GET_RAID_REDIRECT) && (more_Devs_Left))
		{
        	err = engine->Send(MSG_GET_INFO, this_Objs_Tag);
		   if (engine->devInfo_P->raidType != RAID_TYPE_REDIRECT)
            continue;
		}
		// do not print bottom of dual level array (is done when
		// top level is found)
		if ((devs_Type != GET_RAID_REDIRECT) &&
			(engine->devInfo_P->raidParent != -1))
			continue;

		if( more_Devs_Left )
		{
			if( first_time_through == true )
			{
    			switch( devs_Type )
				{
           		case GET_SCSI_DASD:
           		case GET_SCSI_SASD:
           		case GET_SCSI_PRINTER:
           		case GET_SCSI_PROCESSOR:
				case GET_SCSI_WORM:
           		case GET_SCSI_JUKEBOX:
           		case GET_SCSI_CD_ROM:
              	case GET_SCSI_SCANNER:
  	            case GET_SCSI_OPTICAL:
				case GET_SCSI_PRO_ROOT:
       	   		case GET_SCSI_PRO_CONNECTION:
				case GET_RAID_REDIRECT:
         			output.add_Item(EventStrings[STR_LIST_DEV_PHYS_HEADER]);
      			   break;

				case GET_ARRAY:
					if (printDummyStr)
					{
            			output.add_Item(EventStrings[STR_LOGICAL_VIEW]);
					}
           			output.add_Item(EventStrings[STR_LIST_DEV_ARRAY_HEADER]);
      				break;

		        case GET_SCSI_HBA:
            		if (showCache)
					   output.add_Item(EventStrings[STR_LIST_DEV_CTLR_HEADER]);
				   else
						output.add_Item(EventStrings[STR_LIST_DEV_CTLR_HEADER_NO_CACHE]);
                  
         			break;

         		case GET_RAID:
			         output.add_Item(EventStrings[STR_LIST_DEV_PHYS_HEADER]);
         			break;

		        case GET_RAID_HOT_SPARE:
			         output.add_Item(EventStrings[STR_LIST_DEV_HOT_SPARE_HEADER]);
         			retrieve_Hiddens	= true;
         			break;

				case GET_ARRAYNAME:
						output.add_Item(EventStrings[STR_LIST_ARRAYNAME_HEADER]);

         		case GET_SCSI_BCD:
         		case GET_RAID_BCD:
            		default:
      			// _asm int 3;
         			break;
		      }

           	output.add_Item( STR_DEV_DIVIDING_LINE );
				first_time_through = false;
			}
			err |= List_This_Device(output, this_Objs_Tag, devs_Type);
			output.add_Item( "\n" );

			// GET_RAID_HOT_SPARE is Added by Qi Zhu
			if ((devs_Type == GET_ARRAY) || (devs_Type ==GET_RAID_HOT_SPARE))
			{
				int comp_Index;
				uLONG smallLBA = 0;
				int dataDrives;
				int prev_level = 0;
				dptID_S *idlist = new dptID_S[PHYS_LIST_SIZE];
				dptID_S *idlist2 = new dptID_S[PHYS_LIST_SIZE];

				// get number of components in this array
				int numComps = EngineFindIDs(FID_RESET_OUTBUFF, 
				MSG_ID_ALL_COMPONENTS, this_Objs_Tag, idlist, 
				PHYS_LIST_SIZE);

				// if level is 1 and raid is 1 or 5, this array may
				// be protected by hot spare(s)
				err |= engine->Send(MSG_GET_INFO, this_Objs_Tag);
				if ((engine->devInfo_P->level == 1) &&
                ((engine->devInfo_P->raidType == RAID_5) || 
                 (engine->devInfo_P->raidType == RAID_1)))
				{
					// get the info for this raid
					dataDrives = EngineFindIDs(FID_RESET_OUTBUFF, 
					MSG_ID_ALL_COMPONENTS, this_Objs_Tag, 
					idlist2, PHYS_LIST_SIZE) - 1;
					smallLBA = engine->devInfo_P->capacity.maxLBA / dataDrives;
				}

				for (comp_Index = 0; comp_Index < numComps; ++comp_Index)
				{
					err |= engine->Send(MSG_GET_INFO, idlist[comp_Index].tag);

					// look for hot spares if done printing array
					if ((prev_level == 2) && 
					(engine->devInfo_P->level == 1) &&
					(smallLBA > 0))
						Look_for_Hot_Spares (output, smallLBA, devs_Type);

					// list this raid
			   
					err |= List_This_Device(output, idlist[comp_Index].tag, devs_Type);
					output.add_Item( "\n" );

					// if level is 1 and raid is 1 or 5, this array may
					// be protected by hot spare(s)
					err |= engine->Send(MSG_GET_INFO, idlist[comp_Index].tag);
					if ((engine->devInfo_P->level == 1) &&
					((engine->devInfo_P->raidType == RAID_5) || 
                    (engine->devInfo_P->raidType == RAID_1)))
					{
						// get the info for this raid
						dataDrives = EngineFindIDs(FID_RESET_OUTBUFF, 
						MSG_ID_ALL_COMPONENTS, idlist[comp_Index].tag, 
						idlist2, PHYS_LIST_SIZE) - 1;
						smallLBA = engine->devInfo_P->capacity.maxLBA / dataDrives;
					}
					prev_level = engine->devInfo_P->level;
				}

				if ((prev_level == 2) && (smallLBA > 0))
				{
					Look_for_Hot_Spares (output, smallLBA, devs_Type);
				}
				delete [] idlist;
				delete [] idlist2;
			}
		}
	}

	if( first_time_through == true )
	{
		err |= Dpt_Error::DPT_CMD_ERR_CANT_FIND_COMPONENT;
	}
	EXIT();
	return( err );
}

Command::Dpt_Error List_Device::Look_for_Hot_Spares(
   String_List &output, uLONG smallLBA, get_Devs_Type	devs_Type)
{
   int comp_Index;
   dptID_S *idlist2 = new dptID_S[PHYS_LIST_SIZE];
	Dpt_Error err;

   // get siblings, loop through for hotspares
   int numSibs = EngineFindIDs(FID_RESET_OUTBUFF, 
      MSG_ID_LOGICALS, engine->hbaInfo_P->attachedTo, idlist2, PHYS_LIST_SIZE);
  	for(comp_Index = 0; comp_Index < numSibs; ++comp_Index)
   {
      err |= engine->Send(MSG_GET_INFO, idlist2[comp_Index].tag);
      if ((engine->devInfo_P->raidType == RAID_HOT_SPARE) &&
          (engine->devInfo_P->capacity.maxLBA >= smallLBA))
         // RAID is protected - print hot spare (it protects the above printed RAID)
      {
	   	err |= List_This_Device(output, idlist2[comp_Index].tag, devs_Type);
         output.add_Item( "\n" );
      }
   }
   delete [] idlist2;
   return (err);
}

// in the beginning... this just printed LSUs.  Since it only is used
// to list cache settings, it was changed to print LSUs and member
// drives, but I didn't change the name, so it is misleading.
Command::Dpt_Error List_Device::List_LSU_Devices(
							String_List		&output)
{
	ENTER( "Command::Dpt_Error		List_Device::List_LSU_Devices(" );
	int dev_Index;
	Dpt_Error err;
   get_Devs_Type dev_Type;
   bool obj_Found = true;
   SCSI_Address obj_Address;
   SCSI_Address null_Address;
	bool first_time_through	= true;
   bool tag_Valid;

   // set each field to -1 to find all devices
   null_Address.hba = -1;
   null_Address.bus = -1;
   null_Address.id = -1;
   null_Address.lun = -1;
   null_Address.level = -1;

	for (dev_Index = 0; obj_Found; dev_Index++)
	{
      DPT_TAG_T obj_Tag = Get_Address_by_Index(
         null_Address, dev_Index, &obj_Found, &dev_Type);

        // for REDIRECT, make sure the type is correct, otherwise the
        // header is printed and no devices
      if ((dev_Type == GET_RAID_REDIRECT) && (obj_Found))
      {
        	err = engine->Send(MSG_GET_INFO, obj_Tag);
		   if (engine->devInfo_P->raidType != RAID_TYPE_REDIRECT)
            continue;
      }

		if (obj_Found)
		{
         obj_Address = DPT_Tag_to_Address(obj_Tag, &tag_Valid);
         if (!tag_Valid) 
            continue;

         // send msg to device to get the raid parent info
         engine->Send(MSG_GET_INFO, obj_Tag);
         // if raid parent is not 0xffffffff, then this device
         // is the member of an array
         if (engine->devInfo_P->raidParent != -1)
            continue;

    		switch(dev_Type)
			{
            case GET_RAID:
               dev_Type = GET_ARRAY;
         	case GET_SCSI_DASD:
            case GET_RAID_REDIRECT:
            case GET_ARRAY:
		      case GET_RAID_HOT_SPARE:
            case GET_SCSI_CD_ROM:
            case GET_SCSI_SASD:
               if (first_time_through)
               {
			         output.add_Item (EventStrings[STR_LIST_CACHE_HEADER]);
                  output.add_Item (STR_DEV_DIVIDING_LINE);
				      first_time_through = false;
               }
			      err |= List_This_Device(output, obj_Tag, dev_Type);
               output.add_Item( "\n" );

               // print elements of the array
      			if (dev_Type == GET_ARRAY)
		      	{
      				int comp_Index;
                  dptID_S *idlist = new dptID_S[PHYS_LIST_SIZE];

                  // get number of components in this array
                  int numComps = EngineFindIDs(FID_RESET_OUTBUFF, 
                     MSG_ID_ALL_COMPONENTS, obj_Tag, idlist, 
                     PHYS_LIST_SIZE);

		      		for (comp_Index = 0; comp_Index < numComps; ++comp_Index)
				      {
                     err |= engine->Send(MSG_GET_INFO, idlist[comp_Index].tag);

                     // list this raid
                     err |= List_This_Device(output, idlist[comp_Index].tag, dev_Type);
                     output.add_Item( "\n" );
                  }
                  delete [] idlist;
		      	}
        			break;
	      }
		}
	}

	if( first_time_through == true )
	{
		err |= Dpt_Error::DPT_CMD_ERR_CANT_FIND_COMPONENT;
	}
	EXIT();
	return( err );
}



Command::Dpt_Error	List_Device::List_Devices(
							String_List		&output)
{
	ENTER( "Command::Dpt_Error	List_Device::List_Devices(" );
	int				dev_Index;
	bool			more_Devs_Left	= true;
	Dpt_Error		err;
	bool			filter_This_Out	= false;
	bool			first_time_through = true;

	
	for( dev_Index = 0;
		more_Devs_Left && !err.Is_Error();
		dev_Index++ )
	{
		DPT_TAG_T		hba_Tag;

		hba_Tag = Get_HBA_by_Index(dev_Index, &more_Devs_Left);

	   dptNVRAM_S nvram_Buf;

	   if (hba_Tag != -1)
	   {
		   engine->Reset();	
		   err = engine->Send( MSG_GET_NV_INFO, hba_Tag );
		   if( !err.Is_Error() )
		   {
			   engine->Extract( &nvram_Buf, sizeof( nvram_Buf )) ;
			   unsigned char NvOemFlags;

				//
				// Pull out the flags field where the bit resides
				//
				NvOemFlags = nvram_Buf.getOEMflags();

				//
				// If the bit is set, clear our show cache flag
				//
				if(NvOemFlags & NV_HIDE_CACHE)
					showCache = false;
				else 
				{
					showCache = true;
					break;
				}
		   }
	   }
	}

	more_Devs_Left	= true;
	for( dev_Index = 0;
		more_Devs_Left && !err.Is_Error();
		dev_Index++ )
	{
		get_Devs_Type	devs_Type;
		DPT_TAG_T		this_Objs_Tag;

      switch(list_What)
      {
         case LIST_VERSION:
         case LIST_CONTROLLERS:
         case LIST_BATTERY:
   			devs_Type = GET_SCSI_HBA;
            // CR2072 - can specify hba or get all
            if (obj_Root.hba != -1)
    		   	this_Objs_Tag = Get_HBA_by_Index( obj_Root.hba, &more_Devs_Left);
            else
               this_Objs_Tag = Get_HBA_by_Index( dev_Index, &more_Devs_Left);
		   	filter_This_Out = false;
            break;
         default:
   			this_Objs_Tag = Get_Dev_by_Index( obj_Root.hba,
								dev_Index, &more_Devs_Left, &devs_Type );
	   		if (devs_Type == GET_SCSI_HBA)
		   	{
			   	filter_This_Out	= true;
   			}
	   			else
		   	{
			   	filter_This_Out	= false;
   			}
            break;
      }

		if( more_Devs_Left && !filter_This_Out )
		{
			if( first_time_through == true )
			{
            switch(list_What)
            {
               case LIST_VERSION:
				    {

					   if (showCache)
						   output.add_Item(EventStrings[STR_LIST_DEV_VERSION_HEADER]);
					   else
							output.add_Item(EventStrings[STR_LIST_DEV_VERSION_HEADER_NO_CACHE]);
                  	  // CR2072 - if hba specified, get out of for()
					  if (obj_Root.hba != -1) 
						 more_Devs_Left = false;
					}
				  break;
               case LIST_CONTROLLERS:
				   {

					   if (showCache)
						   output.add_Item(EventStrings[STR_LIST_DEV_CTLR_HEADER]);
					   else
							output.add_Item(EventStrings[STR_LIST_DEV_CTLR_HEADER_NO_CACHE]);
                  
					   // CR2072 - if hba specified, get out of for()
					  if (obj_Root.hba != -1) 
						 more_Devs_Left = false;
				   }
                  break;
               case LIST_BATTERY:
  					   output.add_Item(EventStrings[STR_LIST_BATTERY_HEADER]);
                  if (obj_Root.hba != -1) 
                     more_Devs_Left = false;
                  break;
               default:
                  if (printDummyStr)
            			output.add_Item(EventStrings[STR_PHYSICAL_VIEW]);
   					output.add_Item(EventStrings[STR_LIST_DEV_PHYS_HEADER]);
                  break;
            }
				output.add_Item( STR_DEV_DIVIDING_LINE );
				first_time_through = false;
			}
         if (list_What == LIST_BATTERY)
   			err |= List_Battery_Info( output, this_Objs_Tag);
         else
   			err |= List_This_Device( output, this_Objs_Tag, devs_Type);
         output.add_Item( "\n" );
		}
	}
	if ( first_time_through == true )
	{
		err |= Dpt_Error::DPT_CMD_ERR_CANT_FIND_COMPONENT;
	}

	EXIT();
	return( err );
}


Command::Dpt_Error	List_Device::List_Inquiry(
							String_List		&output)
{
	ENTER( "Command::Dpt_Error	List_Device::List_Inquiry(" );
	int				dev_Index;
	bool			more_Devs_Left	= true;
	Dpt_Error		err;
	bool			filter_This_Out	= false;
	bool			first_time_through = true;

	for( dev_Index = 0;
		more_Devs_Left && !err.Is_Error();
		dev_Index++ )
	{
		get_Devs_Type	devs_Type;
		DPT_TAG_T		this_Objs_Tag;

		this_Objs_Tag = Get_Dev_by_Index( obj_Root.hba,
							dev_Index, &more_Devs_Left, &devs_Type );
		if( devs_Type == GET_SCSI_HBA )
			filter_This_Out = true;
		else
			filter_This_Out = false;

		if( more_Devs_Left && !filter_This_Out )
		{
			if( first_time_through == true )
			{
				output.add_Item(EventStrings[STR_LIST_INQUIRY_HEADER]);
				output.add_Item( STR_DEV_DIVIDING_LINE );
				first_time_through = false;
			}
			err |= List_This_Device( output, this_Objs_Tag, devs_Type);
         output.add_Item( "\n" );
		}
	}
	if ( first_time_through == true )
	{
		err |= Dpt_Error::DPT_CMD_ERR_CANT_FIND_COMPONENT;
	}
   else
   {
      output.add_Item( "\n");
      output.add_Item(EventStrings[STR_LIST_INQUIRY_HEADER_MAP1]);
		output.add_Item(EventStrings[STR_LIST_INQUIRY_HEADER_MAP2]);
		output.add_Item(EventStrings[STR_LIST_INQUIRY_HEADER_MAP3]);
		output.add_Item(EventStrings[STR_LIST_INQUIRY_HEADER_MAP4]);
		output.add_Item(EventStrings[STR_LIST_INQUIRY_HEADER_MAP5]);
		output.add_Item(EventStrings[STR_LIST_INQUIRY_HEADER_MAP6]);
		output.add_Item(EventStrings[STR_LIST_INQUIRY_HEADER_MAP7]);
		output.add_Item(EventStrings[STR_LIST_INQUIRY_HEADER_MAP8]);
		output.add_Item(EventStrings[STR_LIST_INQUIRY_HEADER_MAP9]);
		output.add_Item(EventStrings[STR_LIST_INQUIRY_HEADER_MAP10]);
		output.add_Item(EventStrings[STR_LIST_INQUIRY_HEADER_MAP11]);
		output.add_Item(EventStrings[STR_LIST_INQUIRY_HEADER_MAP12]);
		output.add_Item(EventStrings[STR_LIST_INQUIRY_HEADER_KEY]);
   }

	EXIT();
	return( err );
}


Command::Dpt_Error List_Device::List_Battery_Info(
						String_List		&output,
						DPT_TAG_T		hbaTag)
{
	Dpt_Error err;
	Dpt_Error tempErr;
	dptBatteryInfo_S batteryInfo;
	char temp_Buf[BUF_LEN];

	err = engine->Send (MSG_GET_INFO, hbaTag);
	if (!err.Is_Error ())
	{
		dptCaddr_S *addr_P = &engine->devInfo_P->addr;
		sprintf (temp_Buf, "d%d", addr_P->hba);
		output.add_Item (temp_Buf);
		output.add_Item (Strip_Trailing_Whitespace (engine->devInfo_P->vendorID));
		output.add_Item (Strip_Trailing_Whitespace (engine->devInfo_P->productID));

      // if board doesn't have battery, will return err
		tempErr = engine->Send (MSG_GET_BATTERY_INFO, hbaTag);
	   if (tempErr.Is_Error()
		   || (engine->Extract(&batteryInfo, sizeof(batteryInfo)) == 0))
		{
         output.add_Item (EventStrings[STR_NO_BATTERY]);
  		   memset (&batteryInfo, 0, sizeof(batteryInfo));
         return (err); // return no error
   	}

      // status - battery status values (from get_info.h)
      GetBatteryEventString((uSHORT) batteryInfo.status, temp_Buf);
      output.add_Item (temp_Buf);
/*      switch (batteryInfo.status)
      {
         // No battery was detected
         case BATTERY_STAT_NO_BATTERY:
            output.add_Item (EventStrings[STR_NO_BATTERY]);
            break;
         // Battery is charging
         case BATTERY_STAT_FAST_CHARGE:
            output.add_Item (EventStrings[STR_BBU_CHARGING]);
            break;
         // Battery is discharging
         case BATTERY_STAT_DISCHARGE:
            output.add_Item (EventStrings[STR_BBU_DISCHARGING]);
            break;
         // A full-charge is less than predictive failure threshold
         case BATTERY_STAT_BAD:
            output.add_Item (EventStrings[STR_BBU_BAD]);
            break;
         // Battery is fully charged
         case BATTERY_STAT_FULL:
         case BATTERY_STAT_TRICKLE_CHARGE:
            output.add_Item (EventStrings[STR_BBU_FULL]);
            break;
         // Initial calibration charge
         case BATTERY_STAT_INIT_CHARGE:
            output.add_Item (EventStrings[STR_BBU_INIT_CHARGE]);
            break;
         // Initial calibration discharge
         case BATTERY_STAT_INIT_DISCHARGE:
            output.add_Item (EventStrings[STR_BBU_INIT_DISCHARGE]);
            break;
         // Initial calibration recharge
         case BATTERY_STAT_INIT_RECHARGE:
            output.add_Item (EventStrings[STR_BBU_INIT_RECHARGE]);
            break;
         // Maintenance calibration charge
         case BATTERY_STAT_MAINTENANCE_CHARGE:
            output.add_Item (EventStrings[STR_BBU_MAINT_CHARGE]);
            break;
         // Maintenence calibration discharge
         case BATTERY_STAT_MAINTENANCE_DISCHARGE:
            output.add_Item (EventStrings[STR_BBU_MAINT_DISCHARGE]);
            break;
         // unknown/invalid
         default:
            output.add_Item (EventStrings[STR_UNKNOWN]);
            break;
      }*/

      // voltage
		sprintf (temp_Buf, EventStrings[STR_FILLIN_MV], batteryInfo.voltage);
		output.add_Item (temp_Buf);

      // current
		sprintf (temp_Buf, EventStrings[STR_FILLIN_MA], batteryInfo.current);
		output.add_Item (temp_Buf);

      // full capacity
		sprintf (temp_Buf, EventStrings[STR_FILLIN_MAH], batteryInfo.fullChargeCapacity);
		output.add_Item (temp_Buf);

      // remaining capacity
		sprintf (temp_Buf, EventStrings[STR_FILLIN_MAH], batteryInfo.remainingCapacity);
		output.add_Item (temp_Buf);

      // remaining time
      int nHours = batteryInfo.remainingTime / 60;
		sprintf (temp_Buf, EventStrings[STR_FILLIN_HRS], nHours);
		output.add_Item (temp_Buf);

   }
   return (err);
}


Command::Dpt_Error	List_Device::List_This_Device(
						String_List		&output,
						DPT_TAG_T		this_Objs_Tag,
						get_Devs_Type	devs_Type
						)
{
	ENTER( "Command::Dpt_Error	List_Device::List_This_Device(" );
	Dpt_Error err;
	const long ONE_K = 1024;
   add_plus = false;

	err = engine->Send (MSG_GET_INFO, this_Objs_Tag);
	if( !err.Is_Error() )
		{
		dptCaddr_S *addr_P = &engine->devInfo_P->addr;
		char *obj_Type_Str = EventStrings[STR_UNKNOWN];
		char *raid_Type_Ptr = 0;
		bool show_Capacity = false;
		long capacity_in_Mb;
		long cache_Size_in_Mb = -1;
		char temp_Buf[BUF_LEN];
		int indent = 0;

		{
		long capacity_in_Kb;
		capacity_in_Kb = (engine->devInfo_P->capacity.maxLBA + 1) / 2;
		capacity_in_Mb = capacity_in_Kb / ONE_K;
		}

		switch( devs_Type )
			{
			case GET_ARRAY:
SHOW_REDIRECT_DASD:
				if ( engine->devInfo_P->raidParent != (DPT_TAG_T)-1 )
					{
					indent = 1;
					err = engine->Send( MSG_GET_INFO,
					  engine->devInfo_P->raidParent );
					if(( !err.Is_Error() )
					 && ( engine->devInfo_P->raidParent != (DPT_TAG_T)-1 ))
						{
						indent = 2;
						}
					// Reread
					err = engine->Send( MSG_GET_INFO, this_Objs_Tag );
               save_indent = indent;
					}
            else
            {
               if ((engine->devInfo_P->raidType == RAID_TYPE_HOT_SPARE) &&
                  (save_indent > 0))
                  indent = save_indent;
            }
				switch ( engine->devInfo_P->raidType )
					{
					case RAID_TYPE_0:
						obj_Type_Str = EventStrings[STR_RAID_0];
                  show_Capacity = true;
                  goto SHOW_PHYSICAL;
						break;

					case RAID_TYPE_1:
						obj_Type_Str = EventStrings[STR_RAID_1];
                  show_Capacity = true;
                  goto SHOW_PHYSICAL;
						break;

					case RAID_TYPE_5:
						obj_Type_Str = EventStrings[STR_RAID_5];
                  show_Capacity = true;
                  goto SHOW_PHYSICAL;
						break;

					case RAID_TYPE_HOT_SPARE:
						obj_Type_Str = EventStrings[STR_RAID_HOT_SPARE];
                  add_plus = true;
						break;
                  // what is going on here?  this is never reached! kds
				goto SHOW_PHYSICAL;    
					case RAID_TYPE_REDIRECT:
						if (engine->devInfo_P->flags2 & FLG_DEV_MANUAL_JBOD_CONFIGURED)
							obj_Type_Str = EventStrings[STR_RAID_JBOD];
						else
							obj_Type_Str = EventStrings[STR_RAID_REDIRECT];
						show_Capacity = true;
						goto SHOW_PHYSICAL;
						break;

					default:
						obj_Type_Str = EventStrings[STR_SCSI_DASD];
						break;
					}
				show_Capacity = true;


			case GET_SCSI_DASD:
				//if ((engine->devInfo_P->raidType & RAID_TYPE_REDIRECT) && (engine->devInfo_P->flags2 & FLG_DEV_MANUAL_JBOD_CONFIGURED))
					obj_Type_Str = EventStrings[STR_SCSI_DASD];
				show_Capacity	= true;

SHOW_PHYSICAL:
                Show_Physical(output, obj_Type_Str, indent, 
                      show_Capacity, capacity_in_Mb, 
                      this_Objs_Tag, devs_Type);
				break;

			case GET_SCSI_SASD:
				obj_Type_Str = EventStrings[STR_SCSI_SASD];
				show_Capacity	= false;
				goto SHOW_PHYSICAL;

			case GET_SCSI_WORM:
				obj_Type_Str = EventStrings[STR_SCSI_WORM];
				show_Capacity	= true;
				goto SHOW_PHYSICAL;

			case GET_SCSI_JUKEBOX:
				obj_Type_Str = EventStrings[STR_SCSI_JUKEBOX];
				show_Capacity	= false;
				goto SHOW_PHYSICAL;

			case GET_SCSI_CD_ROM:
				obj_Type_Str = EventStrings[STR_SCSI_CD_ROM];
				show_Capacity	= false;
				goto SHOW_PHYSICAL;

			case GET_SCSI_OPTICAL:
				obj_Type_Str = EventStrings[STR_SCSI_OPTICAL];
				show_Capacity	= true;
				goto SHOW_PHYSICAL;

			case GET_SCSI_PRINTER:
				obj_Type_Str = EventStrings[STR_SCSI_PRINTER];
				output.add_Item( obj_Type_Str );
				break;

			case GET_SCSI_PROCESSOR:
            if (strstr(engine->devInfo_P->vendorExtra, EventStrings[STR_SAFTE]))
   				obj_Type_Str = EventStrings[STR_SAFTE];
            else
	   			obj_Type_Str = EventStrings[STR_SCSI_PROCESSOR];
            goto SHOW_PHYSICAL;
				break;

			case GET_SCSI_SCANNER:
				obj_Type_Str = EventStrings[STR_SCSI_SCANNER];
				output.add_Item( obj_Type_Str );
				break;

			case GET_SCSI_PRO_ROOT:
				obj_Type_Str = EventStrings[STR_SCSI_PRO_ROOT];
				output.add_Item( obj_Type_Str );
				break;

			case GET_SCSI_PRO_CONNECTION:
				obj_Type_Str = EventStrings[STR_SCSI_PRO_CONNECTION];
				output.add_Item( obj_Type_Str );
				break;

			case GET_SCSI_HBA:
					{
					dptHBAinfo_S *hba_Info = engine->hbaInfo_P;

					obj_Type_Str = EventStrings[STR_HBA];

						{
					long cache_Size_in_Kb;
					cache_Size_in_Kb = hba_Info->memSize;
					cache_Size_in_Mb = cache_Size_in_Kb / ONE_K / ONE_K;
						}
					sprintf ( temp_Buf, "d%d", addr_P->hba );
					output.add_Item( temp_Buf );

               switch (list_What)
               {
                  case LIST_VERSION:
                  case LIST_TAGS:
                     break;
                  default:
                   {
      					DPTControllerMap map;
	      				for ( int bus = 0; bus < 3; ++bus )
		   				{
		      				char * String = map.getChannelString(
			      			  addr_P->hba, bus );

				      		output.add_Item( ( *String == 'c' )
					      	  ? String : ( hba_Info->chanInfo[bus].flags
							      ? "--" : "  " ));
      						delete [] String;
	   					}
                    }
                     break;
                }

					output.add_Item( Strip_Trailing_Whitespace( engine->devInfo_P->vendorID ) );
					output.add_Item( Strip_Trailing_Whitespace( engine->devInfo_P->productID ) );
               if (list_What == LIST_TAGS)
                  return (err);
					
					char  hbaRevision[5];
					strcpy(hbaRevision,hba_Info->revision);
					dptNVRAM_S nvram_Buf;
				   uCHAR NvOemFlags = 0;
				   engine->Reset();	
				   err = engine->Send( MSG_GET_NV_INFO, this_Objs_Tag );
				   if( !err.Is_Error() )
				   {
					   engine->Extract( &nvram_Buf, sizeof( nvram_Buf )) ;
					   
 
						//
						// Pull out the flags field where the bit resides
						//
						NvOemFlags = nvram_Buf.getOEMflags();
 
						
				   }
					//
					// If the bit is set, clear our show cache flag
					//
					if(NvOemFlags & NV_HIDE_CACHE)
					{
						sprintf(temp_Buf, " ");
						output.add_Item( temp_Buf );
					}
					else
					{
						sprintf(temp_Buf, "%ld%s", cache_Size_in_Mb, EventStrings[STR_MB]);
						output.add_Item( temp_Buf );
						
					}
					output.add_Item(hbaRevision);

					engine->Reset();
					engine->Insert( (uCHAR)0x2E );	// NVRAM page
					engine->Insert( (uCHAR)0x40 );	// Interpret and current

					// get the defaults page
                    Dpt_Error status;
					status = engine->Send( MSG_GET_MODE_PAGE, this_Objs_Tag );

					temp_Buf[0] = '\0';
					if( status.Success() )
						{
						if (( engine->Extract( temp_Buf, 108 ) == 0 )
						 || ( engine->Extract( temp_Buf, 8 ) == 0 ))
							{
							temp_Buf[0] = '\0';
							}
						else
							{
							temp_Buf[8] = '\0';
							}
						}
					output.add_Item( temp_Buf );

					temp_Buf[0] = '\0';
					if( status.Success() )
						{
						if ( engine->Extract( temp_Buf, 15 ) == 0 )
							{
							temp_Buf[0] = '\0';
							}
						else
							{
							char * String = temp_Buf;

							temp_Buf[15] = '\0';
							while (*String &&
							  ((('0' <= *String) && (*String <= '9')) ||
							   (('A' <= *String) && (*String <= 'Z')) ||
							    (*String == '-')))
								{
								++String;
								}
							*String = '\0';
							}
						}
					}
               switch (list_What)
               {
                  case LIST_VERSION:
                     // print BIOS and SMOR versions
                     GetSmorBiosSignatures(this_Objs_Tag, output);
                     // print serial #
	    			   	output.add_Item( temp_Buf );
                     break;
                  default:
                     // print serial #
       					output.add_Item( temp_Buf );
                     // print controller status
      					dptHBAinfo_S hbaInfo;
	      				engine->Reset();
		      			err |= engine->Send( MSG_GET_INFO, this_Objs_Tag );
						
			      		if ( !err.Is_Error() )
				      		engine->Extract( &hbaInfo, sizeof(dptHBAinfo_S) );
						
					{
      					Dpt_Status status(this_Objs_Tag);
	      				if ( ( !err.Is_Error() ) && ( hbaInfo.status.flags & FLG_STAT_ALARM_ON ) )
                     {
#if defined _DPT_SOLARIS
   	   					output.add_Item(EventStrings[STR_ALARM]);
   	   					output.add_Item(" ");
   	   					output.add_Item(EventStrings[STR_ON]);
#else
	   	   				output.add_Item(EventStrings[STR_DEGRADED]);
#endif
                     }
   			   		else
   				   		output.add_Item ((char *)status);
					}
                     break;
               }
				break;

			case GET_SCSI_BCD:
				obj_Type_Str = EventStrings[STR_SCSI_BRIDGE_CTLR];
				break;

			case GET_RAID_BCD:
				obj_Type_Str = EventStrings[STR_RAID_BRIDGE_CTLR];
				break;

			case GET_RAID_REDIRECT:
				obj_Type_Str = EventStrings[STR_RAID_REDIRECT];
				switch( engine->devInfo_P->raidType )
                {
					case RAID_TYPE_REDIRECT:
                        {
                        show_Capacity = true;
                        Show_Physical(output, obj_Type_Str, indent,
                            show_Capacity, capacity_in_Mb, 
                            this_Objs_Tag, devs_Type);
                		output.add_Item( "\n" );
        				int comp_Index;
		        		bool more_Comp_Left = true;
				        get_Devs_Type comp_Type;
        				DPT_TAG_T this_Comp_Tag;

		        		for(comp_Index = 0; more_Comp_Left; ++comp_Index)
        				{
		        			this_Comp_Tag = Get_Component(this_Objs_Tag,
				        	  comp_Index, &more_Comp_Left, &comp_Type);
        					if((more_Comp_Left) && (comp_Type == GET_SCSI_DASD))
		        			{
				        		err |= List_This_Device(output, this_Comp_Tag, devs_Type);
                        output.add_Item( "\n" );
        					}
		        		}
                        }
                        break;

                    case RAID_TYPE_0:
                    case RAID_TYPE_1:
                    case RAID_TYPE_5:
                    case RAID_TYPE_HOT_SPARE:
						return (err);
                    default:
                        goto SHOW_REDIRECT_DASD;
                }
                break;

			case GET_RAID:
			case GET_RAID_HOT_SPARE:
			case GET_ARRAYNAME:
				
				obj_Type_Str = EventStrings[STR_RAID];

				switch( engine->devInfo_P->raidType )
					{
					case RAID_TYPE_0:
						raid_Type_Ptr = EventStrings[STR_RAID_0];
							{
							bool obj_Found = false;
							get_Devs_Type dev_Type;
							DPT_TAG_T component;

							component = Get_Component(this_Objs_Tag, 0,
							  &obj_Found, &dev_Type);
							if (( obj_Found ) && ( dev_Type == GET_RAID ))
								{
								err	|= engine->Send( MSG_GET_INFO, component );
								switch( engine->devInfo_P->raidType )
									{
									case RAID_TYPE_1:
										raid_Type_Ptr = EventStrings[STR_RAID_10];
										break;

									case RAID_TYPE_5:
										raid_Type_Ptr = EventStrings[STR_RAID_50];
										break;
									}
								}
							}
						// Re-Read the device information.
						err	|= engine->Send( MSG_GET_INFO, this_Objs_Tag );
						goto PRINT_RAID;

					case RAID_TYPE_1:
						if ( engine->devInfo_P->raidParent != (DPT_TAG_T)-1 )
							{
							return (err);
							}
						raid_Type_Ptr = EventStrings[STR_RAID_1];
						goto PRINT_RAID;

					case RAID_TYPE_5:
						if ( engine->devInfo_P->raidParent != (DPT_TAG_T)-1 )
							{
							return (err);
							}
						raid_Type_Ptr = EventStrings[STR_RAID_5];
						goto PRINT_RAID;

PRINT_RAID:
						// we don't support logical drive numbers (yet)
						if (devs_Type == GET_ARRAYNAME)
							obj_Type_Str = (char *)engine->devInfo_P->dptName;
						else
							obj_Type_Str = raid_Type_Ptr;
						
						show_Capacity	= true;
						goto SHOW_PHYSICAL;
#if 0
						output.add_Item( "?" );
						output.add_Item( Strip_Trailing_Whitespace( engine->devInfo_P->productID ) );
							{
							DPTControllerMap map;
							char * String = map.getTargetString(
							  addr_P->hba, addr_P->chan, addr_P->id, addr_P->lun );
							output.add_Item( String );
							delete [] String;
							}
						sprintf( temp_Buf, "%ld%s", capacity_in_Mb, EventStrings[STR_MB]);
						output.add_Item( temp_Buf );
							{
							Dpt_Status status(this_Objs_Tag);
							output.add_Item ( (char *)status );
							}
						output.add_Item( raid_Type_Ptr );
						break;
#endif

					case RAID_TYPE_HOT_SPARE:
						raid_Type_Ptr = EventStrings[STR_RAID_HOT_SPARE];
						goto PRINT_STANDALONE;

					case RAID_TYPE_REDIRECT:

						if (engine->devInfo_P->flags2 & FLG_DEV_MANUAL_JBOD_CONFIGURED)
							raid_Type_Ptr = EventStrings[STR_RAID_JBOD];
						else
							raid_Type_Ptr = EventStrings[STR_RAID_REDIRECT];
						
						goto PRINT_RAID;
PRINT_STANDALONE:
							{
							DPTControllerMap map;
							char * String = map.getTargetString(
							  addr_P->hba, addr_P->chan, addr_P->id, addr_P->lun );
							output.add_Item( String );
							delete [] String;
							}
						sprintf( temp_Buf, "%ld%s", capacity_in_Mb, EventStrings[STR_MB]);
						output.add_Item( temp_Buf );
						output.add_Item( raid_Type_Ptr );
						break;

					default:
						// Added by Qi Zhu (Show the physical address of Hot spare)	
						if (engine->devInfo_P->raidParent!=(DPT_TAG_T)-1)
						{												
							{
								DPTControllerMap map;
								char * String = map.getTargetString(
									addr_P->hba, addr_P->chan, addr_P->id, addr_P->lun);
								sprintf(temp_Buf, "%*s%s", 1, "", String);
								output.add_Item(temp_Buf);
								delete [] String;
							}
							sprintf( temp_Buf, "%ld%s",capacity_in_Mb, EventStrings[STR_MB]);
							output.add_Item( temp_Buf );
							output.add_Item( EventStrings[STR_RAID_HOT_SPARE]);
						}else {
							raid_Type_Ptr = EventStrings[STR_UNKNOWN];
						}
						break;
					}
				break;
			}
		}

	EXIT();
	return( err );
}

//=============================================================================
//
//Name: DeviceMibInterface_C::GetSmorBiosSignatures
//
//Abstract:  Searches HBA's flash memory for DPT SIG structures to create
//           signature strings for the BIOS and SMOR's versions
//
//Parameters: hbaNumber_in - index into global HBA table structures
//
//Return Values:  None
//
//Error Codes:
//
//Global Variables Affected:
//
//Notes:
//
//-----------------------------------------------------------------------------
void List_Device::GetSmorBiosSignatures(DPT_TAG_T hba_Tag,
                                       String_List &output)
{

    //
    // Save some data from the engine (I have no idea what this data is).
    //
    size_t saveSize = engine->fromEng_P->allocSize + sizeof(dptData_S) - 1;
    void *saveData = new char[saveSize];
    if ( saveData != NULL )
    {
        memcpy(saveData, engine->fromEng_P, saveSize);
    }



    //
    // Get the BIOS version number from flash memory.  No exceptions so check the
    // first byte of the signature and make sure it isn't zero.
    //
    BiosFlashMemory biosFlash(*engine, hba_Tag);
    dpt_sig_S biosSignature = biosFlash.GetSignature();
    if ( biosSignature.dsSignature[0] != 0 )
    {
        char version[10];
        sprintf(version, "%d.%c%c", biosSignature.dsVersion,
                    biosSignature.dsRevision, biosSignature.dsSubRevision);
        output.add_Item(version);
    }       
    else
    {
        output.add_Item("");
    }



    //
    // Get the SMOR version number from flash memory.  No exceptions so check the
    // first byte of the signature and make sure it isn't zero.
    //
    SmorFlashMemory smorFlash(*engine, hba_Tag);
    dpt_sig_S smorSignature = smorFlash.GetSignature();
    if ( smorSignature.dsSignature[0] != 0 )
    {
        dpt_sig_S &sig = smorSignature;
        char build[8];

        build[0] = '\0';

        // Look to see if we've got something like 
        // "xxxx xxxx xxx Vn.nn/bbb mm/dd/yyyy" and if so get the build string
        // out and chop off all but the textual part of description

        for (int i = 0; i < (int) strlen(sig.dsDescription) - 3; i++)
        {
            if (sig.dsDescription[i] == 'V' &&
                isdigit(sig.dsDescription[i+1]) &&
                sig.dsDescription[i+2] == '.')
            {
                int j;

                // Back up and terminated textual part of description
                for (j = i - 1; j > 0; j--)
                {
                    if (sig.dsDescription[j] != ' ')
                    {
                        sig.dsDescription[j + 1] = '\0';

                        break;
                    }
                }

                // See if we can find the build number (after a '/' and
                // before a space)
                for (j = i; sig.dsDescription[j] != '\0'; j++)
                {
                    if (sig.dsDescription[j] == ' ')
                        break;

                    if (sig.dsDescription[j] == '/')
                    {
                        int k;
                        for (k = 0;
                             k < sizeof(build) &&
                             sig.dsDescription[j + k] != ' ' &&
                                        sig.dsDescription[j + k] != '\0';
                             k++)
                        {
                            build[k] = sig.dsDescription[j + k];
                        }

                        build[k] = '\0';
                        break;
                    }
                }
                break;
            }
        }

        char version[10];
        sprintf(version, "%d.%c%c%s", sig.dsVersion, sig.dsRevision,
                sig.dsSubRevision, build);

        output.add_Item(version);
    }
    else
    {
        output.add_Item("");
    }



    // Restore the information we saved
    if (saveData != 0)
        memcpy(engine->fromEng_P, saveData, saveSize);

    // Deallocate Memory
    delete [] saveData;

} //List_Device::GetSmorBiosSignatures


Command	&List_Device::Clone() const
	{
	ENTER( "Command	&List_Device::Clone() const" );
	EXIT();
	return( *new List_Device( *this ) );
	}

void List_Device::Show_Physical(String_List &output, 
                  char * obj_Type_Str, int indent, bool show_Capacity, 
                  long capacity_in_Mb, DPT_TAG_T this_Objs_Tag, get_Devs_Type devs_Type)
{
	dptCaddr_S *addr_P = &engine->devInfo_P->addr;
	int i = 0;
	char temp_Buf[BUF_LEN];
	char temp2[21];
	uCHAR caps = engine->devInfo_P->inqFlag2;
	Dpt_Error err;

	DPTControllerMap map;
	char * String = map.getTargetString(
		  addr_P->hba, addr_P->chan, addr_P->id, addr_P->lun);
	if (add_plus)
   		sprintf(temp_Buf, "%*s+%s", indent, "", String);
	else
   		sprintf(temp_Buf, "%*s%s", indent, "", String);
	delete [] String;
	output.add_Item(temp_Buf);
	switch (list_What)
	{
		 case LIST_INQUIRY_VENDOR:
		 case LIST_INQUIRY:
		 case LIST_CACHE:
         break;
      default:
       	output.add_Item(obj_Type_Str);
         break;
   }
   if (list_What == LIST_TAGS)
      return;
	output.add_Item(Strip_Trailing_Whitespace(engine->devInfo_P->vendorID));
	if (strcmp(obj_Type_Str, "Redirect"))
		output.add_Item(Strip_Trailing_Whitespace(engine->devInfo_P->productID));
	else
		output.add_Item("      ");
//	output.add_Item(vendor_Product_Info_Buf);
   switch (list_What)
   {     
      case LIST_CACHE:
         Show_Cache_Mode(this_Objs_Tag, devs_Type, output);
         break;
      case LIST_INQUIRY_VENDOR:
      case LIST_INQUIRY:
         {

         uCHAR ScsiCmd[12];

         // print FW version
		 // Added by Qi Zhu: 8 bytes for Maxtor
		 if (showvendor==true) {
			char maxtor_buf[9];
			memset(maxtor_buf,0,sizeof(maxtor_buf));
			memcpy(maxtor_buf, engine->devInfo_P->vendorExtra+8, sizeof(maxtor_buf));
			output.add_Item(maxtor_buf);
		 }else {
			output.add_Item(engine->devInfo_P->revision);
		 }
         // print serial number
         engine->Reset();
         engine->Insert((uSHORT)CP_DATA_IN);
         engine->Insert((uLONG)255); // inquiry length
         ScsiCmd[0] = SC_INQUIRY;
         ScsiCmd[1] = FLG_SCSI_EVPD;
         ScsiCmd[2] = 0x80;
         ScsiCmd[3] = 0;
         ScsiCmd[4] = 255; // inquiry length
         ScsiCmd[5] = 0;
         engine->Insert(ScsiCmd, 12);
	     err = engine->Send (MSG_SCSI_CMD, this_Objs_Tag);
         if (!err.Is_Error())
         {
            uCHAR result[255];
	   		engine->Extract (result, 255);
            // length of result message is in result[3], regardless,
            // stop at 20 b/c that is all the room allocated in output
            for (i = 0; i < result[3] && (i < 20); i++)
            {
               if (isprint(result[i + 4]))
                  temp2[i] = result[i + 4];
               else
                  break;
            }
         }
         else
         {
            strcpy(temp2, "-------");
            i = 7;
         }
         temp2[i] = '\0';
       	 output.add_Item(temp2);
         // end, reset to continue
         engine->Reset();
	     engine->Send (MSG_GET_INFO, this_Objs_Tag);
         // Mark X or - or O  for Capabilities
         // Order of these capabilities (in "caps"):
         // soft reset, command queueing, --, linked commands,
         // synch, Wide 16, wide 32, Relative Address
         int check;
         for (check = 1, i = 0; i < 8; i++, check = 1 << i)
         {
            if (check == 4) continue;
            if (caps & check)
               output.add_Item("X");
            else
               output.add_Item("-");
         }
         // SCSI II
         if (engine->devInfo_P->version == 2)
            output.add_Item("X");
         else
            output.add_Item("-");
         // SMART
         if (engine->devInfo_P->flags2 & FLG_DEV_SMART_ACTIVE)
            output.add_Item("X");
         else
            output.add_Item("-");
         // SCAM
         // if adapter doesn't support, put O
         if (!(engine->hbaInfo_P->flags2 & FLG_HBA_SCAM))
            output.add_Item("O");
         // else get the capability
         else
         {
            dptChanInfo_S *ChanInfo_P;
            // this is for future 4th channel
            if (engine->devInfo_P->addr.chan < 3)
               ChanInfo_P = &engine->hbaInfo_P->chanInfo[engine->devInfo_P->addr.chan];
            else
               ChanInfo_P = &engine->hbaInfo_P->chan4;
            if (ChanInfo_P->scamIdMap & (1 << engine->devInfo_P->addr.id))
               output.add_Item("X");
            else
               output.add_Item("-");
         }
         // SCSI-3
         if (engine->devInfo_P->version == 3)
            output.add_Item("X");
         else
            output.add_Item("-");
         // SAF-TE
         if (engine->devInfo_P->flags2 & FLG_DEV_SAFTE)
            output.add_Item("X");
         else
            output.add_Item("-");
         }
         break;
      default:
      	if(show_Capacity)
	      {
			// show NO capacity if reserve conflict flag is set (usually during cluster environment on secondary node)
			if (capacity_in_Mb == 0 && (engine->devInfo_P->flags3 & FLG_DEV_RES_CONFLICT)){
				output.add_Item("      ");
			}
			else{
				sprintf( temp_Buf, "%ld%s", capacity_in_Mb, EventStrings[STR_MB]);
   				output.add_Item(temp_Buf);
			}
   	   }
      	else
	      {
   		   output.add_Item("-----");
   	   }
	{
		// show a Reserve State if reserve conflict flag is set (usually during cluster environment on secondary node)
		if (engine->devInfo_P->flags3 & FLG_DEV_RES_CONFLICT)
			output.add_Item("Reserved");
		else{
			Dpt_Status status(this_Objs_Tag);
   			output.add_Item((char *)status);
		}
	}
         break;
   }
}


void List_Device::Show_Cache_Mode(DPT_TAG_T obj_Tag, get_Devs_Type devs_Type, String_List &output)
{
   char Buffer[256];
   for (int i = 0; i < CACHE_MODE_LEVELS; i++)
   {
      // SetCache the drive
      engine->Reset();
      engine->Insert((uCHAR) 0x08);
      if (i == 0)
         // get info from hba (logical)
         engine->Insert((uCHAR) 0x60);
      else
         // get info from device (physical)
         engine->Insert((uCHAR) 0x40);

      // get the page
      Dpt_Error err = engine->Send(MSG_GET_MODE_PAGE, obj_Tag);
      if (err.Is_Error())
      {
	      output.add_Item(EventStrings[STR_ER]);
      }
      else
      {
         // i=0 is from the logical, i=1 is from the physical
         if (((devs_Type == GET_ARRAY) && (i > 0) && (engine->devInfo_P->raidParent == -1)) ||
             ((devs_Type == GET_ARRAY) && (i == 0) && (engine->devInfo_P->raidParent != -1)) ||
             ((devs_Type != GET_ARRAY) && (i > 0)))
             continue;
         engine->Extract(Buffer, sizeof(Buffer));
         output.add_Item((Buffer[4] & 4) ? EventStrings[STR_WRITE_BACK] : EventStrings[STR_WRITE_THRU]);
         // If it is on, then check if the battery is affecting this
         if (Buffer[4] & 4)
         {
   			// Only display a message if we have a forced write
	   		// through bit set.
		   	// Find the parent adapter, and ask it if it's battery
   			// is OK.
	   		dptBatteryInfo_S batteryInfo;

			   DPT_TAG_T hbaTag;
   			if ((hbaTag = engine->devInfo_P->hbaTag) == 0) 
	   		{
		   		hbaTag = engine->devInfo_P->attachedTo;
			   }
   			err = engine->Send( MSG_GET_BATTERY_INFO, hbaTag );
            // don't save in totalErr - if board doesn't have battery, will return err
            //totalErr |= err;
	
			   if (err.Is_Error()
			   || (engine->Extract(&batteryInfo, sizeof(batteryInfo)) == 0))
   			{
	   		   memset (&batteryInfo, 0, sizeof(batteryInfo));
		   	}

   			if(!err.Is_Error() && (batteryInfo.flags & BATTERY_FLAG_FORCED_WRITE_THROUGH))
	   		{
		   		// Based on whether or not we are in one of the
			   	// following states display a message.
				   switch (batteryInfo.status)
   				{
	   				case BATTERY_STAT_MAINTENANCE_DISCHARGE:
		   			case BATTERY_STAT_MAINTENANCE_CHARGE:
			   		case BATTERY_STAT_INIT_CHARGE:
				   	case BATTERY_STAT_INIT_DISCHARGE:
					   case BATTERY_STAT_INIT_RECHARGE:
   						// Let the user know that the controller's
	   					// cache is in write through mode because we
		   				// are in a maint. or initial calibration
			   			// operation.
				   		output.add_Item (EventStrings[STR_OFF_DUE2_MAINT_OPER]);
					   	break;
   					default:
	   					// Let the user know that their battery is
		   				// crapping out on them, because it has dropped
			   			// below the Auto write through threshold and
				   		// we are not in any type of calibration
					   	// operation.
						   output.add_Item (EventStrings[STR_OFF_DUE2_BATTERY_COND]);
   				}
	   		}
		   }
      }
// remove this line because going from 2 columns to 1 column
// the column removed was the one that had "--"
//      if (i == 0) output.add_Item (" / ");
   }
}

Command::Dpt_Error	List_Device::List_Tags(
							String_List		&output)
{
	ENTER( "Command::Dpt_Error	List_Device::List_Tags(" );
	int dev_Index;
	bool more_Devs_Left = true;
	Dpt_Error err;

	get_Devs_Type devs_Type;
   SCSI_Address addr;
   addr.hba = -1;
   addr.bus = -1;
   addr.id = -1;
   addr.lun = -1;

	output.add_Item(EventStrings[STR_LIST_TAGS_HEADER]);
	output.add_Item( STR_DEV_DIVIDING_LINE );

	for (dev_Index = 0; more_Devs_Left && !err.Is_Error(); dev_Index++)
	{

      DPT_TAG_T this_Objs_Tag = Get_Address_by_Index (addr,
			dev_Index, &more_Devs_Left, &devs_Type );

		if (more_Devs_Left)
		{
      	char tempBuf[5];
		   sprintf (tempBuf, "%d", this_Objs_Tag);
   		output.add_Item (tempBuf);

  			err |= List_This_Device (output, this_Objs_Tag, devs_Type);
         output.add_Item ("\n");
		}
	}

	EXIT();
	return( err );
}

/*** END OF FILE ***/
