+}
+
+static void
+ofp_print_port_features(struct ds *string, uint32_t features)
+{
+ static const struct bit_name feature_bits[] = {
+ { OFPPF_10MB_HD, "10MB-HD" },
+ { OFPPF_10MB_FD, "10MB-FD" },
+ { OFPPF_100MB_HD, "100MB-HD" },
+ { OFPPF_100MB_FD, "100MB-FD" },
+ { OFPPF_1GB_HD, "1GB-HD" },
+ { OFPPF_1GB_FD, "1GB-FD" },
+ { OFPPF_10GB_FD, "10GB-FD" },
+ { OFPPF_COPPER, "COPPER" },
+ { OFPPF_FIBER, "FIBER" },
+ { OFPPF_AUTONEG, "AUTO_NEG" },
+ { OFPPF_PAUSE, "AUTO_PAUSE" },
+ { OFPPF_PAUSE_ASYM, "AUTO_PAUSE_ASYM" },
+ { 0, NULL },
+ };
+
+ ofp_print_bit_names(string, features, feature_bits);
+ ds_put_char(string, '\n');
+}
+
+static void
+ofp_print_port_config(struct ds *string, uint32_t config)
+{
+ static const struct bit_name config_bits[] = {
+ { OFPPC_PORT_DOWN, "PORT_DOWN" },
+ { OFPPC_NO_STP, "NO_STP" },
+ { OFPPC_NO_RECV, "NO_RECV" },
+ { OFPPC_NO_RECV_STP, "NO_RECV_STP" },
+ { OFPPC_NO_FLOOD, "NO_FLOOD" },
+ { OFPPC_NO_FWD, "NO_FWD" },
+ { OFPPC_NO_PACKET_IN, "NO_PACKET_IN" },
+ { 0, NULL },
+ };
+
+ ofp_print_bit_names(string, config, config_bits);
+ ds_put_char(string, '\n');
+}
+
+static void
+ofp_print_port_state(struct ds *string, uint32_t state)
+{
+ static const struct bit_name state_bits[] = {
+ { OFPPS_LINK_DOWN, "LINK_DOWN" },
+ { 0, NULL },
+ };
+ uint32_t stp_state;
+
+ /* The STP state is a 2-bit field so it doesn't fit in with the bitmask
+ * pattern. We have to special case it.
+ *
+ * OVS doesn't support STP, so this field will always be 0 if we are
+ * talking to OVS, so we'd always print STP_LISTEN in that case.
+ * Therefore, we don't print anything at all if the value is STP_LISTEN, to
+ * avoid confusing users. */
+ stp_state = state & OFPPS_STP_MASK;
+ if (stp_state) {
+ ds_put_cstr(string, (stp_state == OFPPS_STP_LEARN ? "STP_LEARN"
+ : stp_state == OFPPS_STP_FORWARD ? "STP_FORWARD"
+ : "STP_BLOCK"));
+ state &= ~OFPPS_STP_MASK;
+ if (state) {
+ ofp_print_bit_names(string, state, state_bits);
+ }
+ } else {
+ ofp_print_bit_names(string, state, state_bits);