MODULE iom$tape_queue_manager_mtr;
?? RIGHT := 110 ??

?? NEWTITLE := 'Global Declarations Referenced By This Module', EJECT ??
?? PUSH (LISTEXT := ON) ??
*copyc ioe$tape_io_conditions
*copyc mme$condition_codes
*copyc iot$command
*copyc iot$io_function
*copyc iot$ipi_tape_log_data
*copyc iot$lockword
*copyc iot$pp_interface_table
*copyc iot$pp_number
*copyc iot$pp_response
*copyc iot$read_tape_description
*copyc iot$tape_block_count
*copyc iot$tape_collected_pp_response
*copyc iot$tape_command_heap
*copyc iot$tape_command_table_entry
*copyc iot$tape_completion_packet
*copyc iot$tape_device_status
*copyc iot$tape_request_block
*copyc iot$tape_track
*copyc iot$unit_interface_table
*copyc iot$write_tape_description
*copyc mmt$buffer_descriptor
*copyc mmt$io_type
*copyc mmt$rma_list
*copyc ost$informative_message_record
*copyc ost$page_size
*copyc ost$signature_lock
*copyc syt$monitor_request_code
?? POP ??
*copyc dpp$convert_int_to_str_dec
*copyc dpp$convert_int_to_str_octal
*copyc dpp$display_error
*copyc dsp$report_system_message
*copyc mmp$build_lock_rma_list_tape
*copyc mmp$unlock_rma_list
*copyc mtp$error_stop
*copyc tmp$check_taskid
*copyc tmp$set_task_ready
*copyc cmv$logical_pp_table_p
*copyc cmv$logical_unit_table
*copyc osv$boot
*copyc osv$boot_is_executing
*copyc osv$external_interrupt_selector
*copyc osv$mainframe_wired_heap
*copyc tmv$null_global_task_id
*copyc cml$system_informative_message
*copyc i#move
*copyc i#real_memory_address
?? OLDTITLE ??
?? NEWTITLE := 'Global Declarations Declared By This Module', EJECT ??

  VAR
    iov$display_microcode_load: [XDCL, STATIC, #GATE] boolean := FALSE,

    iov$tape_out_track: [XDCL, #GATE, oss$mainframe_wired] ARRAY [ 1 .. 256 ] of iot$tape_track,
    iov$tape_in_track: [XDCL, #GATE, oss$mainframe_wired] ARRAY [ 1 .. 256 ] of iot$tape_track,
    iov$in_ptr: [XDCL, #GATE, oss$mainframe_wired] integer := 0,
    iov$out_ptr: [XDCL, #GATE, oss$mainframe_wired] integer := 0,

    iov$tape_process_pp_response: [XDCL, STATIC, #GATE, oss$mainframe_wired]
         iot$response_processor := ^iop$tape_process_pp_response,

    iov$tape_completion_q_table: [XDCL, #GATE] ^array [1 .. *] of
         iot$tape_completion_packet := NIL;

?? OLDTITLE ??
?? NEWTITLE := ' iop$ensure_tape_io_complete ' ??
?? EJECT ??

{ PURPOSE:
{ This routine will initiate a synchronize command on any unit that is
{ assigned to the input job_name.  This will ensure that all outstanding
{ write requests will complete in a timely manner.

  PROCEDURE [XDCL] iop$ensure_tape_io_complete (
        job_name: jmt$system_supplied_name);

    VAR
      ccc_cart_unit_comm_buffer_p: ^iot$ccc_cart_unit_comm_buffer,
      i: 0 .. 0ff(16),
      lun: iot$logical_unit;

    FOR i := LOWERBOUND (iov$tape_completion_q_table^) TO UPPERBOUND (iov$tape_completion_q_table^) DO
      lun := iov$tape_completion_q_table^ [i].lun;
      IF (iov$tape_completion_q_table^ [i].cart_writes_pending > 0) AND
            cmv$logical_unit_table^ [lun].status.assigned AND
            (cmv$logical_unit_table^ [lun].status.assigned_jsn = job_name) THEN
        RESET cmv$logical_unit_table^ [lun].unit_communication_buffer_pva;
        NEXT ccc_cart_unit_comm_buffer_p IN cmv$logical_unit_table^ [lun].
              unit_communication_buffer_pva;
        ccc_cart_unit_comm_buffer_p^.force_sync := 1;
        iov$tape_completion_q_table^ [i].sync_set := TRUE;
      IFEND;
    FOREND;

  PROCEND iop$ensure_tape_io_complete;

?? OLDTITLE ??
?? NEWTITLE := ' iop$tape_process_pp_response ' ??
?? EJECT ??

  PROCEDURE [XDCL] iop$tape_process_pp_response (pp_response_header_p:
        ^iot$pp_response;
        detailed_status_p: ^iot$detailed_status;
        pp_number: 1 .. ioc$pp_count;
    VAR mon_status: syt$monitor_status);

    TYPE
      status_code = 0 .. 0ffff(16);

    VAR
      iov$good_cart_microcode_load: [XDCL] integer := 0,
      iov$bad_cart_microcode_load: [XDCL] integer := 0,
      iov$tape_task_id_check: [XDCL] integer := 0,
      iov$tape_task_id_not_found: [XDCL] integer := 0;

    VAR
      address_pair_count: 0 .. mmc$max_rma_list_length,
      block_id_status_area_p: ^iot$tape_bid_status_response,
      cart_tape_write: boolean,
      ccc_cart_dev_stat_p: ^iot$ccc_cart_device_status,
      ccc_cart_log_p: ^iot$ccc_cart_error_log,
      ccc_cart_sense_p: ^iot$ccc_cart_sense_bytes,
      ccc_cart_unit_comm_buffer_p: ^iot$ccc_cart_unit_comm_buffer,
      channel: cmt$physical_channel,
      channel_length: 1 .. 2,
      completion_q_table_p: ^iot$tape_completion_entry,
      error_status: status_code,
      first_logical_unit: iot$logical_unit,
      ii: [STATIC] integer := 1,
      io_error: iot$io_error,
      io_type: iot$io_function,
      io_id: iot$io_id,
      ioid: mmt$io_identifier,
      iou: dst$iou_number,
      ipi_status_p: ^iot$ipi_tape_status,
      ipi_source_p: ^cell,
      ipi_dest_p: ^cell,
      ipi_status_length: 0 .. (ioc$ipi_tape_status_size + ioc$ipi_max_status_size),
      j: 1 .. ioc$max_multiple_tape_requests,
      list_p: ^mmt$rma_list,
      logical_unit: iot$logical_unit,
      message_data: ^SEQ(*),
      message_record: ost$informative_message_record,
      msg_recorded: boolean,
      msg1: string(63),
      msg2: string(63),
      msg3: string(63),
      p_device_status: ^iot$tape_device_status,
      p_extended_status: ^iot$tape_extended_status,
      p_next_request: ^iot$io_request,
      p_unit_table: ^iot$unit_interface_table,
      seq_p: ^SEQ ( * ),
      status_code_p: ^status_code,
      tape_log_data: iot$ipi_tape_log_data,
      tape_log_seq_p: ^SEQ ( * ),
      tape_pp_response_p: ^iot$tape_collected_pp_response,
      tape_request_p: ^iot$wired_tape_request,
      temp_detailed_status_p: ^SEQ ( * ),
      temp_tape_request_p: ^iot$wired_tape_request,
      unit_type: iot$unit_type,
      unlock_io_type: iot$io_function;

    BEGIN
      mon_status.normal := TRUE;
      logical_unit := pp_response_header_p^.logical_unit;

      IF (pp_response_header_p^.response_code.primary_response =
            ioc$normal_response) OR (pp_response_header_p^.response_code.
            primary_response = ioc$abnormal_response) THEN

{ Verify task is still active if flag is set to check the task id.  This can occur
{ if a terminate task or job occurred with tape request(s) active.

        completion_q_table_p := pp_response_header_p^.request^.pp_request_p;

        IF completion_q_table_p^.check_task_id THEN
          tmp$check_taskid (completion_q_table_p^.task_id, tmc$opt_return, mon_status);
          iov$tape_task_id_check := iov$tape_task_id_check + 1;
          IF NOT mon_status.normal THEN  { task id gone, ignore response
            mon_status.normal := TRUE;
            completion_q_table_p^.waiting_response := FALSE;
            completion_q_table_p^.request_not_processed := FALSE;
            completion_q_table_p^.io_id := 0;
            completion_q_table_p^.io_request := NIL;
            completion_q_table_p^.task_id := tmv$null_global_task_id;
            completion_q_table_p^.check_task_id := FALSE;
            cmv$logical_unit_table^ [logical_unit].unit_interface_table^.unit_status.disabled := FALSE;
            iov$tape_task_id_not_found := iov$tape_task_id_not_found + 1;
            RETURN;
          IFEND;
        IFEND;
        tape_request_p := pp_response_header_p^.request^.device_request_p;
        tape_pp_response_p := tape_request_p^.pp_response_p;

        IF tape_pp_response_p = NIL THEN
{ There is no response to process so exit }
           RETURN;
        IFEND;

        tape_pp_response_p^.pp_no := pp_number;
        tape_pp_response_p^.pp_response := pp_response_header_p^;
        io_id := tape_request_p^.io_id;
        io_error := ioc$no_error;


{ Temporary save of requests for debugging.
      iov$in_ptr := ii;
      iov$tape_in_track [ii].id := io_id;
      iov$tape_in_track [ii].time := #free_running_clock(0);
      iov$tape_in_track [ii].lun  := logical_unit;

      IF ii < 256 THEN
        ii := ii + 1;
      ELSE
        ii := 1;
      IFEND;



{ Release process_io_completions CIO response area ASAP.
{ Must use redefined sequence pointer (temp_detailed_status_p) because the original pointer
{  is not passed in as a VAR and cannot be used to RESET the detailed status Sequence Structure.

        temp_detailed_status_p := detailed_status_p;
        RESET temp_detailed_status_p;

        IF NOT ((cmv$logical_pp_table_p^ [pp_number].controller_info.controller_type = cmc$mt5698_xx) OR
              (cmv$logical_pp_table_p^ [pp_number].controller_info.controller_type = cmc$mt5680_xx)) THEN

{ Move block_id status responses to wired request area.

          NEXT block_id_status_area_p IN temp_detailed_status_p;
          tape_pp_response_p^.block_id_status_area := block_id_status_area_p^;

{ Move device status to wired request area

          NEXT p_device_status IN temp_detailed_status_p;
          tape_pp_response_p^.device_status := p_device_status^;

{ Check pp response length and if extended status is included, handle extended device status.

          IF pp_response_header_p^.response_length > (ioc$min_response_length +
                 ioc$bid_area_size + ioc$device_status_size) THEN
            NEXT p_extended_status IN temp_detailed_status_p;
            tape_pp_response_p^.extended_device_status := p_extended_status^;
          IFEND;


        ELSEIF (cmv$logical_pp_table_p^ [pp_number].controller_info.controller_type = cmc$mt5698_xx) THEN

{ Move block_id status responses to wired request area.

          NEXT block_id_status_area_p IN temp_detailed_status_p;
          tape_pp_response_p^.ipi_block_id_status_area := block_id_status_area_p^;

{ Move ipi status to wired request area.

          ipi_status_length := pp_response_header_p^.response_length -
                (ioc$min_response_length + ioc$bid_area_size);

          IF ipi_status_length > 0 THEN
            NEXT ipi_status_p IN temp_detailed_status_p;
            ipi_source_p := ipi_status_p;
            ipi_dest_p := ^tape_pp_response_p^.ipi_tape_status;
            i#move (ipi_source_p, ipi_dest_p, ipi_status_length);
          IFEND;

        ELSEIF (cmv$logical_pp_table_p^ [pp_number].controller_info.controller_type = cmc$mt5680_xx) THEN

{ Clear flag (PVA) in unit communication buffer indicating task is waiting for write completion.
{ This is only done for cartridge tape write operations if the task is waiting for IO complete.

          IF (tape_request_p^.ready_task AND (tape_request_p^.io_type = ioc$explicit_write)) OR
                (iov$tape_completion_q_table^ [tape_request_p^.completion_q_index].sync_set) THEN
            RESET cmv$logical_unit_table^ [logical_unit].unit_communication_buffer_pva;
            NEXT ccc_cart_unit_comm_buffer_p IN cmv$logical_unit_table^ [logical_unit].
                  unit_communication_buffer_pva;
            ccc_cart_unit_comm_buffer_p^.request_pva := NIL;
            IF (ccc_cart_unit_comm_buffer_p^.force_sync <> 0) AND
                  (cmv$logical_unit_table^ [logical_unit].unit_interface_table^.next_request = NIL) THEN
              ccc_cart_unit_comm_buffer_p^.force_sync := 0;
              iov$tape_completion_q_table^ [tape_request_p^.completion_q_index].sync_set := FALSE;
            IFEND;
          IFEND;

{ Move general/detailed status to wired response area.

          NEXT ccc_cart_dev_stat_p IN temp_detailed_status_p;
          tape_pp_response_p^.ccc_cart_device_status := ccc_cart_dev_stat_p^;

{ Move sense bytes and error log data to wired response area if they are included in the response.

          IF pp_response_header_p^.response_length > ioc$min_ccc_cart_resp_size THEN
            NEXT ccc_cart_sense_p IN temp_detailed_status_p;
            tape_pp_response_p^.ccc_cart_sense_bytes := ccc_cart_sense_p^;
            IF pp_response_header_p^.response_length = ioc$max_ccc_cart_resp_size THEN
              NEXT ccc_cart_log_p IN temp_detailed_status_p;
              tape_pp_response_p^.ccc_cart_error_log := ccc_cart_log_p^;
            IFEND;
          IFEND;

        IFEND;

{ Unlock pages for data transfer request.

        io_type := tape_request_p^.io_type;
        cart_tape_write :=
              (cmv$logical_pp_table_p^ [pp_number].controller_info.controller_type = cmc$mt5680_xx) AND
                  (io_type = ioc$explicit_write);
        IF ((io_type = ioc$explicit_read) OR (io_type = ioc$explicit_write)) AND
              (tape_request_p^.data_pages_locked) THEN
          list_p := tape_request_p^.list_p;
          address_pair_count := tape_request_p^.address_pair_count;
          IF address_pair_count > 0 THEN
            IF (io_type = ioc$explicit_read) AND NOT tape_request_p^.cache_purge_required_data THEN
              unlock_io_type := ioc$explicit_read_no_purge;
            ELSE
              unlock_io_type := io_type;
            IFEND;
            IF io_type = ioc$explicit_read THEN
              address_pair_count := address_pair_count - 1;
            IFEND;
            mmp$unlock_rma_list (unlock_io_type, list_p, address_pair_count, ioid,
                  {MF_JOB_FILE} FALSE, io_error, mon_status);
            IF NOT mon_status.normal THEN
              mtp$error_stop ('IOT1 - abnormal unlock status');
            IFEND;
            IF io_type = ioc$explicit_read THEN { unlock transfer count buffer(s) page
              list_p := #LOC (tape_request_p^.wired_command_heap_p^.rma_list [address_pair_count + 1]);
              IF NOT tape_request_p^.cache_purge_required_length THEN
                unlock_io_type := ioc$explicit_read_no_purge;
              ELSE
                unlock_io_type := io_type;
              IFEND;
              mmp$unlock_rma_list (unlock_io_type, list_p, 1, ioid, {MF_JOB_FILE} FALSE,
                    io_error, mon_status);
              IF NOT mon_status.normal THEN
                mtp$error_stop ('IOT1 - abnormal unlock status');
              IFEND;
            ELSEIF cart_tape_write THEN
              iov$tape_completion_q_table^ [tape_request_p^.completion_q_index].cart_writes_pending :=
                    iov$tape_completion_q_table^ [tape_request_p^.completion_q_index].cart_writes_pending - 1;
              IF tape_request_p^.ijle_p^.active_cart_tape_write > 0 THEN
                tape_request_p^.ijle_p^.active_cart_tape_write :=
                      tape_request_p^.ijle_p^.active_cart_tape_write - 1;
              IFEND;
            IFEND;
          IFEND;
        IFEND;

{ Check for disable of unit by PP and unlock pages of any pending requests.

        IF tape_pp_response_p^.pp_response.alert_conditions.disabled_unit THEN
           p_unit_table := cmv$logical_unit_table^ [logical_unit].unit_interface_table;
           IF p_unit_table^.next_request <> NIL THEN
             p_next_request := p_unit_table^.next_request;
             temp_tape_request_p := p_next_request^.device_request_p;
            /pending_request_pages_unlocked/
             FOR j := 1 TO ioc$max_multiple_tape_requests DO
               io_type := temp_tape_request_p^.io_type;
               IF ((io_type = ioc$explicit_read) OR (io_type = ioc$explicit_write)) AND
                     (temp_tape_request_p^.data_pages_locked) THEN
                 list_p := temp_tape_request_p^.list_p;
                 address_pair_count := temp_tape_request_p^.address_pair_count;
                 IF address_pair_count > 0 THEN
                   IF io_type = ioc$explicit_read THEN
                     address_pair_count := address_pair_count - 1;
                   IFEND;
                   mmp$unlock_rma_list (ioc$no_io, list_p, address_pair_count, ioid,
                         {MF_JOB_FILE} FALSE, io_error, mon_status);
                   IF NOT mon_status.normal THEN
                     mtp$error_stop ('IOT1 - abnormal unlock status');
                   IFEND;
                   IF io_type = ioc$explicit_read THEN { unlock transfer count buffer(s) page
                     list_p := #LOC (temp_tape_request_p^.wired_command_heap_p^.rma_list
                           [address_pair_count + 1]);
                     mmp$unlock_rma_list (ioc$no_io, list_p, 1, ioid, {MF_JOB_FILE} FALSE,
                           io_error, mon_status);
                     IF NOT mon_status.normal THEN
                       mtp$error_stop ('IOT1 - abnormal unlock status');
                     IFEND;
                   ELSEIF cart_tape_write THEN
                     iov$tape_completion_q_table^ [temp_tape_request_p^.completion_q_index].
                           cart_writes_pending :=  iov$tape_completion_q_table^
                           [temp_tape_request_p^.completion_q_index].cart_writes_pending - 1;
                     IF (temp_tape_request_p^.ijle_p^.active_cart_tape_write > 0) THEN
                       temp_tape_request_p^.ijle_p^.active_cart_tape_write :=
                             temp_tape_request_p^.ijle_p^.active_cart_tape_write - 1;
                      IFEND;
                   IFEND;
                 IFEND;
               IFEND;
               p_next_request := temp_tape_request_p^.request.next_pp_request;
               IF p_next_request = NIL THEN
                  EXIT /pending_request_pages_unlocked/
               IFEND;
               temp_tape_request_p := p_next_request^.device_request_p;
             FOREND /pending_request_pages_unlocked/
           IFEND;
        IFEND;

{ Set waiting response in iov$tape_completion_q_table.

        IF completion_q_table_p^.io_id = io_id THEN
          completion_q_table_p^.waiting_response := TRUE;
          IF tape_request_p^.ready_task THEN
            tmp$set_task_ready (tape_request_p^.task_id, 0, tmc$rc_ready_conditional_wi);
          IFEND;
        ELSE
          msg_recorded := FALSE;
          message_record.message_type := cml$system_informative_message;
          message_record.message := 'Invalid tape io_id encountered';
          message_data := #SEQ(message_record);
          dsp$report_system_message (message_data, dsc$general_system_message,
                dsc$informative_message, msg_recorded);
          msg1 := 'Invalid io_id encountered';
          msg2 := ',lun = ';
          dpp$convert_int_to_str_dec (2, logical_unit, msg2 (8, *));
          msg3 := ' pp# ';
          dpp$convert_int_to_str_dec (2, pp_number, msg3 (8, *));
          dpp$display_error (msg1);
          dpp$display_error (msg2);
          dpp$display_error (msg3);
         RETURN;
        IFEND;

{ Check for unsolicited response.

      ELSEIF (pp_response_header_p^.response_code.primary_response =
          ioc$unsolicited_response) THEN
        IF cmv$logical_pp_table_p^ [pp_number].flags.configured = FALSE THEN
          RETURN;
        IFEND;

        first_logical_unit := cmv$logical_pp_table_p^ [pp_number].pp_info.pp_interface_table_p^.
              first_logical_unit;

        channel.number := cmv$logical_pp_table_p^ [pp_number].pp_info.pp_interface_table_p^.
             unit_descriptors [first_logical_unit].physical_path.channel_number;

        iou := cmv$logical_pp_table_p^ [pp_number].pp_info.channel.iou_number;

        unit_type := cmv$logical_pp_table_p^ [pp_number].pp_info.pp_interface_table_p^.
              unit_descriptors [first_logical_unit].unit_interface_table^.unit_type;

        IF channel.number > 9 THEN
          channel_length := 2;
        ELSE
          channel_length := 1;
        IFEND;

        IF cmv$logical_pp_table_p^ [pp_number].pp_info.channel_interlock_p^.
              channel_characteristics [channel.number].concurrent_channel THEN
          msg2 := 'IOU   CCH  ';
          dpp$convert_int_to_str_dec (channel_length, channel.number, msg2 (10, *));
          channel.concurrent := TRUE;
          IF (cmv$logical_pp_table_p^ [pp_number].controller_info.controller_type = cmc$mt5698_xx) THEN
            IF cmv$logical_pp_table_p^ [pp_number].pp_info.pp_interface_table_p^.
                  unit_descriptors [first_logical_unit].physical_path.port = 0 THEN
              channel.port := cmc$port_a;
            ELSE
              channel.port := cmc$port_b;
            IFEND;
          ELSE
            channel.port := cmc$unspecified_port;
          IFEND;
        ELSE
          msg2 := 'IOU   CH  ';
          dpp$convert_int_to_str_dec (channel_length, channel.number, msg2 (9, *));
          channel.concurrent := FALSE;
          channel.port := cmc$unspecified_port;
        IFEND;
        dpp$convert_int_to_str_dec (1, iou, msg2 (4, *));

{ Must use redefined sequence pointer (temp_detailed_status_p) because the original pointer
{  is not passed in as a VAR and cannot be used to RESET the detailed status Sequence Structure.

        temp_detailed_status_p := detailed_status_p;
        RESET temp_detailed_status_p;

{ Move the sequence pointer by the block_id response area if necessary.

        IF NOT (cmv$logical_pp_table_p^ [pp_number].controller_info.controller_type = cmc$mt5680_xx) THEN
          NEXT block_id_status_area_p IN temp_detailed_status_p;
        IFEND;

{ Process IPI unsolicited response.

        IF (cmv$logical_pp_table_p^ [pp_number].controller_info.controller_type = cmc$mt5698_xx) THEN

          tape_log_data.unit_type := ioc$ipi_reel;
          NEXT ipi_status_p IN temp_detailed_status_p;

{ The following code that issues a message to the critical window is disabled.  It is
{ only to be used for debugging purposes.

          IF iov$display_microcode_load THEN
            msg1 := '5698_1X CONTROLLER ERROR';
            msg3 := 'ERROR_ID =     (10)';
            dpp$convert_int_to_str_dec (4, ipi_status_p^.error_id, msg3 (12, *));
            dpp$display_error (msg1);
            dpp$display_error (msg2);
            dpp$display_error (msg3);
          IFEND;

          IF osv$boot THEN   { do not save logical unit number if in boot
            tape_log_data.logical_unit := 0;
          ELSE
            tape_log_data.logical_unit := pp_response_header_p^.logical_unit;
          IFEND;

          tape_log_data.iou_number := iou;
          tape_log_data.response_length := pp_response_header_p^.response_length;
          tape_log_data.channel := channel;
          tape_log_data.interface_error_code := pp_response_header_p^.interface_error_code;

          IF (tape_log_data.response_length <= ioc$min_ipi_total_resp_size) THEN
            IF tape_log_data.logical_unit = 0 THEN
              tape_log_data.controller_number := cmv$logical_pp_table_p^ [pp_number].
                  pp_info.pp_interface_table_p^.unit_descriptors [first_logical_unit].
                  physical_path.controller_number;
              tape_log_data.unit_number := 0;
            ELSE
              tape_log_data.controller_number := cmv$logical_pp_table_p^ [pp_number].pp_info.
                    pp_interface_table_p^.unit_descriptors [tape_log_data.logical_unit].
                    physical_path.controller_number;
              tape_log_data.unit_number := cmv$logical_pp_table_p^ [pp_number].pp_info.
                    pp_interface_table_p^.unit_descriptors [tape_log_data.logical_unit].
                    physical_path.physical_unit_number;
            IFEND;

          ELSE  {use slave,facility address from major status

            tape_log_data.controller_number := ipi_status_p^.major_status_header.slave_address;
            IF ipi_status_p^.major_status_header.facility_address = 0ff(16) THEN
              tape_log_data.unit_number := 0;
            ELSE
              tape_log_data.unit_number := ipi_status_p^.major_status_header.facility_address;
            IFEND;
          IFEND;

          ipi_status_length := pp_response_header_p^.response_length -
                (ioc$min_response_length + ioc$bid_area_size);
          IF ipi_status_length > 0 THEN
            ipi_source_p := ipi_status_p;
            ipi_dest_p := ^tape_log_data.ipi_status;
            i#move (ipi_source_p, ipi_dest_p, ipi_status_length);
          IFEND;

          tape_log_seq_p := #SEQ (tape_log_data);
          RESET tape_log_seq_p;
          NEXT seq_p: [[REP (ipi_status_length + ioc$length_to_ipi_status) of cell]] IN tape_log_seq_p;
          RESET seq_p;

          dsp$report_system_message (seq_p, dsc$tape_errors, dsc$unrecovered_error, msg_recorded);
          RETURN;

        ELSEIF (cmv$logical_pp_table_p^ [pp_number].controller_info.controller_type = cmc$mt5680_xx) THEN

          NEXT ccc_cart_dev_stat_p IN temp_detailed_status_p;

          IF (ccc_cart_dev_stat_p^.error_id = ioc$ccc_cart_no_pp_eid) OR
                osv$boot_is_executing THEN { do not log good load or error during boot
            RESET temp_detailed_status_p;
          ELSE
            tape_log_data.unit_type := ioc$ccc_cart;
            tape_log_data.logical_unit := 0;
            tape_log_data.iou_number := iou;
            tape_log_data.response_length := pp_response_header_p^.response_length;
            tape_log_data.channel := channel;
            tape_log_data.interface_error_code := pp_response_header_p^.interface_error_code;
            tape_log_data.unit_number := 0;
            tape_log_data.controller_number := cmv$logical_pp_table_p^ [pp_number].
                pp_info.pp_interface_table_p^.unit_descriptors [first_logical_unit].
                physical_path.controller_number;
            tape_log_data.ccc_cart_status := ccc_cart_dev_stat_p^;
            tape_log_seq_p := #SEQ (tape_log_data);
            RESET tape_log_seq_p;
            NEXT seq_p: [[REP (ioc$ccc_cart_device_status_size + ioc$length_to_ipi_status) of cell]]
                  IN tape_log_seq_p;
            RESET seq_p;

            dsp$report_system_message (seq_p, dsc$tape_errors, dsc$unrecovered_error, msg_recorded);
            IF (ccc_cart_dev_stat_p^.error_id = ioc$ccc_cart_microcode_load) THEN
              RESET temp_detailed_status_p;
            ELSE
              RETURN; { error is not microcode load error
            IFEND;
          IFEND;
        IFEND;

{ Pick up the first 16 bits of status.  For ISMT/698/5680 it is general status.

        NEXT status_code_p IN temp_detailed_status_p;
        error_status := status_code_p^;

{ Process microcode load response for ISMT, 698 and 5680.
{ Check the status returned by the hardware after the controlware/microcode is loaded.
{ For 5680, a critical window message is issued only if abnormal load status is
{ returned during deadstart. The variable iov$display_microcode_load is only for
{ internal use.

        IF (unit_type = ioc$dt_mt5682_1x) THEN
          IF error_status = 200(16) THEN
            iov$good_cart_microcode_load := iov$good_cart_microcode_load + 1;
          ELSE
            iov$bad_cart_microcode_load := iov$bad_cart_microcode_load + 1;
          IFEND;
          IF NOT iov$display_microcode_load AND NOT osv$boot THEN
            RETURN;  {do not issue any critical window message for 5680_11 if not during deadstart
          IFEND;
        IFEND;

        IF error_status = 0ffc(16) THEN
          msg1 := 'FUNCTION TIMEOUT ON CY170 DMA ADAPTER';
          dpp$display_error (msg1);
          dpp$display_error (msg2);
          RETURN;
        IFEND;

        IF unit_type = ioc$dt_mt639_1 THEN
          msg1 := 'ISMT MICROCODE LOAD ERROR';
        ELSEIF unit_type = ioc$dt_mt698_3x THEN
          msg1 := '698_XX MICROCODE LOAD ERROR';
        ELSE { 5680_11
          msg1 := '5680_11 MICROCODE LOAD ERROR';
        IFEND;

        IF error_status = 200(16) THEN
          IF unit_type = ioc$dt_mt639_1 THEN
            msg1 := 'ISMT MICROCODE LOADED';
          ELSEIF unit_type = ioc$dt_mt698_3x THEN
            msg1 := '698_XX MICROCODE LOADED';
          ELSE
            msg1 := '5680_11 MICROCODE LOADED';
            IF NOT iov$display_microcode_load THEN
              RETURN;  { do not issue good load message for 5680_11
            IFEND;
          IFEND;
          dpp$display_error (msg1);
          dpp$display_error (msg2);
        ELSEIF error_status = 0fff(16) THEN
          msg3 := 'FUNCTION TIMEOUT ON AUTOLOAD FUNCTION';
          dpp$display_error (msg1);
          dpp$display_error (msg2);
          dpp$display_error (msg3);
        ELSEIF error_status = 0ffe(16) THEN
          msg3 := 'CHANNEL PARITY ERROR ON AUTOLOAD FUNCTION';
          dpp$display_error (msg1);
          dpp$display_error (msg2);
          dpp$display_error (msg3);
        ELSE
          msg3 := 'GENERAL_STATUS=    ';
          dpp$convert_int_to_str_octal (4, error_status, msg3 (16, *));
          dpp$display_error (msg1);
          dpp$display_error (msg2);
          dpp$display_error (msg3);
        IFEND;
      IFEND;
    END
  PROCEND iop$tape_process_pp_response;

?? OLDTITLE ??
?? NEWTITLE := ' iop$tape_queue_request ' ??
?? EJECT ??

  PROCEDURE [XDCL, #GATE] iop$tape_queue_request (VAR request_block:
    iot$tape_request_block);

    VAR
      actual_lock: iot$lockword,
      address_pair_count: 0 .. mmc$max_rma_list_length,
      completion_q_table_p: ^iot$tape_completion_entry,
      dmv$external_interrupt_selector: [XREF] 0 .. 0ff(16),
      initial_lock: [STATIC] iot$lockword := [FALSE, 0, [FALSE, 0, 0]],
      io_error: iot$io_error,
      io_type: iot$io_function,
      ioid: mmt$io_identifier,
      iov$tape_unit_q_lock_rejects: [XDCL] integer := 0,
      iov$max_tape_unit_q_lock_wait: [XDCL] integer := 0,
      iov$page_frame_not_assigned: [XDCL] integer := 0,
{     iov$tape_requests: [XDCL] array [1 .. 11] of iot$wired_tape_request,
      ii: [STATIC] integer := 1,


      n: iot$tape_command_index,
      new_lock: [STATIC] iot$lockword := [TRUE, 0, [TRUE, FALSE, 0, 0]],
      new_lock2: [STATIC] iot$lockword := [TRUE, 8000(16), [TRUE, FALSE, 0, 0]],
      new_time: integer,
      old_time: integer,
      p_unit_table: ^iot$unit_interface_table,
      p_next_request: ^iot$io_request,
      p_previous_request: ^iot$wired_tape_request,
      p_lockword: ^iot$lockword,
      rma: integer,
      request_header: ^iot$wired_tape_request,
      result: 0 .. 2,
      sen: iot$logical_unit,
      status: syt$monitor_status;

    BEGIN
      request_block.status.normal := TRUE;
      status.normal := TRUE;

      request_block.io_request_p^.response_processor_p :=
            ^iop$tape_process_pp_response;
      request_header := request_block.io_request_p^.device_request_p;
      io_type := request_header^.io_type;
      io_error := ioc$no_error;
      IF dmv$external_interrupt_selector = 1 THEN
        request_header^.request.interrupt.value := TRUE;
      ELSE
        request_header^.request.interrupt.value := FALSE;
      IFEND;
      request_header^.request.interrupt.port_number := osv$external_interrupt_selector;
      sen := request_header^.request.logical_unit;
      p_unit_table := cmv$logical_unit_table^ [sen].unit_interface_table;

{ Check for unit_disabled status to turn back request unless the request is being requeued
{ from recovery.  IF the requeue is from recovery and the unit is disabled, do not lock
{ the data pages for the read or write.

      IF p_unit_table^.unit_status.disabled THEN
        IF NOT request_header^.recovery_requeue THEN
          request_block.status.normal := FALSE;
          request_block.status.condition := ioe$tape_unit_disabled;
          RETURN;
        ELSE {request is from recovery requeue}
          IF (io_type = ioc$explicit_read) OR (io_type = ioc$explicit_write) THEN
            request_header^.data_pages_locked := FALSE;
          ELSE
            mtp$error_stop ('IO05 - Invalid tape requeue');
            RETURN;
          IFEND;
        IFEND;
      IFEND;

{Check for empty io_id slot in completion queue table.  If not empty, halt system.

      completion_q_table_p := request_block.io_request_p^.pp_request_p;

      IF completion_q_table_p^.io_id <> 0 THEN
        mtp$error_stop ('IO05 - No request slot found when queueing request.');
      IFEND;

{ Build RMA list and lock pages.

      IF ((io_type = ioc$explicit_read) OR (io_type = ioc$explicit_write)) AND
            (request_header^.data_pages_locked) THEN
        mmp$build_lock_rma_list_tape (request_header, status);
        IF NOT status.normal THEN
          request_block.status := status;
          IF status.condition = mme$page_frame_not_assigned THEN
            iov$page_frame_not_assigned := iov$page_frame_not_assigned + 1;
          IFEND;
          RETURN;
        IFEND;

{ Complete setting up request for reads of more than 1 block.  The remainder of the
{ transfer count buffers must be set up.  The transfer count buffers for read are always
{ in the same memory page.

        IF (io_type = ioc$explicit_read) AND (request_header^.no_of_data_commands > 1) THEN
          rma := request_header^.list_p^ [request_header^.address_pair_count].rma;
          FOR n := 2 TO request_header^.no_of_data_commands DO
            request_header^.request.tape_command [n * 2 + 1].address :=
                  rma + (#OFFSET (request_header^.wired_read_description_p^ [n].block_transfer_length) -
                  #OFFSET (request_header^.wired_read_description_p^ [1].block_transfer_length));
          FOREND;
        ELSEIF (request_header^.pp_response_p^.controller_type = cmc$mt5680_xx) AND
              (io_type = ioc$explicit_write) THEN
          iov$tape_completion_q_table^ [request_header^.completion_q_index].cart_writes_pending :=
                iov$tape_completion_q_table^ [request_header^.completion_q_index].cart_writes_pending + 1;
          request_header^.ijle_p^.active_cart_tape_write :=
                request_header^.ijle_p^.active_cart_tape_write + 1;
        IFEND;
      IFEND;

{ Set unit queue lockword. }

      p_lockword := ^cmv$logical_unit_table^ [sen].unit_interface_table^.unit_q_lockword;
      old_time := #free_running_clock (0);

   /set_lock/
      WHILE TRUE DO
        #compare_swap (p_lockword^, initial_lock, new_lock, actual_lock,
              result);
        IF result = 0 THEN
          EXIT /set_lock/;
        IFEND;
        new_time := #free_running_clock (0);
        IF new_time < old_time + 5000 THEN

{ Keep statistics on number of unit queue lock rejects and maximum wait in microseconds.

          IF iov$tape_unit_q_lock_rejects < 7fffffffffffffff(16) THEN
            iov$tape_unit_q_lock_rejects := iov$tape_unit_q_lock_rejects + 1;
            IF new_time - old_time > iov$max_tape_unit_q_lock_wait THEN
              iov$max_tape_unit_q_lock_wait := new_time - old_time;
            IFEND;
          IFEND;
        ELSE { timeout of 5 milliseconds occurred
          request_block.status.normal := FALSE;
          request_block.status.condition := ioe$tape_pp_q_locked;
          IF ((io_type = ioc$explicit_read) OR (io_type = ioc$explicit_write)) AND
                (request_header^.data_pages_locked) THEN  { unlock locked pages
            address_pair_count := request_header^.address_pair_count;
            IF io_type = ioc$explicit_read THEN
              address_pair_count := address_pair_count - 1;
            IFEND;
            mmp$unlock_rma_list (ioc$no_io, request_header^.list_p, address_pair_count,
                  ioid, {MF_JOB_FILE} FALSE, io_error, status);
            IF io_type = ioc$explicit_read THEN { unlock transfer count buffer(s) page
              mmp$unlock_rma_list (ioc$no_io, #LOC (request_header^.wired_command_heap_p^.rma_list
                    [address_pair_count + 1]), 1, ioid, {MF_JOB_FILE} FALSE, io_error, status);
            ELSEIF (request_header^.pp_response_p^.controller_type = cmc$mt5680_xx) AND
                  (io_type = ioc$explicit_write) THEN
              iov$tape_completion_q_table^ [request_header^.completion_q_index].cart_writes_pending :=
                    iov$tape_completion_q_table^ [request_header^.completion_q_index].cart_writes_pending - 1;
              request_header^.ijle_p^.active_cart_tape_write :=
                    request_header^.ijle_p^.active_cart_tape_write - 1;
            IFEND;
          IFEND;
          RETURN;
        IFEND;
      WHILEND /set_lock/;

{ Place request in queue. }

      i#real_memory_address (^request_header^.request, rma);
      IF p_unit_table^.next_request = NIL THEN
        p_unit_table^.next_request := request_block.io_request_p;
        p_unit_table^.next_request_rma := rma;
      ELSE
        p_next_request := p_unit_table^.next_request;
        REPEAT
          p_previous_request := p_next_request^.device_request_p;
          p_next_request := p_previous_request^.request.next_pp_request;
        UNTIL p_next_request = NIL;
        p_previous_request^.request.next_pp_request := request_block.io_request_p;
        p_previous_request^.request.next_pp_request_rma := rma;
      IFEND;


{ Temporary save of requests for debugging.
      iov$out_ptr := ii;
      iov$tape_out_track [ii].id := request_header^.io_id;
      iov$tape_out_track [ii].time := #free_running_clock(0);
      iov$tape_out_track [ii].lun := sen;

      IF ii < 256 THEN
        ii := ii + 1;
      ELSE
        ii := 1;
      IFEND;


{ Clear unit queue lockword. }

      REPEAT
        #compare_swap (p_lockword^, new_lock, initial_lock,
              actual_lock, result);
      UNTIL result <> 2;
      IF result <> 0 THEN
        REPEAT
          #compare_swap (p_lockword^, new_lock2, initial_lock,
                actual_lock, result);
        UNTIL result <> 2;
        IF result <> 0 THEN
          mtp$error_stop ('IO05 - invalid unit queue lockword.');
        IFEND;
      IFEND;

{ Place io_id and pointer to iot$io_request in completion queue packet.

      completion_q_table_p^.check_task_id := FALSE;
      completion_q_table_p^.io_id := request_header^.io_id;
      completion_q_table_p^.io_request := request_block.io_request_p;

{ Temporary save of requests for debugging.
{     iov$tape_requests_p := ^iov$tape_requests [ii];
{     iov$tape_requests [ii] := request_header^;
{     IF ii < 11 THEN
{       ii := ii + 1;
{     ELSE
{       ii := 1;
{     IFEND;
{ End temporary code.
    END
  PROCEND iop$tape_queue_request;
?? EJECT ??

MODEND iom$tape_queue_manager_mtr;
