Пример #1
0
    def parse_ex(self, data, expandernum):
        report_phy_status_head = \
        (
         (  0   , 1*8, "int", "pc"         , "PAGE CODE"),
         (  2   , 2*8, "int", "length"     , "PAGE LENGTH"),
         )
        phy_status_descriptor = \
        (
         (  0   , 1*8, "int", "phyid"            , "PHY ID"),
         (  1   , 1*8, "int", "linkrate"         , "Negotiated Physical Link Rate"),
         (  4   , 4*8, "int", "rxerrors"         , "SAS2 Rx Error Count"),
         (  8   , 4*8, "int", "invaliddwords"    , "Invalid Dword Count"),
         ( 12   , 4*8, "int", "disparityerrors"  , "Disparity Error Count"),
         ( 16   , 4*8, "int", "lossdwordsynchs"  , "Loss of Dword Synchronization Count"),
         ( 20   , 4*8, "int", "resetsfailed"     , "PHY Reset Failed Count"),
         ( 24   , 4*8, "int", "codeviolations"   , "Code Violation Error Count"),
         ( 28   , 2*8, "int", "prbserrors"       , "PRBS Error Count"),
         ( 30   , 2*8, "int", "testpatternerrors", "Test Pattern Error Count"),
         ( 32   , 2*8, "int", "crcerrors"        , "CRC Error Count"),
         ( 34   , 2*8, "int", "iccrcerrors"      , "In-connection CRC Error Count"),
         ( 36   , 1*8, "int", "phychanges"       , "PHY Change Count Register"),
         ( 37   , 1*8, "int", "ssplmonitor1"     , "SSPL Monitor 1"),
         ( 38   , 1*8, "int", "ssplmonitor2"     , "SSPL Monitor 2"),
         ( 40   , 4*8, "int", "connectstatus"    , "SSPL Connection Status"),
         ( 44   , 4*8, "int", "interruptstatus"  , "SLICE_TOP:SSPL - Interrupt Status 0"),
         )
        negotiated_physical_link_rate = \
        {
         0x00:("UNKNOWN", "PHY is enabled: unknown physical link rate*"),
         0x01:("DISABLE", "PHY is disabled"),
         0x02:("PHY_RESET_PROBLEM", "PHY is enabled; the PHY obtained dword synchronization for at least one physical link rate during the SAS speed negotiation sequence, but the SAS speed negotiation sequence failed. These failures may be Logged in the SMP REPORT PHY ERROR LOG function and/or the Protocol-Specific Port Log page."),
         0x03:("SPINUP_HOLD", "PHY is enabled; detected a SATA device and entered the SATA spinup hold state. The LINK RESET and HARD RESET operations in the SMP PHY CONTROL function may be used to release the PHY. This field shall be updated to this value at SATA spinup hold time if SATA spinup hold is supported."),
         0x04:("PORT_SELECTOR", "PHY is enabled; detected a SATA port selector. The physical link rate has not been negotiated since the last time the phy's SP state machine entered the SP0:OOB_COMINIT state. The SATA spinup hold state has not been entered since the last time the phy's SP state machine entered the SP0:OOB_COMINIT state. The value in this field may change to 3h, 8h, or 9h if attached to the active PHY of the SATA port selector. Presence of a SATA port selector is indicated by the ATTACHED SATA PORT SELECTOR bit."),
         0x08:("G1", "PHY is enabled; 1.5 Gbps physical link rate. This field shall be updated to this value after the speed negotiation sequence completes."),
         0x09:("G2", "PHY is enabled; 3.0 Gbps physical link rate. This field shall be updated to 9h after the speed negotiation sequence completes."),
         0x0a:("G3", "PHY is enabled; 6.0 Gbps physical link rate. This field shall be updated to ah after the speed negotiation sequence completes."),
         0x0b:("G4", "PHY is enabled; 12.0 Gbps physical link rate. This field shall be updated to bh after the speed negotiation sequence completes."),
         }

        bo = 0  # byte offset
        head = Cmd.extract(data[bo:], report_phy_status_head, bo)
        bo += 4

        descriptors = []
        while bo < 2 + head.length.val:
            descriptors.append(
                Cmd.extract(data[bo:], phy_status_descriptor, bo))
            bo += 48

        head.append(
            Cmd.Field(descriptors, 4, "descriptors",
                      "PHY Status Descriptor List"), "descriptors")
        head.append(
            Cmd.Field(expandernum, -1, "expandernum", "Expander Number"),
            "expandernum")
        return head
Пример #2
0
    def parse_0e(self, data):
        download_microcode_head = \
        (
         (  0   , 1*8, "int", "pc"         , "page code"),
         (  1   , 1*8, "int", "secondaries", "number of secondary subenclosures"),
         (  2   , 2*8, "int", "length"     , "pagelength"),
         (  4   , 4*8, "int", "gen"        , "generation code"),
         (  8   , 0  , "str", "descriptors", "additional element descriptor list"),
         )
        descriptor_format = \
        (
         (  1   , 1*8, "int", "subid"            , "subenclosure identifier"),
         (  2   , 1*8, "int", "status"           , "subenclosure download microcode status"),
         (  3   , 1*8, "int", "additional_status", "subenclosure download microcode additional status"),
         (  4   , 4*8, "int", "maxsize"          , "subenclosure download microcode maximum size"),
         ( 11   , 1*8, "int", "expected_id"      , "subenclosure download microcode exected buffer id"),
         ( 12   , 4*8, "int", "expected_offset"  , "subenclosure download microcode expected buffer offset"),
         )
        status_text = \
        { # ses3r06.pdf table 52
         # Codes indicating interim status
         0x00: "No download microcode operation in progress.",
         0x01: "Download microcode operation in progress. The enclosure services process has received one or more Download Microcode Control diagnostic pages and is awaiting additional microcode data.",
         0x02: "Download microcode operation data transfer complete, currently updating non-volatile storage",
         0x03: "The enclosure services process is currently updating non-volatile storage with deferred microcode",
         # Codes indicating completion with no errors
         0x10: "Download microcode operation complete with no error. The enclosure services process begins using the new microcode after returning this status.",
         0x11: "Download microcode operation complete with no error. The enclosure services process (e.g., a standalone enclosure services process) begins using the new microcode after the next hard reset or power on.",
         0x12: "Download microcode operation complete with no error. The enclosure services process (e.g., an attached enclosure services process) begins using the new microcode after the next power on.",
         0x13: "Download microcode operation complete with no error. The enclosure services process (e.g., an attached enclosure services process) begins using the new microcode after: a) processing a Download Microcode Control diagnostic page specifying the active deferred microcode mode; b) hard reset; or c) power on.",
         # Codes indicating completion with errors
         0x80: "Error in one or more of the Download Microcode Control diagnostic page fields, new microcode discarded. The SUBENCLOSURE DOWNLOAD MICROCODE ADDITIONAL STATUS field shall be set to the offset of the lowest byte of the field in the Download Microcode Control diagnostic page that is in error.",
         0x81: "Microcode image error (e.g., a problem detected from a vendor specific check of the microcode image such as a checksum), new microcode discarded",
         0x82: "Download microcode timeout, new microcode discarded. The enclosure services process may discard microcode data after a vendor specific amount of time if it does not receive the entire microcode image.",
         0x83: "Internal error in the download microcode operation; new microcode image is needed before a hard reset or power on (e.g., a flash ROM write failed and no backup ROM image is available).",
         0x84: "Internal error in the download microcode operation; hard reset and power on safe (e.g., the enclosure services process will use a backup ROM image on hard reset or power on).",
         0x85: "Processed a Download Microcode Control diagnostic page with the DOWNLOAD MICROCODE MODE field set to 0Fh (i.e., activate deferred microcode) when there is no deferred microcode.",
         }

        bo = 0  # byte offset
        head = Cmd.extract(data[bo:], download_microcode_head, bo)
        bo += 8

        descriptors = []
        for encidx in range(1 + head.secondaries.val):  # @UnusedVariable
            descriptor = Cmd.extract(data[bo:], descriptor_format, bo)
            bo += 16
            descriptor.append(
                Cmd.Field(status_text[descriptor.status.val], -1,
                          "status_text", "status meaning"), "status_text")
            descriptors.append(descriptor)

        head.descriptors.val = descriptors
        return head
Пример #3
0
    def parse_07(self, data):
        element_descriptor_head = \
        (
         (  0   , 1*8, "int", "pc"        , "page code"),
         (  2   , 2*8, "int", "length"    , "pagelength"),
         (  4   , 4*8, "int", "gen"       , "generation code"),
         (  8   , 0  , "str", "enclosures", "enclosure descriptor list"),
         )
        descriptor_head = \
        (
         (  0   , 2*8, "str", None    , "reserved"),
         (  2   , 2*8, "int", "length", "descriptor length"),
         )
        # This must be lists instead of tuples so we can modify the length.
        descriptor_text = \
        [
         [  0   , 0*8, "str", "text", "type descriptor text"],
         ]

        if not self.page01:
            # We need the information from SES page 0x01 before we can
            # parse this page.
            return None

        bo = 0  # byte offset
        head = Cmd.extract(data[bo:], element_descriptor_head, bo)
        bo += 8

        enclosures07 = []
        for enclosure01 in self.page01.enclosures.val:
            typelist07 = []
            for typedef01 in enclosure01.typedesc.val:
                ellist07 = []
                for elnum in range(1 +
                                   typedef01.possible.val):  # @UnusedVariable
                    length = Cmd.extract(data[bo:], descriptor_head,
                                         bo).length.val
                    bo += 4
                    descriptor_text[0][1] = length * 8
                    ellist07.append(Cmd.extract(data[bo:], descriptor_text,
                                                bo))
                    bo += length
                typelist07.append({
                    "type": typedef01.type.val,
                    "text": typedef01.text.val,
                    "elements": ellist07,
                })
            enclosures07.append(typelist07)
        head.enclosures = Cmd.Field(enclosures07, 8, "enclosures",
                                    "list of enclosures")
        return head
        pass
Пример #4
0
    def parse_05(self, data):
        """
        return a ListDict of Fields, including "enclosures": list of enclosures
        an enclosure is a dictionary with "subid": enclosure number and "types": list of types
        a type is a dictionary with "type": element type and "elements": list of status elements, starting with the overall status element
        a status element is a list of Fields
        """
        threshold_in = \
        (
         (  0   , 1*8, "int", "pc"        , "page code"),
         (( 1,4), 1  , "int", "invop"     , "invalid operation requested"),
         (  2   , 2*8, "int", "length"    , "pagelength"),
         (  4   , 4*8, "int", "gen"       , "generation code"),
         (  8   , 0  , "str", "enclosures", "enclosure descriptor list"),
         )
        threshold_descriptor = \
        (
         (  0   , 1*8, "int", "hicrit", "high critical threshold"),
         (  1   , 1*8, "int", "hiwarn", "high warning threshold"),
         (  2   , 1*8, "int", "lowarn", "low warning threshold"),
         (  3   , 1*8, "int", "locrit", "low critical threshold"),
         )

        if not self.page01:
            # We need the information from SES page 0x01 before we can
            # parse this page.
            return None

        bo = 0  # byte offset
        head = Cmd.extract(data[bo:], threshold_in, bo)
        bo += 8

        enclosures05 = []
        for enclosure01 in self.page01.enclosures.val:
            typelist05 = []
            for typedef01 in enclosure01.typedesc.val:
                ellist05 = []
                for elnum in range(1 +
                                   typedef01.possible.val):  # @UnusedVariable
                    ellist05.append(
                        Cmd.extract(data[bo:], threshold_descriptor, bo))
                    bo += 4
                typelist05.append({
                    "type": typedef01.type.val,
                    "text": typedef01.text.val,
                    "elements": ellist05,
                })
            enclosures05.append(typelist05)
        head.enclosures = Cmd.Field(enclosures05, 8, "enclosures",
                                    "list of enclosures")
        return head
Пример #5
0
    def parse_0a(self, data):
        additional_element_head = \
        (
         (  0   , 1*8, "int", "pc"         , "page code"),
         (  2   , 2*8, "int", "length"     , "pagelength"),
         (  4   , 4*8, "int", "gen"        , "generation code"),
         (  8   , 0  , "str", "descriptors", "additional element descriptor list"),
         )

        descriptor_head = \
        (
         (( 0,7), 1  , "int", "invalid" , "invalid"),
         (( 0,4), 1  , "int", "eip"     , "element index present"),
         (( 0,3), 4  , "int", "protocol", "protocol identifier"),
         (  1   , 1*8, "int", "length"  , "additional element status descriptor length"),
         )
        descriptor_eip1 = \
        (
         (( 0,0), 1  , "int", "eiioe", "element index includes overall elements"),
         (  1   , 1*8, "int", "index", "element index"),
         )

        sas_specific_head = \
        (
         (  0   , 1*8, "int", "numphys", "number of phy descriptors"),
         (( 1,7), 2  , "int", "type"   , "descriptor type (00b)"),
         (( 1,0), 1  , "int", "notall" , "not all phys"),
        #(  2   , 0  , "str", "phydescriptors", "phy descriptor list"),
         )
        sas_specific_eip1 = \
        (
         (  1   , 1*8, "int", "slotnum", "device slot number"),
        #(  4   , 0  , "str", "phydescriptors", "phy descriptor list"),
         )

        phy_descriptor = \
        (
         (( 0,6), 3  , "int", "type"    , "device type"),
         (( 2,3), 1  , "int", "ssp_init", "ssp initiator port"),
         (( 2,2), 1  , "int", "stp_init", "stp initiator port"),
         (( 2,1), 1  , "int", "smp_init", "smp initiator port"),
         (( 3,7), 1  , "int", "selector", "sata port selector"),
         (( 3,3), 1  , "int", "ssp_targ", "ssp target port"),
         (( 3,2), 1  , "int", "stp_targ", "stp target port"),
         (( 3,1), 1  , "int", "smp_targ", "smp target port"),
         (( 3,0), 1  , "int", "device"  , "sata device"),
         (  4   , 8*8, "int", "attached_sas_addr", "attached sas_specific address"),
         ( 12   , 8*8, "int", "sas_addr", "sas_specific address"),
         ( 20   , 1*8, "int", "phy_id"  , "phy identifier"),
         )

        if not self.page01:
            # We need the information from SES page 0x01 before we can
            # parse this page.
            return None

        bo = 0  # byte offset
        head = Cmd.extract(data[bo:], additional_element_head, bo)
        bo += 8

        endbo = bo - 4 + head.length.val
        descriptors = []
        while bo < endbo:
            # Retrieve a descriptor header.
            dhead = Cmd.extract(data[bo:], descriptor_head, bo)
            bo += 2
            if dhead.eip.val:
                dhead += Cmd.extract(data[bo:], descriptor_eip1, bo)
                bo += 2
                index_remaining = dhead.index.val
                foundit = False
                for enclosure in self.page01.enclosures.val:
                    for typedef in enclosure.typedesc.val:
                        if index_remaining > typedef.possible.val + dhead.eiioe.val:
                            index_remaining -= typedef.possible.val + dhead.eiioe.val
                        else:
                            foundit = True
                            break
                    if foundit:
                        break
                dhead.index.val = (enclosure.subid.val, typedef.type.val,
                                   index_remaining - dhead.eiioe.val)

            if dhead.protocol.val != 0x06:
                return None  # Only SAS protocol is supported.

            sas_specific = Cmd.extract(data[bo:], sas_specific_head, bo)
            bo += 2
            if dhead.eip.val:
                sas_specific += Cmd.extract(data[bo:], sas_specific_eip1, bo)
                bo += 2

            phylist = []
            phybo = bo
            for phynum in range(sas_specific.numphys.val):  # @UnusedVariable
                phylist.append(Cmd.extract(data[bo:], phy_descriptor, bo))
                bo += 28
            sas_specific.append(
                Cmd.Field(phylist, phybo, "phydescriptors",
                          "phy descriptor list"), "phydescriptors")
            descriptor = dhead + sas_specific
            descriptors.append(descriptor)

        head.descriptors.val = descriptors
        return head
Пример #6
0
    def parse_02(self, data):
        """
        return a ListDict of Fields, including "enclosures": list of enclosures
        an enclosure is a dictionary with "subid": enclosure number and "types": list of types
        a type is a dictionary with "type": element type and "elements": list of status elements, starting with the overall status element
        a status element is a list of Fields
        """
        enclosure_status = \
        (
         (  0   , 1*8, "int", "pc"        , "page code"),
         (( 1,4), 1  , "int", "invop"     , "invop"),
         (( 1,3), 1  , "int", "info"      , "info"),
         (( 1,2), 1  , "int", "non_crit"  , "non-crit"),
         (( 1,1), 1  , "int", "crit"      , "crit"),
         (( 1,0), 1  , "int", "unrecov"   , "unrecov"),
         (  2   , 2*8, "int", "length"    , "pagelength"),
         (  4   , 4*8, "int", "gen"       , "generation code"),
         (  8   , 0  , "str", "enclosures", "enclosure descriptor list"),
         )
        status_element = \
        (
         (( 0,6), 1  , "int", "prdfail" , "prdfail"),
         (( 0,5), 1  , "int", "disabled", "disabled"),
         (( 0,4), 1  , "int", "swap"    , "swap"),
         (( 0,3), 4  , "int", "elstat"  , "element status code"),
         (  1   , 3*8, "int", "status"  , "element type specific status information"),
         )
        specific_status = \
        {
         0x01: (  # Device Slot
                  (  1   , 1*8, "int", "slot_address"         , "SLOT ADDRESS"),
                  (( 2,7), 1  , "int", "app_client_bypassed_a", "APP CLIENT BYPASSED A"),
                  (( 2,6), 1  , "int", "do_not_remove"        , "DO NOT REMOVE"),
                  (( 2,5), 1  , "int", "enclosure_bypassed_a" , "ENCLOSURE BYPASSED A"),
                  (( 2,4), 1  , "int", "enclosure_bypassed_b" , "ENCLOSURE BYPASSED B"),
                  (( 2,3), 1  , "int", "ready_to_insert"      , "READY TO INSERT"),
                  (( 2,2), 1  , "int", "rmv"                  , "RMV"),
                  (( 2,1), 1  , "int", "ident"                , "IDENT"),
                  (( 2,0), 1  , "int", "report"               , "REPORT"),
                  (( 3,7), 1  , "int", "app_client_bypassed_b", "APP CLIENT BYPASSED B"),
                  (( 3,6), 1  , "int", "fault_sensed"         , "FAULT SENSED"),
                  (( 3,5), 1  , "int", "fault_reqstd"         , "FAULT REQSTD"),
                  (( 3,4), 1  , "int", "device_off"           , "DEVICE OFF"),
                  (( 3,3), 1  , "int", "bypassed_a"           , "BYPASSED A"),
                  (( 3,2), 1  , "int", "bypassed_b"           , "BYPASSED B"),
                  (( 3,1), 1  , "int", "device_bypassed_a"    , "DEVICE BYPASSED A"),
                  (( 3,0), 1  , "int", "device_bypassed_b"    , "DEVICE BYPASSED B"),
                ),
         0x02: (  # Power Supply
                  (( 1,7), 1  , "int", "ident"           , "IDENT"),
                  (( 2,3), 1  , "int", "dc_over_voltage" , "DC OVER VOLTAGE"),
                  (( 2,2), 1  , "int", "dc_under_voltage", "DC UNDER VOLTAGE"),
                  (( 2,1), 1  , "int", "dc_over_current" , "DC OVER CURRENT"),
                  (( 3,7), 1  , "int", "hot_swap"        , "HOT SWAP"),
                  (( 3,6), 1  , "int", "fail"            , "FAIL"),
                  (( 3,5), 1  , "int", "rqsted_on"       , "RQSTED ON"),
                  (( 3,4), 1  , "int", "off"             , "OFF"),
                  (( 3,3), 1  , "int", "overtmp_fail"    , "OVERTMP FAIL"),
                  (( 3,2), 1  , "int", "temp_warn"       , "TEMP WARN"),
                  (( 3,1), 1  , "int", "ac_fail"         , "AC FAIL"),
                  (( 3,0), 1  , "int", "dc_fail"         , "DC FAIL"),
                ),
         0x03: (  # Cooling
                  (( 1,7), 1  , "int", "ident"     , "IDENT"),
                  (( 1,2),11  , "int", "fan_speed" , "ACTUAL FAN SPEED"), # units of 10 RPM
                  (( 3,7), 1  , "int", None        , "HOT SWAP"),
                  (( 3,6), 1  , "int", "fail"      , "FAIL"),
                  (( 3,5), 1  , "int", None        , "RQSTED ON"),
                  (( 3,4), 1  , "int", "off"       , "OFF"),
                  (( 3,2), 3  , "int", "speed_code", "ACTUAL SPEED CODE"),
                ),
         0x04: (  # Temperature Sensor
                  (( 1,7), 1  , "int", None, "IDENT"),
                  (( 1,6), 1  , "int", None, "FAIL"),
                  (  2   , 1*8, "int", None, "TEMPERATURE"),
                  (( 3,3), 1  , "int", None, "OT FAILURE"),
                  (( 3,2), 1  , "int", None, "OT WARNING"),
                  (( 3,1), 1  , "int", None, "UT FAILURE"),
                  (( 3,0), 1  , "int", None, "UT WARNING"),
                ),
         0x07: (  # Enclosure Services Controller Electronics
                  (( 1,7), 1  , "int", None, "IDENT"),
                  (( 1,6), 1  , "int", None, "FAIL"),
                  (( 2,0), 1  , "int", None, "REPORT"),
                  (( 3,7), 1  , "int", None, "HOT SWAP"),
                ),
         0x0c: (  # Display
                  (( 1,7), 1  , "int", None, "IDENT"),
                  (( 1,6), 1  , "int", None, "FAIL"),
                  (( 1,1), 2  , "int", None, "DISPLAY MODE STATUS"),
                  (  2   , 2*8, "int", None, "DISPLAY CHARACTER STATUS"),
                ),
         0x0e: (  # Enclosure
                  (( 1,7), 1  , "int", None, "IDENT"),
                  (( 2,7), 6  , "int", None, "TIME UNTIL POWER CYCLE"),
                  (( 2,1), 1  , "int", None, "FAILURE INDICATION"),
                  (( 2,0), 1  , "int", None, "WARNING INDICATION"),
                  (( 3,7), 6  , "int", None, "REQUESTED POWER OFF DURATION"),
                  (( 3,1), 1  , "int", None, "FAILURE REQUESTED"),
                  (( 3,0), 1  , "int", None, "WARNING REQUESTED"),
                ),
         0x17: (  # Array Device Slot
                  (( 1,7), 1  , "int", None, "OK"),
                  (( 1,6), 1  , "int", None, "RSVD DEVICE"),
                  (( 1,5), 1  , "int", None, "HOT SPARE"),
                  (( 1,4), 1  , "int", None, "CONS CHK"),
                  (( 1,3), 1  , "int", None, "IN CRIT ARRAY"),
                  (( 1,2), 1  , "int", None, "IN FAILED ARRAY"),
                  (( 1,1), 1  , "int", None, "REBUILD/REMAP"),
                  (( 1,0), 1  , "int", None, "R/R ABORT"),
                  (( 2,7), 1  , "int", None, "APP CLIENT BYPASSED A"),
                  (( 2,6), 1  , "int", None, "DO NOT REMOVE"),
                  (( 2,5), 1  , "int", None, "ENCLOSURE BYPASSED A"),
                  (( 2,4), 1  , "int", None, "ENCLOSURE BYPASSED B"),
                  (( 2,3), 1  , "int", None, "READY TO INSERT"),
                  (( 2,2), 1  , "int", None, "RMV"),
                  (( 2,1), 1  , "int", None, "IDENT"),
                  (( 2,0), 1  , "int", None, "REPORT"),
                  (( 3,7), 1  , "int", None, "APP CLIENT BYPASSED B"),
                  (( 3,6), 1  , "int", None, "FAULT SENSED"),
                  (( 3,5), 1  , "int", None, "FAULT REQSTD"),
                  (( 3,4), 1  , "int", None, "DEVICE OFF"),
                  (( 3,3), 1  , "int", None, "BYPASSED A"),
                  (( 3,2), 1  , "int", None, "BYPASSED B"),
                  (( 3,1), 1  , "int", None, "DEVICE BYPASSED A"),
                  (( 3,0), 1  , "int", None, "DEVICE BYPASSED B"),
                ),
         0x18: (  # SAS Expander
                  (( 1,7), 1  , "int", None, "IDENT"),
                  (( 1,6), 1  , "int", None, "FAIL"),
                ),
         0x19: (  # SAS Connector
                  (( 1,7), 1  , "int", None, "IDENT"),
                  (( 1,6), 7  , "int", None, "CONNECTOR TYPE"),
                  (  2   , 1*8, "int", None, "CONNECTOR PHYSICAL LINK"),
                  (( 3,6), 1  , "int", None, "FAIL"),
                ),
         #01 array
         #18 sas expander
         #sas connector (external ports)
         #19 sas connector (hdd ports)
         #0e enclosure
         #04 temperature sensor
         #02 power supply
         #03 cooling
         #0c display
         #07?SEP
         }

        if not self.page01:
            # We need the information from SES page 0x01 before we can
            # parse this page.
            return None

        bo = 0  # byte offset
        head = Cmd.extract(data[bo:], enclosure_status, bo)
        bo += 8

        enclosures02 = []
        for enclosure01 in self.page01.enclosures.val:
            typelist02 = []
            for typedef01 in enclosure01.typedesc.val:
                ellist02 = []
                for elnum in range(1 + typedef01.possible.val):
                    fieldlist = Cmd.extract(data[bo:], status_element, bo)
                    if typedef01.type.val in specific_status and elnum > 0:
                        fieldlist += Cmd.extract(
                            data[bo:], specific_status[typedef01.type.val],
                            bo + 1)
                        pass
                    ellist02.append(fieldlist)
                    bo += 4
                typelist02.append({
                    "type": typedef01.type.val,
                    "text": typedef01.text.val,
                    "elements": ellist02,
                })
            enclosures02.append(typelist02)
        head.enclosures = Cmd.Field(enclosures02, 8, "enclosures",
                                    "list of enclosures")
        #head.append(Cmd.Field(enclosures02, 8, "enclosures", "list of enclosures"), "enclosures")
        self.page02 = head
        return head