?? RIGHT := 110 ??
*copyc osd$default_pragmats
?? NEWTITLE := 'NOS/VE Device Management' ??
?? NEWTITLE := '  Module Header' ??
MODULE dmm$device_flaw_management;

{
{  PURPOSE:
{
{    This module contains the code which is responsible for the definition,
{    display, and removal of software flaws on the devices maintained by
{    device management.
{
{  DESIGN:
{
{    The commands for flawing are entered in LCU and after processing in
{    this module, the correct device log is updated by the logger.
{
?? PUSH (LISTEXT := ON) ??
?? TITLE := 'XREF procedures', EJECT ??
*copyc dmp$close_dat_r3
*copyc dmp$convert_to_dau_address
*copyc dmp$get_logical_unit_number
*copyc dmp$get_physical_attributes
*copyc dmp$open_dat_r3
*copyc dmp$process_manual_flaw
*copyc dmp$store_sc_flaw_command
*copyc cmp$pc_get_logical_unit
*copyc dsp$log_system_message
*copyc osp$set_status_abnormal
?? TITLE := 'Global type declarations', EJECT ??
*copyc cml$ms_media_flaw_change
*copyc cmt$element_definition
*copyc cmt$product_identification
*copyc dmt$device_allocation_unit
*copyc dmt$error_condition_codes
*copyc dmt$log_flaw_init_data
*copyc dmt$flaw_dau_definition
*copyc dmt$flaw_duplication
*copyc dmt$physical_device_attributes
*copyc dmt$sc_flaw_command
*copyc iot$cylinder
*copyc iot$logical_unit
*copyc ost$status
*copyc rmt$recorded_vsn
?? POP ??
?? TITLE := '  dmp$define_remove_ms_flaw', EJECT ??
  PROCEDURE [XDCL, #GATE] dmp$define_remove_ms_flaw (recorded_vsn: rmt$recorded_vsn;
        p_phys_adrs: ^dmt$physical_flaw_address;
        trk_specified: boolean;
        sec_specified: boolean;
        operation_code: dmt$flaw_operation_code;
        initiator_code: dmt$flaw_initiator_code;
    VAR status: ost$status);

{   PURPOSE:
{     This procedure is the 'executive procedure' called by LCU whenever a flaw
{     is to be defined or removed.  It will call all other procedures needed to
{     execute the command, and will control the operation.

    VAR
      dau_address: dmt$dau_address,
      end_dau_address: dmt$dau_address,
      flaw_logging_data: dmt$log_flaw_init_data,
      ignore_status: ost$status,
      p_dat: ^dmt$ms_device_allocation_table,
      p_dat_entry: ^dmt$ms_device_allocation_unit,
      p_log_data: ^SEQ (*),
      p_sc_flaw: ^dmt$sc_flaw_command,
      previous_status: dmt$dau_status;

    dmp$convert_to_dau_address (recorded_vsn, p_phys_adrs, trk_specified, sec_specified, dau_address,
       end_dau_address, status);
    IF status.normal THEN

  { Save information to be used when statistics are transmitted.

      flaw_logging_data.recorded_vsn := recorded_vsn;
      flaw_logging_data.first_dau := dau_address;
      flaw_logging_data.last_dau := end_dau_address;
      flaw_logging_data.operation_code := operation_code;
      flaw_logging_data.initiator_code := initiator_code;

  { If only one DAU is to be altered, then check the DAU's status before changing.
  { Return status if the change can not be performed.

      IF dau_address = end_dau_address THEN
        dmp$open_dat_r3 (recorded_vsn, p_dat, status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;

        p_dat_entry := ^p_dat^.body [dau_address];
        previous_status := p_dat_entry^.dau_status;

        dmp$close_dat_r3 (p_dat, status);
        IF NOT status.normal THEN
          RETURN;
        IFEND;

        IF operation_code = dmc$oc_flaw_define THEN
          IF (previous_status = dmc$dau_software_flawed) OR (previous_status = dmc$dau_ass_to_mf_swr_flawed)
                 OR (previous_status = dmc$dau_ass_to_file_swr_flawed) OR
                 (previous_status = dmc$dau_hardware_flawed) THEN
            osp$set_status_abnormal (dmc$device_manager_ident, dme$address_already_flawed,
               'dmp$define_remove_ms_flaw', status);
            RETURN;
          IFEND;
        ELSEIF operation_code = dmc$oc_flaw_remove THEN
          IF ((previous_status <> dmc$dau_software_flawed)
                 AND (previous_status <> dmc$dau_ass_to_mf_swr_flawed)
                 AND (previous_status <> dmc$dau_ass_to_file_swr_flawed)) THEN
            osp$set_status_abnormal (dmc$device_manager_ident, dme$address_not_sw_flawed,
               'dmp$define_remove_ms_flaw', status);
            RETURN;
          IFEND;
        IFEND;
      IFEND;

  { Call the logger procedure to change the DAU status to flawed.

      dmp$process_manual_flaw (recorded_vsn, dau_address, end_dau_address, operation_code, status);
      IF status.normal THEN

  { Issue statistics about flaw to the engineering log.

        p_log_data := #SEQ (flaw_logging_data);
        dsp$log_system_message (cml$ms_media_flaw_change, p_log_data, ignore_status);
      IFEND;
    IFEND;

    IF NOT status.normal THEN

      CASE status.condition OF

        = dme$logging_unavailable, dme$recorded_vsn_not_in_lun =
          IF (operation_code = dmc$oc_flaw_define) AND (initiator_code = dmc$ic_operator_initiated) THEN
            osp$set_status_abnormal (dmc$device_manager_ident, dme$flawing_deferred,
               'dmp$define_remove_ms_flaw', status);
            PUSH p_sc_flaw;
            p_sc_flaw^.rvsn := recorded_vsn;
            p_sc_flaw^.phys_adrs.cylinder := p_phys_adrs^.cylinder;
            p_sc_flaw^.phys_adrs.track := p_phys_adrs^.track;
            p_sc_flaw^.phys_adrs.sector := p_phys_adrs^.sector;
            p_sc_flaw^.trk_specified := trk_specified;
            p_sc_flaw^.sec_specified := sec_specified;
            p_sc_flaw^.flaw_processed := FALSE;
            dmp$store_sc_flaw_command (p_sc_flaw);
          IFEND;

      ELSE
      CASEND;
    IFEND;

  PROCEND dmp$define_remove_ms_flaw;
?? TITLE := '  dmp$identify_flawed_daus', EJECT ??
  PROCEDURE [XDCL, #GATE] dmp$identify_flawed_daus (recorded_vsn: rmt$recorded_vsn;
        p_flaw_dau_definition: ^array [1 .. *] of dmt$flaw_dau_definition;
        p_flaw_duplication: ^array [1 .. *] of dmt$flaw_duplication;
    VAR big_enough_array: boolean;
    VAR status: ost$status);

{   PURPOSE:
{     This procedure will open and scan the DAT.  It will collect information in
{     an array about all DAU's that have a current flaw status.  An entry will
{     contain the first to last DAU if a consecutive number of DAUs are flawed.
{     However, if a consecutive number of DAUs are flawed with hardware and
{     software status, only the range of DAUs with hardware or software status
{     will be included in each entry.  It is the 'executive' procedure called
{     by the LCU command display_ms_flaw.

    VAR
      dat_index: dmt$dau_address,
      daus_per_cyl: integer,
      i: integer,
      p_dat: ^dmt$ms_device_allocation_table,
      p_dat_entry: ^dmt$ms_device_allocation_unit,
      p_physical_attributes: ^dmt$physical_device_attributes,
      previous_status: dmt$dau_status,
      sector_offset_within_cylinder: integer;

    big_enough_array := FALSE;
    i := LOWERBOUND (p_flaw_dau_definition^) - 1;
    previous_status := dmc$dau_usable;

    PUSH p_physical_attributes: [1 .. 4];
    p_physical_attributes^[1].keyword := dmc$maus_per_cylinder;
    p_physical_attributes^[2].keyword := dmc$sectors_per_mau;
    p_physical_attributes^[3].keyword := dmc$sectors_per_track;
    p_physical_attributes^[4].keyword := dmc$maus_per_dau;

    get_device_information (recorded_vsn, p_physical_attributes, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    daus_per_cyl := p_physical_attributes^[1].maus_per_cylinder DIV
       p_physical_attributes^[4].maus_per_dau;

    dmp$open_dat_r3 (recorded_vsn, p_dat, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    /locate_flawed_daus/
      FOR dat_index := 0 TO p_dat^.header.number_of_entries - 1 DO

        p_dat_entry := ^p_dat^.body [dat_index];

        CASE p_dat_entry^.dau_status OF

        = dmc$dau_software_flawed, dmc$dau_ass_to_mf_swr_flawed,
               dmc$dau_ass_to_file_swr_flawed =

          IF (previous_status = dmc$dau_software_flawed) OR (previous_status = dmc$dau_ass_to_mf_swr_flawed)
               OR (previous_status = dmc$dau_ass_to_file_swr_flawed) THEN

{ Set only last sector addresses.

            p_flaw_dau_definition^[i].last_dau := dat_index;
            p_flaw_dau_definition^[i].last.cylinder := dat_index DIV daus_per_cyl;
            sector_offset_within_cylinder := ((dat_index MOD daus_per_cyl) *
               p_physical_attributes^[2].sectors_per_mau * p_physical_attributes^[4].maus_per_dau) +
                  (p_physical_attributes^[4].maus_per_dau * p_physical_attributes^[2].sectors_per_mau) - 1;
            p_flaw_dau_definition^[i].last.track := sector_offset_within_cylinder DIV
               p_physical_attributes^[3].sectors_per_track;
            p_flaw_dau_definition^[i].last.sector := sector_offset_within_cylinder MOD
               p_physical_attributes^[3].sectors_per_track;
          ELSE

{ If the arrary is not big enough for all flaws, exit after closing the DAT with boolean value in
{ big_enough_array equal to false.

            i := i + 1;
            IF i > UPPERBOUND (p_flaw_dau_definition^) THEN
              dmp$close_dat_r3 (p_dat, status);
                RETURN;
            IFEND;

{ Set both first and last sector addresses and flaw type.

            p_flaw_dau_definition^[i].entry_initialized := TRUE;
            p_flaw_dau_definition^[i].first_dau := dat_index;
            p_flaw_dau_definition^[i].first.cylinder := dat_index DIV daus_per_cyl;
            sector_offset_within_cylinder := (dat_index MOD daus_per_cyl)
               * p_physical_attributes^[2].sectors_per_mau * p_physical_attributes^[4].maus_per_dau;
            p_flaw_dau_definition^[i].first.track := sector_offset_within_cylinder DIV
               p_physical_attributes^[3].sectors_per_track;
            p_flaw_dau_definition^[i].first.sector := sector_offset_within_cylinder MOD
               p_physical_attributes^[3].sectors_per_track;

            p_flaw_dau_definition^[i].last_dau := dat_index;
            p_flaw_dau_definition^[i].last.cylinder := dat_index DIV daus_per_cyl;
            sector_offset_within_cylinder := ((dat_index MOD daus_per_cyl)
               * p_physical_attributes^[2].sectors_per_mau * p_physical_attributes^[4].maus_per_dau) +
                   (p_physical_attributes^[4].maus_per_dau * p_physical_attributes^[2].sectors_per_mau) - 1;
            p_flaw_dau_definition^[i].last.track := sector_offset_within_cylinder DIV
               p_physical_attributes^[3].sectors_per_track;
            p_flaw_dau_definition^[i].last.sector := sector_offset_within_cylinder MOD
               p_physical_attributes^[3].sectors_per_track;
            p_flaw_dau_definition^[i].reserved := (p_dat_entry^.dau_status = dmc$dau_hardware_flawed)
          IFEND;
        = dmc$dau_hardware_flawed =
          IF previous_status = p_dat_entry^.dau_status THEN

{ Set only last sector addresses.

            p_flaw_dau_definition^[i].last_dau := dat_index;
            p_flaw_dau_definition^[i].last.cylinder := dat_index DIV daus_per_cyl;
            sector_offset_within_cylinder := ((dat_index MOD daus_per_cyl) *
               p_physical_attributes^[2].sectors_per_mau * p_physical_attributes^[4].maus_per_dau) +
                   (p_physical_attributes^[4].maus_per_dau * p_physical_attributes^[2].sectors_per_mau) - 1;
            p_flaw_dau_definition^[i].last.track := sector_offset_within_cylinder DIV
               p_physical_attributes^[3].sectors_per_track;
            p_flaw_dau_definition^[i].last.sector := sector_offset_within_cylinder MOD
               p_physical_attributes^[3].sectors_per_track;
          ELSE

{ If the arrary is not big enough for all flaws, exit after closing the DAT with boolean value in
{ big_enough_array equal to false.

            i := i + 1;
            IF i > UPPERBOUND (p_flaw_dau_definition^) THEN
              dmp$close_dat_r3 (p_dat, status);
              RETURN;
            IFEND;

{ Set both first and last sector addresses and flaw type.

            p_flaw_dau_definition^[i].entry_initialized := TRUE;
            p_flaw_dau_definition^[i].first_dau := dat_index;
            p_flaw_dau_definition^[i].first.cylinder := dat_index DIV daus_per_cyl;
            sector_offset_within_cylinder := (dat_index MOD daus_per_cyl)
               * p_physical_attributes^[2].sectors_per_mau * p_physical_attributes^[4].maus_per_dau;
            p_flaw_dau_definition^[i].first.track := sector_offset_within_cylinder DIV
               p_physical_attributes^[3].sectors_per_track;
            p_flaw_dau_definition^[i].first.sector := sector_offset_within_cylinder MOD
               p_physical_attributes^[3].sectors_per_track;

            p_flaw_dau_definition^[i].last_dau := dat_index;
            p_flaw_dau_definition^[i].last.cylinder := dat_index DIV daus_per_cyl;
            sector_offset_within_cylinder := ((dat_index MOD daus_per_cyl)
               * p_physical_attributes^[2].sectors_per_mau * p_physical_attributes^[4].maus_per_dau) +
                  (p_physical_attributes^[4].maus_per_dau * p_physical_attributes^[2].sectors_per_mau) - 1;
            p_flaw_dau_definition^[i].last.track := sector_offset_within_cylinder DIV
               p_physical_attributes^[3].sectors_per_track;
            p_flaw_dau_definition^[i].last.sector := sector_offset_within_cylinder MOD
               p_physical_attributes^[3].sectors_per_track;
            p_flaw_dau_definition^[i].reserved := (p_dat_entry^.dau_status = dmc$dau_hardware_flawed)
          IFEND;
        ELSE
        CASEND;
        previous_status := p_dat_entry^.dau_status;
      FOREND /locate_flawed_daus/;

      dmp$close_dat_r3 (p_dat, status);
      IF NOT status.normal THEN
        RETURN;
      IFEND;

{ Call procedure to create an array of LCU flaw commands needed to duplicate the flaws found.

      duplicate_flaw_commands (p_flaw_dau_definition, p_flaw_duplication, p_physical_attributes,
         big_enough_array, status)

  PROCEND dmp$identify_flawed_daus;
?? TITLE := ' duplicate_flaw_commands', EJECT ??
  PROCEDURE duplicate_flaw_commands (p_flaw_dau_definition: ^array [1 .. *] of dmt$flaw_dau_definition;
        p_flaw_duplication: ^array [1 .. *] of dmt$flaw_duplication;
        p_physical_attributes: ^dmt$physical_device_attributes;
    VAR big_enough_array: boolean;
    VAR status: ost$status);

{   PURPOSE:
{     This procedure, given an array of the DAU's that are flawed on a device,
{     will generate the define_ms_flaw commands needed by LCU to duplicate all
{     of the flaws if the device has been initialized.

    VAR
      daus_per_cyl: integer,
      first_dau: integer,
      i: integer,
      j: integer,
      last_dau: integer,
      phys_adrs: dmt$physical_flaw_address,
      sector_offset_within_cylinder: integer;

    big_enough_array := FALSE;
    j := LOWERBOUND (p_flaw_duplication^) - 1;

    daus_per_cyl := p_physical_attributes^[1].maus_per_cylinder DIV p_physical_attributes^[4].maus_per_dau;

    /small_array_exit/
    BEGIN

      /duplicate_flaws/
      FOR i := LOWERBOUND (p_flaw_dau_definition^) TO UPPERBOUND (p_flaw_dau_definition^) DO

        IF p_flaw_dau_definition^[i].entry_initialized = FALSE THEN
          EXIT /duplicate_flaws/;
        IFEND;

{ Skip all reserved flaws which are hardware, cip, and maintenance reserved space.

        IF p_flaw_dau_definition^[i].reserved = TRUE THEN
          CYCLE /duplicate_flaws/;
        IFEND;

        first_dau := (p_flaw_dau_definition^[i].first_dau);
        last_dau := (p_flaw_dau_definition^[i].last_dau);
        phys_adrs.cylinder := p_flaw_dau_definition^[i].first_dau DIV daus_per_cyl;
        sector_offset_within_cylinder := (first_dau MOD daus_per_cyl)
           * p_physical_attributes^[2].sectors_per_mau * p_physical_attributes^[4].maus_per_dau;
        phys_adrs.track := sector_offset_within_cylinder DIV p_physical_attributes^[3].sectors_per_track;
        phys_adrs.sector := sector_offset_within_cylinder MOD p_physical_attributes^[3].sectors_per_track;

        /build_flaws_for_entry/
        WHILE first_dau <= last_dau DO

{ Check if a sector flaw.

          /sector_flaw/
          WHILE (phys_adrs.sector <> 0) OR (phys_adrs.track <> 0) OR
                ((last_dau - first_dau + 1 ) < (daus_per_cyl)) DO

            j := j + 1;
            IF j > UPPERBOUND (p_flaw_duplication^) THEN
              EXIT /small_array_exit/;
            IFEND;

            p_flaw_duplication^[j].entry_initialized := TRUE;
            p_flaw_duplication^[j].cylinder := phys_adrs.cylinder;
            p_flaw_duplication^[j].track := phys_adrs.track;
            p_flaw_duplication^[j].track_specified := TRUE;
            p_flaw_duplication^[j].sector := phys_adrs.sector;
            p_flaw_duplication^[j].sector_specified := TRUE;

            first_dau := first_dau + 1;

            IF first_dau > last_dau THEN
              EXIT /build_flaws_for_entry/;
            IFEND;

            phys_adrs.cylinder := first_dau DIV daus_per_cyl;
            sector_offset_within_cylinder := (first_dau MOD daus_per_cyl)
                 * p_physical_attributes^[2].sectors_per_mau * p_physical_attributes^[4].maus_per_dau;
            phys_adrs.track := sector_offset_within_cylinder DIV p_physical_attributes^[3].sectors_per_track;
            phys_adrs.sector := sector_offset_within_cylinder MOD p_physical_attributes^[3].sectors_per_track;

          WHILEND /sector_flaw/;

{ Check if cylinder flaw.

          /cylinder_flaw/
          WHILE (last_dau - first_dau + 1 >= daus_per_cyl) DO

            j := j + 1;
            IF j > UPPERBOUND (p_flaw_duplication^) THEN
              EXIT /small_array_exit/;
            IFEND;

            p_flaw_duplication^[j].entry_initialized := TRUE;
            p_flaw_duplication^[j].cylinder := phys_adrs.cylinder;
            p_flaw_duplication^[j].track_specified := FALSE;
            p_flaw_duplication^[j].sector_specified := FALSE;

            first_dau := first_dau + daus_per_cyl;

            IF first_dau > last_dau THEN
              EXIT /build_flaws_for_entry/;
            IFEND;

            phys_adrs.cylinder := first_dau DIV daus_per_cyl;
            sector_offset_within_cylinder := (first_dau MOD daus_per_cyl)
               * p_physical_attributes^[2].sectors_per_mau * p_physical_attributes^[4].maus_per_dau;
            phys_adrs.track := sector_offset_within_cylinder DIV p_physical_attributes^[3].sectors_per_track;
            phys_adrs.sector := sector_offset_within_cylinder MOD p_physical_attributes^[3].sectors_per_track;

          WHILEND /cylinder_flaw/;

        WHILEND /build_flaws_for_entry/;

      FOREND /duplicate_flaws/;

      big_enough_array := TRUE;

    END /small_array_exit/;

  PROCEND duplicate_flaw_commands;

?? TITLE := '  get_device_information', EJECT ??
  PROCEDURE get_device_information (recorded_vsn: rmt$recorded_vsn;
    VAR p_physical_attributes: ^dmt$physical_device_attributes;
    VAR status: ost$status);

{   PURPOSE:
{     This procedure is called to get the physical attributes of the device ident-
{     ified by the volume's recorded_vsn needed to make calculations for a flaw
{     operation.

    VAR
      element_def: ^cmt$element_definition,
      lun: iot$logical_unit,
      product_id: cmt$product_identification;

    dmp$get_logical_unit_number (recorded_vsn, lun, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;

    PUSH element_def;

    cmp$pc_get_logical_unit (lun, element_def, status);
    IF NOT status.normal THEN
      RETURN;
    IFEND;
    product_id := element_def^.product_id;

    dmp$get_physical_attributes (product_id, p_physical_attributes, status);

  PROCEND get_device_information;
MODEND dmm$device_flaw_management;


