/*============================================================================*
 *============================================================================*
 * Component:           plextor-tool
 * Filename:            common-functions.c
 *                                                                             
 * Authors:             Georg Huettenegger
 *                                                                             
 * Date of Creation:    Thu Aug 12 16:57:39 1999
 *                                                                             
 * Last Modification:   Thu Aug 12 16:57:39 1999
 *                                                                             
 * Copyright:           Georg Huettenegger                            
 *                                                                             
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *                                                                             
 *============================================================================*
 *============================================================================*
 */


/*============================================================================*
 *                                                                             
 * DESCRIPTION:                                                                
 * ------------                                                                
 * This file has the actual definition of the global variables.
 *                                                                             
 *----------------------------------------------------------------------------*
 */
#ifdef __linux__

int fd = -1;
int new_sg_interface = 0;

#endif

#if (defined WIN32) || (defined _WIN32)

int force = 0;

#endif

int drive_type;
int mmc_drive;

char *prog_name;
int verbose = 0;

#include "plextor-tool.h"

#ifdef __linux__

/*============================================================================*
 *                                                                             
 * DESCRIPTION:                                                                
 * ------------                                                                
 * The devices that are searched under Linux.
 *                                                                             
 *----------------------------------------------------------------------------*
 */

char *default_number_dev_list[] =
{
  "/dev/sg0", "/dev/sg1", "/dev/sg2", "/dev/sg3",
  "/dev/sg4", "/dev/sg5", "/dev/sg6", "/dev/sg7",
  "/dev/sg8", "/dev/sg9", "/dev/sg10", "/dev/sg11",
  "/dev/sg12", "/dev/sg13", "/dev/sg14", "/dev/sg15",
  "/dev/sg16", "/dev/sg17", "/dev/sg18", "/dev/sg19",
  "/dev/sg20", "/dev/sg21", "/dev/sg22", "/dev/sg23",
  "/dev/sg24", "/dev/sg25", "/dev/sg26",
  0
};

char *default_letter_dev_list[] =
{
  "/dev/sga", "/dev/sgb", "/dev/sgc", "/dev/sgd",
  "/dev/sge", "/dev/sgf", "/dev/sgg", "/dev/sgh",
  "/dev/sgi", "/dev/sgj", "/dev/sgk", "/dev/sgl",
  "/dev/sgm", "/dev/sgn", "/dev/sgo", "/dev/sgp",
  "/dev/sgq", "/dev/sgr", "/dev/sgs", "/dev/sgt",
  "/dev/sgu", "/dev/sgv", "/dev/sgw", "/dev/sgx",
  "/dev/sgy", "/dev/sgz",
  0
};

#endif /* __linux__ */

/*============================================================================*
 *                                                                             
 * DESCRIPTION:                                                                
 * ------------                                                                
 * The available inactivity timer values.
 *                                                                             
 *----------------------------------------------------------------------------*
 */
static const char spindwninfinite[] = "infinite";
static const char spindwninf_alt[]  = "0";
static const char spindwn125ms[]    = "125ms";
static const char spindwn250ms[]    = "250ms";
static const char spindwn500ms[]    = "500ms";
static const char spindwn1sec[]     = "1sec";
static const char spindwn2sec[]     = "2sec";
static const char spindwn4sec[]     = "4sec";
static const char spindwn8sec[]     = "8sec";
static const char spindwn16sec[]    = "16sec";
static const char spindwn32sec[]    = "32sec";
static const char spindwn1min[]     = "1min";
static const char spindwn2min[]     = "2min";
static const char spindwn4min[]     = "4min";
static const char spindwn8min[]     = "8min";
static const char spindwn16min[]    = "16min";
static const char spindwn32min[]    = "32min";

int ReturnPlextorDriveType (const unsigned char * product_string)
{
  switch (*product_string)
  {
    case '4':
      if (*(product_string+1) == '0')
        return PLEX_40;
      else
        if (*(product_string+3) == 'E')
          return PLEX_4_5;
        else
          return PLEX_4;
    case '6':
      return PLEX_6;
    case '8':
      return PLEX_8;
    case '1':
      return PLEX_12;
    case '2':
      return PLEX_20;
    case '3':
      return PLEX_32;
    default:
    {
      printf ("UNKNOWN MODEL! EXITING!\n");
      exit (1);
    }
  }
}

int FieldValueForInactivityString (const char * spindown_string)
{
  if (!strcmp (spindown_string, spindwninfinite))
    return 0;
  if (!strcmp (spindown_string, spindwninf_alt))
    return 0;
  if (!strcmp (spindown_string, spindwn125ms))
    return 1;
  if (!strcmp (spindown_string, spindwn250ms))
    return 2;
  if (!strcmp (spindown_string, spindwn500ms))
    return 3;
  if (!strcmp (spindown_string, spindwn1sec))
    return 4;
  if (!strcmp (spindown_string, spindwn2sec))
    return 5;
  if (!strcmp (spindown_string, spindwn4sec))
    return 6;
  if (!strcmp (spindown_string, spindwn8sec))
    return 7;
  if (!strcmp (spindown_string, spindwn16sec))
    return 8;
  if (!strcmp (spindown_string, spindwn32sec))
    return 9;
  if (!strcmp (spindown_string, spindwn1min))
    return 0xA;
  if (!strcmp (spindown_string, spindwn2min))
    return 0xB;
  if (!strcmp (spindown_string, spindwn4min))
    return 0xC;
  if (!strcmp (spindown_string, spindwn8min))
    return 0xD;
  if (!strcmp (spindown_string, spindwn16min))
    return 0xE;
  if (!strcmp (spindown_string, spindwn32min))
    return 0xF;
#ifdef VERBOSE_OUTPUT
  if (verbose)
    printf ("No known spin-down setting!\n");
#endif
  return -1;
}


const char * PrintInactivityString (int spindown)
{
  switch (spindown)
  {
    case 0:
      return spindwninfinite;
    case 1:
      return spindwn125ms;
    case 2:
      return spindwn250ms;
    case 3:
      return spindwn500ms;
    case 4:
      return spindwn1sec;
    case 5:
      return spindwn2sec;
    case 6:
      return spindwn4sec;
    case 7:
      return spindwn8sec;
    case 8:
      return spindwn16sec;
    case 9:
      return spindwn32sec;
    case 0xA:
      return spindwn1min;
    case 0xB:
      return spindwn2min;
    case 0xC:
      return spindwn4min;
    case 0xD:
      return spindwn8min;
    case 0xE:
      return spindwn16min;
    case 0xF:
      return spindwn32min;
    default:
      return spindwninfinite;
  };
}

int ReturnMMCDriveType (int speed)
{
  switch (speed)
  {
    case 1:
      return MMC_1;
    case 2:
      return MMC_2;
    case 4:
      return MMC_4;
    case 6:
      return MMC_6;
    case 8:
      return MMC_8;
    case 12:
      return MMC_16;
    case 20:
      return MMC_20;
    case 24:
      return MMC_24;
    case 32:
      return MMC_32;
    case 0:
    default:
    {
      return MMC_0;
    }
  }
}

#ifdef __linux__

/*============================================================================*
 *                                                                             
 * DESCRIPTION:                                                                
 * ------------                                                                
 * Two functions for error printing. Mostly taken from sg_err.c
 *                                                                             
 *----------------------------------------------------------------------------*
 */
static const char unknown[] = "UNKNOWN";

static const char * statuses[] = {
/* 0-4 */ "Good", "Check Condition", "Condition Met", unknown, "Busy",
/* 5-9 */ unknown, unknown, unknown, "Intermediate", unknown,
/* a-c */ "Intermediate-Condition Met", unknown, "Reservation Conflict",
/* d-10 */ unknown, unknown, unknown, unknown,
/* 11-14 */ "Command Terminated", unknown, unknown, "Queue Full",
/* 15-1a */ unknown, unknown, unknown,  unknown, unknown, unknown,
/* 1b-1f */ unknown, unknown, unknown,  unknown, unknown,
};

void print_scsi_status (int masked_status) {
    printf("status: %s\n",statuses[masked_status]);
}

#define D 0x001  /* DIRECT ACCESS DEVICE (disk) */
#define T 0x002  /* SEQUENTIAL ACCESS DEVICE (tape) */
#define L 0x004  /* PRINTER DEVICE */
#define P 0x008  /* PROCESSOR DEVICE */
#define W 0x010  /* WRITE ONCE READ MULTIPLE DEVICE */
#define R 0x020  /* READ ONLY (CD-ROM) DEVICE */
#define S 0x040  /* SCANNER DEVICE */
#define O 0x080  /* OPTICAL MEMORY DEVICE */
#define M 0x100  /* MEDIA CHANGER DEVICE */
#define C 0x200  /* COMMUNICATION DEVICE */

struct error_info{
    unsigned char code1, code2;
    unsigned short int devices;
    const char * text;
};

struct error_info2{
    unsigned char code1, code2_min, code2_max;
    unsigned short int devices;
    const char * text;
};

static struct error_info2 additional2[] =
{
  {0x40,0x00,0x7f,D,"Ram failure (%x)"},
  {0x40,0x80,0xff,D|T|L|P|W|R|S|O|M|C,"Diagnostic failure on component (%x)"},
  {0x41,0x00,0xff,D,"Data path failure (%x)"},
  {0x42,0x00,0xff,D,"Power-on or self-test failure (%x)"},
  {0, 0, 0, 0, NULL}
};

static struct error_info additional[] =
{
  {0x00,0x01,T,"Filemark detected"},
  {0x00,0x02,T|S,"End-of-partition/medium detected"},
  {0x00,0x03,T,"Setmark detected"},
  {0x00,0x04,T|S,"Beginning-of-partition/medium detected"},
  {0x00,0x05,T|S,"End-of-data detected"},
  {0x00,0x06,D|T|L|P|W|R|S|O|M|C,"I/O process terminated"},
  {0x00,0x11,R,"Audio play operation in progress"},
  {0x00,0x12,R,"Audio play operation paused"},
  {0x00,0x13,R,"Audio play operation successfully completed"},
  {0x00,0x14,R,"Audio play operation stopped due to error"},
  {0x00,0x15,R,"No current audio status to return"},
  {0x01,0x00,D|W|O,"No index/sector signal"},
  {0x02,0x00,D|W|R|O|M,"No seek complete"},
  {0x03,0x00,D|T|L|W|S|O,"Peripheral device write fault"},
  {0x03,0x01,T,"No write current"},
  {0x03,0x02,T,"Excessive write errors"},
  {0x04,0x00,D|T|L|P|W|R|S|O|M|C,
     "Logical unit not ready, cause not reportable"},
  {0x04,0x01,D|T|L|P|W|R|S|O|M|C,
     "Logical unit is in process of becoming ready"},
  {0x04,0x02,D|T|L|P|W|R|S|O|M|C,
     "Logical unit not ready, initializing command required"},
  {0x04,0x03,D|T|L|P|W|R|S|O|M|C,
     "Logical unit not ready, manual intervention required"},
  {0x04,0x04,D|T|L|O,"Logical unit not ready, format in progress"},
  {0x05,0x00,D|T|L|W|R|S|O|M|C,"Logical unit does not respond to selection"},
  {0x06,0x00,D|W|R|O|M,"No reference position found"},
  {0x07,0x00,D|T|L|W|R|S|O|M,"Multiple peripheral devices selected"},
  {0x08,0x00,D|T|L|W|R|S|O|M|C,"Logical unit communication failure"},
  {0x08,0x01,D|T|L|W|R|S|O|M|C,"Logical unit communication time-out"},
  {0x08,0x02,D|T|L|W|R|S|O|M|C,"Logical unit communication parity error"},
  {0x09,0x00,D|T|W|R|O,"Track following error"},
  {0x09,0x01,W|R|O,"Tracking servo failure"},
  {0x09,0x02,W|R|O,"Focus servo failure"},
  {0x09,0x03,W|R|O,"Spindle servo failure"},
  {0x0A,0x00,D|T|L|P|W|R|S|O|M|C,"Error log overflow"},
  {0x0C,0x00,T|S,"Write error"},
  {0x0C,0x01,D|W|O,"Write error recovered with auto reallocation"},
  {0x0C,0x02,D|W|O,"Write error - auto reallocation failed"},
  {0x10,0x00,D|W|O,"Id crc or ecc error"},
  {0x11,0x00,D|T|W|R|S|O,"Unrecovered read error"},
  {0x11,0x01,D|T|W|S|O,"Read retries exhausted"},
  {0x11,0x02,D|T|W|S|O,"Error too long to correct"},
  {0x11,0x03,D|T|W|S|O,"Multiple read errors"},
  {0x11,0x04,D|W|O,"Unrecovered read error - auto reallocate failed"},
  {0x11,0x05,W|R|O,"L-ec uncorrectable error"},
  {0x11,0x06,W|R|O,"Circ unrecovered error"},
  {0x11,0x07,W|O,"Data resynchronization error"},
  {0x11,0x08,T,"Incomplete block read"},
  {0x11,0x09,T,"No gap found"},
  {0x11,0x0A,D|T|O,"Miscorrected error"},
  {0x11,0x0B,D|W|O,"Unrecovered read error - recommend reassignment"},
  {0x11,0x0C,D|W|O,"Unrecovered read error - recommend rewrite the data"},
  {0x12,0x00,D|W|O,"Address mark not found for id field"},
  {0x13,0x00,D|W|O,"Address mark not found for data field"},
  {0x14,0x00,D|T|L|W|R|S|O,"Recorded entity not found"},
  {0x14,0x01,D|T|W|R|O,"Record not found"},
  {0x14,0x02,T,"Filemark or setmark not found"},
  {0x14,0x03,T,"End-of-data not found"},
  {0x14,0x04,T,"Block sequence error"},
  {0x15,0x00,D|T|L|W|R|S|O|M,"Random positioning error"},
  {0x15,0x01,D|T|L|W|R|S|O|M,"Mechanical positioning error"},
  {0x15,0x02,D|T|W|R|O,"Positioning error detected by read of medium"},
  {0x16,0x00,D|W|O,"Data synchronization mark error"},
  {0x17,0x00,D|T|W|R|S|O,"Recovered data with no error correction applied"},
  {0x17,0x01,D|T|W|R|S|O,"Recovered data with retries"},
  {0x17,0x02,D|T|W|R|O,"Recovered data with positive head offset"},
  {0x17,0x03,D|T|W|R|O,"Recovered data with negative head offset"},
  {0x17,0x04,W|R|O,"Recovered data with retries and/or circ applied"},
  {0x17,0x05,D|W|R|O,"Recovered data using previous sector id"},
  {0x17,0x06,D|W|O,"Recovered data without ecc - data auto-reallocated"},
  {0x17,0x07,D|W|O,"Recovered data without ecc - recommend reassignment"},
  {0x18,0x00,D|T|W|R|O,"Recovered data with error correction applied"},
  {0x18,0x01,D|W|R|O,"Recovered data with error correction and retries applied"},
  {0x18,0x02,D|W|R|O,"Recovered data - data auto-reallocated"},
  {0x18,0x03,R,"Recovered data with circ"},
  {0x18,0x04,R,"Recovered data with lec"},
  {0x18,0x05,D|W|R|O,"Recovered data - recommend reassignment"},
  {0x19,0x00,D|O,"Defect list error"},
  {0x19,0x01,D|O,"Defect list not available"},
  {0x19,0x02,D|O,"Defect list error in primary list"},
  {0x19,0x03,D|O,"Defect list error in grown list"},
  {0x1A,0x00,D|T|L|P|W|R|S|O|M|C,"Parameter list length error"},
  {0x1B,0x00,D|T|L|P|W|R|S|O|M|C,"Synchronous data transfer error"},
  {0x1C,0x00,D|O,"Defect list not found"},
  {0x1C,0x01,D|O,"Primary defect list not found"},
  {0x1C,0x02,D|O,"Grown defect list not found"},
  {0x1D,0x00,D|W|O,"Miscompare during verify operation"},
  {0x1E,0x00,D|W|O,"Recovered id with ecc correction"},
  {0x20,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid command operation code"},
  {0x21,0x00,D|T|W|R|O|M,"Logical block address out of range"},
  {0x21,0x01,M,"Invalid element address"},
  {0x22,0x00,D,"Illegal function (should use 20 00, 24 00, or 26 00)"},
  {0x24,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in cdb"},
  {0x25,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit not supported"},
  {0x26,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in parameter list"},
  {0x26,0x01,D|T|L|P|W|R|S|O|M|C,"Parameter not supported"},
  {0x26,0x02,D|T|L|P|W|R|S|O|M|C,"Parameter value invalid"},
  {0x26,0x03,D|T|L|P|W|R|S|O|M|C,"Threshold parameters not supported"},
  {0x27,0x00,D|T|W|O,"Write protected"},
  {0x28,0x00,D|T|L|P|W|R|S|O|M|C,"Not ready to ready transition (medium may have changed)"},
  {0x28,0x01,M,"Import or export element accessed"},
  {0x29,0x00,D|T|L|P|W|R|S|O|M|C,"Power on, reset, or bus device reset occurred"},
  {0x2A,0x00,D|T|L|W|R|S|O|M|C,"Parameters changed"},
  {0x2A,0x01,D|T|L|W|R|S|O|M|C,"Mode parameters changed"},
  {0x2A,0x02,D|T|L|W|R|S|O|M|C,"Log parameters changed"},
  {0x2B,0x00,D|T|L|P|W|R|S|O|C,"Copy cannot execute since host cannot disconnect"},
  {0x2C,0x00,D|T|L|P|W|R|S|O|M|C,"Command sequence error"},
  {0x2C,0x01,S,"Too many windows specified"},
  {0x2C,0x02,S,"Invalid combination of windows specified"},
  {0x2D,0x00,T,"Overwrite error on update in place"},
  {0x2F,0x00,D|T|L|P|W|R|S|O|M|C,"Commands cleared by another initiator"},
  {0x30,0x00,D|T|W|R|O|M,"Incompatible medium installed"},
  {0x30,0x01,D|T|W|R|O,"Cannot read medium - unknown format"},
  {0x30,0x02,D|T|W|R|O,"Cannot read medium - incompatible format"},
  {0x30,0x03,D|T,"Cleaning cartridge installed"},
  {0x31,0x00,D|T|W|O,"Medium format corrupted"},
  {0x31,0x01,D|L|O,"Format command failed"},
  {0x32,0x00,D|W|O,"No defect spare location available"},
  {0x32,0x01,D|W|O,"Defect list update failure"},
  {0x33,0x00,T,"Tape length error"},
  {0x36,0x00,L,"Ribbon, ink, or toner failure"},
  {0x37,0x00,D|T|L|W|R|S|O|M|C,"Rounded parameter"},
  {0x39,0x00,D|T|L|W|R|S|O|M|C,"Saving parameters not supported"},
  {0x3A,0x00,D|T|L|W|R|S|O|M,"Medium not present"},
  {0x3B,0x00,T|L,"Sequential positioning error"},
  {0x3B,0x01,T,"Tape position error at beginning-of-medium"},
  {0x3B,0x02,T,"Tape position error at end-of-medium"},
  {0x3B,0x03,L,"Tape or electronic vertical forms unit not ready"},
  {0x3B,0x04,L,"Slew failure"},
  {0x3B,0x05,L,"Paper jam"},
  {0x3B,0x06,L,"Failed to sense top-of-form"},
  {0x3B,0x07,L,"Failed to sense bottom-of-form"},
  {0x3B,0x08,T,"Reposition error"},
  {0x3B,0x09,S,"Read past end of medium"},
  {0x3B,0x0A,S,"Read past beginning of medium"},
  {0x3B,0x0B,S,"Position past end of medium"},
  {0x3B,0x0C,S,"Position past beginning of medium"},
  {0x3B,0x0D,M,"Medium destination element full"},
  {0x3B,0x0E,M,"Medium source element empty"},
  {0x3D,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid bits in identify message"},
  {0x3E,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit has not self-configured yet"},
  {0x3F,0x00,D|T|L|P|W|R|S|O|M|C,"Target operating conditions have changed"},
  {0x3F,0x01,D|T|L|P|W|R|S|O|M|C,"Microcode has been changed"},
  {0x3F,0x02,D|T|L|P|W|R|S|O|M|C,"Changed operating definition"},
  {0x3F,0x03,D|T|L|P|W|R|S|O|M|C,"Inquiry data has changed"},
  {0x43,0x00,D|T|L|P|W|R|S|O|M|C,"Message error"},
  {0x44,0x00,D|T|L|P|W|R|S|O|M|C,"Internal target failure"},
  {0x45,0x00,D|T|L|P|W|R|S|O|M|C,"Select or reselect failure"},
  {0x46,0x00,D|T|L|P|W|R|S|O|M|C,"Unsuccessful soft reset"},
  {0x47,0x00,D|T|L|P|W|R|S|O|M|C,"Scsi parity error"},
  {0x48,0x00,D|T|L|P|W|R|S|O|M|C,"Initiator detected error message received"},
  {0x49,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid message error"},
  {0x4A,0x00,D|T|L|P|W|R|S|O|M|C,"Command phase error"},
  {0x4B,0x00,D|T|L|P|W|R|S|O|M|C,"Data phase error"},
  {0x4C,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit failed self-configuration"},
  {0x4E,0x00,D|T|L|P|W|R|S|O|M|C,"Overlapped commands attempted"},
  {0x50,0x00,T,"Write append error"},
  {0x50,0x01,T,"Write append position error"},
  {0x50,0x02,T,"Position error related to timing"},
  {0x51,0x00,T|O,"Erase failure"},
  {0x52,0x00,T,"Cartridge fault"},
  {0x53,0x00,D|T|L|W|R|S|O|M,"Media load or eject failed"},
  {0x53,0x01,T,"Unload tape failure"},
  {0x53,0x02,D|T|W|R|O|M,"Medium removal prevented"},
  {0x54,0x00,P,"Scsi to host system interface failure"},
  {0x55,0x00,P,"System resource failure"},
  {0x57,0x00,R,"Unable to recover table-of-contents"},
  {0x58,0x00,O,"Generation does not exist"},
  {0x59,0x00,O,"Updated block read"},
  {0x5A,0x00,D|T|L|P|W|R|S|O|M,"Operator request or state change input (unspecified)"},
  {0x5A,0x01,D|T|W|R|O|M,"Operator medium removal request"},
  {0x5A,0x02,D|T|W|O,"Operator selected write protect"},
  {0x5A,0x03,D|T|W|O,"Operator selected write permit"},
  {0x5B,0x00,D|T|L|P|W|R|S|O|M,"Log exception"},
  {0x5B,0x01,D|T|L|P|W|R|S|O|M,"Threshold condition met"},
  {0x5B,0x02,D|T|L|P|W|R|S|O|M,"Log counter at maximum"},
  {0x5B,0x03,D|T|L|P|W|R|S|O|M,"Log list codes exhausted"},
  {0x5C,0x00,D|O,"Rpl status change"},
  {0x5C,0x01,D|O,"Spindles synchronized"},
  {0x5C,0x02,D|O,"Spindles not synchronized"},
  {0x60,0x00,S,"Lamp failure"},
  {0x61,0x00,S,"Video acquisition error"},
  {0x61,0x01,S,"Unable to acquire video"},
  {0x61,0x02,S,"Out of focus"},
  {0x62,0x00,S,"Scan head positioning error"},
  {0x63,0x00,R,"End of user area encountered on this track"},
  {0x64,0x00,R,"Illegal mode for this track"},
  {0, 0, 0, NULL}
};

static const char *snstext[] = {
    "None",                     /* There is no sense information */
    "Recovered Error",          /* The last command completed successfully
                                   but used error correction */
    "Not Ready",                /* The addressed target is not ready */
    "Medium Error",             /* Data error detected on the medium */
    "Hardware Error",           /* Controller or device failure */
    "Illegal Request",
    "Unit Attention",           /* Removable medium was changed, or
                                   the target has been reset */
    "Data Protect",             /* Access to the data is blocked */
    "Blank Check",              /* Reached unexpected written or unwritten
                                   region of the medium */
    "Key=9",                    /* Vendor specific */
    "Copy Aborted",             /* COPY or COMPARE was aborted */
    "Aborted Command",          /* The target aborted the command */
    "Equal",                    /* A SEARCH DATA command found data equal */
    "Volume Overflow",          /* Medium full with still data to be written */
    "Miscompare",               /* Source data and data on the medium
                                   do not agree */
    "Key=15"                    /* Reserved */
};

/* Print sense information */
void sg_print_sense(const char * leadin, const unsigned char * sense_buffer,
                    int sb_len)
{
    int i, s;
    int sense_class, valid, code;
    const char * error = NULL;

    sense_class = (sense_buffer[0] >> 4) & 0x07;
    code = sense_buffer[0] & 0xf;
    valid = sense_buffer[0] & 0x80;

    if (sense_class == 7) {     /* extended sense data */
        s = sense_buffer[7] + 8;
        if(s > sb_len)
           s = sb_len;

        if (!valid)
            printf("[valid=0] ");
        printf("Info fld=0x%x, ", (int)((sense_buffer[3] << 24) |
               (sense_buffer[4] << 16) | (sense_buffer[5] << 8) |
               sense_buffer[6]));

        if (sense_buffer[2] & 0x80)
           printf( "FMK ");     /* current command has read a filemark */
        if (sense_buffer[2] & 0x40)
           printf( "EOM ");     /* end-of-medium condition exists */
        if (sense_buffer[2] & 0x20)
           printf( "ILI ");     /* incorrect block length requested */

        switch (code) {
        case 0x0:
            error = "Current";  /* error concerns current command */
            break;
        case 0x1:
            error = "Deferred"; /* error concerns some earlier command */
                /* e.g., an earlier write to disk cache succeeded, but
                   now the disk discovers that it cannot write the data */
            break;
        default:
            error = "Invalid";
        }

        printf("%s ", error);

        if (leadin)
            printf("%s: ", leadin);
        printf("sense key: %s\n", snstext[sense_buffer[2] & 0x0f]);

        /* Check to see if additional sense information is available */
        if(sense_buffer[7] + 7 < 13 ||
           (sense_buffer[12] == 0  && sense_buffer[13] ==  0)) goto done;

        for(i=0; additional[i].text; i++)
            if(additional[i].code1 == sense_buffer[12] &&
               additional[i].code2 == sense_buffer[13])
                printf("Additional sense indicates: %s\n",
                       additional[i].text);

        for(i=0; additional2[i].text; i++)
            if(additional2[i].code1 == sense_buffer[12] &&
               additional2[i].code2_min >= sense_buffer[13]  &&
               additional2[i].code2_max <= sense_buffer[13]) {
                printf("Additional sense indicates: ");
                printf(additional2[i].text, sense_buffer[13]);
                printf("\n");
            };
    } else {    /* non-extended sense data */

         /*
          * Standard says:
          *    sense_buffer[0] & 0200 : address valid
          *    sense_buffer[0] & 0177 : vendor-specific error code
          *    sense_buffer[1] & 0340 : vendor-specific
          *    sense_buffer[1..3] : 21-bit logical block address
          */

        if (leadin)
            printf("%s: ", leadin);
        if (sense_buffer[0] < 15)
            printf("old sense: key %s\n", snstext[sense_buffer[0] & 0x0f]);
        else
            printf("sns = %2x %2x\n", sense_buffer[0], sense_buffer[2]);

        printf("Non-extended sense class %d code 0x%0x ", sense_class, code);
        s = 4;
    }

 done:
    printf("Raw sense data (in hex):\n  ");
    for (i = 0; i < s; ++i) {
        if ((i > 0) && (0 == (i % 24)))
            printf("\n  ");
        printf("%02x ", sense_buffer[i]);
    }
    printf("\n");
    return;
}

#define SENSE_BUFF_LEN 32       /* Arbitrary, could be larger */
#define DEF_TIMEOUT 60000       /* 60,000 millisecs == 60 seconds */

static unsigned char cmd[SCSI_OFF + 18];      /* SCSI command buffer (old sg)*/

int perform_SCSI_cmd(unsigned cmd_len,         /* command length */
                     unsigned char *cmd_buff,  /* command buffer */
                     unsigned in_size,         /* input data size */
                     unsigned char *i_buff,    /* input buffer */
                     unsigned out_size,        /* output data size */
                     unsigned char *o_buff     /* output buffer */
                     )
{
  /* either construct command for old handle_SCSI_cmd or for new
     execute_new_SCSI_cmd */
#ifdef SG_IO
  if (new_sg_interface)
#else
  /* don't use new interface if SG_IO is not defined! */
  if (0)
#endif
  { /* new sg interface used */
    if (verbose)
      printf ("Executing command using new scsi generic interface.\n");
    return (execute_new_SCSI_cmd (cmd_len, cmd_buff, in_size, i_buff,
                                  out_size, o_buff));
  }
  else
  { /* old sg interface used */
    memcpy (cmd + SCSI_OFF, cmd_buff, cmd_len);
    memcpy (cmd + SCSI_OFF + cmd_len, i_buff, in_size);
  
  /*
   * +------------------+
   * | struct sg_header | <- cmd
   * +------------------+
   * | copy of cmdblk   | <- cmd + SCSI_OFF
   * +------------------+
   */
  
    return (handle_SCSI_cmd(cmd_len, in_size, cmd,
                            out_size, o_buff ));
  }
  /* never reached */
  return 0;
}

int execute_new_SCSI_cmd(unsigned cmd_len,          /* command length */
                         unsigned char * cmd_buff,  /* command buffer */
                         unsigned in_size,          /* input data size */
                         unsigned char * i_buff,    /* input buffer */
                         unsigned out_size,         /* output data size */
                         unsigned char * o_buff)    /* output buffer */
{
  /* we don't need this function if we don't have SG_IO defined */
#ifdef SG_IO
  unsigned char sense_b[SENSE_BUFF_LEN];
  sg_io_hdr_t io_hdr;
  
  memset(&io_hdr, 0, sizeof(sg_io_hdr_t));
  io_hdr.interface_id = 'S';
  io_hdr.cmd_len = cmd_len;
  io_hdr.mx_sb_len = SENSE_BUFF_LEN;
  if (in_size)
  {
    io_hdr.dxfer_direction = SG_DXFER_TO_DEV;
    io_hdr.dxfer_len = in_size;
    io_hdr.dxferp = i_buff;
  }
  else
    if (out_size)
    {
      io_hdr.dxfer_direction = SG_DXFER_FROM_DEV;
      io_hdr.dxfer_len = out_size;
      io_hdr.dxferp = o_buff;
    }
    else
    {
      io_hdr.dxfer_direction = SG_DXFER_NONE;
    }
  io_hdr.cmdp = cmd_buff;
  io_hdr.sbp = sense_b;
  io_hdr.timeout = DEF_TIMEOUT;

  if (ioctl(fd, SG_IO, &io_hdr) < 0) {
    perror("SG_IO (mode select) error");
    return -1;
  }
  if ( (io_hdr.masked_status == 0) &&
       (io_hdr.host_status == 0) &&
       (io_hdr.driver_status == 0) )
    return 0;
  else
  { /* we got some error (could also be a recovered error but we ignore
       this possibility) */
    if (verbose)
    {
      print_scsi_status (io_hdr.masked_status);
      if (io_hdr.sb_len_wr)
      { // we got sense information
        sg_print_sense ("mode select error", sense_b, io_hdr.sb_len_wr);
      }
      else
        printf ("No sense buffer information!\n");
    }
    return -1;
  }

#endif
  /* never reached */
  return 0;
}

/* process a complete SCSI cmd. Use the generic SCSI interface. */
int handle_SCSI_cmd(unsigned cmd_len,         /* command length */
                    unsigned in_size,         /* input data size */
                    unsigned char *i_buff,    /* input buffer */
                    unsigned out_size,        /* output data size */
                    unsigned char *o_buff     /* output buffer */
                    )
{
  int status = 0;
  struct sg_header *sg_hd;
  
  /* safety checks */
  if (!cmd_len) return -1;            /* need a cmd_len != 0 */
  if (!i_buff) return -1;             /* need an input buffer != NULL */
#ifdef SG_BIG_BUFF
  if (SCSI_OFF + cmd_len + in_size > SG_BIG_BUFF) return -1;
  if (SCSI_OFF + out_size > SG_BIG_BUFF) return -1;
#else
  if (SCSI_OFF + cmd_len + in_size > 4096) return -1;
  if (SCSI_OFF + out_size > 4096) return -1;
#endif
  
  if (!o_buff) out_size = 0;      /* no output buffer, no output size */

  /* generic SCSI device header construction */
  sg_hd = (struct sg_header *) i_buff;
  sg_hd->reply_len   = SCSI_OFF + out_size;
  sg_hd->twelve_byte = cmd_len == 12;
  sg_hd->result = 0;
#if     0
  sg_hd->pack_len    = SCSI_OFF + cmd_len + in_size; /* not necessary */
  sg_hd->pack_id;     /* not used */
  sg_hd->other_flags; /* not used */
#endif
  
  /* send command */
  status = write( fd, i_buff, SCSI_OFF + cmd_len + in_size );
  if ( status < 0 || status != SCSI_OFF + cmd_len + in_size ||
       sg_hd->result ) {
    /* some error happened */
    fprintf( stderr, "write(generic) result = 0x%x cmd = 0x%x\n",
             sg_hd->result, i_buff[SCSI_OFF] );
    perror("");
    return status;
  }
  
  if (!o_buff) o_buff = i_buff;       /* buffer pointer check */
  
  /* retrieve result */
  status = read( fd, o_buff, SCSI_OFF + out_size);
  if ( status < 0 || status != SCSI_OFF + out_size || sg_hd->result ) {
    /* some error happened */
    fprintf( stderr, "read(generic) status = 0x%x, result = 0x%x, "
             "cmd = 0x%x\n",
             status, sg_hd->result, o_buff[SCSI_OFF] );
    fprintf( stderr, "read(generic) sense "
             "%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n",
             sg_hd->sense_buffer[0],         sg_hd->sense_buffer[1],
             sg_hd->sense_buffer[2],         sg_hd->sense_buffer[3],
             sg_hd->sense_buffer[4],         sg_hd->sense_buffer[5],
             sg_hd->sense_buffer[6],         sg_hd->sense_buffer[7],
             sg_hd->sense_buffer[8],         sg_hd->sense_buffer[9],
             sg_hd->sense_buffer[10],        sg_hd->sense_buffer[11],
             sg_hd->sense_buffer[12],        sg_hd->sense_buffer[13],
             sg_hd->sense_buffer[14],        sg_hd->sense_buffer[15]);
    if (status < 0)
      perror("");
  }
  /* Look if we got what we expected to get */
  if (status == SCSI_OFF + out_size) status = 0; /* got them all */
  
  return status;  /* 0 means no error */
}


/* use mmc set speed command to set read speed
    */
int MMCSetReadSpeed ( int read_speed )
{
  static unsigned char Inqbuffer[ SCSI_OFF + SP_CMDLEN];
  unsigned char cmdblk [SP_CMDLEN] =
  { SET_SPEED_CMD,  /* command */
    0,  /* lun/reserved */
    (read_speed*176) >> 8,  /* read speed part 1 */
    (read_speed*176) % 255,  /* read speed part 2 */
    0xFF, /* write speed part1 => max. */
    0xFF, /* write speed part2 => max. */
    0,  /* 6 / reserved */
    0,
    0,
    0, /* 9 / reserved */
    0 };/* reserved/flag/link */
  
  memcpy( cmd + SCSI_OFF, cmdblk, sizeof(cmdblk) );
  
  /*
   * +------------------+
   * | struct sg_header | <- cmd
   * +------------------+
   * | copy of cmdblk   | <- cmd + SCSI_OFF
   * +------------------+
   */
  
  if (handle_SCSI_cmd(sizeof(cmdblk), 0, cmd,
                      sizeof(Inqbuffer) - SCSI_OFF, Inqbuffer )) {
    return -1;
  }
  return 0;
}

/* request vendor brand and
   model */
const unsigned char *Inquiry ( void )
{
  static unsigned char Inqbuffer[ SCSI_OFF + INQUIRY_REPLY_LEN ];
  unsigned char cmdblk [ INQUIRY_CMDLEN ] =
  { INQUIRY_CMD,  /* command */
    0,  /* lun/reserved */
    0,  /* page code */
    0,  /* reserved */
    INQUIRY_REPLY_LEN,  /* allocation length */
    0 };/* reserved/flag/link */
  
  memcpy( cmd + SCSI_OFF, cmdblk, sizeof(cmdblk) );
  
  /*
   * +------------------+
   * | struct sg_header | <- cmd
   * +------------------+
   * | copy of cmdblk   | <- cmd + SCSI_OFF
   * +------------------+
   */
  
  if (handle_SCSI_cmd(sizeof(cmdblk), 0, cmd,
                      sizeof(Inqbuffer) - SCSI_OFF, Inqbuffer )) {
    fprintf( stderr, "Inquiry failed\n" );
    exit(2);
  }
  return (Inqbuffer + SCSI_OFF);
}

#endif /* __linux__ */

#if (defined WIN32) || (defined _WIN32)

int windows_initialize_scsi ()
{
  OSVERSIONINFO   osvi;

  /*
  ** Make sure we're not running under Win32s.  ASPI for Win32 does not
  ** run under Win32s so we'll just jump ship if we detect it.  We'll also
  ** take advantage of our access to OSVERSIONINFO to note if we need CTL3D
  ** and whether this is NT or not.
  */

  osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  GetVersionEx( &osvi );
  if( osvi.dwPlatformId == VER_PLATFORM_WIN32s )
  {
    fprintf (stderr, "Wrong platform!\n");
    return FALSE;
  }
  if( !InitASPI() )
  {
    return FALSE;
  }
  pbt = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(BENCHTARGET) );
  if (!pbt)
    return FALSE;
  return TRUE;
}

/*****************************************************************************/
/*****************************************************************************/

BOOL InitASPI( VOID )
{
  UINT                fuPrevErrorMode;
  DWORD               dwSupportInfo;
  DWORD               dwLastError;
  SRB_GetSetTimeouts  srbTimeouts;

  /*
  ** Load ASPI for Win32.  If it won't load display a warning.
  */

  fuPrevErrorMode = SetErrorMode( SEM_NOOPENFILEERRORBOX );
  ghinstWNASPI32 = LoadLibrary( "WNASPI32" );
  dwLastError = GetLastError();
  SetErrorMode( fuPrevErrorMode );

  if( !ghinstWNASPI32 )
  {
    fprintf (stderr, "Could not load ASPI for Win32!\n");
    return FALSE;
  }

  /*
  ** Get the ASPI entry points.  Note that only two functions are mandatory:
  ** GetASPI32SupportInfo and SendASPI32Command.  The code will run if the
  ** others are not present.
  */

  (FARPROC)gpfnGetASPI32SupportInfo = GetProcAddress( ghinstWNASPI32, "GetASPI32SupportInfo" );
  (FARPROC)gpfnSendASPI32Command = GetProcAddress( ghinstWNASPI32, "SendASPI32Command" );
  (FARPROC)gpfnGetASPI32Buffer = GetProcAddress( ghinstWNASPI32, "GetASPI32Buffer" );
  (FARPROC)gpfnFreeASPI32Buffer = GetProcAddress( ghinstWNASPI32, "FreeASPI32Buffer" );
  (FARPROC)gpfnTranslateASPI32Address = GetProcAddress( ghinstWNASPI32, "TranslateASPI32Address" );

  if( !gpfnGetASPI32SupportInfo || !gpfnSendASPI32Command )
  {
    fprintf (stderr, "Could not load the ASPI32 DLL!\n");
    return FALSE;
  }

  /*
  ** Check if ASPI initialized ok.  Display an error box if it didn't.
  */

  dwSupportInfo = gpfnGetASPI32SupportInfo();
  if (force)
  {
    while (HIBYTE(LOWORD(dwSupportInfo)) == SS_NO_ASPI)
    {
      dwSupportInfo = gpfnGetASPI32SupportInfo();
    };
  }
  if( HIBYTE(LOWORD(dwSupportInfo)) != SS_COMP &&
      HIBYTE(LOWORD(dwSupportInfo)) != SS_NO_ADAPTERS )
  {
    fprintf (stderr, "ASPI did not initialize correctly!\n");
    return FALSE;
  }

  /*
  ** Set timeouts for ALL devices to 15 seconds.  Nothing we deal with should
  ** take that long to do ANYTHING.  We are just doing inquiries to most
  ** devices, and then simple reads to CDs, disks, etc. so 10 seconds (even
  ** if they have to spin up) should be plenty. 
  */

  memset( &srbTimeouts, 0, sizeof(SRB_GetSetTimeouts) );
  srbTimeouts.SRB_Cmd = SC_GETSET_TIMEOUTS;
  srbTimeouts.SRB_HaId = 0xFF;
  srbTimeouts.SRB_Flags = SRB_DIR_OUT;
  srbTimeouts.SRB_Target = 0xFF;
  srbTimeouts.SRB_Lun = 0xFF;
  srbTimeouts.SRB_Timeout = 15*2;
  gpfnSendASPI32Command( &srbTimeouts );

  return TRUE;
}

void windows_list_plextor_mmc_drives ()
{
  BYTE            byHaId;
  BYTE            byMaxHaId;
  BYTE            byTarget;
  BYTE            byMaxTarget;
  DWORD           dwASPIStatus;
  SRB_HAInquiry   srbHAInquiry;

  dwASPIStatus = gpfnGetASPI32SupportInfo();
  if( HIBYTE(LOWORD(dwASPIStatus)) == SS_COMP )
  {
    byMaxHaId = LOBYTE(LOWORD(dwASPIStatus));
    for( byHaId = 0; byHaId < byMaxHaId; byHaId++ )
    {
      /*
      ** Do a host adapter inquiry to get max target count.  If the 
      ** target count isn't 8 or 16 then go with a default of 8.
      */

      memset( &srbHAInquiry, 0, sizeof(SRB_HAInquiry) );
      srbHAInquiry.SRB_Cmd = SC_HA_INQUIRY;
      srbHAInquiry.SRB_HaId = byHaId;

      gpfnSendASPI32Command( (LPSRB)&srbHAInquiry );
      if( srbHAInquiry.SRB_Status != SS_COMP )
      {
        continue;
      }

      byMaxTarget = srbHAInquiry.HA_Unique[3];
      if( byMaxTarget != 8 && byMaxTarget != 16 )
      {
        byMaxTarget = 8;
      }

      /*
      ** Loop over all the targets on this host adapter.
      */

      for( byTarget = 0; byTarget < byMaxTarget; byTarget++ )
      {
        const unsigned char *result;
        char name_part1[3];
        char name_part2[3];

        sprintf (name_part1, "%d", byHaId);
        sprintf (name_part2, "%d", byTarget);
        if ((result = open_and_check_drive (name_part1, name_part2)))
        {
          printf ("Found: [%d:%d] %.8s %.15s\n",
                  (BYTE)byHaId, (BYTE)byTarget, result + 8,
                  result + 16);
        }
        else
          continue;
      }
    }

  }
}

/*****************************************************************************/
/*****************************************************************************/

VOID TermASPI( VOID )
{
  /*  
  ** Unload ASPI if it has been loaded.
  */

  if( ghinstWNASPI32 )
  {
    FreeLibrary( ghinstWNASPI32 );
  }
}

/*****************************************************************************/
/*****************************************************************************/

BOOL SyncExecWithRetry
  (
   PBENCHTARGET    pbt,
   BYTE            byFlags,
   DWORD           dwBufferBytes,
   PBYTE           pbyBuffer,
   BYTE            byCDBBytes,
   PBYTE           pbyCDB
   )
{
  BOOL            bRetry = TRUE;
  DWORD           dwASPIStatus;
  HANDLE          heventExec;
  SRB_ExecSCSICmd srbExec;

  /*
  ** Setup the SRB for this command.
  */

  memset( &srbExec, 0, sizeof(SRB_ExecSCSICmd) );
  memcpy( srbExec.CDBByte, pbyCDB, byCDBBytes );

  srbExec.SRB_Cmd = SC_EXEC_SCSI_CMD;
  srbExec.SRB_HaId = pbt->byHaId;
  srbExec.SRB_Flags = byFlags;
  srbExec.SRB_Target = pbt->byTarget;
  srbExec.SRB_BufLen = dwBufferBytes;
  srbExec.SRB_BufPointer = pbyBuffer;
  srbExec.SRB_SenseLen = SENSE_LEN;
  srbExec.SRB_CDBLen = byCDBBytes;

  /*
  ** Create an event (if possible) and issue the command.  After sending
  ** the command wait for completion.
  */

 RetryExec:
  {
    heventExec = CreateEvent( NULL, TRUE, FALSE, NULL );
    if( heventExec )
    {
      srbExec.SRB_Flags |= SRB_EVENT_NOTIFY;
      srbExec.SRB_PostProc = (LPVOID)heventExec;
        
      dwASPIStatus = gpfnSendASPI32Command( (LPSRB)&srbExec );
      if( dwASPIStatus == SS_PENDING )
      {
        WaitForSingleObject( heventExec, INFINITE );
      }
      CloseHandle( heventExec );
    }
    else
    {
      gpfnSendASPI32Command( (LPSRB)&srbExec );
      while( srbExec.SRB_Status == SS_PENDING );
    }
  }

  /*
  ** Check for errors.  We'll retry on the first unit attention.  Anything
  ** else will generate an error dialog.
  */

  if( srbExec.SRB_Status != SS_COMP )
  {
    if( bRetry && 
        (srbExec.SRB_TargStat != STATUS_CHKCOND ||
         (srbExec.SenseArea[2] & 0x0F) != KEY_UNITATT) )
    {
      bRetry = FALSE;
      goto RetryExec;
    }
    else
    {
      fprintf (stderr, "command failed, error: %x!\n", srbExec.SRB_Status);
      fprintf (stderr, "tarstat: %x, hastat: %x\n", srbExec.SRB_TargStat,
               srbExec.SRB_HaStat);
      fprintf( stderr, "sense codes: "
	       "%x %x %x %x %x %x %x %x %x %x %x %x %x %x %x %x\n",
	       srbExec.SenseArea[0],         srbExec.SenseArea[1],
	       srbExec.SenseArea[2],         srbExec.SenseArea[3],
	       srbExec.SenseArea[4],         srbExec.SenseArea[5],
	       srbExec.SenseArea[6],         srbExec.SenseArea[7],
	       srbExec.SenseArea[8],         srbExec.SenseArea[9],
	       srbExec.SenseArea[10],        srbExec.SenseArea[11],
	       srbExec.SenseArea[12],        srbExec.SenseArea[13],
	       srbExec.SenseArea[14],        srbExec.SenseArea[15]);
      return FALSE;
    }
  }

  return TRUE;    
}

#endif /* WIN32 or _WIN32 */

const unsigned char *ModeSenseAllPages ( void )
{
#ifdef __linux__
  static unsigned char MSbuffer[ SCSI_OFF + MODE_SENSE_REPLY_LEN ];
  unsigned char cmdblk [ MODE_SENSE_CMDLEN ] =
  { MODE_SENSE_CMD,  /* command */
    0,  /* lun/reserved */
    0x3F,  /* page code */
    0,  /* reserved */
    MODE_SENSE_REPLY_LEN,  /* allocation length */
    0 };/* reserved/flag/link */
  
  memcpy( cmd + SCSI_OFF, cmdblk, sizeof(cmdblk) );
  
  /*
   * +------------------+
   * | struct sg_header | <- cmd
   * +------------------+
   * | copy of cmdblk   | <- cmd + SCSI_OFF
   * +------------------+
   */
  
  if (handle_SCSI_cmd(sizeof(cmdblk), 0, cmd,
                      sizeof(MSbuffer) - SCSI_OFF, MSbuffer ))
  {
    fprintf( stderr, "Mode Sense failed!\n" );
    exit(2);
  }
  return (MSbuffer + SCSI_OFF);
#endif
#if (defined WIN32) || (defined _WIN32)
  BOOL            bExec;
  BYTE            bySenseCDB[MODE_SENSE_CMDLEN];
  static BYTE     bySenseBuf[MODE_SENSE_REPLY_LEN];
  /*
  ** Issue an SENSE, do it with retry even though unit
  ** attentions shouldn't bother an SENSE
  */
  memset (bySenseCDB, 0, MODE_SENSE_CMDLEN);
  bySenseCDB[0] = MODE_SENSE_CMD;
  bySenseCDB[2] = 0x3F; /* the page code */
  bySenseCDB[4] = MODE_SENSE_REPLY_LEN;
  
  bExec = SyncExecWithRetry (pbt, SRB_DIR_IN | SRB_EVENT_NOTIFY,
                             MODE_SENSE_REPLY_LEN, bySenseBuf,
			     MODE_SENSE_CMDLEN, bySenseCDB);
  
  /*
  ** Make sure the command worked.  If it failed
  ** error (guards against certain device drivers and against
  ** vendor unique devices).
  */
  
  if (!bExec)
  {
    fprintf (stderr, "Mode Sense failed!\n");
    exit (2);
  }
  return (unsigned char *) bySenseBuf;
#endif
}

/* question values of mode page 2Ah */
const unsigned char *ModeSenseMMCSpeeds ( void )
{
#ifdef __linux__
  static unsigned char MSbuffer[ SCSI_OFF + MODE_SENSE_REPLY_LEN ];
  unsigned char cmdblk [ MODE_SENSE_CMDLEN ] =
  { MODE_SENSE_CMD,  /* command */
    0,  /* lun/reserved */
    0x2A,  /* page code */
    0,  /* reserved */
    MODE_SENSE_REPLY_LEN,  /* allocation length */
    0 };/* reserved/flag/link */
  
  memcpy( cmd + SCSI_OFF, cmdblk, sizeof(cmdblk) );
  
  /*
   * +------------------+
   * | struct sg_header | <- cmd
   * +------------------+
   * | copy of cmdblk   | <- cmd + SCSI_OFF
   * +------------------+
   */
  
  if (handle_SCSI_cmd(sizeof(cmdblk), 0, cmd,
                      sizeof(MSbuffer) - SCSI_OFF, MSbuffer ))
  {
    fprintf( stderr, "Mode Sense failed!\n" );
    exit(2);
  }
  return (MSbuffer + SCSI_OFF);
#endif
#if (defined WIN32) || (defined _WIN32)
  BOOL            bExec;
  BYTE            bySenseCDB[MODE_SENSE_CMDLEN];
  static BYTE     bySenseBuf[MODE_SENSE_REPLY_LEN];
  /*
  ** Issue an SENSE, do it with retry even though unit
  ** attentions shouldn't bother an SENSE
  */
  memset (bySenseCDB, 0, MODE_SENSE_CMDLEN);
  bySenseCDB[0] = MODE_SENSE_CMD;
  bySenseCDB[2] = 0x2A; /* page code */
  bySenseCDB[4] = MODE_SENSE_REPLY_LEN;
  
  bExec = SyncExecWithRetry (pbt, SRB_DIR_IN | SRB_EVENT_NOTIFY,
                             MODE_SENSE_REPLY_LEN, bySenseBuf,
			     MODE_SENSE_CMDLEN, bySenseCDB);
  
  /*
  ** Make sure the command worked.  If it failed
  ** error (guards against certain device drivers and against
  ** vendor unique devices).
  */
  
  if (!bExec)
  {
    fprintf (stderr, "Mode Sense failed!\n");
    exit (2);
  }
  return (unsigned char *) bySenseBuf;
#endif
}

/* question values of mode page 31h */
const unsigned char *ModeSenseSpeed ( void )
{
#ifdef __linux__
  static unsigned char MSbuffer[ SCSI_OFF + MODE_SENSE_REPLY_LEN ];
  unsigned char cmdblk [ MODE_SENSE_CMDLEN ] =
  { MODE_SENSE_CMD,  /* command */
    0,  /* lun/reserved */
    0x31,  /* page code */
    0,  /* reserved */
    MODE_SENSE_REPLY_LEN,  /* allocation length */
    0 };/* reserved/flag/link */
  
  memcpy( cmd + SCSI_OFF, cmdblk, sizeof(cmdblk) );
  
  /*
   * +------------------+
   * | struct sg_header | <- cmd
   * +------------------+
   * | copy of cmdblk   | <- cmd + SCSI_OFF
   * +------------------+
   */
  
  if (handle_SCSI_cmd(sizeof(cmdblk), 0, cmd,
                      sizeof(MSbuffer) - SCSI_OFF, MSbuffer ))
  {
    fprintf( stderr, "Mode Sense failed!\n" );
    exit(2);
  }
  return (MSbuffer + SCSI_OFF);
#endif
#if (defined WIN32) || (defined _WIN32)
  BOOL            bExec;
  BYTE            bySenseCDB[MODE_SENSE_CMDLEN];
  static BYTE     bySenseBuf[MODE_SENSE_REPLY_LEN];
  /*
  ** Issue an SENSE, do it with retry even though unit
  ** attentions shouldn't bother an SENSE
  */
  memset (bySenseCDB, 0, MODE_SENSE_CMDLEN);
  bySenseCDB[0] = MODE_SENSE_CMD;
  bySenseCDB[2] = 0x31; /* page code */
  bySenseCDB[4] = MODE_SENSE_REPLY_LEN;
  
  bExec = SyncExecWithRetry (pbt, SRB_DIR_IN | SRB_EVENT_NOTIFY,
                             MODE_SENSE_REPLY_LEN, bySenseBuf,
			     MODE_SENSE_CMDLEN, bySenseCDB);
  
  /*
  ** Make sure the command worked.  If it failed
  ** error (guards against certain device drivers and against
  ** vendor unique devices).
  */
  
  if (!bExec)
  {
    fprintf (stderr, "Mode Sense failed!\n");
    exit (2);
  }
  return (unsigned char *) bySenseBuf;
#endif
}

/* question values of mode page Dh */
const unsigned char *ModeSenseCDPars ( void )
{
#ifdef __linux__
  static unsigned char MSbuffer[ SCSI_OFF + MODE_SENSE_REPLY_LEN ];
  unsigned char cmdblk [ MODE_SENSE_CMDLEN ] =
  { MODE_SENSE_CMD,  /* command */
    0,  /* lun/reserved */
    0xD,  /* page code */
    0,  /* reserved */
    MODE_SENSE_REPLY_LEN,  /* allocation length */
    0 };/* reserved/flag/link */
  
  memcpy( cmd + SCSI_OFF, cmdblk, sizeof(cmdblk) );
  
  /*
   * +------------------+
   * | struct sg_header | <- cmd
   * +------------------+
   * | copy of cmdblk   | <- cmd + SCSI_OFF
   * +------------------+
   */
  
  if (handle_SCSI_cmd(sizeof(cmdblk), 0, cmd,
                      sizeof(MSbuffer) - SCSI_OFF, MSbuffer )) {
    fprintf( stderr, "Mode Sense failed!\n" );
    exit(2);
  }
  return (MSbuffer + SCSI_OFF);
#endif
#if (defined WIN32) || (defined _WIN32)
  BOOL            bExec;
  BYTE            bySenseCDB[MODE_SENSE_CMDLEN];
  static BYTE     bySenseBuf[MODE_SENSE_REPLY_LEN];
  /*
  ** Issue an SENSE, do it with retry even though unit
  ** attentions shouldn't bother an SENSE
  */
  memset (bySenseCDB, 0, MODE_SENSE_CMDLEN);
  bySenseCDB[0] = MODE_SENSE_CMD;
  bySenseCDB[2] = 0xD; /* page code */
  bySenseCDB[4] = MODE_SENSE_REPLY_LEN;
  
  bExec = SyncExecWithRetry (pbt, SRB_DIR_IN | SRB_EVENT_NOTIFY,
                             MODE_SENSE_REPLY_LEN, bySenseBuf,
			     MODE_SENSE_CMDLEN, bySenseCDB);
  
  /*
  ** Make sure the command worked.  If it failed
  ** error (guards against certain device drivers and against
  ** vendor unique devices).
  */
  
  if (!bExec)
  {
    fprintf (stderr, "Mode Sense failed!\n");
    exit (2);
  }
  return (unsigned char *) bySenseBuf;
#endif
}

/* question values of mode page Eh */
const unsigned char *ModeSenseAudioControl ( void )
{
#ifdef __linux__
  static unsigned char MSbuffer[ SCSI_OFF + MODE_SENSE_REPLY_LEN ];
  unsigned char cmdblk [ MODE_SENSE_CMDLEN ] =
  { MODE_SENSE_CMD,  /* command */
    0,  /* lun/reserved */
    0xE,  /* page code */
    0,  /* reserved */
    MODE_SENSE_REPLY_LEN,  /* allocation length */
    0 };/* reserved/flag/link */
  
  memcpy( cmd + SCSI_OFF, cmdblk, sizeof(cmdblk) );
  
  /*
   * +------------------+
   * | struct sg_header | <- cmd
   * +------------------+
   * | copy of cmdblk   | <- cmd + SCSI_OFF
   * +------------------+
   */
  
  if (handle_SCSI_cmd(sizeof(cmdblk), 0, cmd,
                      sizeof(MSbuffer) - SCSI_OFF, MSbuffer )) {
    fprintf( stderr, "Mode Sense failed!\n" );
    exit(2);
  }
  return (MSbuffer + SCSI_OFF);
#endif
#if (defined WIN32) || (defined _WIN32)
  BOOL            bExec;
  BYTE            bySenseCDB[MODE_SENSE_CMDLEN];
  static BYTE     bySenseBuf[MODE_SENSE_REPLY_LEN];
  /*
  ** Issue an SENSE, do it with retry even though unit
  ** attentions shouldn't bother an SENSE
  */
  memset (bySenseCDB, 0, MODE_SENSE_CMDLEN);
  bySenseCDB[0] = MODE_SENSE_CMD;
  bySenseCDB[2] = 0xE; /* page code */
  bySenseCDB[4] = MODE_SENSE_REPLY_LEN;
  
  bExec = SyncExecWithRetry (pbt, SRB_DIR_IN | SRB_EVENT_NOTIFY,
                             MODE_SENSE_REPLY_LEN, bySenseBuf,
			     MODE_SENSE_CMDLEN, bySenseCDB);
  
  /*
  ** Make sure the command worked.  If it failed
  ** error (guards against certain device drivers and against
  ** vendor unique devices).
  */
  
  if (!bExec)
  {
    fprintf (stderr, "Mode Sense failed!\n");
    exit (2);
  }
  return (unsigned char *) bySenseBuf;
#endif
}

/* change mode page 31h */
int ModeSelectSpeed (unsigned char speed, unsigned char speed_settings )
{
#ifdef __linux__
  unsigned char cmdblk [ MS_CMDLEN ] =
  { MODE_SELECT_CMD,  /* command */
    0,  /* lun/reserved */
    0,  /* reserved */
    0,  /* reserved */
    MS_31_PAR_LEN,  /* parameter list length */
    0 };/* control byte */
#endif
#if (defined WIN32) || (defined _WIN32)
  BOOL            bExec;
  BYTE            bySelectCDB[MS_CMDLEN];
#endif
  unsigned char parlist [ MS_31_PAR_LEN ] =
  { 0, /* reserved */
    0, /* medium type */
    0, /* reserverd */
    0, /* block descriptor length */
    0x31, /* drive speed page */
    0x02, /* parameter length */
    0x0, /* speed to be set!*/
    0 }; /* reserved/disav/diswt/spdmode */
  
  parlist[6] = speed;
  parlist[7] = speed_settings;

#ifdef __linux__
  if (perform_SCSI_cmd (MS_CMDLEN, cmdblk, MS_31_PAR_LEN, parlist,
                        0, 0x0))
    return 2;
#endif
#if (defined WIN32) || (defined _WIN32)
  /*
  ** Issue an SELECT, do it with retry even though unit
  ** attentions shouldn't bother an SELECT
  */
  memset (bySelectCDB, 0, MS_CMDLEN);
  bySelectCDB[0] = MODE_SELECT_CMD;
  bySelectCDB[4] = MS_31_PAR_LEN;

  bExec = SyncExecWithRetry (pbt, SRB_DIR_OUT | SRB_EVENT_NOTIFY,
                             MS_31_PAR_LEN, parlist, MS_CMDLEN, bySelectCDB);

  /*
  ** Make sure the command worked.  If it failed
  ** error (guards against certain device drivers and against
  ** vendor unique devices).
  */

  if (!bExec)
    return 2;
#endif
  if (verbose)
    printf ("Mode Select was successful!\n");
  return 0;
}

/* change mode page Dh */
int ModeSelectCDPars (unsigned char inactivity,
                      const unsigned char * msf_numbers )
{
#ifdef __linux__
  unsigned char cmdblk [ MS_CMDLEN ] =
  { MODE_SELECT_CMD,  /* command */
    0,  /* lun/reserved */
    0,  /* reserved */
    0,  /* reserved */
    MS_D_PAR_LEN,  /* parameter list length */
    0 };/* control byte */
#endif
#if (defined WIN32) || (defined _WIN32)
  BOOL            bExec;
  BYTE            bySelectCDB[MS_CMDLEN];
#endif
  unsigned char parlist [ MS_D_PAR_LEN ] =
  { 0, /* reserved */
    0, /* medium type */
    0, /* reserverd */
    0, /* block descriptor length */
    0xD, /* cd parameters page */
    0x06, /* parameter length */
    0, /* reserved */
    0x0, /* inactivity timer multiplier */
    0x0, /* 1/2 of MSF-S */
    0x0, /* 2/2 of MSF-S */
    0x0, /* 1/2 of MSF */
    0x0 }; /* 2/2 of MSF */
    
  parlist[7] = inactivity;
  parlist[8] = msf_numbers[0];
  parlist[9] = msf_numbers[1]; /* supposedly 0x3C */
  parlist[10] = msf_numbers[2];
  parlist[11] = msf_numbers[3]; /* supposedly 0x4B */

#ifdef __linux__
  if (perform_SCSI_cmd (MS_CMDLEN, cmdblk, MS_D_PAR_LEN, parlist,
                        0, 0x0))
    return 2;
#endif
#if (defined WIN32) || (defined _WIN32)
  /*
  ** Issue an SELECT, do it with retry even though unit
  ** attentions shouldn't bother an SELECT
  */
  memset (bySelectCDB, 0, MS_CMDLEN);
  bySelectCDB[0] = MODE_SELECT_CMD;
  bySelectCDB[4] = MS_D_PAR_LEN;

  bExec = SyncExecWithRetry (pbt, SRB_DIR_OUT | SRB_EVENT_NOTIFY,
                             MS_D_PAR_LEN, parlist, MS_CMDLEN, bySelectCDB);

  /*
  ** Make sure the command worked.  If it failed
  ** error (guards against certain device drivers and against
  ** vendor unique devices).
  */

  if (!bExec)
    return 2;
#endif
  if (verbose)
    printf ("Mode Select was successful!\n");
  return 0;
}

/* change mode page Eh */
int ModeSelectAudioControl (unsigned char left_volume,
			    unsigned char right_volume,
			    const unsigned char * sense_result )
{
#ifdef __linux__
  unsigned char cmdblk [ MS_CMDLEN ] =
  { MODE_SELECT_CMD,  /* command */
    0,  /* lun/reserved */
    0,  /* reserved */
    0,  /* reserved */
    MS_E_PAR_LEN,  /* parameter list length */
    0 };/* control byte */
#endif
#if (defined WIN32) || (defined _WIN32)
  BOOL            bExec;
  BYTE            bySelectCDB[MS_CMDLEN];
#endif
  unsigned char parlist [ MS_E_PAR_LEN ] =
  { 0, /* reserved */
    0, /* medium type */
    0, /* reserverd */
    0, /* block descriptor length */
    0xE, /* cd audio control page */
    0x0E, /* parameter length */
    0, /* reserved/IMMED/SOTC/reserved */
    0, /* reserved */
    0, /* reserved */
    0, /* obsolete */
    0, /* obsolete */
    0, /* obsolete */
    0, /* reserved/ouput port 0 channel selection */
    0, /* output port 0 volume */
    0, /* reserved/ouput port 1 channel selection */
    0, /* output port 1 volume */
    0, /* reserved/ouput port 2 channel selection */
    0, /* output port 2 volume */
    0, /* reserved/ouput port 3 channel selection */
    0 }; /* output port 3 volume */
    
  parlist[6] = *(sense_result+14);
  parlist[12] = *(sense_result+20);
  parlist[13] = left_volume;
  parlist[14] = *(sense_result+22);
  parlist[15] = right_volume;
  parlist[16] = *(sense_result+24);
  parlist[17] = *(sense_result+25);
  parlist[18] = *(sense_result+26);
  parlist[19] = *(sense_result+27);

#ifdef __linux__
  if (perform_SCSI_cmd (MS_CMDLEN, cmdblk, MS_E_PAR_LEN, parlist,
                        0, 0x0))
    return 2;
#endif
#if (defined WIN32) || (defined _WIN32)
  /*
  ** Issue an SELECT, do it with retry even though unit
  ** attentions shouldn't bother an SELECT
  */
  memset (bySelectCDB, 0, MS_CMDLEN);
  bySelectCDB[0] = MODE_SELECT_CMD;
  bySelectCDB[4] = MS_E_PAR_LEN;

  bExec = SyncExecWithRetry (pbt, SRB_DIR_OUT | SRB_EVENT_NOTIFY,
                             MS_E_PAR_LEN, parlist, MS_CMDLEN, bySelectCDB);

  /*
  ** Make sure the command worked.  If it failed
  ** error (guards against certain device drivers and against
  ** vendor unique devices).
  */

  if (!bExec)
    return 2;
#endif
  if (verbose)
    printf ("Mode Select was successful!\n");
  return 0;
}

/* acquires the list of all availabe mode pages and searches for the page
   with the numer 0x2A (which should be available within every mmc drive).
*/
int ModeSensePage2aFromAllPages ()
{
  const unsigned char * pages_result;
  int i, length;

  pages_result = ModeSenseAllPages ();
  length = *pages_result;
  i = 12; /* start index for page number */
  while (i < length)
  {
    if (*(pages_result+i) == 0x2a)
    {
      /* no plextor drive (better not with plextor specific extensions;
         could be plextor writer) */
      drive_type = PLEX_0;
      /* set maximum mmc speed */
      mmc_drive = ReturnMMCDriveType
        (((*(pages_result+i+8) << 8) + (*(pages_result+i+9))) / 176);
      return 1;
    }
    /* increment i so that it points to the next page number */
    i += *(pages_result+i+1) + 2;
  }
  return 0;
}

/* used to set fd/pbt and drive_type for drive identified by
   drive_name_part1, drive_name_part2
*/
const unsigned char * open_and_check_drive (const char *drive_name_part1,
                                            const char *drive_name_part2)
{
#ifdef __linux__
  const unsigned char * result;
  if ((fd = open(drive_name_part1, O_RDWR)) >= 0)
  {
#ifdef SG_IO
    /* check whether we have a version >= 3 of the scsi generic driver */
    int t, res = ioctl(fd, SG_GET_VERSION_NUM, &t);
    if ((res >= 0) && (t >= 30000))
    {
      if (verbose)
        printf ("New scsi generic interface found (ver: %d.%d.%d)."
                " Partly using new system call.\n",
                t / 10000, t % 10000 / 100, t % 10000 % 100);
      new_sg_interface = 1;
    }
#endif
    result = Inquiry ();
    if (DTYPE_CDROM == result[0])
    {
      if (!strncmp (PLEXTOR_STRING, (char *) result+INQUIRY_VENDOR, 8))
      {
	if (!strncmp (PLEXTOR_CDROM_COMMON,(char *)result+INQUIRY_PRODUCT, 10))
	{
	  /* set type of plextor cd-rom drive */
	  drive_type = ReturnPlextorDriveType (result+INQUIRY_SPEED);
	  /* this is no MMC drive (but could be plextor writer) */
	  mmc_drive = MMC_0;
	  return result;
	}
      }
    }
     
    /* check if it may be a mmc drive (plextor or other cd-r(w) */
    if (ModeSensePage2aFromAllPages ())
      return result;
    /* close if check failed */
    close (fd);
  }
#endif
#if (defined WIN32) || (defined _WIN32)
  BYTE            byInquiryCDB[INQUIRY_CMDLEN];
  static BYTE     byInquiryBuf[SIZE_INQUIRYBUF];
  BOOL            bExec;
  SRB_GDEVBlock   srbGDEVBlock;
  /*
  ** Issue get device type call to see if there is a device we're
  ** interested in at this address.  We're interested in CDROMs.
  */
  
  memset( &srbGDEVBlock, 0, sizeof(SRB_GDEVBlock) );
  srbGDEVBlock.SRB_Cmd = SC_GET_DEV_TYPE;
  srbGDEVBlock.SRB_HaId = atoi (drive_name_part1);
  srbGDEVBlock.SRB_Target = atoi (drive_name_part2);
  
  gpfnSendASPI32Command( (LPSRB)&srbGDEVBlock );
  if (srbGDEVBlock.SRB_Status != SS_COMP ||
      (srbGDEVBlock.SRB_DeviceType != DTYPE_CDROM) )
  {
    return NULL;
  }
  /*
  ** Issue an INQUIRY, do it with retry even though unit
  ** attentions shouldn't bother an INQUIRY.
  */
  pbt->byHaId = atoi (drive_name_part1);
  pbt->byTarget = atoi (drive_name_part2);
  memset (byInquiryCDB, 0, INQUIRY_CMDLEN);
  byInquiryCDB[0] = SCSI_INQUIRY;
  byInquiryCDB[4] = SIZE_INQUIRYBUF;
  
  bExec = SyncExecWithRetry (pbt, SRB_DIR_IN, SIZE_INQUIRYBUF, byInquiryBuf,
                             INQUIRY_CMDLEN, byInquiryCDB);

  /*
  ** Make sure the inquiry worked.  If it failed, or if the
  ** inquiry data returns a different device type than we got
  ** before (guards against certain device drivers and against
  ** vendor unique devices).
  */
  
  if (bExec)
  {
    if (byInquiryBuf[0] == DTYPE_CDROM)
    {
      if (!strncmp (PLEXTOR_STRING, (char *) byInquiryBuf+INQUIRY_VENDOR, 8))
      {
	if (!strncmp (PLEXTOR_CDROM_COMMON,
		      (char *) byInquiryBuf+INQUIRY_PRODUCT, 10))
	{
	  drive_type = ReturnPlextorDriveType (byInquiryBuf+INQUIRY_SPEED);
	  return (unsigned char *) byInquiryBuf;
	}
      }
    }
     
    /* check if it may be a mmc drive (plextor or other cd-r(w) */
    if (ModeSensePage2aFromAllPages ())
      return (unsigned char *) byInquiryBuf;
  }
#endif
  return NULL;
}

#if (defined WIN32) || (defined _WIN32)
int MMCSetReadSpeed ( int read_speed )
{
  BOOL bExec;
  BYTE bySetCDB [SP_CMDLEN];

  /*
  ** Issue an set speed, do it with retry even though unit
  ** attentions shouldn't bother an set speed.
  */

  memset (bySetCDB, 0, SP_CMDLEN);
  bySetCDB[0] = SET_SPEED_CMD;
  bySetCDB[2] = (read_speed*176) >> 8;  /* read speed part 1 */
  bySetCDB[3] = (read_speed*176) % 255;  /* read speed part 2 */
  bySetCDB[4] = 0xFF; /* write speed part1 */
  bySetCDB[5] = 0xFF; /* write speed part2 */
  
  bExec = SyncExecWithRetry (pbt, SRB_DIR_OUT | SRB_EVENT_NOTIFY,
			     0, NULL, SP_CMDLEN, bySetCDB);

  /*
  ** Make sure the inquiry worked.  If it failed, or if the
  ** inquiry data returns a different device type than we got
  ** before (guards against certain device drivers and against
  ** vendor unique devices).
  */
  
  if(!bExec)
  {
    return -1;
  }
  return 0;
}
#endif

#ifdef __linux__

void linux_list_plextor_mmc_drives ()
{
  char *dev_name;
  const unsigned char *result;
  char **dev_list;
  int found = 0;

  if (geteuid ())
  {
    printf ("You are not super-user: To find all devices you might want to\n");
    printf ("run this program as super-user.\n");
            
  }
  dev_list = default_number_dev_list;
  if (verbose)
    printf ("%s: starting to search for drives:\n", prog_name);
  while ((dev_name = *dev_list++))
  {
    if (verbose)
      printf ("%s: scanning: %s...\n", prog_name, dev_name);
    if ((result = open_and_check_drive (dev_name, dev_name)))
    {
      printf ("Found Plextor(r)/MMC drive: %s is a %.23s\n", dev_name,
              result + INQUIRY_VENDOR);
      close (fd);
      found = 1;
    }
  }
  if (found)
  {
    if (verbose)
    printf ("Already found numerical scsi generic devices not checking for "
            "further devices\n");
    return;
  }
  dev_list = default_letter_dev_list;
  while ((dev_name = *dev_list++))
  {
    if (verbose)
      printf ("%s: scanning: %s...\n", prog_name, dev_name);
    if ((result = open_and_check_drive (dev_name, dev_name)))
    {
      printf ("Found Plextor(r)/MMC drive: %s is a %.23s\n", dev_name,
              result + INQUIRY_VENDOR);
      close (fd);
      found = 1;
    }
  }
}

#endif

/*============================================================================*
 *============================================================================*
 * EOF common-functions.c
 *============================================================================*
 *============================================================================*
 */
