Don't guard col_set_str (COL_INFO/COL_PROTOCOL) with col_check
[obnox/wireshark/wip.git] / epan / dissectors / packet-pw-atm.c
index c5fb4cfebb72c69340c0c30e3371d92f150494fd..63c920b21da381c2ade882a0fecd89d796689b07 100644 (file)
@@ -1,9 +1,10 @@
 /* packet-pw-atm.c
  * Routines for ATM PW dissection: it should be conform to RFC 4717.
  *
- * Copyright 2009 _FF_
+ * Copyright 2009 _FF_, _ATA_
  *
  * Francesco Fondelli <francesco dot fondelli, gmail dot com>
+ * Artem Tamazov <artem [dot] tamazov [at] tellabs [dot] com>
  *
  * $Id$
  *
     DONE:
         - ATM N-to-One Cell Mode (with CW)
         - ATM N-to-One Cell Mode (no CW)
-    TODO:
         - ATM One-to-One Cell Mode
         - ATM AAL5 SDU Mode
         - ATM AAL5 PDU Mode
-
-        Please pick an item from the TODO list, move code out of #if 0
-        (see below) and implement a dissector for the given encapsulation
-        mode.  The N-to-One Cell Mode is the only mandatory encap
-        mode.  One-to-One/SDU/PDU modes are optional.
 */
 
 #ifdef HAVE_CONFIG_H
 #include <string.h>
 #include <glib.h>
 #include <epan/packet.h>
+#include <epan/expert.h>
+#include <epan/prefs.h>
 
+#include <wiretap/wtap.h> /*for atm pseudo header*/
 #include "packet-mpls.h"
+#include "packet-atm.h"
+#include "packet-pw-atm.h"
+#include "packet-pw-common.h"
 
-static gint proto_pw_atm_n2o_cw = -1;
-static gint proto_pw_atm_n2o_nocw = -1;
-#if 0
-static gint proto_pw_atm_o2o_cw = -1;
-static gint proto_pw_atm_o2o_nocw = -1;
-static gint proto_pw_atm_aal5_pdu_cw = -1;
-static gint proto_pw_atm_aal5_pdu_nocw = -1;
-static gint proto_pw_atm_aal5_sdu_cw = -1;
-static gint proto_pw_atm_aal5_sdu_nocw = -1;
-#endif
+static gint proto_n1_nocw = -1;
+static gint proto_n1_cw = -1;
+static gint proto_11_or_aal5_pdu = -1;
+static gint proto_aal5_sdu = -1;
+/* subordinate dissectors: */
+static gint proto_control_word = -1;
+static gint proto_cell_header = -1;
+static gint proto_cell = -1;
 
-static gint ett_pw_atm = -1;
-
-static int hf_pw_atm_n2o_cw = -1;
-static int hf_pw_atm_n2o_cw_flags = -1;
-static int hf_pw_atm_n2o_cw_length = -1;
-static int hf_pw_atm_n2o_cw_sequence_number = -1;
-static int hf_pw_atm_n2o_nocw = -1;
-#if 0
-static int hf_pw_atm_o2o_cw = -1;
-static int hf_pw_atm_o2o_cw_sequence_number = -1;
-static int hf_pw_atm_o2o_cw_flags = -1;
-static int hf_pw_atm_o2o_cw_flags_m = -1;
-static int hf_pw_atm_o2o_cw_flags_v = -1;
-static int hf_pw_atm_o2o_cw_flags_res = -1;
-static int hf_pw_atm_o2o_cw_flags_pti = -1;
-static int hf_pw_atm_o2o_cw_flags_c = -1;
-static int hf_pw_atm_o2o_nocw = -1;
-static int hf_pw_atm_aal5_pdu_cw = -1;
-static int hf_pw_atm_aal5_pdu_cw_sequence_number = -1;
-static int hf_pw_atm_aal5_pdu_nocw = -1;
-static int hf_pw_atm_aal5_sdu_cw = -1;
-static int hf_pw_atm_aal5_sdu_cw_sequence_number = -1;
-static int hf_pw_atm_aal5_sdu_nocw = -1;
-#endif
+static gint ett_encaps = -1;
+static gint ett_cw = -1;
+static gint ett_cell_header = -1;
+static gint ett_cell = -1;
+
+static int hf_pw_type_n1_cw = -1;
+static int hf_pw_type_n1_nocw = -1;
+static int hf_pw_type_11_vcc = -1;
+static int hf_pw_type_11_vpc = -1;
+static int hf_pw_type_aal5_sdu = -1;
+static int hf_pw_type_aal5_pdu = -1;
+
+static int hf_cell_h_vpi = -1;
+static int hf_cell_h_vci = -1;
+static int hf_cell_h_pti = -1;
+static int hf_cell_h_clp = -1;
+static int hf_cell_h_m = -1;
+static int hf_cell_h_v = -1;
+static int hf_cell_h_rsv = -1;
+static int hf_aal5_pdu_rsv = -1;
+static int hf_aal5_pdu_u = -1;
+static int hf_aal5_pdu_e = -1;
+
+static int hf_cw_bits03 = -1;
+static int hf_pref_cw_rsv = -1;
+static int hf_generic_cw_rsv = -1;
+static int hf_pref_cw_flags = -1;
+static int hf_pref_cw_a5s_t = -1;
+static int hf_pref_cw_a5s_e = -1;
+static int hf_pref_cw_a5s_c = -1;
+static int hf_pref_cw_a5s_u = -1;
+static int hf_pref_cw_len = -1;
+static int hf_pref_cw_rsvlen = -1;
+static int hf_cw_seq = -1;
+static int hf_n1_cw_ncells = -1;
+static int hf_n1_nocw_ncells = -1;
+static int hf_11_ncells = -1;
+static int hf_gen_cw_atmbyte = -1;
+static int hf_cell_payload_len = -1;
+
+static dissector_handle_t dh_cell;
+static dissector_handle_t dh_cell_header;
+static dissector_handle_t dh_control_word;
+static dissector_handle_t dh_atm_truncated;
+static dissector_handle_t dh_atm_untruncated;
+static dissector_handle_t dh_atm_oam_cell;
+static dissector_handle_t dh_padding;
+static dissector_handle_t dh_data;
+
+#define PTI_IS_ADMIN(pti) ((pti) == 4 || (pti) == 5 || (pti) == 6)  /*see atm_pt_vals[]*/
+
+#define MODE_11(mode)                  (PWATM_MODE_11_VCC == (mode) || PWATM_MODE_11_VPC == (mode))
+#define MODE_N1(mode)                  (PWATM_MODE_N1_NOCW == (mode)|| PWATM_MODE_N1_CW == (mode))
+#define MODE_11_OR_AAL5_PDU(mode)      (MODE_11(mode) || PWATM_MODE_AAL5_PDU == (mode))
+
+#define VALUE_SELECTOR_VPC_VCC_PDU(mode,val_vpc,val_vcc,val_pdu)\
+       ((PWATM_MODE_11_VPC == (mode))                          \
+               ? (val_vpc)                                     \
+               : ((PWATM_MODE_11_VCC == (mode))                \
+                       ? (val_vcc)                             \
+                       : ((PWATM_MODE_AAL5_PDU == (mode))      \
+                               ? (val_pdu)                     \
+                               : 0                             \
+                         )                                     \
+                 )                                             \
+       )
+
+#define UPDATE_CUMULATIVE_VALUE(cumulative_val,new_val)\
+       do\
+       {\
+               if (-2 >= (cumulative_val))\
+               {\
+               }\
+               else if (-1 == (cumulative_val))\
+               {\
+                       (cumulative_val) = (new_val);\
+               }\
+               else if ((new_val) != (cumulative_val))\
+               {\
+                       (cumulative_val) = -2;\
+               }\
+       }\
+       while(0)
+
+#define SIZEOF_ATM_CELL_PAYLOAD 48
+#define SIZEOF_N1_PW_CELL_HEADER 4
+#define SIZEOF_11_VCC_PW_CELL_HEADER 1
+#define SIZEOF_11_VPC_PW_CELL_HEADER 3
+#define SIZEOF_N1_PW_CELL      (SIZEOF_ATM_CELL_PAYLOAD+SIZEOF_N1_PW_CELL_HEADER)
+#define SIZEOF_11_VCC_PW_CELL  (SIZEOF_ATM_CELL_PAYLOAD+SIZEOF_11_VCC_PW_CELL_HEADER)
+#define SIZEOF_11_VPC_PW_CELL  (SIZEOF_ATM_CELL_PAYLOAD+SIZEOF_11_VPC_PW_CELL_HEADER)
+
+const char pwc_longname_pw_atm_n1_cw[] = "MPLS PW ATM N-to-One encapsulation, with CW";
+const char pwc_longname_pw_atm_n1_nocw[] = "MPLS PW ATM N-to-One encapsulation, no CW";
+const char pwc_longname_pw_atm_11_or_aal5_pdu[] = "MPLS PW ATM One-to-One or AAL5 PDU encapsulation";
+const char pwc_longname_pw_atm_aal5_sdu[] = "MPLS PW ATM AAL5 CPCS-SDU mode encapsulation";
 
-static dissector_handle_t data_h;
-static dissector_handle_t atm_h;
+static const char longname_pw_atm_11_vcc[] = "MPLS PW ATM One-to-One VCC Cell Transport";
+static const char longname_pw_atm_11_vpc[] = "MPLS PW ATM One-to-One VPC Cell Transport";
+static const char longname_pw_atm_aal5_pdu[] = "MPLS PW ATM AAL5 PDU encapsulation";
 
-/* ATM One-to-One Cell Mode bits in "ATM Specific" CW field */
-#define PW_ATM_O2O_CW_M   0x80
-#define PW_ATM_O2O_CW_V   0x40
-#define PW_ATM_O2O_CW_RES 0x30
-#define PW_ATM_O2O_CW_PTI 0x0E
-#define PW_ATM_O2O_CW_C   0x01
+static const char shortname_n1_cw[] = "MPLS PW ATM N:1 CW";
+static const char shortname_n1_nocw[] = "MPLS PW ATM N:1 no CW";
+static const char shortname_11_or_aal5_pdu[] = "MPLS PW ATM 1:1 / AAL5 PDU";
+static const char shortname_11_vpc[] = "MPLS PW ATM 1:1 VPC";
+static const char shortname_11_vcc[] = "MPLS PW ATM 1:1 VCC";
+static const char shortname_aal5_sdu[] = "MPLS PW ATM AAL5 SDU";
+static const char shortname_aal5_pdu[] = "MPLS PW ATM AAL5 PDU";
 
-static void
-dissect_pw_atm_n2o_cw(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+/*
+ * These options are needed to support Nokia AXE and stuff alike.
+ * Note that these options will affect PW type auto-guessing, if such heuristic
+ * implemented in the future.
+ */
+static gboolean pref_n1_cw_allow_cw_length_nonzero     = FALSE;
+static gboolean pref_n1_cw_extend_cw_length_with_rsvd  = FALSE;
+static gboolean pref_aal5_sdu_allow_cw_length_nonzero  = FALSE;
+static gboolean pref_aal5_sdu_extend_cw_length_with_rsvd= FALSE;
+
+
+static
+int pw_cell_size(const pwatm_mode_t mode, const pwatm_submode_t submode)
 {
-        proto_tree *pw_atm_tree = NULL;
-        proto_item *ti = NULL;
-        tvbuff_t *next_tvb = NULL;
-        guint16 sequence_number = 0;
-        guint8 flags = 0;
-        guint8 length = 0;
-        guint16 ncells = 0;
-        guint16 remains = 0;
-        guint16 i = 0;
-
-        if (tvb_reported_length_remaining(tvb, 0) < 4) {
-                if (tree)
-                        proto_tree_add_text(tree, tvb, 0, -1, 
-                                            "Error processing Message");
-                return;
-        }
-
-        if (tree) {
-                ti = proto_tree_add_boolean(tree, hf_pw_atm_n2o_cw, 
-                                            tvb, 0, 0, TRUE);
-                PROTO_ITEM_SET_HIDDEN(ti);
-                ti = proto_tree_add_item(tree, proto_pw_atm_n2o_cw, 
-                                         tvb, 0, 4, FALSE);
-                pw_atm_tree = proto_item_add_subtree(ti, ett_pw_atm);
-                if (pw_atm_tree == NULL)
-                        return;
-
-                flags = tvb_get_guint8(tvb, 0) & 0x0F;
-                proto_tree_add_uint_format(
-                        pw_atm_tree,
-                        hf_pw_atm_n2o_cw_flags,
-                        tvb, 0, 1, flags,
-                        "Flags: 0x%02x",
-                        flags);
-
-                length = tvb_get_guint8(tvb, 1) & 0x3F;
-                proto_tree_add_uint_format(
-                        pw_atm_tree,
-                        hf_pw_atm_n2o_cw_length,
-                        tvb, 1, 1, length,
-                        "Length: %u",
-                        flags);
-
-                sequence_number = tvb_get_ntohs(tvb, 2);
-                proto_tree_add_uint_format(
-                        pw_atm_tree,
-                        hf_pw_atm_n2o_cw_sequence_number,
-                        tvb, 2, 2, sequence_number,
-                        "Sequence Number: %u",
-                        sequence_number);
-        }
-
-        /* FF: pass info to the ATM dissector, see packet_info.h for details */
-        pinfo->pw_atm_encap_type = 1;
-
-        /*
-         * FF: RFC 4717: "The number of cells encapsulated in a particular 
-         * frame can be inferred by the frame length" but "if the control 
-         * word is used, then the flag and length bits in the control word 
-         * are not used [and must be set to 0]" so... no reported_length in
-         * tvb_new_subset().
-         */
-        ncells = tvb_length_remaining(tvb, 4) / 52;
-        pinfo->pw_atm_ncells = ncells;
-        remains = tvb_length_remaining(tvb, 4) % 52;
-
-        for (i = 0; i < ncells; i++) {
-                next_tvb = tvb_new_subset(tvb, 
-                                          4 + (i * 52), 
-                                          52, 
-                                          -1);
-                call_dissector(atm_h, next_tvb, pinfo, tree);
-        }
-
-        /* 
-         * FF: RFC 4717 "if the pseudowire traverses a network link that 
-         * requires a minimum frame size, with a minimum frame size of 64 
-         * octets, then such links will apply padding to the pseudowire PDU 
-         * to reach its minimum frame size" or this is a malformed PDU,
-         * anyway...
-         */
-        if (remains) {
-                next_tvb = tvb_new_subset(tvb,
-                                          4 + (i * 52), 
-                                          remains, 
-                                          -1);
-                call_dissector(data_h, next_tvb, pinfo, tree);
-        }
+       switch(mode)
+       {
+       case PWATM_MODE_N1_NOCW:
+       case PWATM_MODE_N1_CW:
+               return SIZEOF_N1_PW_CELL;
+       case PWATM_MODE_11_VCC:
+               return SIZEOF_11_VCC_PW_CELL;
+       case PWATM_MODE_11_VPC:
+               return SIZEOF_11_VPC_PW_CELL;
+       case PWATM_MODE_AAL5_PDU:
+               /* AAL5 PDU size is n*48 bytes */
+               return SIZEOF_ATM_CELL_PAYLOAD;
+       case PWATM_MODE_AAL5_SDU:
+               if (PWATM_SUBMODE_ADMIN_CELL == submode)
+               {
+                       return SIZEOF_N1_PW_CELL; /*n:1 encapsulation is used for admin cells*/
+               }
+               else
+               {
+                       DISSECTOR_ASSERT_NOT_REACHED();
+                       return 0;
+               }
+       default:
+               DISSECTOR_ASSERT_NOT_REACHED();
+               return 0;
+       }
 }
 
-static void
-dissect_pw_atm_n2o_nocw(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+static
+int pw_cell_header_size(const pwatm_mode_t mode, const pwatm_submode_t submode)
 {
-        proto_item *ti = NULL;
-        tvbuff_t *next_tvb = NULL;
-        guint16 ncells = 0;
-        guint16 remains = 0;
-        guint16 i = 0;
-
-        /* 
-         * FF: all comments in dissect_pw_atm_n2o_cw() apply here
-         * as well, thus not repeated.
-         */
-
-        if (tvb_reported_length_remaining(tvb, 0) < 52) {
-                if (tree)
-                        proto_tree_add_text(tree, tvb, 0, -1, 
-                                            "Error processing Message");
-                return;
-        }
-
-        if (tree) {
-                ti = proto_tree_add_boolean(tree, hf_pw_atm_n2o_nocw, 
-                                            tvb, 0, 0, TRUE);
-                PROTO_ITEM_SET_HIDDEN(ti);
-        }
-
-        pinfo->pw_atm_encap_type = 1;
-        ncells = tvb_length_remaining(tvb, 0) / 52;
-        pinfo->pw_atm_ncells = ncells;
-        remains = tvb_length_remaining(tvb, 0) % 52;
-
-        for (i = 0; i < ncells; i++) {
-                next_tvb = tvb_new_subset(tvb, 
-                                          (i * 52), 
-                                          52, 
-                                          -1);
-                call_dissector(atm_h, next_tvb, pinfo, tree);
-        }
-
-        if (remains) {
-                next_tvb = tvb_new_subset(tvb,
-                                          (i * 52), 
-                                          remains, 
-                                          -1);
-                call_dissector(data_h, next_tvb, pinfo, tree);
-        }
+       switch(mode)
+       {
+       case PWATM_MODE_N1_NOCW:
+       case PWATM_MODE_N1_CW:
+               return SIZEOF_N1_PW_CELL_HEADER;
+       case PWATM_MODE_11_VCC:
+               return SIZEOF_11_VCC_PW_CELL_HEADER;
+       case PWATM_MODE_11_VPC:
+               return SIZEOF_11_VPC_PW_CELL_HEADER;
+       case PWATM_MODE_AAL5_SDU:
+               if (PWATM_SUBMODE_ADMIN_CELL == submode)
+               {
+                       return SIZEOF_N1_PW_CELL_HEADER; /*n:1 encapsulation is used for admin cells*/
+               }
+               else
+               {
+                       DISSECTOR_ASSERT_NOT_REACHED();
+                       return 0;
+               }
+       case PWATM_MODE_AAL5_PDU: /*not applicable*/
+       default:
+               DISSECTOR_ASSERT_NOT_REACHED();
+               return 0;
+       }
 }
 
-#if 0
-static void
-dissect_pw_atm_o2o_cw(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+static
+int number_of_cells(const pwatm_mode_t mode
+               ,const pwatm_submode_t submode
+               ,const gint payload_size
+               ,gint* const remainder_size)
 {
-        (void)tvb;
-        (void)pinfo;
-        (void)tree;
+       int cells;      
+
+       DISSECTOR_ASSERT(payload_size >= 0);
+       
+       switch(mode)
+       {
+       case PWATM_MODE_N1_NOCW:
+       case PWATM_MODE_N1_CW:
+       case PWATM_MODE_11_VCC:
+       case PWATM_MODE_11_VPC:
+       case PWATM_MODE_AAL5_PDU:
+               cells = payload_size / pw_cell_size(mode,submode);
+               *remainder_size = payload_size - (cells * pw_cell_size(mode,submode));
+               return cells;
+       case PWATM_MODE_AAL5_SDU:
+               if (PWATM_SUBMODE_ADMIN_CELL == submode)
+               {
+                       cells = payload_size / pw_cell_size(mode,submode);
+                       if (cells > 1) cells = 1; /*max. 1 admin cell may be present in aal5 sdu mode */
+                       *remainder_size = payload_size - (cells * pw_cell_size(mode,submode));
+                       return cells;
+               }
+               else
+               {
+                       /*not applicable*/
+               }
+               /*fallthrough*/
+       default:
+               DISSECTOR_ASSERT_NOT_REACHED();
+               *remainder_size = payload_size;
+               return 0;
+       }
+       
 }
 
-static void
-dissect_pw_atm_o2o_nocw(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+
+static
+void col_append_pw_info(packet_info * pinfo
+       ,const int payload_size
+       ,const int cells
+       ,const int padding_size)
 {
-        (void)tvb;
-        (void)pinfo;
-        (void)tree;
+       pwatm_private_data_t* pd;
+       DISSECTOR_ASSERT(pinfo != NULL);
+
+       pd = pinfo->private_data;
+       DISSECTOR_ASSERT(pd != NULL);
+
+       if (pd->props & PWC_ANYOF_CW_BAD)
+       {
+               col_append_str(pinfo->cinfo, COL_INFO, "CW:Bad");
+       }
+       
+       if (pd->props & PWC_PAY_SIZE_BAD)
+       {
+               if (pd->props & PWC_ANYOF_CW_BAD)
+               {
+                       col_append_str(pinfo->cinfo, COL_INFO, ", ");
+               }
+               col_append_str(pinfo->cinfo, COL_INFO, "Payload size:Bad, ");
+               col_append_fstr(pinfo->cinfo, COL_INFO, "%d byte%s"
+                       ,(int)payload_size
+                       ,plurality(payload_size,"","s"));
+       }
+       
+       if (pd->props == 0) /*omit "atm cells" etc if something is wrong*/
+       {
+               /* number of cells may be not known */
+               if (cells >=0)
+                       col_append_fstr(pinfo->cinfo, COL_INFO, "%d ATM cell%s"
+                               ,cells
+                               ,plurality(cells,"","s"));
+               /*
+                * Display ATM-specific attributes which are the same
+                * across all the cells in the pw packet.
+                * Meanings of values:
+                *   (-1) unknown - not displayed,
+                *   (-2) "not the same in all cells" - not displayed
+                *   positive values - ok, displayed
+                */
+               if (pd->cumulative.vpi >= 0)
+                       col_append_fstr(pinfo->cinfo, COL_INFO, ", VPI:%.4d", pd->cumulative.vpi);
+               if (pd->cumulative.vci >= 0)
+                       col_append_fstr(pinfo->cinfo, COL_INFO, ", VCI:%.5d", pd->cumulative.vci);
+               if (pd->cumulative.pti >= 0)
+                       col_append_fstr(pinfo->cinfo, COL_INFO, ", PTI:%.1d", pd->cumulative.pti);
+               if (pd->cumulative.clp >= 0)
+                       col_append_fstr(pinfo->cinfo, COL_INFO, ", CLP:%.1d", pd->cumulative.clp);
+       }
+       
+       if (padding_size != 0)
+       {
+               col_append_fstr(pinfo->cinfo, COL_INFO, ", %d padding"
+                       ,(int)padding_size);
+       }
+}
+
+               
+static
+void prepare_pseudo_header_atm(
+       union wtap_pseudo_header * const ph,
+       const pwatm_private_data_t * const pdata,
+       const unsigned aal)
+{
+       DISSECTOR_ASSERT(NULL != pdata);
+       DISSECTOR_ASSERT(NULL != ph);
+
+       memset(ph,0,sizeof(*ph)); /* it is OK to clear unknown values */
+       ph->atm.flags           = 0; /* status flags */
+       ph->atm.aal             = aal;
+       ph->atm.type            = TRAF_UNKNOWN;
+       ph->atm.subtype         = TRAF_ST_UNKNOWN;
+       ph->atm.vpi             = (pdata->vpi >= 0) ? pdata->vpi : 0 /*unknown*/;
+       ph->atm.vci             = (pdata->vci >= 0) ? pdata->vci : 0 /*unknown*/;
+       ph->atm.aal2_cid        = 0; /*not applicable*//* channel id */
+       ph->atm.channel         = 0; /*unknown*//* link: 0 for DTE->DCE, 1 for DCE->DTE */
+       ph->atm.cells           = 0; /*zero indicates that we do not have trailer info*/
+       /*user-to-user indicator & CPI*/
+       ph->atm.aal5t_u2u       = 0; /* all bits unknown except lsb of UU */    
+       if (pdata->aal5_sdu_frame_relay_cr_bit)
+       { /* Let's give Frame Relay C/R bit to ATM dissector.*/
+               ph->atm.aal5t_u2u |= (1<<8); /*UU octet is at << 8 in aal5t_u2u*/
+       }
+       ph->atm.aal5t_len       = 0; /*unknown*//* length of the packet from trailer*/
+       ph->atm.aal5t_chksum    = 0; /*unknown*//* checksum for AAL5 packet from trailer */
+       return;
 }
 
 
-static void
-dissect_pw_atm_aal5_pdu_cw(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+static
+void dissect_payload_and_padding(
+       tvbuff_t * tvb
+       ,packet_info * pinfo
+       ,proto_tree * tree
+       ,const gint payload_size
+       ,const gint padding_size)
 {
-        (void)tvb;
-        (void)pinfo;
-        (void)tree;
+       int dissected;
+       tvbuff_t* tvb_2;
+       pwatm_private_data_t* pd;
+
+       DISSECTOR_ASSERT(NULL != pinfo);
+       pd = pinfo->private_data;
+       DISSECTOR_ASSERT(NULL != pd);
+
+       for(dissected = 0, pd->pw_cell_number = 0;
+               payload_size > dissected;
+               ++(pd->pw_cell_number))
+       {
+               tvb_2 = tvb_new_subset(tvb,dissected,-1,-1);
+               dissected += call_dissector(dh_cell_header, tvb_2, pinfo, tree);
+                       
+               tvb_2 = tvb_new_subset(tvb,dissected,-1,-1);
+               
+               /*dissect as oam for specific vci/pti, just like atm dissector does*/
+               if (pd->vci >= 0 && pd->pti >=0)
+               {
+                       if (atm_is_oam_cell(pd->vci,pd->pti))
+                       {
+                               pd->cell_mode_oam = TRUE;
+                       }
+               }
+               
+               if (pd->cell_mode_oam)
+               {
+                       union wtap_pseudo_header* pseudo_header_save;
+                       union wtap_pseudo_header ph;
+                       tvbuff_t* tvb_3;
+                       int bytes_to_dissect;
+                       /* prepare buffer for old-style dissector */
+                       /* oam cell is always 48 bytes, but payload_size maybe too small */
+                       if ((payload_size - dissected) >= SIZEOF_ATM_CELL_PAYLOAD)
+                               bytes_to_dissect = SIZEOF_ATM_CELL_PAYLOAD;
+                       else
+                               bytes_to_dissect = (payload_size - dissected);
+                       tvb_3 = tvb_new_subset(tvb_2,0,bytes_to_dissect,-1);
+                       /*aal5_sdu: disable filling columns after 1st (valid) oam cell*/
+                       if (pd->mode == PWATM_MODE_AAL5_SDU && pd->pw_cell_number > 0)
+                       {
+                               pd->enable_fill_columns_by_atm_dissector = FALSE;
+                       }
+                       /* save & prepare new pseudo header for atm aal5 decoding */
+                       pseudo_header_save = pinfo->pseudo_header;
+                       pinfo->pseudo_header = &ph;
+                       prepare_pseudo_header_atm(&ph,pd,AAL_OAMCELL);
+
+                       call_dissector(dh_atm_oam_cell, tvb_3, pinfo, tree);
+                       dissected += bytes_to_dissect;
+                       /* restore pseudo header */
+                       pinfo->pseudo_header = pseudo_header_save;
+               }
+               else
+               {
+                       dissected += call_dissector(dh_cell, tvb_2, pinfo, tree);
+               }
+       }
+       
+       if (padding_size != 0)
+       {
+               tvbuff_t* tvb_2;
+               tvb_2 = tvb_new_subset(tvb
+                               ,(tvb_reported_length_remaining(tvb, 0) - padding_size)
+                               ,-1
+                               ,-1);
+               call_dissector(dh_padding, tvb_2, pinfo, tree);
+       }
+       return;
 }
 
 
-static void
-dissect_pw_atm_aal5_pdu_nocw(tvbuff_t *tvb, packet_info *pinfo, 
-                             proto_tree *tree)
+static
+gboolean too_small_packet_or_notpw(tvbuff_t * tvb
+       ,packet_info * pinfo
+       ,proto_tree * tree
+       ,const int proto_handler
+       ,const char * const proto_name_column)
 {
-        (void)tvb;
-        (void)pinfo;
-        (void)tree;
+       gint packet_size;
+       packet_size = tvb_reported_length_remaining(tvb, 0);
+       /*
+        * FIXME
+        * "4" below should be replaced by something like "min_packet_size_this_dissector"
+        * Also call to dissect_try_cw_first_nibble() should be moved before this block
+        */
+       if (packet_size < 4) /* 4 is smallest size which may be sensible (for PWACH dissector) */
+       {
+               if (tree)
+               {
+                       proto_item  *item;
+                       item = proto_tree_add_item(tree, proto_handler, tvb, 0, -1, FALSE);
+                       expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR,
+                               "PW packet size (%d) is too small to carry sensible information"
+                               ,(int)packet_size);
+               }
+               /* represent problems in the Packet List pane */
+               if (check_col(pinfo->cinfo, COL_PROTOCOL))
+               {
+                       col_set_str(pinfo->cinfo, COL_PROTOCOL, proto_name_column);
+               }
+               col_set_str(pinfo->cinfo, COL_INFO, "Malformed: PW packet is too small");
+               return TRUE;
+       }
+       if (dissect_try_cw_first_nibble(tvb, pinfo, tree))
+       {
+               return TRUE;
+       }
+       return FALSE;
 }
 
-static void
-dissect_pw_atm_aal5_sdu_cw(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+
+/*
+ * NOTE. RFC describes ATM-specific byte in a cumbersome way.
+ * It is a part of CW, but at the same time, it must be repeated
+ * with each cell, _except_ first.
+ *
+ * Alternatively, ATM-specific byte may be considered as part of
+ * PW payload (i.e., as part of pw atm cell header), so we can say that
+ * it is being repeated with each cell.
+ *
+ * This dissector is written according to the latter consideration.
+ */
+static
+void dissect_11_or_aal5_pdu(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
 {
-        (void)tvb;
-        (void)pinfo;
-        (void)tree;
+       const char *proto_name_column;
+       const char *proto_name_tree = NULL;
+       gint payload_size;
+       int cells;
+       pwatm_private_data_t pd = PWATM_PRIVATE_DATA_T_INITIALIZER;
+       void * pd_save = pinfo->private_data;
+       pinfo->private_data = &pd;
+
+       proto_name_column = &shortname_11_or_aal5_pdu[0];
+       if (too_small_packet_or_notpw(tvb,pinfo,tree,proto_11_or_aal5_pdu,proto_name_column))
+       {
+               return;
+       }
+       pd.packet_size = tvb_reported_length_remaining(tvb, 0);
+
+       /*
+        * Guess encapsulation mode according to M & V bits from the 3rd byte of CW.
+        * Also adjust protocol name strings.
+        */
+       {
+               guint8 third_byte;
+               third_byte = tvb_get_guint8(tvb, 3);
+               if (0 == (third_byte & 0x80 /*generic_cw.m*/))
+               { /*1:1 modes*/
+                       if (0 != (third_byte & 0x40 /*generic_cw.v*/))
+                       {
+                               pd.mode = PWATM_MODE_11_VPC;
+                               proto_name_column = &shortname_11_vpc[0];
+                               proto_name_tree = &longname_pw_atm_11_vpc[0];
+                       }
+                       else
+                       {
+                               pd.mode = PWATM_MODE_11_VCC;
+                               proto_name_column = &shortname_11_vcc[0];
+                               proto_name_tree = &longname_pw_atm_11_vcc[0];
+                       }
+               }
+               else
+               {
+                       pd.mode = PWATM_MODE_AAL5_PDU;
+                       proto_name_column = &shortname_aal5_pdu[0];
+                       proto_name_tree = &longname_pw_atm_aal5_pdu[0];
+               }
+       }
+       
+       
+       /* check how "good" is this packet */
+       pd.props = PWC_PACKET_PROPERTIES_T_INITIALIZER;
+       if (0 != (tvb_get_guint8(tvb, 0) & 0xf0 /*bits03*/))
+       {
+               pd.props |= PWC_CW_BAD_BITS03;
+       }
+       if (0 != (tvb_get_guint8(tvb, 0) & 0x0f /*generic_cw.rsvd*/))
+       {
+               pd.props |= PWC_CW_BAD_RSV;
+       }
+       
+       /*
+        * Do not dissect and validate atm-specific byte (3rd byte of CW).
+        * It will be dissected/validated as pw cell header.
+        */
+       
+       /*
+        * Decide about payload length and padding.
+        *
+        * Is padding allowed?
+        *      eth header length  == 14
+        *      mpls label length  ==  4
+        *      cw length          ==  4
+        *      min payload length == 48
+        *      => 14 + 4 + 4 + 48 == 70
+        *        => 70 >= 64
+        *          => no padding allowed
+        */
+       if (MODE_11(pd.mode))
+       {
+               gint bad_padding_size;
+               payload_size = pd.packet_size - (PWC_SIZEOF_CW-1);
+               cells = number_of_cells(pd.mode,pd.submode,payload_size,&bad_padding_size);
+               if (0 == cells || 0 != bad_padding_size)
+               {
+                       pd.props |= PWC_PAY_SIZE_BAD;
+               }
+       }
+       else
+       { /*aal5_pdu mode*/
+               gint bad_padding_size;
+               payload_size = pd.packet_size - PWC_SIZEOF_CW;
+               cells = number_of_cells(pd.mode,pd.submode,payload_size,&bad_padding_size);
+               /* at least 1 cell must be present in the packet in this mode*/
+               if (1 > cells || 0 != bad_padding_size)
+               {
+                       pd.props |= PWC_PAY_SIZE_BAD;
+               }
+               cells = -1; /*this value not needed anymore, suppress pinting of it*/
+       }
+
+       if (PWATM_MODE_AAL5_PDU == pd.mode)
+       {
+               /* sub-dissectors _may_ overwrite columns in aal5_pdu mode */
+               if (check_col(pinfo->cinfo, COL_PROTOCOL))
+               {
+                       col_set_str(pinfo->cinfo, COL_PROTOCOL, proto_name_column);
+               }
+               if (check_col(pinfo->cinfo, COL_INFO))
+               {
+                       col_clear(pinfo->cinfo, COL_INFO);
+                       col_append_pw_info(pinfo, payload_size, cells, 0);
+               }
+       }
+
+       if (tree)
+       {
+               proto_item* item;
+               item = proto_tree_add_item(tree, proto_11_or_aal5_pdu, tvb, 0, -1, FALSE);
+               /*overwrite heading line*/
+               proto_item_set_text(item, proto_name_tree, 0/*-warn gcc 3.4.4*/); 
+               pwc_item_append_text_n_items(item,cells,"good ATM cell");
+               {
+                       proto_tree* tree;
+                       tree = proto_item_add_subtree(item, ett_encaps);
+                       {
+                               proto_item* item;
+                               item = proto_tree_add_boolean(tree
+                                       ,VALUE_SELECTOR_VPC_VCC_PDU(pd.mode
+                                               ,hf_pw_type_11_vpc
+                                               ,hf_pw_type_11_vcc
+                                               ,hf_pw_type_aal5_pdu)
+                                       ,tvb, 0, 0, TRUE);
+                               PROTO_ITEM_SET_GENERATED(item);
+                               if (MODE_11(pd.mode))
+                               {
+                                       item = proto_tree_add_int(tree, hf_11_ncells, tvb, 0, 0, cells);
+                                       PROTO_ITEM_SET_GENERATED(item);
+                               }
+                       }
+               }
+               if (pd.props & PWC_PAY_SIZE_BAD)
+               {
+                       expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR
+                               ,"PW payload size (%d) must be <> 0 and multiple of %d"
+                               ,(int)payload_size,pw_cell_size(pd.mode,pd.submode));
+                       if (payload_size != 0 && MODE_11(pd.mode))
+                       {
+                               expert_add_info_format(pinfo, item, PI_MALFORMED, PI_NOTE,
+                                       "PW ATM cell [%.3d] is broken",(int)cells);
+                       }
+               }
+       }
+       
+       {
+               tvbuff_t* tvb_2;
+               tvb_2 = tvb_new_subset(tvb, 0, PWC_SIZEOF_CW, PWC_SIZEOF_CW);
+               call_dissector(dh_control_word, tvb_2, pinfo, tree);
+               
+               tvb_2 = tvb_new_subset(tvb, (PWC_SIZEOF_CW-1), -1, -1);
+               if (MODE_11(pd.mode))
+               {
+                       dissect_payload_and_padding(tvb_2,pinfo,tree,payload_size,0);
+               }
+               else
+               { /*aal5_pdu mode*/
+                       if (payload_size != 0)
+                       {
+                               tvbuff_t* tvb_3;
+                               union wtap_pseudo_header* pseudo_header_save;
+                               union wtap_pseudo_header ph;
+                               
+                               tvb_3 = tvb_new_subset(tvb_2, 1, -1, -1);
+                               /* prepare pseudo header for atm aal5 decoding */
+                               pseudo_header_save = pinfo->pseudo_header;
+                               pinfo->pseudo_header = &ph;
+                               prepare_pseudo_header_atm(&ph,&pd,AAL_5);
+                               call_dissector(dh_atm_untruncated, tvb_3, pinfo, tree);
+                               /* restore pseudo header */
+                               pinfo->pseudo_header = pseudo_header_save;
+                       }
+               }
+       }
+       
+       if (MODE_11(pd.mode))
+       {
+               /* overwrite everything written by sub-dissectors in 1:1 modes*/
+               if (check_col(pinfo->cinfo, COL_PROTOCOL))
+               {
+                       col_set_str(pinfo->cinfo, COL_PROTOCOL, proto_name_column);
+               }
+               if (check_col(pinfo->cinfo, COL_INFO))
+               {
+                       col_clear(pinfo->cinfo, COL_INFO);
+                       col_append_pw_info(pinfo, payload_size, cells, 0);
+               }
+       }
+       
+       pinfo->private_data = pd_save;
+       return;
 }
 
-static void
-dissect_pw_atm_aal5_sdu_nocw(tvbuff_t *tvb, packet_info *pinfo, 
-                             proto_tree *tree)
+
+static
+void dissect_aal5_sdu(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
 {
-        (void)tvb;
-        (void)pinfo;
-        (void)tree;
+       const char *proto_name_column;
+       gint payload_size;
+       gint padding_size;
+       int cells;
+       pwatm_private_data_t pd = PWATM_PRIVATE_DATA_T_INITIALIZER;
+       void * pd_save = pinfo->private_data;
+
+       pinfo->private_data = &pd;
+       pd.mode = PWATM_MODE_AAL5_SDU;
+
+       proto_name_column = &shortname_aal5_sdu[0];
+       if (too_small_packet_or_notpw(tvb,pinfo,tree,proto_aal5_sdu,proto_name_column))
+       {
+               return;
+       }
+       pd.packet_size = tvb_reported_length_remaining(tvb, 0);
+
+       /* check how "good" is this packet */
+       /* also decide payload length from packet size and CW */
+       if (0 != (tvb_get_guint8(tvb, 0) & 0xf0 /*bits03*/))
+       {
+               pd.props |= PWC_CW_BAD_BITS03;
+       }
+
+       pd.submode = PWATM_SUBMODE_DEFAULT;
+       if (0 != (tvb_get_guint8(tvb, 0) & 0x08 /*preferred_cw.T*/))
+       {
+               pd.submode = PWATM_SUBMODE_ADMIN_CELL;
+       }
+       
+       if (! pref_aal5_sdu_extend_cw_length_with_rsvd)
+       {
+               if (0 != (tvb_get_guint8(tvb, 1) & 0xc0 /*preferred_cw.rsvd*/))
+               {
+                       pd.props |= PWC_CW_BAD_RSV;
+               }
+       }
+       {
+               /* RFC4717:
+                * [ If the packet's length (defined as the length of the layer 2 payload
+                * plus the length of the control word) is less than 64 bytes, the
+                * length field MUST be set to the packet's length.  Otherwise, the
+                * length field MUST be set to zero... Note that the length field
+                * is not used in the N-to-one mode and MUST be set to 0. ]
+                *              
+                * Also we allow some "extensions"conducted by pref_xxx.
+                */
+               gint payload_size_from_packet;
+               int cw_len; /*length field from cw*/
+
+               payload_size_from_packet = pd.packet_size - PWC_SIZEOF_CW;
+               if (pref_aal5_sdu_extend_cw_length_with_rsvd)
+               {
+                       cw_len = tvb_get_guint8(tvb, 1) & 0xff;
+               }
+               else
+               {
+                       cw_len = tvb_get_guint8(tvb, 1) & 0x3f;
+               }
+               
+               /*
+                * Initial assumptions: no padding,
+                * payload size derived from psn packet size.
+                */
+               payload_size = payload_size_from_packet;
+               padding_size = 0;
+               
+               if (0 == cw_len)
+               {
+                       /*keep initial assumptions*/
+               }
+               else if (!pref_aal5_sdu_allow_cw_length_nonzero
+                        && PWATM_SUBMODE_ADMIN_CELL == pd.submode)
+               {
+                       /*
+                        * The "allow CW.Length != 0" option affects
+                        * ATM admin cell submode only, because this submode
+                        * is equal to N:1 encapsulation.
+                        * CW.Length !=0 is always OK for normal (AAL5 payload) submode.
+                        */
+                       pd.props |= PWC_CW_BAD_LEN_MUST_BE_0;
+               }
+               else
+               {
+                       gint payload_size_from_cw;
+                       payload_size_from_cw = cw_len - PWC_SIZEOF_CW;
+                       if (payload_size_from_cw <= 0)
+                       {
+                               pd.props |= PWC_CW_BAD_PAYLEN_LE_0;
+                       }
+                       else if (payload_size_from_cw > payload_size_from_packet)
+                       {
+                               pd.props |= PWC_CW_BAD_PAYLEN_GT_PACKET;
+                       }
+                       else
+                       {
+
+                               payload_size = payload_size_from_cw;
+                               padding_size = payload_size_from_packet - payload_size_from_cw; /* >=0 */
+                               if (padding_size != 0)
+                               {
+                                       /*
+                                        * Padding is not allowed in ATM admin cell submode only,
+                                        * because this submode is equal to N:1 encapsulation.
+                                        * Padding is OK for normal (AAL5 payload) submode.
+                                        */
+                                       if (PWATM_SUBMODE_ADMIN_CELL == pd.submode)
+                                       {
+                                               pd.props |= PWC_CW_BAD_PADDING_NE_0;
+                                               /*restore sizes*/
+                                               payload_size = payload_size_from_packet;
+                                               padding_size = 0;
+                                       }
+                               }
+                       }
+               }
+               
+               if (PWATM_SUBMODE_ADMIN_CELL == pd.submode)
+               {
+                       gint bad_padding_size;
+                       cells = number_of_cells(pd.mode,pd.submode,payload_size,&bad_padding_size);
+                       /* only one atm admin cell is allowed in the packet in this mode*/
+                       if (1 != cells || 0 != bad_padding_size)
+                       {
+                               pd.props |= PWC_PAY_SIZE_BAD;
+                       }
+               }
+               else
+               {
+                       cells = -1; /*unknown*/
+                       /* Any size is allowed for AAL5 SDU payload */
+               }
+       }
+
+       /* fill columns in Packet List */
+       if (check_col(pinfo->cinfo, COL_PROTOCOL))
+       {
+               col_set_str(pinfo->cinfo, COL_PROTOCOL, proto_name_column);
+               if (PWATM_SUBMODE_ADMIN_CELL == pd.submode)
+               {
+                       col_append_str(pinfo->cinfo, COL_PROTOCOL, ", OAM cell");
+               }
+       }
+       
+       if (check_col(pinfo->cinfo, COL_INFO))
+       {
+               col_clear(pinfo->cinfo, COL_INFO);
+               col_append_pw_info(pinfo, payload_size, cells, padding_size);
+       }
+
+       if (tree)
+       {
+               proto_item* item;
+               item = proto_tree_add_item(tree, proto_aal5_sdu, tvb, 0, -1, FALSE);
+               {
+                       proto_tree* tree;
+                       tree = proto_item_add_subtree(item, ett_encaps);
+                       {
+                               item = proto_tree_add_boolean(tree, hf_pw_type_aal5_sdu, tvb, 0, 0, TRUE);
+                               PROTO_ITEM_SET_GENERATED(item);
+                       }
+               }
+               if (pd.props & PWC_PAY_SIZE_BAD)
+               {
+                       DISSECTOR_ASSERT(PWATM_SUBMODE_ADMIN_CELL == pd.submode);
+                       expert_add_info_format(pinfo, item, PI_MALFORMED ,PI_ERROR
+                               ,"In ATM admin cell mode,"
+                               " PW payload size (%d) must be == %d (exactly 1 admin cell)"
+                               ,(int)payload_size,(int)SIZEOF_N1_PW_CELL);
+               }
+       }
+       
+       {
+               tvbuff_t* tvb_2;
+               tvb_2 = tvb_new_subset(tvb, 0, PWC_SIZEOF_CW, PWC_SIZEOF_CW);
+               call_dissector(dh_control_word, tvb_2, pinfo, tree);
+               
+               tvb_2 = tvb_new_subset(tvb, PWC_SIZEOF_CW, -1, -1);
+               if (PWATM_SUBMODE_ADMIN_CELL == pd.submode)
+               {
+                       dissect_payload_and_padding(tvb_2,pinfo,tree,payload_size,padding_size);
+               }
+               else /*AAL5 payload*/
+               {       
+                       if (payload_size != 0)
+                       {
+                               tvbuff_t* tvb_3;
+                               union wtap_pseudo_header* pseudo_header_save;
+                               union wtap_pseudo_header ph;
+                               
+                               tvb_3 = tvb_new_subset(tvb_2, 0, payload_size, payload_size);
+                               /* prepare pseudo header for atm aal5 decoding */
+                               pseudo_header_save = pinfo->pseudo_header;
+                               pinfo->pseudo_header = &ph;
+                               prepare_pseudo_header_atm(&ph,&pd,AAL_5);
+                               call_dissector(dh_atm_truncated, tvb_3, pinfo, tree); /* no PAD and trailer */
+                               /* restore pseudo header */
+                               pinfo->pseudo_header = pseudo_header_save;
+                       }
+                       if (padding_size != 0)
+                       {
+                               tvbuff_t* tvb_3;
+                               tvb_3 = tvb_new_subset(tvb_2, payload_size, padding_size, -1);
+                               call_dissector(dh_padding, tvb_3, pinfo, tree);
+                       }
+               }
+       }
+
+       pinfo->private_data = pd_save;
+       return;
 }
-#endif
 
-void
-proto_register_pw_atm(void)
+
+static
+void dissect_n1_cw(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
 {
-        static hf_register_info hf[] = {
-                /* FF: general */
-                {
-                        &hf_pw_atm_n2o_cw,
-                        {
-                                "ATM PW, N-to-one Cell Mode (with CW)", 
-                                "pw_atm_n2o_cw", FT_BOOLEAN, 
-                                0, NULL, 0x0, NULL, HFILL
-                        }
-                },
-                {
-                        &hf_pw_atm_n2o_nocw,
-                        {
-                                "ATM PW, N-to-one Cell Mode (no CW)", 
-                                "pw_atm_n2o_nocw", FT_BOOLEAN, 
-                                0, NULL, 0x0, NULL, HFILL
-                        }
-                },
-#if 0
-                {
-                        &hf_pw_atm_o2o_cw,
-                        {
-                                "ATM PW, One-to-one Cell Mode (with CW)", 
-                                "pw_atm_o2o_cw", FT_BOOLEAN, 
-                                0, NULL, 0x0, NULL, HFILL
-                        }
-                },
-                {
-                        &hf_pw_atm_o2o_nocw,
-                        {
-                                "ATM PW, One-to-one Cell Mode (no CW)", 
-                                "pw_atm_o2o_nocw", FT_BOOLEAN, 
-                                0, NULL, 0x0, NULL, HFILL
-                        }
-                },
-                {
-                        &hf_pw_atm_aal5_pdu_cw,
-                        {
-                                "ATM PW, AAL5 PDU Frame Mode (with CW)", 
-                                "pw_atm_aal5_pdu_cw", FT_BOOLEAN, 
-                                0, NULL, 0x0, NULL, HFILL
-                        }
-                },
-                {
-                        &hf_pw_atm_aal5_pdu_nocw,
-                        {
-                                "ATM PW, AAL5 PDU Frame Mode (no CW)", 
-                                "pw_atm_aal5_pdu_nocw", FT_BOOLEAN, 
-                                0, NULL, 0x0, NULL, HFILL
-                        }
-                },
-                {
-                        &hf_pw_atm_aal5_sdu_cw,
-                        {
-                                "ATM PW, AAL5 SDU Frame Mode (with CW)", 
-                                "pw_atm_aal5_sdu_cw", FT_BOOLEAN, 
-                                0, NULL, 0x0, NULL, HFILL
-                        }
-                },
-                {
-                        &hf_pw_atm_aal5_sdu_nocw,
-                        {
-                                "ATM PW, AAL5 SDU Frame Mode (no CW)", 
-                                "pw_atm_aal5_sdu_nocw", FT_BOOLEAN, 
-                                0, NULL, 0x0, NULL, HFILL
-                        }
-                },
-                /* FF: ATM One-to-one Cell Mode Control Word fields */
-                {
-                        &hf_pw_atm_o2o_cw_sequence_number,
-                        {
-                                "ATM One-to-one Cell Mode sequence number", 
-                                "pw_atm_o2o_cw_sequence_number", FT_UINT16, 
-                                BASE_DEC, NULL, 0x0, NULL, HFILL
-                        }
-                },
-                {
-                        &hf_pw_atm_o2o_cw_flags,
-                        {
-                                "ATM One-to-one Cell Mode flags",
-                                "pw_atm_o2o_cw_flags", FT_UINT8, 
-                                BASE_HEX, NULL, 0x0, NULL, HFILL 
-                        }
-
-                },
-                {
-                        &hf_pw_atm_o2o_cw_flags_m,
-                        {
-                                "M (transport mode) bit",
-                                "pw_atm_o2o_cw_flags_m", FT_BOOLEAN, 
-                                8, TFS(&flags_set_truth), PW_ATM_O2O_CW_M,
-                                NULL, HFILL 
-                        }
-                },
-                {
-                        &hf_pw_atm_o2o_cw_flags_v,
-                        {
-                                "V (VCI present) bit",
-                                "pw_atm_o2o_cw_flags_v", FT_BOOLEAN, 
-                                8, TFS(&flags_set_truth), PW_ATM_O2O_CW_V,
-                                NULL, HFILL 
-                        }
-                },
-                {
-                        &hf_pw_atm_o2o_cw_flags_res,
-                        {
-                                "Reserved bits",
-                                "pw_atm_o2o_cw_flags_res", FT_BOOLEAN, 
-                                8, TFS(&flags_set_truth), PW_ATM_O2O_CW_RES,
-                                NULL, HFILL 
-                        }
-                },
-                {
-                        &hf_pw_atm_o2o_cw_flags_pti,
-                        {
-                                "PTI bits",
-                                "pw_atm_o2o_cw_flags_pti", FT_BOOLEAN, 
-                                8, TFS(&flags_set_truth), PW_ATM_O2O_CW_PTI,
-                                NULL, HFILL 
-                        }
-                },
-                {
-                        &hf_pw_atm_o2o_cw_flags_c,
-                        {
-                                "C (CLP) bit",
-                                "pw_atm_o2o_cw_flags_c", FT_BOOLEAN, 
-                                8, TFS(&flags_set_truth), PW_ATM_O2O_CW_C,
-                                NULL, HFILL 
-                        }
-                },
-                /* FF: AAL5 PDU Frame Mode Control Word fields */
-                {
-                        &hf_pw_atm_aal5_pdu_cw_sequence_number,
-                        {
-                                "AAL5 PDU Frame Mode sequence number", 
-                                "pw_atm_aal5_pdu_cw_sequence_number", 
-                                FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL
-                        }
-                },
-                /* FF: AAL5 SDU Frame Mode Control Word fields */
-                {
-                        &hf_pw_atm_aal5_sdu_cw_sequence_number,
-                        {
-                                "AAL5 SDU Frame Mode sequence number", 
-                                "pw_atm_aal5_sdu_cw_sequence_number", 
-                                FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL
-                        }
-                },
-#endif
-                /* FF: ATM N-to-one Cell Mode Control Word fields */
-                {
-                        &hf_pw_atm_n2o_cw_flags,
-                        {
-                                "ATM N-to-one Cell Mode flags",
-                                "pw_atm_n2o_cw_flags", FT_UINT8, 
-                                BASE_HEX, NULL, 0x0, NULL, HFILL 
-                        }
-
-                },
-                {
-                        &hf_pw_atm_n2o_cw_length,
-                        {
-                                "ATM N-to-one Cell Mode flags",
-                                "pw_atm_n2o_cw_length", FT_UINT8, 
-                                BASE_HEX, NULL, 0x0, NULL, HFILL 
-                        }
-
-                },
-                {
-                        &hf_pw_atm_n2o_cw_sequence_number,
-                        {
-                                "ATM N-to-one Cell Mode sequence number", 
-                                "pw_atm_n2o_cw_sequence_number", FT_UINT16, 
-                                BASE_DEC, NULL, 0x0, NULL, HFILL
-                        }
-                },
-        };
-
-        static gint *ett[] = {
-                &ett_pw_atm
-        };
-
-        proto_pw_atm_n2o_cw = 
-          proto_register_protocol("ATM PW, N-to-one Cell Mode Control Word",
-                                  "ATM PW, N-to-one Cell Mode (with CW)",
-                                  "pw_atm_n2o_cw");
-        proto_pw_atm_n2o_nocw = 
-          proto_register_protocol("ATM PW, N-to-one Cell Mode (no CW)",
-                                  "ATM PW, N-to-one Cell Mode (no CW)",
-                                  "pw_atm_n2o_nocw");
-#if 0
-        proto_pw_atm_o2o_cw = 
-          proto_register_protocol("ATM PW, One-to-one Cell Mode Control Word",
-                                  "ATM PW, One-to-one Cell Mode (with CW)",
-                                  "pw_atm_o2o_cw");
-        proto_pw_atm_o2o_nocw = 
-          proto_register_protocol("ATM PW, One-to-one Cell Mode (no CW)",
-                                  "ATM PW, One-to-one Cell Mode (no CW)",
-                                  "pw_atm_o2o_nocw");
-        proto_pw_atm_aal5_pdu_cw = 
-          proto_register_protocol("ATM PW, AAL5 PDU Frame Mode Control Word",
-                                  "ATM PW, AAL5 PDU Frame Mode (with CW)",
-                                  "pw_atm_aal5_pdu_cw");
-        proto_pw_atm_aal5_pdu_nocw = 
-          proto_register_protocol("ATM PW, AAL5 PDU Frame Mode (no CW)",
-                                  "ATM PW, AAL5 PDU Frame Mode (no CW)",
-                                  "pw_atm_aal5_pdu_nocw");
-        proto_pw_atm_aal5_sdu_cw = 
-          proto_register_protocol("ATM PW, AAL5 SDU Frame Mode Control Word",
-                                  "ATM PW, AAL5 SDU Frame Mode (with CW)",
-                                  "pw_atm_aal5_sdu_cw");
-        proto_pw_atm_aal5_sdu_nocw = 
-          proto_register_protocol("ATM PW, AAL5 SDU Frame Mode (no CW)",
-                                  "ATM PW, AAL5 SDU Frame Mode (no CW)",
-                                  "pw_atm_aal5_sdu_nocw");
-#endif
+       const char *proto_name_column;
+       gint payload_size;
+       gint padding_size;
+       int cells;
+       pwatm_private_data_t pd = PWATM_PRIVATE_DATA_T_INITIALIZER;
+       void * pd_save = pinfo->private_data;
 
-        proto_register_field_array(proto_pw_atm_n2o_cw, hf, array_length(hf));
-        proto_register_subtree_array(ett, array_length(ett));   
-
-        register_dissector("pw_atm_n2o_cw", 
-                           dissect_pw_atm_n2o_cw, 
-                           proto_pw_atm_n2o_cw);
-        register_dissector("pw_atm_n2o_nocw", 
-                           dissect_pw_atm_n2o_nocw, 
-                           proto_pw_atm_n2o_nocw);
-#if 0
-        register_dissector("pw_atm_o2o_cw", 
-                           dissect_pw_atm_o2o_cw, 
-                           proto_pw_atm_o2o_cw);
-        register_dissector("pw_atm_o2o_nocw", 
-                           dissect_pw_atm_o2o_nocw, 
-                           proto_pw_atm_o2o_nocw);
-        register_dissector("pw_atm_aal5_pdu_cw", 
-                           dissect_pw_atm_aal5_pdu_cw, 
-                           proto_pw_atm_aal5_pdu_cw);
-        register_dissector("pw_atm_aal5_pdu_nocw", 
-                           dissect_pw_atm_aal5_pdu_nocw, 
-                           proto_pw_atm_aal5_pdu_nocw);
-        register_dissector("pw_atm_aal5_sdu_cw", 
-                           dissect_pw_atm_aal5_sdu_cw, 
-                           proto_pw_atm_aal5_sdu_cw);
-        register_dissector("pw_atm_aal5_sdu_nocw", 
-                           dissect_pw_atm_aal5_sdu_nocw, 
-                           proto_pw_atm_aal5_sdu_nocw);
-#endif
+       pinfo->private_data = &pd;
+       pd.mode = PWATM_MODE_N1_CW;
+
+       proto_name_column = &shortname_n1_cw[0];
+       if (too_small_packet_or_notpw(tvb,pinfo,tree,proto_n1_cw,proto_name_column))
+       {
+               return;
+       }
+       pd.packet_size = tvb_reported_length_remaining(tvb, 0);
+
+       /* check how "good" is this packet */
+       /* also decide payload length from packet size and CW */
+       pd.props = PWC_PACKET_PROPERTIES_T_INITIALIZER;
+       if (0 != (tvb_get_guint8(tvb, 0) & 0xf0 /*bits03*/))
+       {
+               pd.props |= PWC_CW_BAD_BITS03;
+       }
+       if (0 != (tvb_get_guint8(tvb, 0) & 0x0f /*preferred_cw.flags*/))
+       {
+               pd.props |= PWC_CW_BAD_FLAGS;
+       }
+       if (! pref_n1_cw_extend_cw_length_with_rsvd)
+       {
+               if (0 != (tvb_get_guint8(tvb, 1) & 0xc0 /*preferred_cw.rsvd*/))
+               {
+                       pd.props |= PWC_CW_BAD_RSV;
+               }
+       }
+       {
+               /* RFC4717:
+                * [ If the packet's length (defined as the length of the layer 2 payload
+                * plus the length of the control word) is less than 64 bytes, the
+                * length field MUST be set to the packet's length.  Otherwise, the
+                * length field MUST be set to zero... Note that the length field
+                * is not used in the N-to-one mode and MUST be set to 0. ]
+                *              
+                * Also we allow some "extensions"conducted by pref_xxx.
+                */
+               gint payload_size_from_packet;
+               int cw_len; /*length field from cw*/
+
+               payload_size_from_packet = pd.packet_size - PWC_SIZEOF_CW;
+               if (pref_n1_cw_extend_cw_length_with_rsvd)
+               {
+                       cw_len = tvb_get_guint8(tvb, 1) & 0xff;
+               }
+               else
+               {
+                       cw_len = tvb_get_guint8(tvb, 1) & 0x3f;
+               }
+               
+               /*
+                * Initial assumptions: no padding,
+                * payload size derived from psn packet size.
+                */
+               payload_size = payload_size_from_packet;
+               padding_size = 0;
+               
+               if (0 == cw_len)
+               {
+                       /*keep initial assumptions*/
+               }
+               else if (!pref_n1_cw_allow_cw_length_nonzero)
+               {
+                       pd.props |= PWC_CW_BAD_LEN_MUST_BE_0;
+               }
+               else
+               {
+                       gint payload_size_from_cw;
+                       payload_size_from_cw = cw_len - PWC_SIZEOF_CW;
+                       if (payload_size_from_cw <= 0)
+                       {
+                               pd.props |= PWC_CW_BAD_PAYLEN_LE_0;
+                       }
+                       else if (payload_size_from_cw > payload_size_from_packet)
+                       {
+                               pd.props |= PWC_CW_BAD_PAYLEN_GT_PACKET;
+                       }
+                       else
+                       {
+
+                               payload_size = payload_size_from_cw;
+                               padding_size = payload_size_from_packet - payload_size_from_cw; /* >=0 */
+                               if (padding_size != 0)
+                               {
+                                       pd.props |= PWC_CW_BAD_PADDING_NE_0;
+                                       /*restore sizes*/
+                                       payload_size = payload_size_from_packet;
+                                       padding_size = 0;
+                               }
+                       }
+               }
+               {
+                       gint bad_padding_size;
+                       cells = number_of_cells(pd.mode,pd.submode,payload_size,&bad_padding_size);
+                       if (0 == cells || 0 != bad_padding_size)
+                       {
+                               pd.props |= PWC_PAY_SIZE_BAD;
+                       }
+               }
+       }
+
+       if (tree)
+       {
+               proto_item* item;
+               item = proto_tree_add_item(tree, proto_n1_cw, tvb, 0, -1, FALSE);
+               pwc_item_append_text_n_items(item,cells,"good ATM cell");
+               {
+                       proto_tree* tree;
+                       tree = proto_item_add_subtree(item, ett_encaps);
+                       {
+                               proto_item* item;
+                               item = proto_tree_add_boolean(tree, hf_pw_type_n1_cw, tvb, 0, 0, TRUE);
+                               PROTO_ITEM_SET_GENERATED(item);
+                               item = proto_tree_add_int(tree, hf_n1_cw_ncells, tvb, 0, 0, cells);
+                               PROTO_ITEM_SET_GENERATED(item);
+                       }
+               }
+               if (pd.props & PWC_PAY_SIZE_BAD)
+               {
+                       if (payload_size != 0)
+                       {
+                               expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR,
+                                       "PW ATM cell [%.3d] is broken",(int)cells);
+                       }
+                       expert_add_info_format(pinfo, item, PI_MALFORMED
+                               , (payload_size == 0) ? PI_ERROR : PI_NOTE
+                               ,"PW payload size (%d) must be <>0 and multiple of %d"
+                               ,(int)payload_size,(int)SIZEOF_N1_PW_CELL);
+               }
+       }
+       
+       {
+               tvbuff_t* tvb_2;
+               tvb_2 = tvb_new_subset(tvb, 0, PWC_SIZEOF_CW, PWC_SIZEOF_CW);
+               call_dissector(dh_control_word, tvb_2, pinfo, tree);
+               
+               tvb_2 = tvb_new_subset(tvb, PWC_SIZEOF_CW, -1, -1);
+               dissect_payload_and_padding(tvb_2,pinfo,tree,payload_size,padding_size);
+       }
+
+       /* fill columns in Packet List */
+       /* overwrite everything written by sub-dissectors */
+       if (check_col(pinfo->cinfo, COL_PROTOCOL))
+       {
+               col_set_str(pinfo->cinfo, COL_PROTOCOL, proto_name_column);
+       }
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+       {
+               col_clear(pinfo->cinfo, COL_INFO);
+               col_append_pw_info(pinfo, payload_size, cells, padding_size);
+       }
+       
+       pinfo->private_data = pd_save;
+       return;
 }
 
-void
-proto_reg_handoff_pw_atm(void)
+
+static
+void dissect_n1_nocw(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
 {
-        dissector_handle_t pw_atm_n2o_cw_h; 
-        dissector_handle_t pw_atm_n2o_nocw_h;
-#if 0
-        dissector_handle_t pw_atm_o2o_cw_h;
-        dissector_handle_t pw_atm_o2o_nocw_h;
-        dissector_handle_t pw_atm_aal5_pdu_cw_h;
-        dissector_handle_t pw_atm_aal5_pdu_nocw_h;
-        dissector_handle_t pw_atm_aal5_sdu_cw_h;
-        dissector_handle_t pw_atm_aal5_sdu_nocw_h;
-#endif
+       const char *proto_name_column = &shortname_n1_nocw[0];
+       gint payload_size;
+       int cells;
+       pwatm_private_data_t pd = PWATM_PRIVATE_DATA_T_INITIALIZER;
+       void * pd_save = pinfo->private_data;
 
-        pw_atm_n2o_cw_h = find_dissector("pw_atm_n2o_cw");
-        dissector_add("mpls.label", LABEL_INVALID, pw_atm_n2o_cw_h);
+       pinfo->private_data = &pd;
+       pd.mode = PWATM_MODE_N1_NOCW;
+       pd.packet_size = tvb_reported_length_remaining(tvb, 0);
 
-        pw_atm_n2o_nocw_h = find_dissector("pw_atm_n2o_nocw");
-        dissector_add("mpls.label", LABEL_INVALID, pw_atm_n2o_nocw_h);
-#if 0
-        pw_atm_o2o_cw_h = find_dissector("pw_atm_o2o_cw");
-        dissector_add("mpls.label", LABEL_INVALID, pw_atm_o2o_cw_h);
+       /* check how "good" is this packet */
+       /* also decide payload length from packet size */
+       pd.props = PWC_PACKET_PROPERTIES_T_INITIALIZER;
+       payload_size = pd.packet_size;
+       {
+               gint bad_padding_size;
+               cells = number_of_cells(pd.mode,pd.submode,pd.packet_size,&bad_padding_size);
+               if (cells == 0 || bad_padding_size != 0)
+               {
+                       pd.props |= PWC_PAY_SIZE_BAD;
+               }
+       }
 
-        pw_atm_o2o_nocw_h = find_dissector("pw_atm_o2o_nocw");
-        dissector_add("mpls.label", LABEL_INVALID, pw_atm_o2o_nocw_h);
+       if (tree)
+       {
+               proto_item* item;
+               item = proto_tree_add_item(tree, proto_n1_nocw, tvb, 0, -1, FALSE);
+               pwc_item_append_text_n_items(item,cells,"ATM cell");
+               {
+                       proto_tree* tree;
+                       tree = proto_item_add_subtree(item, ett_encaps);
+                       {
+                               proto_item* item;
+                               item = proto_tree_add_boolean(tree, hf_pw_type_n1_nocw, tvb, 0, 0, TRUE);
+                               PROTO_ITEM_SET_GENERATED(item);
+                               item = proto_tree_add_int(tree, hf_n1_nocw_ncells, tvb, 0, 0, cells);
+                               PROTO_ITEM_SET_GENERATED(item);
+                       }
+               }
+               if (pd.props & PWC_PAY_SIZE_BAD)
+               {
+                       if (payload_size != 0)
+                       {
+                               expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR,
+                                       "Last PW ATM cell [%.3d] is broken",(int)cells);
+                       }
+                       expert_add_info_format(pinfo, item, PI_MALFORMED
+                               , (payload_size == 0) ? PI_ERROR : PI_NOTE
+                               ,"PW payload size (%d) must be <>0 and multiple of %d"
+                               ,(int)payload_size,(int)SIZEOF_N1_PW_CELL);
+               }
+       }
+       
+       dissect_payload_and_padding(tvb,pinfo,tree,payload_size,0);
+       
+       /* fill columns in Packet List */
+       /* overwrite everything written by sub-dissectors */
+       if (check_col(pinfo->cinfo, COL_PROTOCOL))
+       {
+               col_set_str(pinfo->cinfo, COL_PROTOCOL, proto_name_column);
+       }
 
-        pw_atm_aal5_pdu_cw_h = find_dissector("pw_atm_aal5_pdu_cw");
-        dissector_add("mpls.label", LABEL_INVALID, pw_atm_aal5_pdu_cw_h);
+       if (check_col(pinfo->cinfo, COL_INFO))
+       {
+               col_clear(pinfo->cinfo, COL_INFO);
+               col_append_pw_info(pinfo, payload_size, cells, 0);
+       }
 
-        pw_atm_aal5_pdu_nocw_h = find_dissector("pw_atm_aal5_pdu_nocw");
-        dissector_add("mpls.label", LABEL_INVALID, pw_atm_aal5_pdu_nocw_h);
+       pinfo->private_data = pd_save;
+       return;
+}
 
-        pw_atm_aal5_sdu_cw_h = find_dissector("pw_atm_aal5_sdu_cw");
-        dissector_add("mpls.label", LABEL_INVALID, pw_atm_aal5_sdu_cw_h);
 
-        pw_atm_aal5_sdu_nocw_h = find_dissector("pw_atm_aal5_sdu_nocw");
-        dissector_add("mpls.label", LABEL_INVALID, pw_atm_aal5_sdu_nocw_h);
-#endif
+static
+void proto_item_append_text_cwb3_fields(proto_item * item, const pwatm_private_data_t * const pd)
+{
+       if (NULL == item) return;
+       DISSECTOR_ASSERT(NULL != pd);
+       if (pd->cwb3.m   >= 0)
+               proto_item_append_text(item, "M:%.1u  "  , (unsigned)(pd->cwb3.m));
+       if (pd->cwb3.v   >= 0)
+               proto_item_append_text(item, "V:%.1u  "  , (unsigned)(pd->cwb3.v));
+       if (pd->cwb3.rsv >= 0)
+               proto_item_append_text(item, "RSV:%.1u  ", (unsigned)(pd->cwb3.rsv));
+       if (pd->cwb3.u >= 0)
+               proto_item_append_text(item, "U:%.1u  "  , (unsigned)(pd->cwb3.u));
+       if (pd->cwb3.e >= 0)
+               proto_item_append_text(item, "EFCI:%.1u  ",(unsigned)(pd->cwb3.e));
+       if (pd->cwb3.clp >= 0)
+               proto_item_append_text(item, "CLP:%.1u  ", (unsigned)(pd->cwb3.clp));
+       return;
+}
+               
+
+static
+void dissect_control_word(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+       pwatm_private_data_t* pd;
+       pd = pinfo->private_data;
+       DISSECTOR_ASSERT(pd != NULL);
+
+       /*
+        * NB: do not touch columns -- keep info from previous dissector
+        */
+
+       {
+               gint size;
+               size = tvb_reported_length_remaining(tvb, 0);
+               if (size < PWC_SIZEOF_CW)
+               {
+                       if (tree)
+                       {
+                               proto_item  *item;
+                               item = proto_tree_add_item(tree, proto_control_word, tvb, 0, -1, FALSE);
+                               expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR,
+                                       "Packet (size: %d) is too small to carry MPLS PW Control Word"
+                                       ,(int)size);
+                       }
+                       return;
+               }
+       }
+
+       if (tree)
+       {
+               proto_item* item_top;
+               item_top = proto_tree_add_item(tree, proto_control_word, tvb, 0, -1, FALSE);
+               pwc_item_append_cw(item_top,tvb_get_ntohl(tvb, 0),FALSE);
+               
+               {
+                       proto_tree* tree;
+                       tree = proto_item_add_subtree(item_top, ett_cw);
+                       {
+                               proto_item* item;
+                               /* bits 0..3 */
+                               item = proto_tree_add_item(tree, hf_cw_bits03, tvb, 0, 1, FALSE);
+                               if (pd->props & PWC_CW_BAD_BITS03)
+                               {
+                                       /* add item to tree (and show it) only if its value is wrong*/
+                                       expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR
+                                               ,"Bits 0..3 of Control Word must be 0");
+                               }
+                               else
+                               {
+                                       PROTO_ITEM_SET_HIDDEN(item); /* show only in error cases */
+                               }
+               
+                               /* flags */
+                               if (MODE_N1(pd->mode))
+                               {
+                                       item = proto_tree_add_item(tree, hf_pref_cw_flags, tvb, 0, 1, FALSE);
+                                       if (pd->props & PWC_CW_BAD_FLAGS)
+                                       {
+                                               expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR
+                                                       ,"Flags must be 0 for PW ATM N:1 encapsulation");
+                                       }
+                               }
+                               if (pd->mode == PWATM_MODE_AAL5_SDU)
+                               {
+                                       item = proto_tree_add_item(tree, hf_pref_cw_a5s_t, tvb, 0, 1, FALSE);
+                                       item = proto_tree_add_item(tree, hf_pref_cw_a5s_e, tvb, 0, 1, FALSE);
+                                       item = proto_tree_add_item(tree, hf_pref_cw_a5s_c, tvb, 0, 1, FALSE);
+                                       item = proto_tree_add_item(tree, hf_pref_cw_a5s_u, tvb, 0, 1, FALSE);
+                                       /*
+                                        * rfc4717: [When FRF.8.1 Frame Relay/ATM PVC Service Interworking [RFC3916]
+                                        * traffic is being transported, the CPCS-UU Least Significant Bit
+                                        * (LSB) of the AAL5 CPCS-PDU may contain the Frame Relay C/R bit.
+                                        * The ingress router, PE1, SHOULD copy this bit to the U bit of the
+                                        * control word.  The egress router, PE2, SHOULD copy the U bit to
+                                        * the CPCS-UU Least Significant Bit (LSB) of the AAL5 CPCS PDU.]
+                                        *
+                                        * Let's remember this bit (and then transfer it to ATM dissector).
+                                        */
+                                       pd->aal5_sdu_frame_relay_cr_bit =
+                                               (0 == (tvb_get_guint8(tvb, 0) & 0x01 /*preferred_cw.U*/))
+                                               ? FALSE : TRUE;
+                               }
+                               
+                               /* reserved bits */
+                               if (MODE_11_OR_AAL5_PDU(pd->mode)
+                               || (MODE_N1(pd->mode) && !pref_n1_cw_extend_cw_length_with_rsvd)
+                                       /* for N:1 add RSV only if it is NOT used in length */
+                               || ((pd->mode == PWATM_MODE_AAL5_SDU) && !pref_aal5_sdu_extend_cw_length_with_rsvd)
+                                       /* for AAl5 SDU add RSV only if it is NOT used in length */)
+                               {
+                                       if (MODE_11_OR_AAL5_PDU(pd->mode))
+                                       {
+                                               item = proto_tree_add_item(tree
+                                                       ,hf_generic_cw_rsv, tvb, 0, 1, FALSE);
+                                       }
+                                       else
+                                       { /*preferred cw*/
+                                               item = proto_tree_add_item(tree
+                                                       ,hf_pref_cw_rsv, tvb, 1, 1, FALSE);
+                                       }
+
+                                       if (pd->props & PWC_CW_BAD_RSV)
+                                       {
+                                               expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR
+                                                       ,"Reserved bits in Control Word must be 0");
+                                       }
+                                       else
+                                       {
+                                               PROTO_ITEM_SET_HIDDEN(item); /*...and show only in error cases */
+                                       }
+                               }
+                               
+                               /* length */
+                               if (MODE_N1(pd->mode)
+                               || (PWATM_MODE_AAL5_SDU == pd->mode))
+                               {
+                                       {
+                                               int hf_len = hf_pref_cw_len;
+                                               if (MODE_N1(pd->mode))
+                                               {
+                                                       if (pref_n1_cw_extend_cw_length_with_rsvd)
+                                                               hf_len = hf_pref_cw_rsvlen;
+                                               }
+                                               else /*PW_MODE_AAL5_SDU*/
+                                               {
+                                                       if (pref_aal5_sdu_extend_cw_length_with_rsvd)
+                                                               hf_len = hf_pref_cw_rsvlen;
+                                               }
+                                               item = proto_tree_add_item(tree, hf_len, tvb, 1, 1, FALSE);
+                                       }
+                                       if (pd->props & PWC_CW_BAD_LEN_MUST_BE_0)
+                                       {
+                                               expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR
+                                                       ,"Bad Length: must be 0 for this encapsulation");
+                                       }
+                                       if (pd->props & PWC_CW_BAD_PAYLEN_LE_0)
+                                       {
+                                               expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR
+                                                       ,"Bad Length: too small, must be >= %d"
+                                                       ,(int)(PWC_SIZEOF_CW+SIZEOF_N1_PW_CELL));
+                                       }
+                                       if (pd->props & PWC_CW_BAD_PAYLEN_GT_PACKET)
+                                       {
+                                               expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR
+                                                       ,"Bad Length: must be <= than PSN packet size (%d)"
+                                                       ,(int)pd->packet_size);
+                                       }
+                                       if (pd->props & PWC_CW_BAD_PADDING_NE_0)
+                                       {
+                                               expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR
+                                                       ,"Bad Length: must be == PSN packet size (%d), no padding allowed"
+                                                       ,(int)pd->packet_size);
+                                       }
+                               }
+                               
+                               /* sequence number */
+                               (void)proto_tree_add_item(tree, hf_cw_seq, tvb
+                                       ,MODE_11_OR_AAL5_PDU(pd->mode) ? 1 : 2, 2, FALSE);
+                               
+                               /* atm-specific byte */
+                               if (MODE_11(pd->mode))
+                               {
+                                       (void) proto_tree_add_item(tree, hf_gen_cw_atmbyte, tvb, 3, 1, FALSE);
+                                       /*
+                                        * no need to highlight item in the tree, therefore
+                                        * expert_add_info_format() is not used here.
+                                        */
+                                       item = proto_tree_add_text(tree,tvb,3,1
+                                               ,"ATM-specific byte of CW is fully dissected below as %s%s"
+                                               ,(PWATM_MODE_11_VPC == pd->mode) ? "a part of " : ""
+                                               ,"PW ATM Cell Header [000]");
+                                       PROTO_ITEM_SET_GENERATED(item);
+                                       /*
+                                        * Note: if atm-specific byte contains something wrong
+                                        * (e.g. non-zero RSV or inadequate V), CW is not
+                                        * marked as "bad".
+                                        */
+                               }
+                               
+                               /*3rd byte of CW*/
+                               if (PWATM_MODE_AAL5_PDU == pd->mode)
+                               {
+                                       tvbuff_t* tvb_2;
+                                       tvb_2 = tvb_new_subset(tvb, (PWC_SIZEOF_CW-1), -1, -1);
+                                       call_dissector(dh_cell_header, tvb_2, pinfo, tree);
+                                       proto_item_append_text(item_top,", ");
+                                       proto_item_append_text_cwb3_fields(item_top,pd);
+                               }
+                       }
+               }
+       }
+       return;
+}
+
+
+/*
+ * This function is also used to dissect 3rd byte of CW in AAL5 PDU mode.
+ */
+static
+int dissect_cell_header(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+       pwatm_private_data_t * pd;
+       gboolean is_enough_data;
+       int dissect_size;
+       
+       pd = pinfo->private_data;
+       DISSECTOR_ASSERT (NULL != pd);
+       pd->vpi = pd->vci = pd->pti = -1;
+       pd->cwb3.clp = pd->cwb3.m = pd->cwb3.v = pd->cwb3.rsv = pd->cwb3.u = pd->cwb3.e = -1;
+               
+       if (PWATM_MODE_AAL5_PDU == pd->mode)
+       {
+               if (tvb_reported_length_remaining(tvb, 0) < 1)
+               {
+                       is_enough_data = FALSE;
+                       dissect_size = 0;
+               }
+               else
+               {
+                       is_enough_data = TRUE;
+                       dissect_size = 1;
+               }
+       }
+       else
+       {
+               gint size;
+               size = tvb_reported_length_remaining(tvb, 0);
+
+               if (size < pw_cell_header_size(pd->mode,pd->submode))
+               {
+                       is_enough_data = FALSE;
+                       dissect_size = size;
+               }
+               else
+               {
+                       is_enough_data = TRUE;
+                       dissect_size = pw_cell_header_size(pd->mode,pd->submode);
+               }
+       }
+
+       /*
+        * NB: do not touch columns -- keep info from previous dissector
+        */
+       
+       /* Collect info for upper-level dissectors regardless of
+        * the presence of the tree
+        */
+       if (is_enough_data)
+       {
+               guint8 tmp8;
+               switch (pd->mode)
+               {
+               case PWATM_MODE_AAL5_SDU:
+                       DISSECTOR_ASSERT(pd->submode == PWATM_SUBMODE_ADMIN_CELL);
+                       /*fallthrough for ATM admin cell submode only*/
+               case PWATM_MODE_N1_CW:
+               case PWATM_MODE_N1_NOCW:
+                       pd->vpi         = (tvb_get_ntohs (tvb, 0) >> 4);
+                       pd->vci         = (tvb_get_ntoh24(tvb, 1) >> 4) & 0xffff;
+                       tmp8            = (tvb_get_guint8(tvb, 3));
+                       pd->pti         = (tmp8 >> 1) & 0x07;
+                       pd->cwb3.clp    = (tmp8 >> 0) & 0x01;
+                       UPDATE_CUMULATIVE_VALUE(pd->cumulative.vpi,pd->vpi);
+                       UPDATE_CUMULATIVE_VALUE(pd->cumulative.vci,pd->vci);
+                       UPDATE_CUMULATIVE_VALUE(pd->cumulative.pti,pd->pti);
+                       UPDATE_CUMULATIVE_VALUE(pd->cumulative.clp,pd->cwb3.clp);
+                       /*
+                        * OAM cell mode is always used for aal5_sdu/admin_cell mode,
+                        * even if pti indicates user cell.
+                        */
+                       pd->cell_mode_oam =
+                               (pd->mode == PWATM_MODE_AAL5_SDU && pd->submode == PWATM_SUBMODE_ADMIN_CELL)
+                               || PTI_IS_ADMIN(pd->pti);
+                       break;
+               case PWATM_MODE_11_VPC:
+                       pd->vci = tvb_get_ntohs(tvb, 1);
+                       UPDATE_CUMULATIVE_VALUE(pd->cumulative.vci,pd->vci);
+                       /*fallthrough*/
+               case PWATM_MODE_11_VCC:
+                       tmp8    = (tvb_get_guint8(tvb, 0));
+                       pd->cwb3.m      = (tmp8 >> 7) & 0x1;
+                       pd->cwb3.v      = (tmp8 >> 6) & 0x1;
+                       pd->cwb3.rsv    = (tmp8 >> 4) & 0x3;
+                       pd->pti         = (tmp8 >> 1) & 0x7;
+                       pd->cwb3.clp    = (tmp8 >> 0) & 0x1;
+                       UPDATE_CUMULATIVE_VALUE(pd->cumulative.pti,pd->pti);
+                       UPDATE_CUMULATIVE_VALUE(pd->cumulative.clp,pd->cwb3.clp);
+                       /*
+                        * OAM cell mode is possible if packet contains atm cell (m == 0).
+                        */
+                       pd->cell_mode_oam = PTI_IS_ADMIN(pd->pti) && (pd->cwb3.m == 0);
+                       break;
+               case PWATM_MODE_AAL5_PDU:
+                       tmp8            = (tvb_get_guint8(tvb, 0));
+                       pd->cwb3.m      = (tmp8 >> 7) & 0x1;
+                       pd->cwb3.v      = (tmp8 >> 6) & 0x1;
+                       pd->cwb3.rsv    = (tmp8 >> 3) & 0x7;
+                       pd->cwb3.u      = (tmp8 >> 2) & 0x1;
+                       pd->cwb3.e      = (tmp8 >> 1) & 0x1;
+                       pd->cwb3.clp    = (tmp8 >> 0) & 0x1;
+                       UPDATE_CUMULATIVE_VALUE(pd->cumulative.clp,pd->cwb3.clp);
+                       break;
+               default:
+                       DISSECTOR_ASSERT_NOT_REACHED();
+                       break;
+               }
+       }
+       
+       if (tree)
+       {
+               proto_item* item;
+               
+               item = proto_tree_add_item(tree, proto_cell_header, tvb
+                       ,0
+                       ,dissect_size
+                       ,FALSE);
+               if (PWATM_MODE_AAL5_PDU == pd->mode)
+               {
+                       proto_item_set_text(item, "Third byte of Control Word"); /*overwrite heading line*/
+                       /* cwb3 fileds are appended to CW heading line, not here */
+               }
+               else
+               {
+                       proto_item_append_text(item," [%.3d]",pd->pw_cell_number);
+                       proto_item_append_text(item,", ");
+                       if (pd->vpi >= 0)
+                               proto_item_append_text(item, "VPI:%.4u  ", (unsigned)(pd->vpi));
+                       if (pd->vci >= 0)
+                               proto_item_append_text(item, "VCI:%.5u  ", (unsigned)(pd->vci));
+                       if (pd->pti >= 0)
+                               proto_item_append_text(item, "PTI:%.1u  ", (unsigned)(pd->pti));
+                       proto_item_append_text_cwb3_fields(item,pd);
+               }
+               
+               {       
+                       proto_tree* tree;
+                       tree = proto_item_add_subtree(item, ett_cell_header);
+                       if (is_enough_data)
+                       {
+                               proto_item* item;
+                               if (MODE_N1(pd->mode)
+                                   || (pd->mode == PWATM_MODE_AAL5_SDU && pd->submode == PWATM_SUBMODE_ADMIN_CELL))
+                               {
+                                       (void) proto_tree_add_uint(tree, hf_cell_h_vpi, tvb, 0, 2, (unsigned)pd->vpi);
+                                       (void) proto_tree_add_uint(tree, hf_cell_h_vci, tvb, 1, 3, (unsigned)pd->vci);
+
+                                       item = proto_tree_add_item(tree, hf_cell_h_pti, tvb, 3, 1, FALSE);
+                                       if (NULL == match_strval(pd->pti,atm_pt_vals))
+                                       {
+                                               expert_add_info_format(pinfo, item, PI_UNDECODED, PI_WARN,
+                                                       "Unknown value of PTI field (%d) in the ATM cell header",
+                                                       pd->pti);
+                                       }
+                                       else if (pd->mode == PWATM_MODE_AAL5_SDU && !PTI_IS_ADMIN(pd->pti))
+                                       {
+                                               expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR,
+                                                       "ATM admin cell is transerred;"
+                                                       " PTI field (%d) should be 4, 5 or 6.",
+                                                       pd->pti);
+                                       }
+                                       
+                                       (void) proto_tree_add_item(tree, hf_cell_h_clp, tvb, 3, 1, FALSE);
+                               }
+                               else if (MODE_11_OR_AAL5_PDU(pd->mode))
+                               {
+                                       item = proto_tree_add_item(tree, hf_cell_h_m  , tvb, 0, 1, FALSE);
+                                       if ((0 != pd->cwb3.m) && MODE_11(pd->mode))
+                                       {
+                                               expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR
+                                                       ,"1:1 mode:"
+                                                       " M bit must be 0 to distinguish from AAL5 PDU mode");
+                                       }
+
+                                       item = proto_tree_add_item(tree, hf_cell_h_v  , tvb, 0, 1, FALSE);
+                                       if ((0 == pd->cwb3.v) && (PWATM_MODE_11_VPC == pd->mode))
+                                       {
+                                               expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR
+                                                       ,"1:1 VPC mode:"
+                                                       " V bit must be 1 to indicate that VCI is present");
+                                       }
+                                       if ((0 != pd->cwb3.v) && (PWATM_MODE_11_VCC == pd->mode))
+                                       {
+                                               expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR
+                                                       ,"1:1 VCC mode:"
+                                                       " V bit must be 0 to indicate that VCI is absent");
+                                       }
+                                       if ((0 != pd->cwb3.v) && (PWATM_MODE_AAL5_PDU == pd->mode))
+                                       {
+                                               expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR
+                                                       ,"AAL5 PDU mode:"
+                                                       " V bit must be 0 to indicate that VCI is absent");
+                                       }
+                                       
+                                       item = proto_tree_add_item(tree
+                                               ,(PWATM_MODE_AAL5_PDU == pd->mode)
+                                                       ? hf_aal5_pdu_rsv
+                                                       : hf_cell_h_rsv
+                                               ,tvb, 0, 1, FALSE);
+                                       if (0 != pd->cwb3.rsv)
+                                       {
+                                               expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR
+                                                       ,"Reserved bits in the 3rd byte of CW must be 0");
+                                       }
+                                       else
+                                       {
+                                               PROTO_ITEM_SET_HIDDEN(item); /*...and show only in error cases */
+                                       }
+                       
+                                       if (MODE_11(pd->mode))
+                                       {
+                                               item = proto_tree_add_item(tree, hf_cell_h_pti, tvb, 0, 1, FALSE);
+                                               if (NULL == match_strval(pd->pti,atm_pt_vals))
+                                               {
+                                                       expert_add_info_format(pinfo, item, PI_UNDECODED, PI_WARN,
+                                                               "Unknown value of PTI field (%d) in the atm-specific byte"
+                                                               ,pd->pti);
+                                               }
+                                       }
+                                       else
+                                       {
+                                               (void) proto_tree_add_item(tree, hf_aal5_pdu_u, tvb, 0, 1, FALSE);
+                                               (void) proto_tree_add_item(tree, hf_aal5_pdu_e, tvb, 0, 1, FALSE);
+                                       }
+                                       
+                                       (void) proto_tree_add_item(tree, hf_cell_h_clp, tvb, 0, 1, FALSE);
+                                       
+                                       if (PWATM_MODE_11_VPC == pd->mode)
+                                       {
+                                               (void) proto_tree_add_uint(tree, hf_cell_h_vci, tvb, 1, 2
+                                                       ,(unsigned)pd->vci);
+                                       }
+                               }
+                               else
+                               {
+                                       DISSECTOR_ASSERT_NOT_REACHED();
+                               }
+                       }
+                       else
+                       {
+                               expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR
+                                       ,"Not enough data (size: %d), impossible to decode"
+                                       ,(int)dissect_size);
+                       }
+               }
+       }
+       return dissect_size;
+}
+
+
+static
+int dissect_cell(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+       gboolean is_enough_data;
+       int dissect_size;
+               
+       {
+               gint size;
+               size = tvb_reported_length_remaining(tvb, 0);
+               if (size < SIZEOF_ATM_CELL_PAYLOAD)
+               {
+                       is_enough_data = FALSE;
+                       dissect_size = size;
+               }
+               else
+               {
+                       is_enough_data = TRUE;
+                       dissect_size = SIZEOF_ATM_CELL_PAYLOAD;
+               }
+       }
+
+       /*
+        * NB: do not touch columns -- keep info from previous dissector
+        */
+       
+       if (tree)
+       {
+               proto_item* item;
+               item = proto_tree_add_item(tree, proto_cell, tvb, 0, dissect_size, FALSE);
+               {
+                       pwatm_private_data_t * pd;
+                       pd = pinfo->private_data;
+                       if (NULL != pd)
+                       {
+                               proto_item_append_text(item," [%.3d]",pd->pw_cell_number);
+                       }
+               }
+               pwc_item_append_text_n_items(item,dissect_size,"byte");
+               if (!is_enough_data)
+               {
+                       expert_add_info_format(pinfo, item, PI_MALFORMED, PI_ERROR
+                               ,"Bad length of cell payload: must be == %d"
+                               ,(int)SIZEOF_ATM_CELL_PAYLOAD);
+               }
+               
+               {
+                       proto_tree* tree;
+                       tvbuff_t* tvb_d;
+                       tree = proto_item_add_subtree(item, ett_cell);
+                       tvb_d = tvb_new_subset(tvb, 0, dissect_size, -1);
+                       call_dissector(dh_data, tvb_d, pinfo, tree);
+                       item = proto_tree_add_int(tree, hf_cell_payload_len, tvb, 0, 0, dissect_size);
+                       PROTO_ITEM_SET_HIDDEN(item);
+               }
+       }
+       return dissect_size;
+}
+
+
+void proto_register_pw_atm_ata(void)
+{
+       static const value_string clp_vals[] = {
+               { 0, "High priority" },
+               { 1, "Low priority" },
+               { 0, NULL }
+       };
+       static const value_string m_vals[] = {
+               { 0, "ATM cell" },
+               { 1, "AAL5 payload" },
+               { 0, NULL }
+       };
+       static const value_string u_vals[] = {
+               { 0, "This frame does not contain the last cell of AAL5 PDU" },
+               { 1, "This frame contains the last cell of AAL5 PDU" },
+               { 0, NULL }
+       };
+       static const value_string e_vals[] = {
+               { 0, "Congestion is not experienced" },
+               { 1, "Congestion is experienced for one or more ATM AAL5 cells" },
+               { 0, NULL }
+       };
+       static hf_register_info hfa_cell_header[] = {
+               { &hf_cell_h_vpi        ,{"VPI"                         ,"atm.vpi"
+                                       ,FT_UINT16      ,BASE_DEC       ,NULL           ,0
+                                       ,NULL                                           ,HFILL }}
+               ,{ &hf_cell_h_vci       ,{"VCI"                         ,"atm.vci"
+                                       ,FT_UINT16      ,BASE_DEC       ,NULL           ,0
+                                       ,NULL                                           ,HFILL }}
+               ,{ &hf_cell_h_pti       ,{"Payload Type"                ,"atm.pti"
+                                       ,FT_UINT8       ,BASE_DEC       ,VALS(atm_pt_vals),0x0e
+                                       ,"The 3-bit Payload Type Identifier (PTI) incorporates ATM Layer"
+                                       " PTI coding of the cell.  These bits are set to the value of the"
+                                       " PTI of the encapsulated ATM cell."
+                                                                                       ,HFILL }}
+               ,{ &hf_cell_h_clp       ,{"Cell Loss Priority"          ,"atm.clp"
+                                       ,FT_UINT8       ,BASE_DEC       ,VALS(clp_vals) ,0x01
+                                       ,"The Cell Loss Priority (CLP) field indicates CLP value"
+                                       " of the encapsulated cell."
+                                                                                       ,HFILL }}
+               ,{ &hf_cell_h_m         ,{"Transport Mode"              ,"atm.pw_control_byte.m"
+                                       ,FT_UINT8       ,BASE_DEC       ,VALS(m_vals)   ,0x80
+                                       ,"Bit (M) of the control byte indicates  whether the packet"
+                                       " contains an ATM cell or a frame payload. If set to 0,"
+                                       " the packet contains an ATM cell. If set to 1, the PDU"
+                                       " contains an AAL5 payload."                    
+                                                                                       ,HFILL }}
+               ,{ &hf_cell_h_v         ,{"VCI Present"                 ,"atm.pw_control_byte.v"
+                                       ,FT_BOOLEAN     ,8              ,TFS(&tfs_yes_no),0x40
+                                       ,"Bit (V) of the control byte indicates whether the VCI field"
+                                       " is present in the packet. If set to 1, the VCI field is present"
+                                       " for the cell. If set to 0, no VCI field is present."
+                                                                                       ,HFILL }}
+               ,{ &hf_cell_h_rsv       ,{"Reserved bits"               ,"atm.pw_control_byte.rsv"
+                                       ,FT_UINT8       ,BASE_DEC       ,NULL           ,0x30
+                                       ,"The reserved bits should be set to 0 at the transmitter and"
+                                       " ignored upon reception."
+                                                                                       ,HFILL }}
+               ,{ &hf_aal5_pdu_rsv     ,{"Reserved bits"               ,"atm.pw_control_byte.rsv"
+                                       ,FT_UINT8       ,BASE_DEC       ,NULL           ,0x38
+                                       ,"The reserved bits should be set to 0 at the transmitter and"
+                                       " ignored upon reception."
+                                                                                       ,HFILL }}
+               ,{ &hf_aal5_pdu_u       ,{"U bit"                       ,"atm.pw_control_byte.u"
+                                       ,FT_UINT8       ,BASE_DEC       ,VALS(u_vals)   ,0x04
+                                       ,"Indicates whether this frame contains the last cell of"
+                                       " an AAL5 PDU and represents the value of the ATM User-to-User"
+                                       " bit for the last ATM cell of the PSN frame. Note: The ATM"
+                                       " User-to-User bit is the least significant bit of the PTI"
+                                       " in the ATM header."
+                                                                                       ,HFILL }}
+               ,{ &hf_aal5_pdu_e       ,{"EFCI"                        ,"atm.pw_control_byte.efci"
+                                       ,FT_UINT8       ,BASE_DEC       ,VALS(e_vals)   ,0x02
+                                       ,"EFCI is set to the EFCI state of the last cell of the"
+                                       " AAL5 PDU or AAL5 fragment. Note: The EFCI state is"
+                                       " indicated in the middle bit of each ATM cell's PTI."
+                                                                                       ,HFILL }}
+       };
+static hf_register_info hfa_cell[] = {
+       {&hf_cell_payload_len   ,{"Length"                      ,"atm.cell.len"
+                                       ,FT_INT32       ,BASE_DEC       ,NULL           ,0
+                                       ,NULL                                           ,HFILL }}
+       };
+       
+       #define HF_INITIALIZER_NCELLS(hf_handle)\
+               {&(hf_handle)           ,{"Number of good encapsulated cells","pw.atm.cells"\
+                                       ,FT_INT32       ,BASE_DEC       ,NULL           ,0\
+                                       ,NULL                                           ,HFILL }}
+
+       #define HF_INITIALIZER_PWTYPE(hf_handle,name)\
+               { &hf_handle            ,{name                          ,name\
+                                       ,FT_BOOLEAN     ,0              ,NULL           ,0x0\
+                                       ,"Identifies type of ATM PW. May be used for filtering.",HFILL}}
+
+
+       static hf_register_info hfa_n1_nocw[] = {
+                HF_INITIALIZER_NCELLS(hf_n1_nocw_ncells)
+               ,HF_INITIALIZER_PWTYPE(hf_pw_type_n1_nocw,"pw.type.atm.n1nocw")
+       };
+
+       static hf_register_info hfa_n1_cw[] = {
+                HF_INITIALIZER_NCELLS(hf_n1_cw_ncells)
+               ,HF_INITIALIZER_PWTYPE(hf_pw_type_n1_cw,"pw.type.atm.n1cw")
+       };
+
+       static hf_register_info hfa_11_aal5pdu[] = {
+                HF_INITIALIZER_NCELLS(hf_11_ncells)
+               ,HF_INITIALIZER_PWTYPE(hf_pw_type_11_vcc,"pw.type.atm.11vcc")
+               ,HF_INITIALIZER_PWTYPE(hf_pw_type_11_vpc,"pw.type.atm.11vpc")
+               ,HF_INITIALIZER_PWTYPE(hf_pw_type_aal5_pdu,"pw.type.atm.aal5pdu")
+       };
+
+       static hf_register_info hfa_aal5_sdu[] = {
+               HF_INITIALIZER_PWTYPE(hf_pw_type_aal5_sdu,"pw.type.atm.aal5sdu")
+       };
+
+       static const value_string a5s_t_vals[] = {
+               { 0, "AAL5 payload" },
+               { 1, "ATM admin cell" },
+               { 0, NULL }
+       };
+       
+       static const value_string a5s_e_vals[] = {
+               { 0, "No congestion" },
+               { 1, "Congestion experienced" },
+               { 0, NULL }
+       };
+       
+       static hf_register_info hfa_cw[] = {
+               { &hf_cw_bits03         ,{"Bits 0 to 3"                 ,"pw.cw.bits03"
+                                       ,FT_UINT8       ,BASE_HEX       ,NULL           ,0xf0   
+                                       ,NULL                                           ,HFILL }}
+               ,{ &hf_pref_cw_flags    ,{"Flags"                       ,"pw.cw.flags"          
+                                       ,FT_UINT8       ,BASE_HEX       ,NULL           ,0x0f
+                                       ,NULL                                           ,HFILL }}
+               ,{ &hf_pref_cw_a5s_t    ,{"Payload type"                ,"atm.pt"               
+                                       ,FT_UINT8       ,BASE_DEC       ,VALS(a5s_t_vals),0x08
+                                       ,"Bit (T) of the control word indicates whether the packet contains"
+                                       " an ATM admin cell or an AAL5 payload. If T = 1, the packet"
+                                       " contains an ATM admin cell, encapsulated according to the N:1"
+                                       " cell relay encapsulation. If not set, the PDU"
+                                       " contains an AAL5 payload."                    
+                                                                                       ,HFILL }}
+               ,{ &hf_pref_cw_a5s_e    ,{"EFCI bit"                    ,"atm.efci"             
+                                       ,FT_UINT8       ,BASE_DEC       ,VALS(a5s_e_vals),0x04
+                                       ,"The ingress router sets this bit to 1 if the EFCI bit"
+                                       " of the final cell of those that transported the AAL5 CPCS-SDU is"
+                                       " set to 1, or if the EFCI bit of the single ATM cell to be"
+                                       " transported in the packet is set to 1. Otherwise, this bit"
+                                       " is set to 0."
+                                                                                       ,HFILL }}
+               ,{ &hf_pref_cw_a5s_c    ,{"CLP bit"                     ,"atm.clp"              
+                                       ,FT_UINT8       ,BASE_DEC       ,VALS(clp_vals) ,0x02
+                                       ,"The ingress router sets this bit to 1 if the CLP bit"
+                                       " of any of the ATM cells that transported the AAL5 CPCS-SDU is set"
+                                       " to 1, or if the CLP bit of the single ATM cell to be transported"
+                                       " in the packet is set to 1. Otherwise this bit is set to 0."
+                                                                                       ,HFILL }}
+               ,{ &hf_pref_cw_a5s_u    ,{"U bit (Command/Response)"    ,"pw.cw.aal5sdu.u"              
+                                       ,FT_UINT8       ,BASE_DEC       ,NULL           ,0x01
+                                       ,"When FRF.8.1 Frame Relay/ATM PVC Service Interworking [RFC3916]"
+                                       " traffic is being transported, the Least-Significant Bit of CPCS-UU"
+                                       " of the AAL5 CPCS-PDU may contain the Frame Relay C/R bit."
+                                       " The ingress router copies this bit here."
+                                                                                       ,HFILL }}
+               ,{ &hf_pref_cw_rsv      ,{"Reserved bits"               ,"pw.cw.rsv"
+                                       ,FT_UINT8       ,BASE_DEC       ,NULL           ,0xc0
+                                       ,NULL                                           ,HFILL }}
+               ,{ &hf_generic_cw_rsv   ,{"Reserved bits"               ,"pw.cw.rsv"
+                                       ,FT_UINT8       ,BASE_DEC       ,NULL           ,0x0f
+                                       ,NULL                                           ,HFILL }}
+               ,{ &hf_pref_cw_len      ,{"Length"                      ,"pw.cw.length"
+                                       ,FT_UINT8       ,BASE_DEC       ,NULL           ,0x3f
+                                       ,NULL                                           ,HFILL }}
+               ,{ &hf_pref_cw_rsvlen   ,{"Length (extended)"           ,"pw.cw.length"
+                                       ,FT_UINT8       ,BASE_DEC       ,NULL           ,0xff
+                                       ,NULL                                           ,HFILL }}
+               ,{ &hf_cw_seq           ,{"Sequence number"             ,"pw.cw.seqno"
+                                       ,FT_UINT16      ,BASE_DEC       ,NULL           ,0      
+                                       ,NULL                                           ,HFILL }}
+               ,{ &hf_gen_cw_atmbyte   ,{"ATM-specific byte"           ,"pw.cw.3rd_byte"
+                                       ,FT_UINT8       ,BASE_HEX       ,NULL           ,0xFF   
+                                       ,NULL                                           ,HFILL }}
+       };
+       static gint *ett_array[] = {
+               &ett_encaps
+               ,&ett_cw
+               ,&ett_cell_header
+               ,&ett_cell
+       };
+
+       proto_n1_cw =
+               proto_register_protocol(pwc_longname_pw_atm_n1_cw
+                                       ,shortname_n1_cw
+                                       ,"mplspwatmn1cw");
+       proto_11_or_aal5_pdu =
+               proto_register_protocol(pwc_longname_pw_atm_11_or_aal5_pdu
+                                       ,shortname_11_or_aal5_pdu
+                                       ,"mplspwatm11_or_aal5pdu");
+       proto_aal5_sdu =
+               proto_register_protocol(pwc_longname_pw_atm_aal5_sdu
+                                       ,shortname_aal5_sdu
+                                       ,"mplspwatmaal5sdu");
+       proto_n1_nocw =
+               proto_register_protocol(pwc_longname_pw_atm_n1_nocw
+                                       ,shortname_n1_nocw
+                                       ,"mplspwatmn1nocw");
+       proto_control_word =
+               proto_register_protocol("MPLS PW ATM Control Word"
+                                       ,"MPLS PW ATM Control Word"
+                                       ,"mplspwatmcontrolword");
+       proto_cell_header =
+               proto_register_protocol("MPLS PW ATM Cell Header"
+                                       ,"MPLS PW ATM Cell Header"
+                                       ,"mplspwatmcellheader");
+       proto_cell =
+               proto_register_protocol("ATM Cell"
+                                       ,"ATM Cell"
+                                       ,"mplspwatmcell");
+
+       proto_register_field_array( proto_cell                  ,hfa_cell       ,array_length(hfa_cell));
+       proto_register_field_array( proto_cell_header           ,hfa_cell_header,array_length(hfa_cell_header));
+       proto_register_field_array( proto_control_word          ,hfa_cw         ,array_length(hfa_cw));
+       proto_register_field_array( proto_n1_nocw               ,hfa_n1_nocw    ,array_length(hfa_n1_nocw));
+       proto_register_field_array( proto_n1_cw                 ,hfa_n1_cw      ,array_length(hfa_n1_cw));
+       proto_register_field_array( proto_11_or_aal5_pdu        ,hfa_11_aal5pdu ,array_length(hfa_11_aal5pdu));
+       proto_register_field_array( proto_aal5_sdu              ,hfa_aal5_sdu   ,array_length(hfa_aal5_sdu));
+
+       proto_register_subtree_array(ett_array, array_length(ett_array));
+       
+       register_dissector("mpls_pw_atm_aal5_sdu"       ,dissect_aal5_sdu       ,proto_aal5_sdu);
+       register_dissector("mpls_pw_atm_11_or_aal5_pdu" ,dissect_11_or_aal5_pdu ,proto_11_or_aal5_pdu);
+       register_dissector("mpls_pw_atm_n1_cw"          ,dissect_n1_cw          ,proto_n1_cw);
+       register_dissector("mpls_pw_atm_n1_nocw"        ,dissect_n1_nocw        ,proto_n1_nocw);
+       register_dissector("mpls_pw_atm_control_word"   ,dissect_control_word   ,proto_control_word);
+       new_register_dissector("mpls_pw_atm_cell"       ,dissect_cell           ,proto_cell);
+       new_register_dissector("mpls_pw_atm_cell_header",dissect_cell_header    ,proto_cell_header);
+       {
+               static const char description_allow_cw_length_nonzero[] =
+                       "Enable to allow non-zero Length in Control Word."
+                       " This may be needed to correctly decode traffic from some legacy devices"
+                       " which generate non-zero Length even if there is no padding in the packet."
+                       " Note that Length should have proper value (dissector checks this anyway)."
+                       "\n\n"
+                       "Disable to blame all packets with CW.Length <> 0. This conforms to RFC4717."
+                       ;
+               static const char description_extend_cw_length_with_rsvd[] =
+                       "Enable to use reserved bits (8..9) of Control Word as an extension of CW.Length."
+                       " This may be needed to correctly decode traffic from some legacy devices"
+                       " which uses reserved bits as extension of Length"
+                       "\n\n"
+                       "Disable to blame all packets with CW.Reserved <> 0. This conforms to RFC4717."
+                       ;
+               module_t * module_n1_cw;
+               module_t * module_aal5_sdu;
+               
+               module_n1_cw = prefs_register_protocol(proto_n1_cw,NULL);
+               prefs_register_bool_preference(module_n1_cw
+                       ,"allow_cw_length_nonzero"
+                       ,"Allow CW.Length <> 0"
+                       ,&description_allow_cw_length_nonzero[0]
+                       ,&pref_n1_cw_allow_cw_length_nonzero);
+               prefs_register_bool_preference(module_n1_cw
+                       ,"extend_cw_length_with_rsvd"
+                       ,"Use CW.Reserved as extension of CW.Length"
+                       ,&description_extend_cw_length_with_rsvd[0]
+                       ,&pref_n1_cw_extend_cw_length_with_rsvd);
+                       
+               module_aal5_sdu = prefs_register_protocol(proto_aal5_sdu,NULL);
+               prefs_register_bool_preference(module_aal5_sdu
+                       ,"allow_cw_length_nonzero_aal5"
+                       ,"Allow CW.Length <> 0"
+                       ,&description_allow_cw_length_nonzero[0]
+                       ,&pref_aal5_sdu_allow_cw_length_nonzero);
+               prefs_register_bool_preference(module_aal5_sdu
+                       ,"extend_cw_length_with_rsvd_aal5"
+                       ,"Use CW.Reserved as extension of CW.Length"
+                       ,&description_extend_cw_length_with_rsvd[0]
+                       ,&pref_aal5_sdu_extend_cw_length_with_rsvd);
+       }
+}
+
+
+void proto_reg_handoff_pw_atm_ata(void)
+{
+       dissector_handle_t h;
+       h = find_dissector("mpls_pw_atm_n1_cw");
+       dissector_add( "mpls.label", LABEL_INVALID, h );
+       h = find_dissector("mpls_pw_atm_n1_nocw");
+       dissector_add( "mpls.label", LABEL_INVALID, h );
+       h = find_dissector("mpls_pw_atm_11_or_aal5_pdu");
+       dissector_add( "mpls.label", LABEL_INVALID, h );
+       h = find_dissector("mpls_pw_atm_aal5_sdu");
+       dissector_add( "mpls.label", LABEL_INVALID, h );
 
-        data_h = find_dissector("data");
-        atm_h = find_dissector("atm_4717");
+       dh_cell = find_dissector("mpls_pw_atm_cell");
+       dh_cell_header = find_dissector("mpls_pw_atm_cell_header");
+       dh_control_word = find_dissector("mpls_pw_atm_control_word");
+       dh_atm_truncated = find_dissector("atm_truncated");
+       dh_atm_untruncated = find_dissector("atm_untruncated");
+       dh_atm_oam_cell = find_dissector("atm_oam_cell");
+       dh_padding = find_dissector("pw_padding");
+       dh_data = find_dissector("data");
 }