/* .TITLE CDROM_AUDIO - CDROM Audio program */ /* .IDENT 'X-2' */ /***************************************************************************/ /* */ /* COPYRIGHT (c) 1978, 1980, 1982, 1984 BY */ /* DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS. */ /* ALL RIGHTS RESERVED. */ /* */ /* THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED */ /* ONLY IN ACCORDANCE WITH THE TERMS OF SUCH LICENSE AND WITH THE */ /* INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR ANY OTHER */ /* COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY */ /* OTHER PERSON. NO TITLE TO AND OWNERSHIP OF THE SOFTWARE IS HEREBY */ /* TRANSFERRED. */ /* */ /* THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE */ /* AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT */ /* CORPORATION. */ /* */ /* DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY OF ITS */ /* SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL. */ /* */ /* */ /***************************************************************************/ /*++ */ /* FACILITY: System Help */ /* */ /* ABSTRACT: CDROM_AUDIO */ /* This program tests the Audio support in the VMS SCSI disk class driver */ /* (DKDRIVER). The purpose of this program is to provide a menu driven */ /* interface to the AUDIO functionality in DKDRIVER. It is designed to test*/ /* and show the functionality within DKDRIVER, and is NOT designed as the */ /* optimal interface to DKDRIVER Audio support. */ /* */ /* Related documents: */ /* SCSI-2 document, March-9 1990 */ /* */ /* Functional Specification for Audio Extentions to DKDRIVER */ /* (VMS SCSI Disk Class Driver) */ /* */ /* ENVIRONMENT: User mode */ /* */ /* AUTHOR: Richard L. Napolitano CREATION DATE: 10-Jan-1992 */ /* */ /* MODIFIED BY: */ /* */ /* X-2 RCL001 Rick Lord 2-Oct-95 */ /* */ /* In routine cd_play_lba() initialize invalid_lba to 1 instead */ /* of initializing play_status to 1 - this makes the routine */ /* prompt the user for a starting LBA and a block count instead */ /* of just using whatever's in in1 and in2. */ /* */ /* Change two references to CDVERIFY, one user-visible in a */ /* printf, to CDROM_AUDIO. I have no idea where CDVERIFY came */ /* from. */ /* */ /* X-1 RLN001 Richard L. Napolitano 10-Jan-1992 */ /* Provide basic support for the AUDIO functions. Decode */ /* "format 0" data from Get Status. */ /* */ /***************************************************************************/ /* * Include header file defintions */ #include stdio #include ctype #include ssdef #include descrip /*** Structure Definitions for VMS SCSI Audio support. ***/ /* * The Audio Control Block (AUCB) is the structure passed by applications * to DKDRIVER in order to execute audio operations on a CDROM device. */ typedef struct { short function_code; short aucb_version; unsigned long arg1; unsigned long arg2; unsigned long arg3; unsigned long rsvd1; unsigned long dest_buf_addr; unsigned long dest_buf_cnt; unsigned long dest_buf_trans_cnt; unsigned long command_status; unsigned long scsi_status; unsigned long sense_buf_addr; unsigned long sense_buf_cnt; unsigned long sense_buf_trans_cnt; unsigned long rsvd2; } AUCB_TYPE; /* * Structure designed to allow access to individual bytes in each word. * Also substrucure of the READ_TOC_DATA structure. */ typedef struct { unsigned char byte[4]; } BYTE_LONG; /* * Substructure of the READ_TOC_DATA structure. This structure is used * when the Minutes/Seconds/Frames address mode is being used. For a * description refer to the SCSI-2 document, Table 13-1. */ typedef struct { unsigned char msf_rsvd; unsigned int m_field : 8; unsigned int s_field : 8; unsigned int f_field : 8; } MSF_TYPE; /* * Structures used to retrieve data from a Get_Table-Of-Contents command, * in both MSF and LBA address mode. * * This is the Table Of Contents header. There are only one of these blocks per * GET_TOC command. For a description of this refer to the SCSI-2 document, * Table 13-27. */ typedef struct { unsigned short toc_data_length; char toc_first_track; char toc_last_track; } READ_TOC_HEADER; /* * This is the per track descriptor for the GET_TOC data. There are 0 to n of th * ese blocks per TOC command. They are a total of 8 bytes. For a description of thn * refer to the SCSI-2 document, Table 13-27. */ typedef struct { unsigned char toc_rsvd1; /* 1st byte */ unsigned int pre_emp : 1; unsigned int copy_prohib : 1; unsigned int data_track : 1; unsigned int num_channels : 1; unsigned int toc_adr : 4; /* 2nd byte */ unsigned char toc_track_num; /* 3rd byte */ unsigned char toc_rsvd2; /* 4th byte */ union { BYTE_LONG toc_abs_cdrom_addr; MSF_TYPE msf_field; /* 5th thru 8th bytes */ } MSF_LBA_UNION; } READ_TOC_DATA; /*** Constant defintions for CDROM_AUDIO.C ***/ /* * Definition of SCSI Audio command function codes. These are used by the * program to tell the disk class driver which audio functions to execute. */ #define PAUSE 0 #define RESUME 1 #define PREVENT_REMOVAL 2 #define ALLOW_REMOVAL 3 #define PLAY_AUDIO 4 #define PLAY_AUDIO_MSF 5 #define PLAY_AUDIO_TRACK 6 #define PLAY_TRACK_REL 7 #define READ_HEADER 8 #define GET_STATUS 9 #define GET_TOC 10 #define SET_VOLUME 11 #define GET_VOLUME 12 #define SET_DEFAULT 13 #define GET_DEFAULT 14 #define ONLINE 15 /* Bring CD unit online, clearing any errors */ /* * Definition of Sense key status codes. See table 7-39 in SCSI-2 document. */ #define NO_SENSE 0 #define RECOVERED_ERROR 1 #define NOT_READY 2 #define MEDIUM_ERROR 3 #define HARDWARE_ERROR 4 #define ILLEGAL_REQUEST 5 #define UNIT_ATTENTION 6 #define DATA_PROTECT 7 #define BLANK_CHECK 8 #define VENDOR_SPECIFIC 9 #define COPY_ABORTED 10 #define ABORTED_COMMAND 11 #define EQUAL 12 #define VOLUME_OVERFLOW 13 #define MISCOMPARE 14 /* * Definition of SCSI Status byte codes */ #define SCSI_STS_GOOD 0 #define SCSI_STS_CHECK_CONDITION 1 #define SCSI_STS_CONDITION_MET 2 #define SCSI_STS_BUSY 3 #define SCSI_STS_INTERMEDIATE 8 #define SCSI_STS_INT_COND_MET 10 #define SCSI_STS_RES_CONFLICT 12 #define SCSI_STS_COMMAND_TERM 17 #define SCSI_STS_QUEUE_FULL 20 /* * Define the address mode choices. Minutes, Seconds, Frames * or Logical Block Address */ #define LBA 0 #define MSF 1 /* * Define prevent/removal of CD switch contexts */ #define STOP 0 #define ALLOW 1 /* * Define the maximum TOC data length possible on currently * available CD-ROM media, in bytes, per SCSI-2 document, section 13.2.11 */ #define MAX_TOC_LENGTH 804 /* * Length of Sense data buffer */ #define SENSE_DATA_LENGTH 18 /* * Destination Buffer Count */ #define DEST_BUFFER_COUNT 255 /* * Event Flag */ #define cd_EFN 1 /* * Maximum size for an input character array stream */ #define MAX_ARRAY_L 15 /*** Global values for program ***/ globalvalue IO$_READVBLK, IO$_READPROMPT; short cd_chan; /* IO channel assigned to the device on SYS$QIOW call */ int addr_mode = MSF, /* The audio format switch. Valid values are Minutes,Seconds,Frames (MSF) and Logical Block Address (LBA) */ status, cd_device_desc[2], cd_iosb[2]; char cd_device[] = {"CDDISK"}, /* Device name for CDROM to open */ dest_buf[500], /* Destination buffer for TOC etc.. */ SENSE[SENSE_DATA_LENGTH]; /* Error sense data buffer */ float wait_time = 5.0; /* Default time to wait to reissue command after encountering a Unit Attention error */ /* *++ * * PROCEDURE DESCRIPTION: * * This routine initializes the Audio Control Block. It's called by * each command routine before the SYS$QIOW is issued. * * FORMAL PARAMETERS: * * funct - Audio Function Code * version - Version of the AUCB and the Audio interface * (initially 1) * arg1 - Audio command specific * arg2 - Audio command specific * arg3 - Audio command specific * rsvd - Reserved for future use * dest - Destination Buffer Address * dest_cnt - Destination Buffer Count * sense - Sense Data Buffer address * sense_cnt - Sense Data Buffer count * AUCB - The Audio COntrol Block to be initialized. * * See the DKDRIVER Specification for more details. * * IMPLICIT INPUTS: * * IMPLICIT OUTPUTS: * * FUNCTION VALUE: * * 1 = success * * SIDE EFFECTS: * *-- */ int init_aucb(funct, version, arg1, arg2, arg3, rsvd, dest, dest_cnt, sense, sense_cnt, AUCB) int funct, version, arg1, arg2, arg3, rsvd, dest_cnt, sense_cnt; char *dest,*sense; AUCB_TYPE *AUCB; { /* * Initialize AUCB for a passed CD function command. */ AUCB->function_code = funct; AUCB->aucb_version = version; AUCB->arg1 = arg1; AUCB->arg2 = arg2; AUCB->arg3 = (unsigned long) arg3; AUCB->rsvd1 = rsvd; AUCB->dest_buf_addr = (unsigned long) dest; AUCB->dest_buf_cnt = dest_cnt; AUCB->dest_buf_trans_cnt = 0; AUCB->scsi_status = -1; AUCB->command_status = 0; AUCB->sense_buf_addr= sense; AUCB->sense_buf_cnt = sense_cnt; AUCB->sense_buf_trans_cnt =0; AUCB->rsvd1 = AUCB->rsvd2 = 0; return(1); } /* routine init_aucb */ /* *++ * * PROCEDURE DESCRIPTION: * * This routine parses the SCSI status code and Sense Key data and * determines the correct error message to display. It's called by * each command routine even a status other than SS$_NORMAL was * returned to the AUCB. * * FORMAL PARAMETERS: * * AUCB - The Audio Control Block containing the information from * the command issued. * * IMPLICIT INPUTS: * * IMPLICIT OUTPUTS: * * FUNCTION VALUE: * * Value of 6 (UNIT_ATTENTION) on a Unit Attention error, 0 otherwise. * * SIDE EFFECTS: * *-- */ int complete_aucb(AUCB) AUCB_TYPE AUCB; { int status, return_status = 0; /* Default function value */ static char msg_buffer[132]; /* Buffer for error message returned in the LIB$SYS_GETMSG call */ static $DESCRIPTOR(msg_desc,msg_buffer);/* Descriptor for LIB$SYS_GETMSG */ /* * Print out the destination buffer transfer count, other than 0 valid * with certian commands. */ printf("\nThe destination buffer transfer count is %d\n", AUCB.dest_buf_trans_cnt); /* * SCSI status codes * The low byte of the low order word is loaded with the SCSI status byte * thus the mask used. */ switch ((AUCB.scsi_status & 0X0000003e)>>1) { case SCSI_STS_GOOD: printf("\nSCSI status is (GOOD).\n"); break; case SCSI_STS_CHECK_CONDITION: printf("\nSCSI status is (CHECK CONDITION), "); /* * Sense key data first. * The low byte of the high order word contains the sense key, as * defined in the SCSI-2 document, tables 7-39. */ switch ((AUCB.scsi_status & 0X00FF0000)>>16) { case NO_SENSE: printf("Sense Key Data is 0 (No Sense key info to be reported).\n"); break; case RECOVERED_ERROR: printf("Sense Key Data is 1 (Recovered Error).\n"); break; case NOT_READY: printf("Sense Key Data is 2 (Not Ready).\n"); break; case MEDIUM_ERROR: printf("Sense Key Data is 3 (Medium Error. Terminated with a non-recovered error condition).\n"); break; case HARDWARE_ERROR: printf("Sense Key Data is 4 (Hardware Error. Non-recoverable hardware failure).\n"); break; case ILLEGAL_REQUEST: printf("Sense Key Data is 5 (Illegal Request).\n"); break; case UNIT_ATTENTION: printf("Sense Key Data is 6 (Unit Attention).\n"); /* Set the return status to Unit Attention */ return_status = UNIT_ATTENTION; break; case DATA_PROTECT: printf("Sense Key Data is 7 (Data protected. No read or write allowed).\n"); break; case BLANK_CHECK: printf("Sense Key Data is 8 (Encountered blank medium or end-of-data).\n"); break; case VENDOR_SPECIFIC: printf("Sense Key Data is 9 (Available for Vendor Specific conditions).\n"); break; case COPY_ABORTED: printf("Sense Key Data is 10 (Copy aborted).\n"); break; case ABORTED_COMMAND: printf("Sense Key Data is 11 (Target aborted the command).\n"); break; case EQUAL: printf("Sense Key Data is 12 (SEARCH DATA command equal comparison).\n"); break; case VOLUME_OVERFLOW: printf("Sense Key Data is 13 (Volume Overflow).\n"); break; case MISCOMPARE: printf("Sense Key Data is 14 (Source data didn't match data from medium).\n"); break; default: printf("Sense Key Data is (No valid status)\n"); break; } break; case SCSI_STS_CONDITION_MET: printf("\nSCSI status is (CONDITION MET).\n"); break; case SCSI_STS_BUSY: printf("\nSCSI status is (BUSY).\n"); break; case SCSI_STS_INTERMEDIATE: printf("\nSCSI status is (INTERMEDIATE).\n"); break; case SCSI_STS_INT_COND_MET: printf("\nSCSI status is (INTERMEDAITE-CONDITION MET).\n"); break; case SCSI_STS_RES_CONFLICT: printf("\nSCSI status is (RESERVATION CONFLICT).\n"); break; case SCSI_STS_COMMAND_TERM: printf("\nSCSI status is (COMMAND TERMINATED).\n"); break; case SCSI_STS_QUEUE_FULL: printf("\nSCSI status is (QUEUE FULL).\n"); break; default: printf("\nSCSI status is (Invalid).\n"); break; } /* Determine the message passed back in the AUCB, and print it out */ status = LIB$SYS_GETMSG(&AUCB.command_status, 0, &msg_desc); msg_buffer[131] = 0; /* Null terminate */ printf("\nOperating System Command Status is: %s",msg_buffer); return (return_status); } /* routine complete_aucb */ /* *++ * * PROCEDURE DESCRIPTION: * * This routine toggles the address mode of the routine between * Minutes/Seconds/Frames (MSF) and Logical Block Address (LBA). * * FORMAL PARAMETERS: * * IMPLICIT INPUTS: * * Global address mode value. * * IMPLICIT OUTPUTS: * * FUNCTION VALUE: * * 1 = success. * * SIDE EFFECTS: * * Global value addr_mode updated accordingly. * *-- */ int cd_switch_msf_lba(void) { char toggle; /* Input from the user */ int invalid_response = 1; /* * Prompt user until we get a valid response */ while (invalid_response) { printf("\nYour current AUDIO address format is "); if (addr_mode == LBA) printf("LBA.\n"); else printf("MSF.\n"); printf("\nWould you like to toggle? (Y or N) "); scanf("%s", &toggle); printf("\n"); if ((toggle == 'Y') || (toggle == 'y') || (toggle == 'N') || (toggle == 'n' )) invalid_response = 0; /* Response was ok, exit loop */ else printf("\nPlease enter either 'Y' or 'N' \n"); } /* * Toggle the switch */ if ((toggle == 'Y') || (toggle == 'y')) { if (addr_mode == MSF) addr_mode = LBA; else addr_mode = MSF; } return(1); } /* Change Audio Format Command */ /* *++ * * PROCEDURE DESCRIPTION: * * This routine sets the volume of the CDROM. It not only prompts the * user for a volume value for channels 0 and 1, but determines from * the user how the audio is to be piped through the different channels. * For instance a channel could be muted, or the right and left channels * could be swapped. * * FORMAL PARAMETERS: * * IMPLICIT INPUTS: * * IMPLICIT OUTPUTS: * * FUNCTION VALUE: * * 0 if the SYS$QIOW request failed, 1 otherwise. * * SIDE EFFECTS: * * Volume of the CDROM is modified, as well as how the audio is ported * through the two channels. * *-- */ int cd_set_volume(void) { unsigned long in1, in2, /* Volume input for channel 0,1 */ in8 = 1, in9 = 2; /* Port connections for channel 0,1 */ char blk[MAX_TOC_LENGTH], /* Allocate a local block */ in7, /* Boolean for port switching */ char_in1, char_in2, char_in8, char_in9; /* Input selections read as strings, then converted to integers for validation */ AUCB_TYPE AUCB; /* Local Audio Control Block */ int status, volume_status, retry = 1, invalid_choice = 1; /* Boolean for channel 0/1 volume slectn */ long Arg1,Arg2; /* Channel and port information */ /* * Validate numeric input for volume selection of channel 0 */ while (invalid_choice) { printf("Enter volume for channel 0 (0-255): "); scanf("%s",&char_in1); printf("\n"); if (!isdigit(char_in1)) printf("\nThis selection was not numeric, try again\n\n"); else { /* Convert character to numeric */ in1 = atoi(&char_in1); /* Now make sure the entered value is in the correct range */ if ((in1 < 0) || (in1 > 255)) printf("\n** INCORRECT CHOICE. MUST BE BETWEEN 0-255 **\n"); else invalid_choice = 0; /* Choice is valid */ } } invalid_choice = 1; /* * Validate numeric input for volume selection of channel 0 */ while (invalid_choice) { printf("Enter volume for channel 1 (0-255): "); scanf("%s",&char_in2); printf("\n"); if (!isdigit(char_in2)) printf("\nThis selection was not numeric, try again\n\n"); else { /* Convert character to numeric */ in2 = atoi(&char_in2); if ((in2 < 0) || (in2 > 255)) printf("\n** INCORRECT CHOICE. MUST BE BETWEEN 0-255 **\n"); else invalid_choice = 0; /* Choice is valid */ } } invalid_choice = 1; /* * Validate YES or NO answer from user to change left and right channels */ while (invalid_choice) { /* * Determine if the user wants to manipulate the channels */ printf("Do you want to change right and left channels? Y or N: "); scanf("%s",&in7); printf("\n"); if ((in7 == 'Y') || (in7 == 'y')|| (in7 == 'N') || (in7 == 'n')) invalid_choice = 0; else printf("Please enter either 'Y' or 'N'\n"); } /* * If the user did want to change the channells, give them the valid * choices and then validate their input again. */ if ((in7 == 'Y') || (in7 == 'y')) { invalid_choice = 1; /* Loop until valid input for port numbers are received from user */ while (invalid_choice) { printf("Choices for channel 0: \n"); printf(" 0 = mute\n 1 = no change\n 2 = pipe through channel 1\n "); printf("3 = both channels through channel 0\n "); printf("Choice? "); scanf("%s",&char_in8); printf("\n"); if (!isdigit(char_in8)) printf("\nThis selection was not numeric, try again\n\n"); else { in8 = atoi (&char_in8); if ((in8 < 0) || (in8 > 3)) printf("\n** INCORRECT CHOICE. MUST BE BETWEEN 0-3 **\n"); else invalid_choice = 0; /* Choice is valid */ } } /* while invalid_chan_0_choice */ invalid_choice = 1; /* Loop until valid input for port numbers are received from user */ while (invalid_choice) { printf("Choices for channel 1: \n"); printf(" 0 = mute\n 1 = pipe through channel 0\n 2 = no change\n "); printf("3 = both channels through channel 1 \n"); printf("Choice? "); scanf("%s",&char_in9); printf("\n"); if (!isdigit(char_in9)) printf("\nThis selection was not numeric, try again\n\n"); else { in9 = atoi (&char_in9); if ((in9 < 0) || (in9 > 3)) printf("\n** INCORRECT CHOICE. MUST BE BETWEEN 0-3 **\n"); else invalid_choice = 0; /* Choice is valid */ } } /* while invalid_chan_1_choice */ } /* If the user answered 'Y' */ /* * Setup volume and channel selection information for port 0 and 1. * 31 23 15 7 0 * +---------------------------------------------------------------------+ * |Volume(0-FF) | Output Selection |Volume(0-FF) | Output Selection | * +---------------------------------------------------------------------+ * Port 1 Port 0 * * See the DKDRIVER spec. for more details on the byte ordering. */ Arg1 = ((0xff & in1) <<8); /* Get the volume for port 0 */ Arg1 |= ( (0xff & in2) << 24); /* Get the volume for port 1 */ Arg1 |= (in8); /* Set channel select port 0 */ Arg1 |= (in9 << 16); /* Set channel select port 1 */ printf("The Volume information for ports 1 and 0 is %x\n", (unsigned long)Arg1); /* * We cannot setup the volume and channel selection information for * ports 2 and 3, since the SONY CD does not support this. */ Arg2 = 0; init_aucb(SET_VOLUME,MSF, /* Audio function code */ Arg1, /* Arg 1 */ Arg2, /* Arg 2 */ 0, 0, /* Third argument */ 0, 0, &SENSE,SENSE_DATA_LENGTH,&AUCB); /* /* * Continue to retry as long as there is only a Unit Attention error, * otherwise print out error and don't try anymore. * Unit Attention implies that the CD carrier was removed or the * device was reset. */ while (retry) { printf("Execute SET VOLUME function\n"); status = sys$qiow (cd_EFN, cd_chan, IO$_READPROMPT, cd_iosb, 0, 0, &AUCB, sizeof(AUCB_TYPE), 0, 0, 0, 0); /* Reset the status so that we do the right thing on a NORMAL status */ volume_status = 1; /* Only proceed if there was not a SYS$QIOW error */ if (status == SS$_NORMAL) { /* First check Operating System Command status */ if (AUCB.command_status != SS$_NORMAL) { printf("\nOperating system command status is NOT NORMAL"); /* Make some further checks, and print out the info */ volume_status = complete_aucb(AUCB); /* * If status was anything other than Unit Attention, * stop retrying , otherwise wait 5 seconds and retry */ if (volume_status != UNIT_ATTENTION) retry = 0; else lib$wait(&wait_time); } else { printf("\nOperating system command status is NORMAL"); /* Do not continue looping when the status was OK */ retry = 0; } } else { /* SYS$QIO error, abort command */ printf("\n*** SYS$QIOW ERROR, COMMAND ABORTED ***\n"); return(0); } } /* End of retry */ return(1); } /* cd_set_volume */ /* *++ * * PROCEDURE DESCRIPTION: * * This routine gets the volume information from the CDROM and prints * it out to SYS$OUTPUT. * * FORMAL PARAMETERS: * * IMPLICIT INPUTS: * * IMPLICIT OUTPUTS: * * FUNCTION VALUE: * * 0 if the SYS$QIOW request failed, 1 otherwise. * * SIDE EFFECTS: * *-- */ int cd_get_volume(void) { char blk[MAX_TOC_LENGTH]; /* Allocate a local block */ AUCB_TYPE AUCB; /* Local Audio Control Block */ int retry = 1, status, get_vol_status = 1; init_aucb(GET_VOLUME,MSF, /* Audio function code */ 0, /* Arg 1 */ 0, /* Arg 2 */ 0, 0, /* Third argument */ &blk[0], 32, /* Volume buffer */ &SENSE,SENSE_DATA_LENGTH,&AUCB); /* Audio Control Block */ /* * Continue to retry as long as there is only a Unit Attention error, * otherwise print out error and don't try anymore * Unit Attention implies that the CD carrier was removed or the * device was reset. */ while (retry) { printf("Execute GET VOLUME function\n"); /* Reset the status so that we do the right thing on a NORMAL status */ get_vol_status = 1; status = sys$qiow (cd_EFN, cd_chan, IO$_READPROMPT, cd_iosb, 0, 0, &AUCB, sizeof(AUCB_TYPE), 0, 0, 0, 0); /* Only proceed if there was not a SYS$QIOW error */ if (status == SS$_NORMAL) { /* First check Operating System Command status */ if (AUCB.command_status != SS$_NORMAL) { printf("\nOperating system command status is NOT NORMAL"); /* Make some further checks, and print out the info */ get_vol_status = complete_aucb(AUCB); /* * If status was anything other than Unit Attention, * stop retrying , otherwise wait 5 seconds and retry */ if (get_vol_status != UNIT_ATTENTION) retry = 0; else lib$wait(&wait_time); } else { printf("\nOperating system command status is NORMAL"); /* Do not continue looping when the status was OK */ retry = 0; } } else { /* SYS$QIO error, abort command */ printf("\n*** SYS$QIOW ERROR, COMMAND ABORTED ***\n"); return(0); } } /* End of retry */ /* * If the Get worked, continue */ if (get_vol_status == SS$_NORMAL) { printf("\nVolume information port 1 and 0: %x\n",(unsigned long)AUCB.arg1); printf("Volume information port 3 and 2: %x\n",(unsigned long)AUCB.arg2); printf("Port 0's volume is %d, Port 1's volume is %d\n", ((AUCB.arg1 & 0X0000FF00)>>8),((AUCB.arg1 & 0XFF000000)>>24)); } return(1); } /* cd_get_volume */ /* *++ * * PROCEDURE DESCRIPTION: * * This procedure reads the Table of Contents from the CDROM and, * depending on the CDROM address format, dumps the TOC data in the * appropriate format. * * FORMAL PARAMETERS: * * IMPLICIT INPUTS: * * IMPLICIT OUTPUTS: * * FUNCTION VALUE: * * 0 if the SYS$QIOW request failed, 1 otherwise. * * SIDE EFFECTS: * *-- */ int cd_read_toc(void) { char blk[MAX_TOC_LENGTH], /* Allocate a local block */ *audio_data = {"AUDIO"}, /* ASCII translation of the toc_data->data_track bit */ *audio_pre = {"WITHOUT"}, /* ASCII translation of the toc_data->pre_emp bit */ *dig_copy = {"PROHIBITED"}, /* ASCII translation of the toc_data->copy_prohib bit */ *num_chan = {"TWO"}; /* ASCII translation of the toc_data->num_channels bit */ int i, retry = 1, status, get_toc_status = 1; /* Init to 1 because we wont reset unless there is an error */ AUCB_TYPE AUCB; /* Local Audio Control Block */ READ_TOC_HEADER *toc_header; /* Structure ptr for the header informatin of the data returned from the GET_TOC command */ READ_TOC_DATA *toc_data; /* Structure ptr for the format of the data returned from the GET_TOC command */ BYTE_LONG temp_abs_cdrom_addr; /* Structure that allows for the transposal of the Absolute CDROM address in the TOC structure of the 4 bytes. They come out in reverse order of most signifigant byte because of the little endian format */ /* * Initialize the AUCB with the correct information for a GET_TOC * command, then issue the command */ init_aucb(GET_TOC,MSF, /* Audio function code */ addr_mode, /* Arg 1 */ 0, /* Arg 2 */ 0, 0, /* Third argument */ &blk[0], MAX_TOC_LENGTH, /* Request Sense buffer */ &SENSE,SENSE_DATA_LENGTH,&AUCB); /* Audio Control Block */ /* * Continue to retry as long as there is only a Unit Attention error, * otherwise print out error and don't try anymore * Unit Attention implies that the CD carrier was removed or the * device was reset. */ while (retry) { printf("Execute READ TOC function\n"); /* Reset the status so that we do the right thing on a NORMAL status */ get_toc_status = 1; status = sys$qiow (cd_EFN, cd_chan, IO$_READPROMPT, cd_iosb, 0, 0, &AUCB, sizeof(AUCB_TYPE), 0, 0, 0, 0); /* Reset the status so that we do the right thing */ get_toc_status = 1; /* Only proceed if there was not a SYS$QIOW error */ if (status == SS$_NORMAL) { /* First check Operating System Command status */ if (AUCB.command_status != SS$_NORMAL) { printf("\nOperating system command status is NOT NORMAL"); /* Make some further checks, and print out the info */ get_toc_status = complete_aucb(AUCB); /* * If status was anything other than Unit Attention, * stop retrying , otherwise wait 5 seconds and retry */ if (get_toc_status != UNIT_ATTENTION) retry = 0; else lib$wait(&wait_time); } else { printf("\nOperating system command status is NORMAL"); /* Do not continue looping when the status was OK */ retry = 0; } } else { /* SYS$QIO error, abort command */ printf("\n*** SYS$QIOW ERROR, COMMAND ABORTED ***\n"); return(0); } } /* End of retry */ /* * If the Get worked, then print out the Table Of Contents */ if (get_toc_status == SS$_NORMAL) { /* * Determine the number of TOC track descriptors */ toc_header = blk; /* * We must switch the order of the bytes so that the most signifigant * byte is in the correct place. */ toc_header->toc_data_length = (toc_header->toc_data_length >> 8) | ((unsigned short)blk[0]) << 8; /* * Print out the information in the header and other pertinant info */ printf("\nYour audio format is currently set to "); if (addr_mode == LBA) printf("LBA.\n"); else printf("MSF.\n"); printf("\nThe first track is: %d, The last track is: %d", toc_header->toc_first_track , toc_header->toc_last_track); printf("\n\n"); /* * Map the data returned from the GET_TOC to the record structure * with the correct format, and then transpose the bytes in reverse * order so that the most significant byte is in the correct place. */ toc_data = &blk[4]; /* * Upper limit = (total TOC data length in bytes - size of the TOC * header in bytes)/size of the TOC structure in bytes */ for (i=1; i<= (toc_header->toc_data_length - sizeof(READ_TOC_HEADER)) /sizeof(READ_TOC_DATA); i++) { if (addr_mode == LBA) { /* * Swap the byte order so that the address can be printed out * correctly. */ temp_abs_cdrom_addr.byte[3] = toc_data->MSF_LBA_UNION.toc_abs_cdrom_addr.byte[0]; temp_abs_cdrom_addr.byte[2] = toc_data->MSF_LBA_UNION.toc_abs_cdrom_addr.byte[1]; temp_abs_cdrom_addr.byte[1] = toc_data->MSF_LBA_UNION.toc_abs_cdrom_addr.byte[2]; temp_abs_cdrom_addr.byte[0] = toc_data->MSF_LBA_UNION.toc_abs_cdrom_addr.byte[3]; } /* * Format the toc_data->* bits for output */ if (toc_data->data_track) strcpy(audio_data, "DATA"); else { strcpy(audio_data, "AUDIO"); if (toc_data->pre_emp) strcpy (audio_pre, "WITH"); else strcpy (audio_pre, "WITHOUT"); if (toc_data->copy_prohib) strcpy (dig_copy, "PERMITTED"); else strcpy (dig_copy, "PROHIBITED"); if (toc_data->num_channels) strcpy (num_chan, "FOUR"); else strcpy (num_chan, "TWO"); }; /* * Print out the each toc data record information. * This is the Sub-channel Q control bits */ printf("\nThis is track number : %4d",toc_data->toc_track_num); printf("\nThis is a %s track.",audio_data); if (!toc_data->data_track) { printf("\nIt has audio %s pre-emphasis.",audio_pre); printf("\nDigital copy is %s.",dig_copy); printf("\nIt has %s channel audio.",num_chan); }; /* * This is the Sub-channel Q mode information */ printf("\nSub-channel Q info type is: %4x",toc_data->toc_adr); /* * The logical block address or minutes/seconds/frames info */ if (addr_mode == LBA) printf("\nAbsolute CD-ROM Address: %8d", temp_abs_cdrom_addr); else printf("\nMinutes: %4d Seconds : %4d Frames : %4d", toc_data->MSF_LBA_UNION.msf_field.m_field, toc_data->MSF_LBA_UNION.msf_field.s_field, toc_data->MSF_LBA_UNION.msf_field.f_field); printf("\n=========================================================\n"); /* * Get the next toc_data block */ toc_data++; } /* End for looping through toc_data info */ } /* End of dumping data if status was sucessful */ return(1); } /* cd_read_toc */ /* *++ * * PROCEDURE DESCRIPTION: * * This routine gets the current status and position of the CDROM, * and dumps the output to SYS$OUTPUT. * * FORMAL PARAMETERS: * * IMPLICIT INPUTS: * * IMPLICIT OUTPUTS: * * FUNCTION VALUE: * * 0 if the SYS$QIOW request failed, 1 otherwise. * * SIDE EFFECTS: * *-- */ int cd_read_sub(void) { char blk[50]; /* Allocate a local block */ AUCB_TYPE AUCB; /* Local Audio Control Block */ BYTE_LONG temp_lba; /* Allows byte order of LBA to be swapped so that it can be printed out correctly */ int i, retry = 1, status, get_status = 1, format, invalid_fmt = 1; char in[80]; /* Validate the input of the format code, it must be numeric */ while (invalid_fmt) { fflush(stdin); printf("Enter sub-channel data format (0,1 or 2): "); scanf("%s",&in); /* Convert the character string to an integer */ format = atoi (&in); printf("\n"); format = strtol(&in,0,10); if ( !((format >= 0) || (format <= 2)) ) printf("The format # you entered was invalid, try again.\n"); else invalid_fmt = 0; } init_aucb(GET_STATUS,MSF, /* Audio function code */ addr_mode, /* Arg 1, MSF/LBA format */ format, /* Arg 2, data format */ 0, 0, /* Third argument */ &blk[0],50, /* Read Subchannel buffer */ &SENSE,SENSE_DATA_LENGTH,&AUCB); /* Audio Control Block */ /* * Continue to retry as long as there is only a Unit Attention error, * otherwise print out error and don't try anymore * Unit Attention implies that the CD carrier was removed or the * device was reset. */ while (retry) { printf("Execute GET STATUS function\n"); status = sys$qiow (cd_EFN, cd_chan, IO$_READPROMPT, cd_iosb, 0, 0, &AUCB, sizeof(AUCB_TYPE), 0, 0, 0, 0); /* Reset the status so that we do the right thing */ get_status = 1; /* Only proceed if there was not a SYS$QIOW error */ if (status == SS$_NORMAL) { /* First check Operating System Command status */ if (AUCB.command_status != SS$_NORMAL) { printf("\nOperating system command status is NOT NORMAL"); /* Make some further checks, and print out the info */ get_status = complete_aucb(AUCB); /* * If status was anything other than Unit Attention, * stop retrying , otherwise wait 5 seconds and retry */ if (get_status != UNIT_ATTENTION) retry = 0; else lib$wait(&wait_time); } else { printf("\nOperating system command status is NORMAL"); /* Do not continue looping when the status was OK */ retry = 0; } } else { /* SYS$QIO error, abort command */ printf("\n*** SYS$QIOW ERROR, COMMAND ABORTED ***\n"); return(0); } } /* End of retry */ /* * If the Get worked, continue */ if (get_status == SS$_NORMAL) { printf("\nStart dump of read sub-channel data\n"); for (i=0; i < 25 ; i++) printf("%02x|",(unsigned char)(blk[i])); printf("\nCompleted dump of sub-channel data\n"); /* * Print out Audio status string based on Audio status code. */ switch (blk[1]) { case 17: printf("Audio status is (Play operation in progress).\n"); break; case 18: printf("Audio status is (Play operation paused).\n"); break; case 19: printf("Audio status is (Play operation completed successfully).\n"); break; case 20: printf("Audio status is (Play operation stopped due to error).\n"); break; default: printf("Audio status is (No valid status)\n"); break; } if (format == 1) { /* * Format data based on MSF/LBA switch */ if (addr_mode == MSF) printf("Current track is %d, Absolute time is %d:%d:%d, Relative time is %d:%d:%d.\n", blk[5+1],blk[5+4],blk[5+5],blk[5+6],blk[5+8],blk[5+9],blk[5+10]); else { /* * Swap the byte order so that the current CD position(address) * can be printed out correctly. */ temp_lba.byte[3] = blk[5+3]; temp_lba.byte[2] = blk[5+4]; temp_lba.byte[1] = blk[5+5]; temp_lba.byte[0] = blk[5+6]; printf("Absoulte LBA is: %8d, ", temp_lba); temp_lba.byte[3] = blk[5+7]; temp_lba.byte[2] = blk[5+8]; temp_lba.byte[1] = blk[5+9]; temp_lba.byte[0] = blk[5+10]; printf("Relative LBA is: %8d\n", temp_lba); } /* else addr_mode != MSF */ } /* if format 1 */ } /* If get_Status ok */ return(1); } /* cd_read_sub */ /* *++ * * PROCEDURE DESCRIPTION: * * This routine reads a block of data at the specified address (LBA). * The CDROM must be mounted for this QIO to complete successfully. * * FORMAL PARAMETERS: * * IMPLICIT INPUTS: * * IMPLICIT OUTPUTS: * * FUNCTION VALUE: * * 1 = success * * SIDE EFFECTS: * *-- */ int cd_read_blk(void) { unsigned long in4; /* LBA to be read */ char ch_in4, /* Character input for LBA */ blk[512]; /* Allocate a local block */ AUCB_TYPE AUCB; /* Local Audio Control Block */ int i,j, invalid_lba = 1, /* Boolean for the input of the LBA */ qio_status, status; static char msg_buffer[132]; /* Buffer for the message descriptor passed to the LIB$SYS_GETMSG routine */ static $DESCRIPTOR(msg_desc,msg_buffer); /* Error message descriptor */ /* Validate the input of the LBA, it must be numeric */ while (invalid_lba) { printf("Enter Logical Block Address(LBA) to be read: "); scanf("%s",&ch_in4); printf("\n"); if (!isdigit(ch_in4)) printf("The LBA you entered was not numeric, try again.\n"); else invalid_lba = 0; } /* Convert the input char string to numeric */ in4 = atoi(&ch_in4); printf("Issue a READBLK QIO\n"); qio_status = sys$qiow (cd_EFN, cd_chan, IO$_READVBLK, cd_iosb, 0,0,&blk[0], 512, in4, 0, 0, 0); /* Continue if the SYS$QIOW completed sucessfully */ if (qio_status == SS$_NORMAL) { /* For the READBLK command we must also check the CD_IOSB for errors */ if ((cd_iosb[0] == SS$_NORMAL) || (cd_iosb[0] == 33554433)) { /* Everything seems in order, dump out the information */ printf("\nStart dump of READ BLOCK data\n"); /* * Continue for as many rows as columns of 25 will allow . * Each section will be one byte, in hex format */ for (j=0;j < (512/25); j++) { printf("\n"); for (i=0; i < 25; i++) printf("%02x|",(unsigned char)(blk[i+(j*25)])); } /* Print out balance of bytes since 512/25 has a remainder */ printf("\n"); for (i=0; i < (512 % 25); i++) printf("%02x|",(unsigned char)(blk[i+(j*25)])); printf("\nCompleted dump of READ BLOCK data\n"); } else { status = LIB$SYS_GETMSG(&cd_iosb[0], 0, &msg_desc); msg_buffer[131] = 0; /* Null terminate */ printf("\nCD_IOSB status is: %s",msg_buffer); } } else { /* Check the various returned status values */ status = LIB$SYS_GETMSG(&qio_status, 0, &msg_desc); msg_buffer[131] = 0; /* Null terminate */ printf("\nREAD BLOCK status is: %s",msg_buffer); } return(1); } /* cd_read_blk */ /* *++ * * PROCEDURE DESCRIPTION: * * This routine will play an audio track determined by the MSF * data entered by the user. * * FORMAL PARAMETERS: * * IMPLICIT INPUTS: * * IMPLICIT OUTPUTS: * * FUNCTION VALUE: * * 0 if the SYS$QIOW request failed, 1 otherwise. * * SIDE EFFECTS: * *-- */ int cd_play_msf(void) { unsigned long in1,in2,in3, /* Starting MSF */ in4,in5,in6; /* Ending MSF */ int i, length, status, play_status = 1, retry = 1, first_num, second_num, /* Boolean values indicating what number we are currently validating */ invalid_msf = 1; char str[MAX_ARRAY_L], /* Used to load the input into, and then parse apart */ ch_in1[MAX_ARRAY_L], /* The minutes input, in character format */ ch_in2[MAX_ARRAY_L], /* The seconds input, in character format */ ch_in3[MAX_ARRAY_L]; /* The frames input, in character format */ AUCB_TYPE AUCB; /* Local Audio Control Block */ /* * Valididate the beginning MSF. The format expected is "num,num,num" */ while (invalid_msf) /* If we don't have valid input, ask again */ { for (i=0; i