// msDevTest.C

// $Source: /projects/HMS/nsr/udfAncil/devsTest/msACobj.cpp,v $
// $Revision: 1.8 $     $Author: hmgr $
// $State: Exp $        $Locker:  $
// $Date: 96/02/05 09:13:53 $

//      Unpublished Confidential Information of Hewlett-Packard
//      Company. Do not disclose.  Copyright (c) Hewlett-Packard
//      Company 1995. All rights reserved
// 
// The contents of this software are proprietary and confidential to
// the Hewlett- Packard Company, and are limited in distribution to
// those with a direct need to know.  Individuals having access to
// this software are responsible for maintaining the confidentiality
// of the content and for keeping the software secure when not in use.
// Transfer to any party is strictly forbidden other than as expressly
// permitted in writing by Hewlett-Packard Company.  Unauthorized
// transfer to or possession by any unauthorized party may be a
// criminal offense.
//
//                  RESTRICTED RIGHTS LEGEND
//
//      Use,  duplication,  or disclosure by the U. S. Government is
//      subject to restrictions as set forth in subparagraph (c) (1)
//      (ii) in the Rights in Technical Data and Computer Software
//      clause in DFARS 252.227-7013 or any other successor clause.
//
//                      HEWLETT-PACKARD COMPANY
//                      3000 Hanover St.
//                      Palo Alto, CA  94304 U.S.A.
//
//      Rights for non-DOD U.S. Government Departments and Agencies
//      are as set forth in FAR 52.227-19 (c) (1,2) or any other
//      successor clause.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <assert.h>
#include <unistd.h>

#include <nsrLib.h>
#include <nsrRMSdv.h>
#include <nsrRSSdv.h>
#include "dlist.h"
#include "msACobj.h"

//-----------------------------------------------------------------------------
// Function prototypes
//-----------------------------------------------------------------------------

int     surface_cmp(VOID_PTR d1,VOID_PTR d2);           // For dlist() functions
int     surface_free(VOID_PTR d1);                      // For dlist() functions
int     surface_print(VOID_PTR d1);             // For dlist() functions
int     drive_cmp(VOID_PTR d1,VOID_PTR d2);             // For dlist() functions
int     drive_free(VOID_PTR d1);                        // For dlist() functions
int     drive_print(VOID_PTR d1);                       // For dlist() functions
void    parse_error_msg(CHAR_PTR str);


//-----------------------------------------------------------------------------
// surface_cmp():  Comparison function.                         --JKH
//-----------------------------------------------------------------------------

int
surface_cmp(VOID_PTR d1,VOID_PTR d2)
{
    surface *s1 = (surface *) d1;
    surface *s2 = (surface *) d2;


    if((s1 == NULL) || (s2 == NULL)) {
        return 0;
    }
    return (s1->sid - s2->sid);
}

//-----------------------------------------------------------------------------
// surface_free():  Frees memory in a dlist node.               --JKH
//-----------------------------------------------------------------------------

int
surface_free(VOID_PTR d1)
{
    surface *s1 = (surface *) d1;
    if(s1 == NULL) {
        return -1;
    }
    delete s1;
    return 0;
}

//-----------------------------------------------------------------------------
// surface_print():  Prints a surface struct.                   --JKH
//-----------------------------------------------------------------------------

int
surface_print(VOID_PTR d1)
{
    surface *s1 = (surface *) d1;
    if(s1 == NULL) {
        return -1;
    }
    fprintf(stdout,"%d\t  %d%s\n",s1->sid,s1->slot,s1->side ? "B" : "A");
    return 0;
}

//-----------------------------------------------------------------------------
// drive_cmp():  Comparison function.                           --JKH
//-----------------------------------------------------------------------------

int
drive_cmp(VOID_PTR d1,VOID_PTR d2)
{
    drive *s1,*s2;

    s1 = (drive *) d1;
    s2 = (drive *) d2;

    return (s1->tgt - s2->tgt);
}

//-----------------------------------------------------------------------------
// drive_free():  Frees memory in a dlist node.                 --JKH
//-----------------------------------------------------------------------------

int
drive_free(VOID_PTR d1)
{
    drive *s1 = (drive *) d1;
    if(s1 == NULL) {
        return -1;
    }
    delete s1;
    return 0;
}

//-----------------------------------------------------------------------------
// drive_print():  Prints a drive struct.                       --JKH
//-----------------------------------------------------------------------------

int
drive_print(VOID_PTR d1)
{
    drive *s1 = (drive *) d1;
    if(s1 == NULL) {
        return -1;
    }
    fprintf(stdout,"%d\t%d\t%s\n",s1->tgt,s1->lun,(CONST_CHAR_PTR) *(s1->devfile));
    return 0;
}

//-----------------------------------------------------------------------------
// METHOD:  AC::AC                                              --JKH
//
// Constructor for the AC object.
//-----------------------------------------------------------------------------

AC::AC(FILE *outstream) : state(ac_uninitialized), 
                          acdev(NULL),
                          smap(NULL),
                          dmap(NULL),
                          lastsid(SID_T_INVALID),
                          lastoff(0),
                          lastnbytes(0)
{
    INT32       i;
    BYTE        byte;

    if(outstream == NULL) {
        stream = stdout;
    } 
    else {
        stream = outstream;
    }

    // Allocate memory for and fill in the data buffers

    for(i = 0; i < NUM_DATA_BUFFERS; i++) {
        buf[i] = new BYTE[DATA_BUFFER_SIZE];
        if(buf[i] == NULL) {
            state = ac_no_memory;
            return;
        }
        INT32 j;

        switch(i) {
        case 0:
            byte = 'a';
            for(j = 0; j < DATA_BUFFER_SIZE; j++) {
                buf[i][j] = byte;
                if(++byte > 'z') {
                    byte = 'a';
                }
            }
            break;
        case 1:
            byte = 0;
            for(j = 0; j < DATA_BUFFER_SIZE; j++) {
                buf[i][j] = j % 256;
            }
            break;
        case 2:
            for(j = 0; j < DATA_BUFFER_SIZE; j++) {
                buf[i][j] = (255 - (j % 256));
            }
            break;
        default:
            assert(1 == 0);             // Shouldn't happen 
            break;
        }

    }

    // Allocate the dlists for the surface and drive maps

    smap = dlistCreate(surface_cmp);
    if(smap == NULL) {
        state = ac_no_memory;
        for(i = 0; i < NUM_DATA_BUFFERS; i++) {
            delete buf[i];
        }
        return;
    }
    dmap = dlistCreate(drive_cmp);
    if(smap == NULL) {
        state = ac_no_memory;
        dlistDestroy(smap);
        for(i = 0; i < NUM_DATA_BUFFERS; i++) {
            delete buf[i];
        }
        return;
    }

    // Allocate memory for the read buffer

    readbuf = new BYTE[MAX_READ_SIZE];
    if(readbuf == NULL) {
        state = ac_no_memory;
        dlistDestroy(smap);
        dlistDestroy(dmap);
        for(i = 0; i < NUM_DATA_BUFFERS; i++) {
            delete buf[i];
        }
        return;
    }

    // Allocate memory for the write buffer

    writebuf = new BYTE[MAX_WRITE_SIZE];
    if(writebuf == NULL) {
        state = ac_no_memory;
        dlistDestroy(smap);
        dlistDestroy(dmap);
        for(i = 0; i < NUM_DATA_BUFFERS; i++) {
            delete buf[i];
        }
        delete [] readbuf;
        return;
    }
}

//-----------------------------------------------------------------------------
// METHOD:  AC::~AC                                             --JKH
//
// Destructor for the AC object.
//-----------------------------------------------------------------------------

AC::~AC()
{
    INT32       i;

    if(acdev != NULL) {
        delete acdev;
    }

    for(i = 0; i < NUM_DATA_BUFFERS; i++) {
        if(buf[i] != NULL) {
            delete [] buf[i];
        }
    }

    if(readbuf != NULL) {
        delete [] readbuf;
    }
    if(writebuf != NULL) {
        delete [] writebuf;
    }

    if(smap != NULL) {
        dlistApply(smap,surface_free,0);
        dlistDestroy(smap);
    }

    if(dmap != NULL) {
        dlistApply(dmap,drive_free,0);
        dlistDestroy(dmap);
    }

    if((stream != stdout) && (stream != stderr)) {
        fclose(stream);
    }
}

//-----------------------------------------------------------------------------
// METHOD:  AC::registerAC                                      --JKH
//
// Registers a device file for an autochanger.  This call creates an hpACDev;
// if the AC object already had one, the old one is deleted.
//-----------------------------------------------------------------------------

void
AC::registerAC( CONST_CHAR_PTR devfile )
{
    hpACDev     *newac;

    if(devfile == NULL) {
        fprintf(stream,"NULL device file passed to registerAC!\n");
        return;
    }
    newac = new hpACDev(devfile);
    if(newac == NULL) {
        state = ac_no_memory;
        return;
    }
    if(newac->error() != NSR::error_None) {
        fprintf(stream,"Registration failed:  invalid device file %s or AC not \
responding\n",devfile);
        fprintf(stream,"Error is %s\n",(newac->error()).message());
        return;
    }
    if(acdev != NULL) {
        delete acdev;
    }
    acdev = newac;
    state = ac_ready;
#ifdef DEBUG
    acdev->print(0,"");
#endif
    fprintf(stream,"AC:  Autochanger registered with device file %s\n",devfile);
}

//-----------------------------------------------------------------------------
// METHOD:  AC::map                                             --JKH
//
// Maps an (slot,side) pair to an sid.
//-----------------------------------------------------------------------------

void
AC::map( UINT32 slot, UINT32 side )
{
    sid_t       sid;
    surface *   sptr;
    dlist_node *dnode;
    surface     srfc;

    if(state != ac_ready) {
        fprintf(stream,"AC:  cannot execute command, please register an AC first\n");
        return;
    }

    sid = acdev->addSurface(slot,side);
    if(sid == SID_T_INVALID) {
        fprintf(stream,"AC:  addSurface() called failed with error:  %s\n",
               (acdev->error()).message());
        return;
    }

    // Make sure the surface wasn't already mapped.  The hpACDev handles it
    // right; we have to make sure we don't put duplicate entries in our list.

    srfc.sid = sid;
    if((dnode = dlistFind(smap,(VOID_PTR) &srfc)) != NULL) {
        fprintf(stream,"Surface (%d,%d) is already mapped to sid_t %d\n",slot,side,
               ((surface *) (dnode->data))->sid);
        return;
    }

    // Add the new mapping to our list.

    sptr = new surface;
    if(sptr == NULL) {
        state = ac_no_memory;
        return;
    }
    sptr->sid = sid;
    sptr->slot = slot;
    sptr->side = ((side == 0) ? 0 : 1);
    if(dlistInsert(smap,(VOID_PTR) sptr)) {
        assert(dlistError(smap) == DLERR_NOMEM);
        state = ac_no_memory;
        return;
    }
    fprintf(stream,"AC:  Surface (%d,%d) has been mapped to sid_t %d\n",slot,side,sid);
}

//-----------------------------------------------------------------------------
// METHOD:  AC::unmap                                           --JKH
//
// Removes a mapping of an (slot,side) pair to an sid.
//-----------------------------------------------------------------------------

void
AC::unmap( sid_t sid )
{
    surface     srfc;
    surface *   sptr;

    if(state != ac_ready) {
        fprintf(stream,"AC:  cannot execute command, please register an AC first\n");
        return;
    }

    // Remove the entry from the ac device.

    if(acdev->removeSurface(sid)) {
        fprintf(stream,"AC:  removeSurface() call failed with error:  %s\n",
               (acdev->error()).message());
        return;
    }

    // Remove the entry from our own list.

    srfc.sid = sid;
    sptr = (surface *) dlistDatadelete(smap,(VOID_PTR) &srfc);
    if(sptr != NULL) {
        delete sptr;
    }

    fprintf(stream,"AC:  Removed map for sid_t %d\n",sid);
}

//-----------------------------------------------------------------------------
// METHOD:  AC::drivemap                                        --JKH
//
// Associates a device file with a drive in the autochanger.
//-----------------------------------------------------------------------------

void
AC::drivemap( UINT32 tgt,UINT32 lun,CONST_CHAR_PTR devfile )
{
    drive *     dptr;
    dlist_node *dnode;
    drive       drv;

    if(state != ac_ready) {
        fprintf(stream,"AC:  cannot execute command, please register an AC first\n");
        return;
    }

    // Add the mapping to the autochanger.

    if(acdev->addDriveMap(tgt,lun,devfile)) {
        fprintf(stream,"AC:  addDriveMap() call failed with error:  %s\n",
               (acdev->error()).message());
        return;
    }

    // Now replace a mapping, if necessary, and add a new one to our own list.

    drv.tgt = tgt;
    drv.lun = lun;
    dnode = dlistFind(dmap,(VOID_PTR) &drv);
    if(dnode != NULL) {
        dptr = (drive *) dlistDatadelete(dmap,(VOID_PTR) &drv);
        if(dptr != NULL) {
            delete dptr;
        }
    }
    dptr = new drive;
    if(dptr == NULL) {
        state = ac_no_memory;
        return;
    }

    // Add a new one to our list

    dptr->tgt = tgt;
    dptr->lun = lun;
    dptr->devfile = new String(devfile);
    if(dptr->devfile == NULL) {
        delete dptr;
        state = ac_no_memory;
        return;
    }

    if(dlistInsert(dmap,(VOID_PTR) dptr)) {
        assert(dlistError(dmap) == DLERR_NOMEM);
        state = ac_no_memory;
        return;
    }

    fprintf(stream,"AC:  Drive (%d,%d) has been mapped to devfile %s\n",tgt,lun,
           devfile);
}

//-----------------------------------------------------------------------------
// METHOD:  AC::show                                            --JKH
//
// Displays to screen the current drive and surface maps.
//-----------------------------------------------------------------------------

void
AC::show( void )
{
    if(state != ac_ready) {
        fprintf(stream,"AC:  cannot execute command, please register an AC first\n");
        return;
    }
    
    fprintf(stream,"***** Surface Mappings *****\n\n");
    fprintf(stream,"sid_t\tsurface\n");
    fprintf(stream,"-----\t-------\n");
    dlistApply(smap,surface_print,0);
    fprintf(stream,"\n***** Drive Mappings *****\n\n");
    fprintf(stream,"tgt\tlun\tdevice file\n");
    fprintf(stream,"---\t---\t-----------\n");
    dlistApply(dmap,drive_print,0);
#ifdef DEBUG
    acdev->print(0,"Current state of autochanger:\n");
#endif
}

//-----------------------------------------------------------------------------
// METHOD::  AC::sectors                                        --JKH
//
// Displays to screen the number of sectors for the given sid.
//-----------------------------------------------------------------------------

void
AC::sectors( sid_t sid )
{
    UINT32      nsect;

    if(state != ac_ready) {
        fprintf(stream,"AC:  cannot execute command, please register an AC first\n");
        return;
    }
    
    nsect = acdev->numSectors(sid);
    if(nsect == 0) {
        fprintf(stream,"AC:  call to numSectors() failed with error:  %s\n",
               (acdev->error()).message());
        return;
    }
    fprintf(stream,"AC:  surface (sid_t = %d) has %d sectors.\n",sid,nsect);
}

//-----------------------------------------------------------------------------
// METHOD::  AC::getsectors                                     --JKH
//
// Returns the number of sectors for the given sid.
//-----------------------------------------------------------------------------

int
AC::getsectors( sid_t sid )
{
    UINT32      nsect;

    if(state != ac_ready) {
        fprintf(stream,"AC:  cannot execute command, please register an AC first\n");
        return 0;
    }
    
    nsect = acdev->numSectors(sid);
    if(nsect == 0) {
        fprintf(stream,"AC:  call to numSectors() failed with error:  %s\n",
               (acdev->error()).message());
        return 0;
    }
    return nsect;
}

//-----------------------------------------------------------------------------
// METHOD::  AC::size                                           --JKH
//
// Displays to screen the sector size of the given sid.
//-----------------------------------------------------------------------------

void
AC::size( sid_t sid )
{
    UINT32      ssize;

    if(state != ac_ready) {
        fprintf(stream,"AC:  cannot execute command, please register an AC first\n");
        return;
    }
    
    ssize = acdev->sectorSize(sid);
    if(ssize == 0) {
        fprintf(stream,"AC:  call to sectorSize() failed with error:  %s\n",
               (acdev->error()).message());
        return;
    }
    fprintf(stream,"AC:  surface (sid_t = %d) has sector size of %d bytes.\n",
            sid,ssize);
}

//-----------------------------------------------------------------------------
// METHOD::  AC::getsize                                        --JKH
//
// Displays to screen the sector size of the given sid.
//-----------------------------------------------------------------------------

int
AC::getsize( sid_t sid )
{
    UINT32      ssize;

    if(state != ac_ready) {
        fprintf(stream,"AC:  cannot execute command, please register an AC first\n");
        return 0;
    }
    
    ssize = acdev->sectorSize(sid);
    if(ssize == 0) {
        fprintf(stream,"AC:  call to sectorSize() failed with error:  %s\n",
               (acdev->error()).message());
        return 0;
    }
    return ssize;
}

//-----------------------------------------------------------------------------
// METHOD:  AC::getValidSids                                    --JKH
//
// Fills an array with the sid's currently mapped.
//-----------------------------------------------------------------------------

int
AC::getValidSids( sid_t *arr,int max )
{
    int         i;
    dlist_node  *nptr;
    surface     *sptr;

    if(dlistCount(smap) == 0) {
        return 0;
    }

    nptr = dlistHead(smap);
    for(i = 0; (i < dlistCount(smap)) && (i < max); i++) {
        sptr = (surface *) nptr->data;
        arr[i] = sptr->sid;
        nptr = nptr->next;
    }

    return ((max < dlistCount(smap)) ? max : dlistCount(smap));
}

//-----------------------------------------------------------------------------
// METHOD:  AC::read                                            --JKH
//
// Reads data from the specified surface.
//-----------------------------------------------------------------------------

void
AC::read( sid_t sid,UINT32 start,UINT32 nbytes )
{
    INT32       rval;

    if(state != ac_ready) {
        fprintf(stream,"AC:  cannot execute command, please register an AC first\n");
        return;
    }
    if(nbytes > MAX_READ_SIZE) {
        fprintf(stream,"AC:  Read size must be <= %d\n",MAX_READ_SIZE);
        return;
    }

    rval = acdev->read(sid,start,nbytes,readbuf);
    if(rval < (INT32) nbytes) {
        fprintf(stream,"AC:  Read returned short count %d, error is %s\n",rval,
               (acdev->error()).message());
        return;
    }
    fprintf(stream,"AC:  Read successful.\n");
    lastsid = sid;
    lastoff = start;
    lastnbytes = nbytes;
}

//-----------------------------------------------------------------------------
// METHOD:  AC::write                                           --JKH
//
// Writes data to the specified surface.
//-----------------------------------------------------------------------------

void
AC::write( sid_t sid,UINT32 start,UINT32 nbytes,UINT32 which )
{
    INT32       i;
    INT32       rval;

    if(state != ac_ready) {
        fprintf(stream,"AC:  cannot execute command, please register an AC first\n");
        return;
    }
    if(nbytes > MAX_WRITE_SIZE) {
        fprintf(stream,"AC:  Write size must be <= %d\n",MAX_WRITE_SIZE);
        return;
    }
    if((which < 0) || (which >= NUM_DATA_BUFFERS)) {
        fprintf(stream,"AC:  Data buffer must be a value from 0 to %d\n",
               NUM_DATA_BUFFERS - 1);
        return;
    }

    // Set up the write buffer.

    for(i = 0; i < ((nbytes + DATA_BUFFER_SIZE - 1) / DATA_BUFFER_SIZE); i++) {
        memcpy(writebuf + (i*DATA_BUFFER_SIZE),buf[which],DATA_BUFFER_SIZE);
    }

    // Do the write!

    rval = acdev->write(sid,start,nbytes,writebuf);
    if(rval < (INT32) nbytes) {
        fprintf(stream,"AC:  Write returned short count %d, error is %s\n",rval,
               (acdev->error()).message());
        return;
    }
    fprintf(stream,"AC:  Write successful.\n");
}

//-----------------------------------------------------------------------------
// METHOD:  AC::write                                           --JKH
//
// Writes data to the specified surface.
//-----------------------------------------------------------------------------

void
AC::write( sid_t sid,UINT32 start,UINT32 nbytes )
{
    INT32       i;
    INT32       rval;

    if(state != ac_ready) {
        fprintf(stream,"AC:  cannot execute command, please register an AC first\n");
        return;
    }
    if(nbytes > MAX_WRITE_SIZE) {
        fprintf(stream,"AC:  Write size must be <= %d\n",MAX_WRITE_SIZE);
        return;
    }

    // Do the write!

    rval = acdev->write(sid,start,nbytes,writebuf);
    if(rval < (INT32) nbytes) {
        fprintf(stream,"AC:  Write returned short count %d, error is %s\n",rval,
               (acdev->error()).message());
        return;
    }
    fprintf(stream,"AC:  Write successful.\n");
}

//-----------------------------------------------------------------------------
// METHOD:  AC::verify                                          --JKH
//
// Sets the verify mode of the specified surface.
//-----------------------------------------------------------------------------

void
AC::verify( sid_t sid,INT32 mode )
{
    
    if(state != ac_ready) {
        fprintf(stream,"AC:  cannot execute command, please register an AC first\n");
        return;
    }

    if(acdev->verifyMode(sid,mode) == -1) {
        fprintf(stream,"AC:  verifyMode() call failed, error is %s\n",
                (acdev->error()).message());
        return;
    }
    fprintf(stream,"AC:  Verify mode for sid %d set to %s\n",sid,
            (mode == 0) ? "OFF" : "ON");
}

//-----------------------------------------------------------------------------
// METHOD:  AC::writeWOE                                        --JKH
//
// Writes data in write-without-erase mode to the specified surface.
//-----------------------------------------------------------------------------

void
AC::writeWOE( sid_t sid,UINT32 start,UINT32 nbytes,UINT32 which )
{
    INT32       i;
    INT32       rval;

    if(state != ac_ready) {
        fprintf(stream,"AC:  cannot execute command, please register an AC first\n");
        return;
    }
    if(nbytes > MAX_WRITE_SIZE) {
        fprintf(stream,"AC:  Write size must be <= %d\n",MAX_WRITE_SIZE);
        return;
    }
    if((which < 0) || (which >= NUM_DATA_BUFFERS)) {
        fprintf(stream,"AC:  Data buffer must be a value from 0 to %d\n",
               NUM_DATA_BUFFERS - 1);
        return;
    }

    if((which < 0) || (which >= NUM_DATA_BUFFERS)) {
        fprintf(stream,"AC:  Data buffer must be a value from 0 to %d\n",
               NUM_DATA_BUFFERS - 1);
        return;
    }

    // Set up the write buffer.

    for(i = 0; i < ((nbytes + DATA_BUFFER_SIZE - 1) / DATA_BUFFER_SIZE); i++) {
        memcpy(writebuf + (i*DATA_BUFFER_SIZE),buf[which],DATA_BUFFER_SIZE);
    }

    // Do the write!

    rval = acdev->writeWOE(sid,start,nbytes,writebuf);
    if(rval < (INT32) nbytes) {
        fprintf(stream,"AC:  WriteWOE returned short count %d, error is %s\n",rval,
               (acdev->error()).message());
        return;
    }
    fprintf(stream,"AC:  WriteWOE successful.\n");
}

//-----------------------------------------------------------------------------
// METHOD:  AC::writeWOE                                        --JKH
//
// Writes data in write-without-erase mode to the specified surface.
//-----------------------------------------------------------------------------

void
AC::writeWOE( sid_t sid,UINT32 start,UINT32 nbytes )
{
    INT32       i;
    INT32       rval;

    if(state != ac_ready) {
        fprintf(stream,"AC:  cannot execute command, please register an AC first\n");
        return;
    }
    if(nbytes > MAX_WRITE_SIZE) {
        fprintf(stream,"AC:  Write size must be <= %d\n",MAX_WRITE_SIZE);
        return;
    }

    // Do the write!

    rval = acdev->writeWOE(sid,start,nbytes,writebuf);
    if(rval < (INT32) nbytes) {
        fprintf(stream,"AC:  WriteWOE returned short count %d, error is %s\n",rval,
               (acdev->error()).message());
        return;
    }
    fprintf(stream,"AC:  WriteWOE successful.\n");
}

//-----------------------------------------------------------------------------
// METHOD:  AC::erase                                           --JKH
//
// Erases sectors of the specified surface.
//-----------------------------------------------------------------------------

void
AC::erase( sid_t sid,UINT32 start,UINT32 nsectors )
{
    if(state != ac_ready) {
        fprintf(stream,"AC:  cannot execute command, please register an AC first\n");
        return;
    }

    if(acdev->erase(sid,start,nsectors)) {
        fprintf(stream,"AC:  erase failed, error is %s\n",
                (acdev->error()).message());
        return;
    }
    fprintf(stream,"AC:  Erase successful.\n");

}

//-----------------------------------------------------------------------------
// METHOD:  AC::status
//
// Displays element status information about storage slots and drives.
//-----------------------------------------------------------------------------

void
AC::status( void )
{
    INT32                       i;
    struct element_addresses    eltaddr;
    struct element_status       eltstat;

    if(state != ac_ready) {
        fprintf(stream,"AC:  cannot execute command, please register an AC first\n");
        return;
    }

    if(acdev->elementAddresses(eltaddr)) {
        fprintf(stream,"AC:  Element Addresses failed, error is %s\n",
                (acdev->error()).message());
        return;
    }

    fprintf(stream,"***** Autochanger Element Status Information *****\n\n");

    // Get element status information, one element at a time.  We only
    // display if the slot is full.

    for(i = 0; i < eltaddr.num_storages; i++) {
        if(acdev->elementStatus(eltaddr.first_storage + i,eltstat)) {
            fprintf(stream,"AC:  element status failed, error is %s\n",
                    (acdev->error()).message());
            return;
        }
        if(eltstat.full) {
            fprintf(stream,"Slot %d is full\n",i + 1);
        }
    }

    for(i = 0; i < eltaddr.num_data_transfers; i++) {
        if(acdev->elementStatus(eltaddr.first_data_transfer + i,eltstat)) {
            fprintf(stream,"AC:  element status failed, error is %s\n",
                    (acdev->error()).message());
            return;
        }
        fprintf(stderr,"Drive %d at target %d lun %d is ",i+1,
                eltstat.bus_address,eltstat.lun);
        if(eltstat.full) {
            if(eltstat.source_valid) {
                fprintf(stream,"full, currently accessing surface %d%c\n",
                        eltstat.source_element - eltaddr.first_storage + 1,
                        (eltstat.invert) ? 'B' : 'A');
            }
            else {
                fprintf(stream,"ERROR:  Drive %d is occupied but has no valid \
source!\n",i + 1);
            }
        }
        else {
            fprintf(stream,"empty.\n");
        }
    }
}
            
//-----------------------------------------------------------------------------
// METHOD:  AC::eltaddr                                         --JKH
//
// Gets element addresses information.
//-----------------------------------------------------------------------------

int
AC::eltaddr( struct element_addresses& eltaddr)
{
    if(state != ac_ready) {
        fprintf(stream,"AC:  cannot execute command, please register an AC first\n");
        return -1;
    }

    if(acdev->elementAddresses(eltaddr)) {
        fprintf(stream,"AC:  Element Addresses failed, error is %s\n",
                (acdev->error()).message());
        return -1;
    }
}
    
//-----------------------------------------------------------------------------
// METHOD:  AC::eltstat                                         --JKH
//
// Gets element status information.
//-----------------------------------------------------------------------------

int
AC::eltstat( int element,struct element_status& eltstat)
{
    if(state != ac_ready) {
        fprintf(stream,"AC:  cannot execute command, please register an AC first\n");
        return -1;
    }

    if(acdev->elementStatus(element,eltstat)) {
        fprintf(stream,"AC:  Element Status failed, error is %s\n",
                (acdev->error()).message());
        return -1;
    }
}

//-----------------------------------------------------------------------------
// METHOD:  AC::crc                                             --JKH
//
// Calculates a crc of the specified data.  Stolen from distress.c again.
//-----------------------------------------------------------------------------

int
AC::crc(BYTE *data,int len)
{
    int i;
    int csum = 0;

    for (i = 0; i < len; i++) {
        csum = ((unsigned)csum<<1 | (unsigned)csum>>31) ^ (unsigned)data[i];
    }
    return csum;
}

//-----------------------------------------------------------------------------
// METHOD:  AC::display                                         --JKH
//
// Displays the specified 256-byte page of read data.
//-----------------------------------------------------------------------------

void
AC::display( UINT32 which )
{
    INT32       i,j;
    INT32       npages;
    INT32       offset;

    npages = (MAX_READ_SIZE + PAGESIZE - 1) / PAGESIZE;
    if(which >= npages) {
        fprintf(stream,"AC:  display page must be in the range 0 to %d\n",npages);
        return;
    }
    offset = which*PAGESIZE;

    fprintf(stream,"Last Read Information:  sid = %d, offset = %u, nbytes = %u\n",
           lastsid,lastoff,lastnbytes);

    for(i = 0; i < (PAGESIZE / LINESIZE); i++) {
        fprintf(stream,"0x%06x  ",i*LINESIZE + which*PAGESIZE);
        for(j = 0; j < LINESIZE; j++) {
            fprintf(stream,"%02x ",(BYTE) readbuf[i*LINESIZE + j + 
                                                   which*PAGESIZE]);
        }
        fprintf(stream,"| ");
        for(j = 0; j < LINESIZE; j++) {
            if(isprint(readbuf[i*LINESIZE + j + which*PAGESIZE])) {
                fprintf(stream,"%c",readbuf[i*LINESIZE + j + which*PAGESIZE]);
            }
            else {
                fprintf(stream,".");
            }
        }
        fprintf(stream,"\n");
    }
}

