Wireshark-dev: Re: [Wireshark-dev] dissector for DRM Distribution Interfaces
From: Julian Cable <julian_cable@xxxxxxxxx>
Date: Sun, 15 Jul 2007 03:12:18 -0700 (PDT)
Hi,

  I'm re-sending this as it didn't get any response last month. It would be great to get these dissectors into wireshark. Did I do something wrong or is it just a bad time?

cheers

Julian

----- Forwarded Message ----
From: Julian Cable <julian_cable@xxxxxxxxx>
To: wireshark-dev@xxxxxxxxxxxxx
Sent: Tuesday, 12 June, 2007 8:16:44 PM
Subject: dissector for DRM Distribution Interfaces

Hi, I finally finished the dissectors for the Digital Radio Mondiale distribution interfaces. This covers the protocols defined in

ETSI TS 102 820 The Multiplex Distribution Interface (MDI)
and
ETSI TS 102 349 The Receiver Control and Status Interface (RSCI)

I couldn't work out how to use the reassemble functions simultaneously at multiple layers
so I included my own (reassemble2.*). If anyone can work out how to use the normal ones I'd be pleased to know. Mine don't do the "reasembled in" stuff but they are "layer clean" so I can re-use them again in the higher layers of data application decoding (not included in this submission).

The protocols sit on top of the ETSI-DCP protocol I submitted last year.

Patch against SVN attached.

Julian


Now you can scan emails quickly with a reading pane. Get the new Yahoo! Mail.



Yahoo! Mail is the world's favourite email. Don't settle for less, sign up for your free account today.
Index: epan/Makefile.common
===================================================================
--- epan/Makefile.common	(revision 22076)
+++ epan/Makefile.common	(working copy)
@@ -67,6 +67,7 @@
 	proto.c			\
 	range.c			\
 	reassemble.c		\
+	reassemble2.c		\
 	reedsolomon.c		\
 	report_err.c		\
 	req_resp_hdrs.c		\
@@ -193,6 +194,7 @@
 	ptvcursor.h		\
 	range.h			\
 	reassemble.h		\
+	reassemble2.h		\
 	reedsolomon.h		\
 	report_err.h		\
 	req_resp_hdrs.h		\
Index: epan/dissectors/packet-drm-rsci.c
===================================================================
--- epan/dissectors/packet-drm-rsci.c	(revision 0)
+++ epan/dissectors/packet-drm-rsci.c	(revision 0)
@@ -0,0 +1,992 @@
+/* packet-drm-rsci.c
+ * Routines for Digital Radio Mondiale Multiplex Distribution Interface Protocol 
+ * Copyright 2007, British Broadcasting Corporation 
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@xxxxxxxxxxxxx>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Ref:
+ *      ETSI DRM RSCI (ETSI TS 102 349)
+ */
+ 
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+#include <gmodule.h>
+#include <epan/packet.h>
+#include <epan/prefs.h>
+#include "packet-drm-sdc.h"
+#include "packet-drm-rsci.h"
+
+/* Initialize the subtree pointers */
+static gint ett_rsci = -1;
+
+static int hf_di_rpro = -1;
+static int hf_di_fmjd = -1;
+static int hf_di_rdbv = -1;
+static int hf_di_rsta = -1;
+static int hf_di_rfre = -1;
+static int hf_di_rdmo = -1;
+static int hf_di_cfre = -1;
+static int hf_di_cdmo = -1;
+static int hf_di_time = -1;
+static int hf_di_rgps = -1;
+static int hf_di_rgps_source = -1;
+static int hf_di_rgps_sats = -1;
+static int hf_di_rgps_lat = -1;
+static int hf_di_rgps_long = -1;
+static int hf_di_rgps_altitude = -1;
+static int hf_di_rgps_speed = -1;
+static int hf_di_rgps_heading = -1;
+static int hf_di_rinf = -1;
+static int hf_di_ract = -1;
+static int hf_di_rbw_ = -1;
+static int hf_di_rser = -1;
+static int hf_di_rtty = -1;
+static int hf_di_rafs = -1;
+static int hf_di_reas = -1;
+static int hf_di_rpil = -1;
+static int hf_di_rpil_sn = -1;
+static int hf_di_rpil_sr = -1;
+static int hf_di_rwmf = -1;
+static int hf_di_rwmm = -1;
+static int hf_di_rmer = -1;
+static int hf_di_rdop = -1;
+static int hf_di_rpsd = -1;
+static int hf_di_cact = -1;
+static int hf_di_cbws = -1;
+static int hf_di_cbwg = -1;
+static int hf_di_cser = -1;
+static int hf_di_crec = -1;
+
+static const value_string gps_source_vals[] = {
+	{ 0x0,	"Invalid" },
+	{ 0x1,	"GPS Receiver" },
+	{ 0x2,	"Differential GPS Receiver" },
+	{ 0x3,	"Manual Entry" },
+	{ 0,	NULL }
+};
+
+static float
+di_tvb_get_float(tvbuff_t * tvb, guint offset)
+{
+  guint8 m;
+  union { char s; unsigned char u; } n;
+  n.u = tvb_get_guint8(tvb, offset);
+  m = tvb_get_guint8(tvb, offset+1);
+  return ((float)n.s)+((float)m)/256.0;
+}
+
+static float
+di_tvb_get_float2(tvbuff_t * tvb, guint offset)
+{
+  guint8 m;
+  union { gint16 s; guint16 u; } n;
+  n.u = tvb_get_ntohs(tvb, offset);
+  m = tvb_get_guint8(tvb, offset+2);
+  return ((float)n.s)+((float)m)/256.0;
+}
+
+static void
+dissect_rpro (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  if(tree) {
+    proto_tree_add_item (tree, hf_di_rpro, tvb, 0, 1, FALSE);
+  }
+  (void)pinfo;
+}
+
+static void
+dissect_fmjd (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+  if(tree) {
+    if(bytes>0) {
+      guint32 mjd = tvb_get_ntohl(tvb, offset);
+	  guint32 frac_day = tvb_get_ntohl(tvb, offset+4);
+      guint32 y,m,d,h,min; /* TODO */
+	  h = frac_day / 10 / 1000 / 60 / 60;
+	  min = (frac_day / 10 / 1000 / 60) - 60*h;
+	  MjdToDate (mjd, &y, &m, &d);
+      proto_tree_add_text (tree, tvb, offset, bytes, "Date & Time (mjd) %d/%d/%d %2d:%02d", y,d,m,h,min);
+	} else {
+       proto_tree_add_item (tree, hf_di_fmjd, tvb, offset, bytes, FALSE);
+	}
+  }
+  (void)pinfo;
+}
+
+static void
+dissect_time (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+   if(tree) {
+     proto_tree_add_item (tree, hf_di_time, tvb, 0, tvb_length(tvb), FALSE);
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_rgps (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+  proto_tree *gps_tree = NULL;
+  proto_item *ti = NULL;
+  guint8 source, nSats, latmin, longmin;
+  guint16 val,latdeg,longdeg,latfracmin,longfracmin, speed, heading;
+  float altitude;
+  struct tm tm;
+
+  if (bytes != 26)
+    return;
+
+  if(tree) {
+    ti = proto_tree_add_item (tree, hf_di_rgps, tvb, offset, bytes, FALSE);
+    gps_tree = proto_item_add_subtree (ti, ett_rsci);
+
+    source = tvb_get_guint8(tvb, offset);
+    if(source == 0xff)
+    {
+      proto_tree_add_text (gps_tree, tvb, offset, 1, "no GPS source");
+    }
+    else
+    {
+      proto_tree_add_item (gps_tree, hf_di_rgps_source, tvb, offset, 1, FALSE);
+    }
+    offset++;
+
+    nSats = tvb_get_guint8(tvb, offset);
+ 	if(nSats == 0xff)
+ 	{
+      proto_tree_add_text (gps_tree, tvb, offset, 1, "no satellites visible");
+ 	}
+ 	else
+ 	{
+      proto_tree_add_item (tree, hf_di_rgps_sats, tvb, offset, 1, FALSE);
+ 	}
+	offset++;
+
+    val = tvb_get_ntohs(tvb, offset);
+	latdeg = *(gint16*)&val;
+    latmin = tvb_get_guint8(tvb, offset+2);
+    latfracmin = tvb_get_ntohs(tvb, offset+3);
+
+    val = tvb_get_ntohs(tvb, offset+5);
+	longdeg = *(int16_t*)&val;
+    longmin = tvb_get_guint8(tvb, offset+7);
+    longfracmin = tvb_get_ntohs(tvb, offset+8);
+
+    if(latmin == 0xff)
+    {
+      proto_tree_add_text (gps_tree, tvb, offset, 10, "position not available");
+    }
+    else
+    {
+		double latitude, longitude;
+		latitude = (double)latdeg
+		 + ((double)latmin + ((double)latfracmin)/65536.0)/60.0;
+		longitude = (double)longdeg
+		 + ((double)longmin + ((double)longfracmin)/65536.0)/60.0;
+      proto_tree_add_double_format_value(gps_tree, hf_di_rgps_lat, tvb, offset, 5, latitude, "%f", latitude);
+      proto_tree_add_double_format_value(gps_tree, hf_di_rgps_long, tvb, offset, 5, longitude, "%f", longitude);
+    }
+	offset += 10;
+
+    val = tvb_get_guint8(tvb, offset);
+	if(val == 0xff)
+	{
+      proto_tree_add_text (gps_tree, tvb, offset, 3, "altitude not available");
+	}
+	else
+	{
+	  altitude = di_tvb_get_float2(tvb, offset);
+      proto_tree_add_float_format_value(gps_tree, hf_di_rgps_altitude, tvb, offset, 3, altitude, "%f", altitude);
+	}
+
+    tm.tm_hour = tvb_get_guint8(tvb, offset);
+    tm.tm_min = tvb_get_guint8(tvb, offset+1);
+    tm.tm_sec = tvb_get_guint8(tvb, offset+2);
+    tm.tm_year = tvb_get_ntohs(tvb, offset+3) - 1900;
+    tm.tm_mon = tvb_get_guint8(tvb, offset+5);
+    tm.tm_mday = tvb_get_guint8(tvb, offset+6);
+
+    if(tm.tm_hour == 0xff)
+    {
+      proto_tree_add_text (gps_tree, tvb, offset, 7, "time not available");
+    }
+    else
+    {
+      proto_tree_add_text (gps_tree, tvb, offset, 7, "Date & Time %d/%d/%d %2d:%02d:%02d", 
+	  	tm.tm_year+1900, tm.tm_mon,tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+    }
+	offset+=7;
+
+    speed = tvb_get_ntohs(tvb, offset);
+    if(speed == 0xffff)
+    {
+      proto_tree_add_text (gps_tree, tvb, offset, 2, "speed not available");
+    }
+    else
+    {
+	  float s = ((float)speed)/10.0;
+      proto_tree_add_float_format_value(gps_tree, hf_di_rgps_speed, tvb, offset, 2, s, "%f", s);
+    }
+	offset += 2;
+
+    heading = tvb_get_ntohs(tvb, offset);
+    if(heading == 0xffff)
+    {
+      proto_tree_add_text (gps_tree, tvb, offset, 2, "heading not available");
+    }
+    else
+    {
+      proto_tree_add_item (tree, hf_di_rgps_heading, tvb, offset, 1, FALSE);
+    }
+  }
+  (void)pinfo;
+}
+
+static void
+dissect_rdmo (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+    if(bytes>0) {
+      proto_tree_add_item (tree, hf_di_rdmo, tvb, offset, bytes, FALSE);
+	} else {
+      proto_tree_add_text (tree, tvb, offset, 0, "rdmo");
+	}
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_rfre (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+    if(bytes>0) {
+          proto_tree_add_item(tree, hf_di_rfre, tvb, offset, bytes, FALSE);
+	} else {
+          proto_tree_add_text(tree, tvb, offset, 0, "rfre");
+	}
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_rdbv (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+    if(bytes>0) {
+		  int i,n = bytes/2;
+		  int off = offset;
+		  for(i=0; i<n; i++)
+		  {
+            float db = di_tvb_get_float(tvb, off);
+			off += 2;
+		    proto_tree_add_float_format_value(tree, hf_di_rdbv, tvb, off, 2, db, "%f", db);
+		  }
+		} else {
+          proto_tree_add_text (tree, tvb, offset, 0, "rdbv");
+	}
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_rinf (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+    proto_tree_add_item (tree, hf_di_rinf, tvb, offset, bytes, FALSE);
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_ract (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+    if(bytes>0) {
+          proto_tree_add_item (tree, hf_di_ract, tvb, offset, 1, FALSE);
+		} else {
+          proto_tree_add_text (tree, tvb, offset, 0, "ract");
+	}
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_rsta (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+    if(bytes>0) {
+          guint8 sy = tvb_get_guint8(tvb, offset);
+          guint8 f = tvb_get_guint8(tvb, offset+1);
+          guint8 s = tvb_get_guint8(tvb, offset+2);
+          guint8 a = tvb_get_guint8(tvb, offset+3);
+          proto_tree_add_text (tree, tvb, offset, bytes, "status sync:%d FAC:%d SDC:%d Audio:%d", sy,f,s,a);
+		} else {
+          proto_tree_add_text (tree, tvb, offset, 0, "rsta");
+	}
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_rbw_ (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+    if(bytes>0) {
+          float bw = di_tvb_get_float(tvb, offset);
+		  proto_tree_add_float_format_value(tree, hf_di_rbw_, tvb, offset, bytes, bw, "%f", bw);
+		} else {
+          proto_tree_add_text (tree, tvb, offset, 0, "rbw_");
+	}
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_rser (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+    if(bytes>0) {
+          proto_tree_add_item (tree, hf_di_rser, tvb, offset, 1, FALSE);
+		} else {
+          proto_tree_add_text (tree, tvb, offset, 0, "rser");
+	}
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_rtty (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+    if(bytes>0) {
+          guint8 s0 = tvb_get_guint8(tvb, offset);
+          guint8 s1 = tvb_get_guint8(tvb, offset+1);
+          guint8 s2 = tvb_get_guint8(tvb, offset+2);
+          guint8 s3 = tvb_get_guint8(tvb, offset+3);
+          proto_tree_add_text (tree, tvb, offset, bytes, "test type: str0:%d str1:%d str2:%d str3:%d", s0, s1, s2, s3);
+		} else {
+          proto_tree_add_text (tree, tvb, offset, 0, "rtty");
+	}
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_rafs(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+    if(bytes>0) {
+		  int i;
+          proto_item *ti = NULL;
+          guint8 frames = tvb_get_guint8(tvb, offset);
+          guint64 flags = tvb_get_ntohl(tvb, offset+1) << 8;
+          flags |= tvb_get_guint8(tvb, offset+5);
+          ti = proto_tree_add_text (tree, tvb, offset, bytes, "audio frames: ");
+		  for(i=0; i<frames; i++)
+		  {
+            proto_item_append_text (ti, "%d", (flags&0x8000000000ULL)?1:0);
+			flags <<= 1;
+		  }
+		} else {
+          proto_tree_add_text (tree, tvb, offset, 0, "rafs");
+	}
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_reas (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+    if(bytes>0) {
+		  guint i;
+          proto_item *ti = NULL;
+          ti = proto_tree_add_text (tree, tvb, offset, bytes, "audio frame extended status: ");
+		  for(i=0; i<bytes; i++)
+		  {
+		    /* TODO a better decode */
+            proto_item_append_text (ti, "%02x", tvb_get_guint8(tvb, offset+i));
+		  }
+		} else {
+          proto_tree_add_text (tree, tvb, offset, 0, "reas");
+	}
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_rpil (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+    if(bytes>0) {
+       proto_tree *pil_tree = NULL;
+       proto_item *ti = NULL;
+       ti = proto_tree_add_text (tree, tvb, offset, bytes, "Gain Reference Pilots");
+       pil_tree = proto_item_add_subtree (ti, ett_rsci);
+       proto_tree_add_item(pil_tree, hf_di_rpil_sn, tvb, offset, 1, FALSE);
+       proto_tree_add_item(pil_tree, hf_di_rpil_sr, tvb, offset+1, 1, FALSE);
+	   offset += 4;
+	   while(offset<bytes)
+	   {
+	     int j;
+         guint8 pe = tvb_get_guint8(tvb, offset);
+         guint8 po = tvb_get_guint8(tvb, offset+1);
+         guint16 be = tvb_get_guint8(tvb, offset+2);
+         proto_item *pi = proto_tree_add_text (pil_tree, tvb, offset, 3+2*pe,
+		   "Pilots %d offset %d block exponent %d", pe, po, be);
+         proto_tree *p_tree = proto_item_add_subtree (pi, ett_rsci);
+		 offset += 4;
+		 for(j=0; j<pe; j++)
+		 {
+           guint16 i = tvb_get_ntohs(tvb, offset);
+           guint16 q = tvb_get_ntohs(tvb, offset+2);
+           proto_tree_add_text (p_tree, tvb, offset, 4, "I %d Q %d", i, q);
+		   offset += 4;
+		 }
+	   }
+     } else {
+       proto_tree_add_text (tree, tvb, offset, bytes, "rpil");
+	}
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_rwmf (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+    if(bytes>0) {
+          float db = di_tvb_get_float(tvb, offset);
+		  proto_tree_add_float_format_value(tree, hf_di_rwmf, tvb, offset, 2, db, "%f", db);
+		} else {
+          proto_tree_add_text (tree, tvb, offset, 0, "rwmf");
+	}
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_rwmm (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+    if(bytes>0) {
+          float db = di_tvb_get_float(tvb, offset);
+		  proto_tree_add_float_format_value(tree, hf_di_rwmm, tvb, offset, 2, db, "%f", db);
+		} else {
+          proto_tree_add_text (tree, tvb, offset, 0, "rwmm");
+	}
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_rmer (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+    if(bytes>0) {
+          float db = di_tvb_get_float(tvb, offset);
+		  proto_tree_add_float_format_value(tree, hf_di_rmer, tvb, offset, 2, db, "%f", db);
+		} else {
+          proto_tree_add_text (tree, tvb, offset, 0, "rmer");
+	}
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_rbp(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree, char stream)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+    if(bytes>0) {
+          guint8 errs = tvb_get_guint8(tvb, offset);
+          guint8 bits = tvb_get_guint8(tvb, offset+1);
+          proto_tree_add_text (tree, tvb, offset, bytes, "str%c BER %f (%d/%d)", stream, ((float)errs)/((float)bits), errs, bits);
+		} else {
+          proto_tree_add_text (tree, tvb, offset, 0, "rbp%c", stream);
+	}
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_rbp0 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  dissect_rbp(tvb, pinfo, tree, '0');
+}
+
+static void
+dissect_rbp1 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  dissect_rbp(tvb, pinfo, tree, '1');
+}
+
+static void
+dissect_rbp2 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  dissect_rbp(tvb, pinfo, tree, '2');
+}
+
+static void
+dissect_rbp3 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  dissect_rbp(tvb, pinfo, tree, '3');
+}
+
+static void
+dissect_rdel (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+    if(bytes>0) {
+		  guint i;
+          proto_item *ti = NULL;
+          ti = proto_tree_add_text (tree, tvb, offset, bytes, "Delay: ");
+		  for(i=0; i<bytes; i+=3)
+		  {
+            guint8 percent = tvb_get_guint8(tvb, offset+i);
+            float dw = di_tvb_get_float(tvb, offset+i+1);
+            proto_item_append_text (ti, "%d %f ", percent, dw);
+		  }
+		} else {
+          proto_tree_add_text (tree, tvb, offset, 0, "rdel");
+	}
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_rdop(tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+    if(bytes>0) {
+          float dp = di_tvb_get_float(tvb, offset);
+		  proto_tree_add_float_format_value(tree, hf_di_rdop, tvb, offset, 2, dp, "%f", dp);
+		} else {
+          proto_tree_add_text (tree, tvb, offset, 0, "rdop");
+	}
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_rpsd (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+    proto_item *ti = proto_tree_add_text(tree, tvb, offset, bytes, "Power Spectral Density");
+    if(bytes>0) {
+       proto_tree *p_tree = proto_item_add_subtree (ti, ett_rsci);
+	   guint i;
+	   for(i=0; i<bytes; i++)
+	   {
+	     guint8 n = tvb_get_guint8(tvb, i);
+         proto_tree_add_text(p_tree, tvb, i, 1, "%4.1f", 0.0 - ((float)n)/2.0);
+	   }
+    }
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_cact (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+    proto_tree_add_item (tree, hf_di_cact, tvb, offset, bytes, FALSE);
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_cfre (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+	    if(bytes>0) {
+		  float f = ((float)tvb_get_ntohl(tvb, offset))/1000.0;
+          proto_tree_add_float_format_value (tree, hf_di_cfre, tvb, offset, 4, f, "%9.3f kHz", f);
+		} else {
+          proto_tree_add_text (tree, tvb, offset, 0, "cfre");
+		}
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_cdmo (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+	    if(bytes>0) {
+          proto_tree_add_item (tree, hf_di_cdmo, tvb, offset, bytes, FALSE);
+		} else {
+          proto_tree_add_text (tree, tvb, offset, 0, "cdmo");
+		}
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_cbws (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+	    if(bytes>0) {
+          float dp = di_tvb_get_float(tvb, offset);
+		  proto_tree_add_float_format_value(tree, hf_di_cbws, tvb, offset, 2, dp, "%f", dp);
+		} else {
+          proto_tree_add_text (tree, tvb, offset, 0, "cbws");
+   }
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_cbwg (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+	    if(bytes>0) {
+          float dp = di_tvb_get_float(tvb, offset);
+		  proto_tree_add_float_format_value(tree, hf_di_cbwg, tvb, offset, 2, dp, "%f", dp);
+		} else {
+          proto_tree_add_text (tree, tvb, offset, 0, "cbwg");
+   }
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_cser (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+	    if(bytes>0) {
+          proto_tree_add_item (tree, hf_di_cser, tvb, offset, bytes, FALSE);
+		} else {
+   }
+   }
+  (void)pinfo;
+}
+
+static void
+dissect_crec (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint offset = 0;
+  guint bytes = tvb_length(tvb);
+   if(tree) {
+	    if(bytes>0) {
+          proto_tree_add_item (tree, hf_di_crec, tvb, offset, bytes, FALSE);
+		} else {
+          proto_tree_add_text (tree, tvb, offset, 0, "crec");
+   }
+   }
+  (void)pinfo;
+}
+
+void
+register_rsci_dissectors (int proto)
+{
+  static int Initialized = FALSE;
+  if (!Initialized) {
+    dissector_add_string("drm-di.tag", "rpro", create_dissector_handle (dissect_rpro, proto));
+    dissector_add_string("drm-di.tag", "fmjd", create_dissector_handle (dissect_fmjd, proto));
+    dissector_add_string("drm-di.tag", "time", create_dissector_handle (dissect_time, proto));
+    dissector_add_string("drm-di.tag", "rgps", create_dissector_handle (dissect_rgps, proto));
+    dissector_add_string("drm-di.tag", "rdmo", create_dissector_handle (dissect_rdmo, proto));
+    dissector_add_string("drm-di.tag", "rfre", create_dissector_handle (dissect_rfre, proto));
+    dissector_add_string("drm-di.tag", "rdbv", create_dissector_handle (dissect_rdbv, proto));
+    dissector_add_string("drm-di.tag", "rinf", create_dissector_handle (dissect_rinf, proto));
+    dissector_add_string("drm-di.tag", "ract", create_dissector_handle (dissect_ract, proto));
+    dissector_add_string("drm-di.tag", "rsta", create_dissector_handle (dissect_rsta, proto));
+    dissector_add_string("drm-di.tag", "rbw_", create_dissector_handle (dissect_rbw_, proto));
+    dissector_add_string("drm-di.tag", "rser", create_dissector_handle (dissect_rser, proto));
+    dissector_add_string("drm-di.tag", "rtty", create_dissector_handle (dissect_rtty, proto));
+    dissector_add_string("drm-di.tag", "rafs", create_dissector_handle (dissect_rafs, proto));
+    dissector_add_string("drm-di.tag", "reas", create_dissector_handle (dissect_reas, proto));
+    dissector_add_string("drm-di.tag", "rpil", create_dissector_handle (dissect_rpil, proto));
+    dissector_add_string("drm-di.tag", "rwmf", create_dissector_handle (dissect_rwmf, proto));
+    dissector_add_string("drm-di.tag", "rwmm", create_dissector_handle (dissect_rwmm, proto));
+    dissector_add_string("drm-di.tag", "rmer", create_dissector_handle (dissect_rmer, proto));
+    dissector_add_string("drm-di.tag", "rbp0", create_dissector_handle (dissect_rbp0, proto));
+    dissector_add_string("drm-di.tag", "rbp1", create_dissector_handle (dissect_rbp1, proto));
+    dissector_add_string("drm-di.tag", "rbp2", create_dissector_handle (dissect_rbp2, proto));
+    dissector_add_string("drm-di.tag", "rbp3", create_dissector_handle (dissect_rbp3, proto));
+    dissector_add_string("drm-di.tag", "rdel", create_dissector_handle (dissect_rdel, proto));
+    dissector_add_string("drm-di.tag", "rdop", create_dissector_handle (dissect_rdop, proto));
+    dissector_add_string("drm-di.tag", "rpsd", create_dissector_handle (dissect_rpsd, proto));
+    dissector_add_string("drm-di.tag", "cact", create_dissector_handle (dissect_cact, proto));
+    dissector_add_string("drm-di.tag", "cfre", create_dissector_handle (dissect_cfre, proto));
+    dissector_add_string("drm-di.tag", "cdmo", create_dissector_handle (dissect_cdmo, proto));
+    dissector_add_string("drm-di.tag", "cbws", create_dissector_handle (dissect_cbws, proto));
+    dissector_add_string("drm-di.tag", "cbwg", create_dissector_handle (dissect_cbwg, proto));
+    dissector_add_string("drm-di.tag", "cser", create_dissector_handle (dissect_cser, proto));
+    dissector_add_string("drm-di.tag", "crec", create_dissector_handle (dissect_crec, proto));
+  }
+}
+
+void
+register_drm_rsci (int proto)
+{
+  static hf_register_info hf[] = {
+    {&hf_di_rpro,
+     {"RSCI profile", "drm-di.rpro",
+      FT_STRING, BASE_NONE, NULL, 0,
+      "profile", HFILL}
+     },
+    {&hf_di_fmjd,
+     {"Frac MJD", "drm-di.rpro",
+      FT_BYTES, BASE_HEX, NULL, 0,
+      "Fractional MJD", HFILL}
+     },
+    {&hf_di_time,
+     {"time", "drm-di.time",
+      FT_STRING, BASE_NONE, NULL, 0,
+      "time", HFILL}
+     },
+    {&hf_di_rgps,
+     {"GPS data", "drm-di.rgps",
+      FT_BYTES, BASE_HEX, NULL, 0,
+      "GPS Data", HFILL}
+     },
+    {&hf_di_rgps_source,
+     {"GPS Source", "drm-di.rgps.source",
+      FT_UINT8, BASE_DEC, VALS(gps_source_vals), 0,
+      "GPS Source", HFILL}
+     },
+    {&hf_di_rgps_sats,
+     {"Satellites", "drm-di.rgps.source",
+      FT_UINT8, BASE_DEC, NULL, 0,
+      "Satellites visible", HFILL}
+     },
+    {&hf_di_rgps_lat,
+     {"Latitude", "drm-di.rgps.lat",
+      FT_DOUBLE, BASE_DEC, NULL, 0,
+      "Latitude", HFILL}
+     },
+    {&hf_di_rgps_long,
+     {"Longitude", "drm-di.rgps.long",
+      FT_DOUBLE, BASE_DEC, NULL, 0,
+      "Longitude", HFILL}
+     },
+    {&hf_di_rgps_altitude,
+     {"altitude", "drm-di.altitude",
+      FT_FLOAT, BASE_DEC, NULL, 0,
+      "altitude (m)", HFILL}
+     },
+    {&hf_di_rgps_speed,
+     {"speed", "drm-di.speed",
+      FT_FLOAT, BASE_DEC, NULL, 0,
+      "speed (m/s)", HFILL}
+     },
+    {&hf_di_rgps_heading,
+     {"heading", "drm-di.heading",
+      FT_UINT16, BASE_DEC, NULL, 0,
+      "heading (deg)", HFILL}
+     },
+    {&hf_di_rdbv,
+     {"signal strength", "drm-di.rdbv",
+      FT_FLOAT, BASE_DEC, NULL, 0,
+      "signal strength (dB)", HFILL}
+     },
+    {&hf_di_rinf,
+     {"info", "drm-di.rinf",
+      FT_STRING, BASE_NONE, NULL, 0,
+      "Rx info", HFILL}
+     },
+    {&hf_di_ract,
+     {"Rx Active Status", "drm-di.ract",
+      FT_STRING, BASE_NONE, NULL, 0,
+      "on/off status", HFILL}
+     },
+    {&hf_di_rsta,
+     {"Rx Sync Status", "drm-di.rsta",
+      FT_BYTES, BASE_HEX, NULL, 0,
+      "Rx sync status", HFILL}
+     },
+    {&hf_di_rbw_,
+     {"B/W", "drm-di.rbw_",
+      FT_FLOAT, BASE_DEC, NULL, 0,
+      "Bandwidth", HFILL}
+     },
+    {&hf_di_rser,
+     {"selected service", "drm-di.rser",
+      FT_UINT8, BASE_DEC, NULL, 0,
+      "selected service", HFILL}
+     },
+    {&hf_di_rtty,
+     {"received test type", "drm-di.rtty",
+      FT_BYTES, BASE_HEX, NULL, 0,
+      "received test type", HFILL}
+     },
+    {&hf_di_rafs,
+     {"audio status", "drm-di.rafs",
+      FT_BYTES, BASE_HEX, NULL, 0,
+      "audio status", HFILL}
+     },
+    {&hf_di_reas,
+     {"extended audio status", "drm-di.reas",
+      FT_BYTES, BASE_HEX, NULL, 0,
+      "extended audio status", HFILL}
+     },
+    {&hf_di_rpil,
+     {"pilots", "drm-di.rpil",
+      FT_BYTES, BASE_HEX, NULL, 0,
+      "pilots", HFILL}
+     },
+    {&hf_di_rpil_sn,
+     {"symbols per frame", "drm-di.rpil.sn",
+      FT_UINT8, BASE_DEC, NULL, 0,
+      "symbols per frame", HFILL}
+     },
+    {&hf_di_rpil_sr,
+     {"pilot pattern symbol repeat", "drm-di.rpil.sr",
+      FT_UINT8, BASE_DEC, NULL, 0,
+      "amount of symbols until the pilot pattern repeats", HFILL}
+     },
+    {&hf_di_rdmo,
+     {"mode", "drm-di.rdmo",
+      FT_STRING, BASE_NONE, NULL, 0,
+      "current mode", HFILL}
+     },
+    {&hf_di_rfre,
+     {"tuned frequency", "drm-di.rfre",
+      FT_UINT32, BASE_DEC, NULL, 0,
+      "current frequency in Hz", HFILL}
+     },
+    {&hf_di_rmer,
+     {"MER", "drm-di.rmer",
+      FT_FLOAT, BASE_DEC, NULL, 0,
+      "Modulation Error Rate", HFILL}
+     },
+    {&hf_di_rwmm,
+     {"Weighted MER (MSC)", "drm-di.rwmm",
+      FT_FLOAT, BASE_DEC, NULL, 0,
+      "Weighted MER based on MSC", HFILL}
+     },
+    {&hf_di_rwmf,
+     {"Weighted MER (FAC)", "drm-di.rwmf",
+      FT_FLOAT, BASE_DEC, NULL, 0,
+      "Weighted MER based on FAC", HFILL}
+     },
+    {&hf_di_rdop,
+     {"doppler", "drm-di.rdop",
+      FT_FLOAT, BASE_DEC, NULL, 0,
+      "Doppler", HFILL}
+     },
+    {&hf_di_rpsd,
+     {"PSD", "drm-di.rpsd",
+      FT_BYTES, BASE_HEX, NULL, 0,
+      "Power Spectral Density", HFILL}
+     },
+    {&hf_di_cfre,
+     {"set frequency to", "drm-di.cfre",
+      FT_FLOAT, BASE_DEC, NULL, 0,
+      "required frequency in kHz", HFILL}
+     },
+    {&hf_di_cdmo,
+     {"mode command", "drm-di.cdmo",
+      FT_STRING, BASE_NONE, NULL, 0,
+      "required mode", HFILL}
+     },
+    {&hf_di_cact,
+     {"on/off command", "drm-di.cact",
+      FT_STRING, BASE_NONE, NULL, 0,
+      "required on/off state", HFILL}
+     },
+    {&hf_di_cbws,
+     {" command", "drm-di.cbws",
+      FT_STRING, BASE_NONE, NULL, 0,
+      "required on/off state", HFILL}
+     },
+    {&hf_di_cbwg,
+     {"on/off command", "drm-di.cbwg",
+      FT_STRING, BASE_NONE, NULL, 0,
+      "required on/off state", HFILL}
+     },
+    {&hf_di_cser,
+     {"required service", "drm-di.cser",
+      FT_UINT8, BASE_DEC, NULL, 0,
+      "service command", HFILL}
+     },
+    {&hf_di_crec,
+     {"RSCI record command", "drm-di.crec",
+      FT_STRING, BASE_NONE, NULL, 0,
+      "required on/off state", HFILL}
+     }
+  };
+
+  /* Setup protocol subtree array */
+  static gint *ett[] = {
+    &ett_rsci
+  };
+
+  proto_register_field_array (proto, hf, array_length (hf));
+  proto_register_subtree_array (ett, array_length (ett));
+}
+
Index: epan/dissectors/packet-drm-rsci.h
===================================================================
--- epan/dissectors/packet-drm-rsci.h	(revision 0)
+++ epan/dissectors/packet-drm-rsci.h	(revision 0)
@@ -0,0 +1,34 @@
+/* packet-drm-rsci.h
+ * Routines for Digital Radio Mondiale Multiplex Distribution Interface Protocol 
+ * Copyright 2006, British Broadcasting Corporation 
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@xxxxxxxxxxxxx>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+ 
+#ifndef __PACKET_DRM_RSCI_H
+#define __PACKET_DRM_RSCI_H
+
+#include <epan/packet.h>
+
+void register_drm_rsci (int);
+void register_rsci_dissectors(int);
+
+#endif
Index: epan/dissectors/packet-drm-sdc.c
===================================================================
--- epan/dissectors/packet-drm-sdc.c	(revision 0)
+++ epan/dissectors/packet-drm-sdc.c	(revision 0)
@@ -0,0 +1,1179 @@
+/* packet-drm-mdi-sdc.c
+ * Routines for Digital Radio Mondiale Multiplex Distribution Interface Protocol
+ * Service Data Channel (SDC) Elements
+ * Copyright 2007, British Broadcasting Corporation 
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@xxxxxxxxxxxxx>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+ 
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+#include <gmodule.h>
+#include <epan/packet.h>
+#include <epan/prefs.h>
+#include <epan/crcdrm.h>
+#include "packet-drm-sdc.h"
+#include "drm-data.h"
+
+static dissector_table_t sdc_dissector_table;
+
+int proto_drm_sdc = -1;
+static dissector_handle_t sdc_handle;
+static dissector_handle_t sdci_handle;
+static int hf_sdc_afs_index = -1;
+static int hf_sdc_length = -1;
+static int hf_sdc_version = -1;
+static int hf_sdc_type = -1;
+static int hf_sdc_crc = -1;
+static int hf_sdc_crc_ok = -1;
+static int hf_sdc_short_id = -1;
+static int hf_sdc_stream_id = -1;
+static int hf_sdc_audio_coding = -1;
+static int hf_sdc_sbr_flag = -1;
+static int hf_sdc_audio_mode = -1;
+static int hf_sdc_audio_sample_rate = -1;
+static int hf_sdc_text_flag = -1;
+static int hf_sdc_enhancement_flag = -1;
+static int hf_sdc_coder_field = -1;
+static int hf_sdc_label = -1;
+static int hf_sdc_prot_a = -1;
+static int hf_sdc_prot_b = -1;
+static int hf_sdc_data_len_a = -1;
+static int hf_sdc_data_len_b = -1;
+static int hf_sdc_packet_flag = -1;
+static int hf_sdc_app_data = -1;
+static int hf_sdc_data_unit_indicator = -1;
+static int hf_sdc_packet_id = -1;
+static int hf_sdc_data_enhancement_flag = -1;
+static int hf_sdc_application_domain = -1;
+static int hf_sdc_packet_length = -1;
+static int hf_sdc_ca_id = -1;
+static int hf_sdc_ca_data = -1;
+static int hf_sdc_region_id = -1;
+static int hf_sdc_latitude = -1;
+static int hf_sdc_longitude = -1;
+static int hf_sdc_latx = -1;
+static int hf_sdc_longx = -1;
+static int hf_sdc_zone = -1;
+static int hf_sdc_ann_sup = -1;
+static int hf_sdc_ann_sw = -1;
+static int hf_sdc_mjd = -1;
+static int hf_sdc_hour = -1;
+static int hf_sdc_minute = -1;
+
+/* Initialize the subtree pointers */
+static gint ett_sdc = -1;
+static gint ett_sdci = -1;
+static gint ett_type0 = -1;
+static gint ett_type2 = -1;
+static gint ett_type3 = -1;
+static gint ett_type5 = -1;
+static gint ett_type6 = -1;
+static gint ett_type7 = -1;
+static gint ett_type9 = -1;
+static gint ett_type10 = -1;
+static gint ett_type11 = -1;
+
+static const value_string fac_coder_vals[] = {
+	{ 0,	"AAC" },
+	{ 1,	"CELP" },
+	{ 2,	"HVXC" },
+	{ 0,	NULL }
+};
+
+static const value_string fac_sample_rate_vals[] = {
+	{ 0,	"8 kHz" },
+	{ 1,	"12 kHz" },
+	{ 2,	"16 kHz" },
+	{ 3,	"24 kHz" },
+	{ 0,	NULL }
+};
+
+static const value_string fac_protection_vals[] = {
+	{ 0,	"0.5" },
+	{ 1,	"0.6 (0.62 if MSC uses 16-QAM)" },
+	{ 2,	"0.71" },
+	{ 3,	"0.78" },
+	{ 0,	NULL }
+};
+
+static const value_string system_id_vals[] = {
+	{ 0,	"DRM" },
+	{ 1,	"AM" },
+	{ 2,	"AM" },
+	{ 3,	"FM RDS" },
+	{ 4,	"FM RDS" },
+	{ 5,	"FM" },
+	{ 6,	"FM RDS" },
+	{ 7,	"FM RDS" },
+	{ 8,	"FM" },
+	{ 9,	"DAB" },
+	{10,	"DAB" },
+	{11,	"DAB" },
+	{ 0,	NULL }
+};
+
+static const value_string app_domain_vals[] = {
+	{ 0,	"DRM" },
+	{ 1,	"DAB" },
+	{ 2,	"reserved for future definition" },
+	{ 3,	"reserved for future definition" },
+	{ 4,	"reserved for future definition" },
+	{ 5,	"reserved for future definition" },
+	{ 6,	"reserved for future definition" },
+	{ 7,	"reserved for future definition" },
+	{ 0,	NULL }
+};
+
+void MjdToDate (guint32 Mjd, guint32 *Year, guint32 *Month, guint32 *Day)
+{
+    guint32 J, C, Y, M;
+
+    J = Mjd + 2400001 + 68569;
+    C = 4 * J / 146097;
+    J = J - (146097 * C + 3) / 4;
+    Y = 4000 * (J + 1) / 1461001;
+    J = J - 1461 * Y / 4 + 31;
+    M = 80 * J / 2447;
+    *Day = J - 2447 * M / 80;
+    J = M / 11;
+    *Month = M + 2 - (12 * J);
+    *Year = 100 * (C - 49) + Y + J;
+}
+
+
+static void
+dissect_sdc (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  proto_tree *sdc_tree = NULL;
+  tvbuff_t *next_tvb = NULL;
+  proto_item *ti = NULL;
+  guint offset = 0;
+  guint len = tvb_length(tvb);
+  const guint8 *crc_buf;
+  unsigned long c;
+  guint crcp = len-2;
+  drm_data_t *data = (drm_data_t*)pinfo->private_data;
+
+  if(len==0) /* in the RSCI, sdc_ can be empty when rx not synced */
+  {
+    if (tree) {			/* we are being asked for details */
+      proto_tree_add_text(tree, tvb, 0, 0, "DRM Service Data Channel (SDC) (not synced)");
+	}
+    return;
+  }
+
+  if (check_col (pinfo->cinfo, COL_INFO))
+    col_add_str (pinfo->cinfo, COL_INFO, "SDC");
+
+  crc_buf = tvb_get_ptr(tvb, 0, len);
+  c = crc_drm(crc_buf, len, 16, 0x11021, 1);
+
+  if(tree) {
+    ti = proto_tree_add_text(tree, tvb, 0, -1, "DRM Service Data Channel (SDC)");
+    sdc_tree = proto_item_add_subtree (ti, ett_sdc);
+    proto_tree_add_item (sdc_tree, hf_sdc_afs_index, tvb, offset, 1, FALSE);
+  }
+  offset += 1;
+  while(offset<crcp) {
+    gboolean dissected;
+    guint8 type;
+    guint bytes = tvb_get_guint8(tvb, offset);
+    offset++;
+    type = tvb_get_guint8(tvb, offset) >> 4;
+    if(tree) {
+      proto_item *ci;
+      ci = proto_tree_add_item (sdc_tree, hf_sdc_type, tvb, offset, 1, FALSE);
+      PROTO_ITEM_SET_HIDDEN(ci);
+    }
+    data->sdc_bytes = bytes;
+    bytes = bytes/2;
+    if(bytes == 0)
+      break;
+    bytes += 1; /* SDC length is weird because data field is n bytes + 4 bits */
+    next_tvb = tvb_new_subset (tvb, offset, bytes, bytes);
+    dissected = dissector_try_port(sdc_dissector_table, type, next_tvb, pinfo, sdc_tree);
+    if(dissected == FALSE) {
+      if(tree) {
+        proto_tree_add_text (sdc_tree, tvb, offset, bytes, "Type %d", type);
+      }
+    }
+    offset += bytes;
+    /*g_free(next_tvb);*/
+  }
+  if(tree) {
+    proto_item *ci;
+    ci = proto_tree_add_item (sdc_tree, hf_sdc_crc, tvb, crcp, 2, FALSE);
+   	proto_item_append_text(ci, " (%s)", (c==0xe2f0)?"Ok":"bad");
+    ci = proto_tree_add_boolean(sdc_tree, hf_sdc_crc_ok, tvb, crcp, 2, c==0xe2f0);
+    PROTO_ITEM_SET_HIDDEN(ci);
+  }
+}
+
+/*
+• protection level for part A 2 bits.
+• protection level for part B 2 bits.
+• stream description for stream 0 24 bits.
+and optionally, dependent upon the number of streams in the multiplex:
+• stream description for stream 1 24 bits.
+• stream description for stream 2 24 bits.
+• stream description for stream 3 24 bits.
+The stream description for stream 0 depends on whether the MSC mode field of the FAC indicates that the hierarchical
+frame is present or not.
+If the hierarchical frame is not present then the stream description is as follows:
+• data length for part A 12 bits.
+• data length for part B 12 bits.
+If the hierarchical frame is present then the stream description is as follows:
+• protection level for hierarchical 2 bits.
+• rfu 10 bits.
+• data length for hierarchical 12 bits.
+The stream descriptions for streams 1, 2 and 3, when present, are as follows:
+• data length for part A 12 bits.
+• data length for part B 12 bits.
+*/
+
+static void
+dissect_type0 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  proto_tree *sub_tree = NULL;
+  proto_item *ti = NULL;
+  guint offset = 0;
+  if(tree) {
+    guint len = tvb_length(tvb);
+    guint i, streams;
+    drm_data_t *data = (drm_data_t*)pinfo->private_data;
+    guint8 version = 0;
+	if(len==0)
+	  return;
+    if(data) {
+      version = data->sdc_bytes & 1;
+    }
+    streams = (len-1)/3;
+    ti = proto_tree_add_text (tree, tvb, 0, tvb_length(tvb),
+          "Type 0: multiplex description for %s configuration with %d streams",
+          version?"the next":"this", streams
+          );
+    sub_tree = proto_item_add_subtree (ti, ett_type0);
+    proto_tree_add_item (sub_tree, hf_sdc_prot_a, tvb, offset, 1, FALSE);
+    proto_tree_add_item (sub_tree, hf_sdc_prot_b, tvb, offset, 1, FALSE);
+    offset += 1;
+    for(i=0; i<streams; i++) {
+      guint32 len = tvb_get_ntoh24(tvb, offset);
+      proto_tree_add_uint_format (sub_tree, hf_sdc_data_len_a, tvb, offset, 3,
+         len>>12, "part a length for stream %d: %d", i, len>>12);
+      proto_tree_add_uint_format (sub_tree, hf_sdc_data_len_b, tvb, offset, 3,
+         len&0xfff, "part b length for stream %d: %d", i, len&0xfff);
+      offset += 3;
+    }
+  }
+}
+
+static void
+dissect_type1 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  (void)pinfo;
+  if(tree) {
+    guint8 id = tvb_get_guint8(tvb, 0) & 0x0f;
+    char *label = (char*)tvb_get_string(tvb, 1, tvb_length(tvb)-1);
+    proto_tree_add_string_format(tree, hf_sdc_label, tvb, 1, -1, label, 
+    "Type 1: label for service %d: %s", id>>2, label);
+    g_free(label);
+  } 
+}
+
+static void
+dissect_type2 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  proto_tree *sub_tree = NULL;
+  proto_item *ti = NULL;
+  if(tree) {
+    drm_data_t *data = (drm_data_t*)pinfo->private_data;
+    guint8 version = 0;
+    if(data) {
+      version = data->sdc_bytes & 1;
+    }
+    ti = proto_tree_add_text (tree, tvb, 0, tvb_length(tvb),
+          "Type 2: conditional access data for %s configuration",
+          version?"the next":"this"
+          );
+    sub_tree = proto_item_add_subtree (ti, ett_type0);
+    proto_tree_add_item (sub_tree, hf_sdc_ca_id, tvb, 0, 1, FALSE);
+    proto_tree_add_item (sub_tree, hf_sdc_ca_data, tvb, 1, -1, FALSE);
+  }
+}
+
+/*
+Synchronous Multiplex flag 1 bit.
+• Layer flag 1 bit.
+• Service Restriction flag 1 bit.
+• Region/Schedule flag 1 bit.
+• Service Restriction field 0 or 8 bits.
+• Region/Schedule field 0 or 8 bits.
+• n frequencies n × 16 bits.
+*/
+
+static void
+dissect_type3 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  proto_tree *sub_tree = NULL;
+  proto_item *ti = NULL;
+  guint offset = 0;
+  guint8 id;
+  if(tree) {
+    drm_data_t *data = (drm_data_t*)pinfo->private_data;
+    guint8 version = 0;
+    if(data) {
+      version = data->sdc_bytes & 1;
+    }
+    id = tvb_get_guint8(tvb, offset) & 0x0f;
+    offset++;
+    ti = proto_tree_add_text (tree, tvb, 0, -1,
+          "Type 3: %s MFN with %s layer carrying %s services",
+          (id&0x8)?"synchronous":"async", 
+          (id&0x4)?"enhancement":"base", 
+          (id&0x2)?"some":"all"
+          );
+    sub_tree = proto_item_add_subtree (ti, ett_type3);
+    if(id&0x2) {
+      guint8 s = tvb_get_guint8(tvb, offset);
+	  if(s&0x10)
+	    proto_tree_add_text (sub_tree, tvb, offset, 1, "service 0 carried");
+	  if(s&0x20)
+	    proto_tree_add_text (sub_tree, tvb, offset, 1, "service 1 carried");
+	  if(s&0x40)
+	    proto_tree_add_text (sub_tree, tvb, offset, 1, "service 2 carried");
+	  if(s&0x80)
+	    proto_tree_add_text (sub_tree, tvb, offset, 1, "service 3 carried");
+      offset++;
+    }
+    if(id&0x1) {
+      guint8 s = tvb_get_guint8(tvb, offset);
+      proto_tree_add_text (sub_tree, tvb, offset, 1, "using region id %d and schedule id %d",
+        s >> 4, s & 0xf
+       );
+      offset++;
+    }
+    while(offset<tvb_length(tvb)) {
+      proto_tree_add_text (sub_tree, tvb, offset, 2, "%d kHz", tvb_get_ntohs(tvb, offset));
+      offset += 2;      
+    }
+  } 
+}
+
+/*
+• Schedule Id 4 bits
+• Day Code 7 bits
+• Start Time 11 bits
+• Duration 14 bits
+*/
+
+static void
+dissect_type4 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  if(tree) {
+    drm_data_t *data = (drm_data_t*)pinfo->private_data;
+    guint8 id, d, version;
+    guint32 n;
+    guint16 start, duration;
+	guint i;
+    char days[8];
+    version = 0;
+    if(data) {
+      version = data->sdc_bytes & 1;
+    }
+    id = tvb_get_guint8(tvb, 0) & 0x0f;
+    d = tvb_get_guint8(tvb, 1) >> 1;
+    n = tvb_get_ntohl(tvb, 1);
+    start = (n >> 14) & 0x7ff;
+    duration = n & 0x3fff;
+    g_snprintf(days, sizeof(days), "MTWTFSS");
+    for(i=0; i<7; i++)
+      if((d & (1<<(6-i)))==0)
+        days[i]='_';
+    proto_tree_add_text (tree, tvb, 0, -1,
+          "Type 4: schedule %d includes %3d mins from %2d:%02d UTC on days %s",
+          id, duration, start/60, start%60, days
+          );
+  } 
+}
+
+static void
+dissect_type5 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  proto_tree *sub_tree = NULL;
+  proto_item *ti = NULL;
+  guint offset = 0;
+  guint8 n = tvb_get_guint8(tvb, 1);
+  gboolean packet_mode = (n & 0x80)!=0;
+  drm_data_t *data = (drm_data_t*)pinfo->private_data;
+  guint8 version = 0;
+  if(data==NULL)
+    return;
+  version = data->sdc_bytes & 1;
+  if(version==0) {
+    guint8 s = tvb_get_guint8(tvb, 0) & 0x03;
+    data->stream_data[s].valid=TRUE;
+    data->stream_data[s].audio_not_data=FALSE;
+    data->stream_data[s].packet_mode_indicator = packet_mode;
+    data->stream_data[s].enhancement_flag = (n & 0x80)!=0;
+    if(packet_mode) {
+      guint8 packet_id = (n & 0x30) >> 4;
+      data->stream_data[s].data_services[packet_id].data_unit_indicator = (n & 0x40) != 0;
+      data->stream_data[s].data_services[packet_id].application_domain = n & 0x07;
+      n = tvb_get_guint8(tvb, 2);
+      data->stream_data[s].packet_length = n;
+      /*data->stream_data[s].data_services[packet_id].application_data = ;*/
+    } else {
+      data->stream_data[s].data_services[0].application_domain = n & 0x07;
+      /*data->stream_data[s].data_services[0].application_data = ;*/
+    }
+  }
+  
+  if(tree) {
+    ti = proto_tree_add_text (tree, tvb, 0, -1,
+          "Type 5: application information for %s configuration",
+          version?"the next":"this"
+          );
+    sub_tree = proto_item_add_subtree (ti, ett_type5);
+    proto_tree_add_item (sub_tree, hf_sdc_short_id, tvb, offset, 1, FALSE);
+    proto_tree_add_item (sub_tree, hf_sdc_stream_id, tvb, offset, 1, FALSE);
+    offset++;
+    proto_tree_add_item (sub_tree, hf_sdc_packet_flag, tvb, offset, 1, FALSE);
+    if(packet_mode) {
+      proto_tree_add_item (sub_tree, hf_sdc_data_unit_indicator, tvb, offset, 1, FALSE);
+      proto_tree_add_item (sub_tree, hf_sdc_packet_id, tvb, offset, 1, FALSE);
+    }
+    proto_tree_add_item (sub_tree, hf_sdc_data_enhancement_flag, tvb, offset, 1, FALSE);
+    proto_tree_add_item (sub_tree, hf_sdc_application_domain, tvb, offset, 1, FALSE);
+    offset++;
+    if(packet_mode) {
+      proto_tree_add_item (sub_tree, hf_sdc_packet_length, tvb, offset, 1, FALSE);
+      offset++;
+    }
+		if(offset<tvb_length(tvb))
+      proto_tree_add_item (sub_tree, hf_sdc_app_data, tvb, offset, -1, FALSE);
+  }
+}
+
+/*
+• Short Id flags 4 bits.
+• Same Multiplex/Other Service flag 1 bit.
+• Short Id/Announcement Id 2 bits.
+• rfa 1 bit.
+• Announcement support flags 10 bits.
+• Announcement switching flags 10 bits.
+*/
+
+static void
+dissect_type6 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  proto_tree *sub_tree = NULL;
+  proto_item *ti = NULL;
+  (void)pinfo;
+  if(tree) {
+    guint8 s = tvb_get_guint8(tvb, 0) & 0x0f;
+    guint8 n = tvb_get_guint8(tvb, 1);
+    ti = proto_tree_add_text (tree, tvb, 0, -1, 
+      "Type 6: Announcement definition for services %c%c%c%c",
+      (s&1)?'0':'_',
+      (s&2)?'1':'_',
+      (s&4)?'2':'_',
+      (s&8)?'3':'_'
+    );
+    sub_tree = proto_item_add_subtree (ti, ett_type6);
+    if(n&0x80)
+      proto_tree_add_text (sub_tree, tvb, 1, 1,
+        "announcments are carried as defined in Type 11 message with id %2d",
+        (n&0x60)>>5
+      );
+    else
+      proto_tree_add_text (sub_tree, tvb, 1, 1,
+        "announcments are carried in service %2d", (n&0x60)>>5
+      );
+    proto_tree_add_item (sub_tree, hf_sdc_ann_sup, tvb, 1, 3, FALSE);
+    proto_tree_add_item (sub_tree, hf_sdc_ann_sw, tvb, 1, 3, FALSE);
+  }
+}
+
+/*
+• Region Id 4 bits.
+• Latitude 8 bits.
+• Longitude 9 bits.
+• Latitude Extent 7 bits.
+• Longitude Extent 8 bits.
+• n CIRAF Zones n × 8 bits.
+*/
+
+static void
+dissect_type7 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  proto_tree *sub_tree = NULL;
+  proto_item *ti = NULL;
+  guint offset = 0;
+  if(tree) {
+    drm_data_t *data = (drm_data_t*)pinfo->private_data;
+    guint8 id, version = 0;
+    gint16 n, longitude; 
+    if(data) {
+      version = data->sdc_bytes & 1;
+    }
+    id = tvb_get_guint8(tvb, 0) & 0x0f;
+    n = tvb_get_ntohs(tvb, 2) >> 7;
+    if(n & 0x100){
+      guint16 x = 0x200 - 1;
+      longitude = (~x|n);
+    } else {
+      longitude = n;
+    }
+    ti = proto_tree_add_text (tree, tvb, 0, -1,"Type 7: region %2d", id);
+    sub_tree = proto_item_add_subtree (ti, ett_type7);
+    proto_tree_add_item (sub_tree, hf_sdc_region_id, tvb, 0, 1, FALSE);
+    proto_tree_add_item (sub_tree, hf_sdc_latitude, tvb, 1, 1, FALSE);
+    proto_tree_add_int (sub_tree, hf_sdc_longitude, tvb, 2, 2, longitude);
+    proto_tree_add_item (sub_tree, hf_sdc_latx, tvb, 2, 2, FALSE);
+    proto_tree_add_item (sub_tree, hf_sdc_longx, tvb, 4, 1, FALSE);
+    for(offset=5; offset<tvb_length(tvb); offset++)
+      proto_tree_add_item (sub_tree, hf_sdc_zone, tvb, offset, 1, FALSE);
+  }
+}
+
+/*
+ Modified Julian Date 17 bits.
+ UTC (hours and minutes) 11 bits.
+  UTC: this field specifies the current UTC time expressed in 
+  hours (5 bits) and 
+  minutes (6 bits).
+*/
+
+static void
+dissect_type8 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  if(tree) {
+    proto_item *ti = NULL;
+    guint32 dnt = tvb_get_ntohl(tvb, 0);
+    guint32 mjd = (dnt >> 11) & 0x1ffff;
+    guint8 hour = (dnt >> 6) & 0x1f;
+    guint8 min = dnt & 0x3f;
+    guint32 d,m,y;
+    MjdToDate(mjd, &y, &m, &d);
+    proto_tree_add_text (tree, tvb, 0, -1, "Type 8: Date & Time %d/%d/%d (%d) %2d:%02d",
+    d,m,y,
+    mjd, hour, min
+    );
+    ti = proto_tree_add_item (tree, hf_sdc_mjd, tvb, 0, 4, FALSE);    
+    PROTO_ITEM_SET_HIDDEN(ti);
+    ti = proto_tree_add_item (tree, hf_sdc_hour, tvb, 0, 4, FALSE);    
+    PROTO_ITEM_SET_HIDDEN(ti);
+    ti = proto_tree_add_item (tree, hf_sdc_minute, tvb, 0, 4, FALSE);    
+    PROTO_ITEM_SET_HIDDEN(ti);
+  }
+  (void)pinfo;
+}
+
+
+/*
+ • Short Id 2 bits.
+ • Stream Id 2 bits.
+ 
+ • audio coding 2 bits.
+ • SBR flag 1 bit.
+ • audio mode 2 bits.
+ • audio sampling rate 3 bits.
+ 
+ • text flag 1 bit.
+ • enhancement flag 1 bit.
+ • coder field 5 bits.
+ • rfa 1 bit.
+*/
+
+static void
+dissect_type9 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  proto_tree *sub_tree = NULL;
+  proto_item *ti = NULL;
+  drm_data_t *data = (drm_data_t*)pinfo->private_data;
+  guint8 version = 0;
+  if(data==NULL)
+    return;
+  version = data->sdc_bytes & 1;
+  if(version==0) {
+    guint8 s = tvb_get_guint8(tvb, 0) & 0x03;
+    guint8 n = tvb_get_guint8(tvb, 1);
+    data->stream_data[s].valid=TRUE;
+    data->stream_data[s].audio_not_data=TRUE;
+    data->stream_data[s].audio_coding = (n & 0xc0) >> 6;
+    data->stream_data[s].sbr = (n & 0x20)!=0;
+    data->stream_data[s].audio_mode = (n & 0x18) >> 3;
+    data->stream_data[s].sampling_rate_index = (n & 0x07);
+    n = tvb_get_guint8(tvb, 2);
+    data->stream_data[s].text_active = (n & 0x80)!=0;
+    data->stream_data[s].enhancement_flag = (n & 0x40)!=0;
+    data->stream_data[s].coder_field = (n & 0x3e) >> 1;
+  }
+  if(tree) {
+    ti = proto_tree_add_text (tree, tvb, 0, tvb_length(tvb),
+          "Type 9: audio info for %s configuration",
+          version?"the next":"this"
+          );
+    sub_tree = proto_item_add_subtree (ti, ett_type9);
+    proto_tree_add_item (sub_tree, hf_sdc_short_id, tvb, 0, 1, FALSE);
+    proto_tree_add_item (sub_tree, hf_sdc_stream_id, tvb, 0, 1, FALSE);
+    proto_tree_add_item (sub_tree, hf_sdc_audio_coding, tvb, 1, 1, FALSE);
+    proto_tree_add_item (sub_tree, hf_sdc_sbr_flag, tvb, 1, 1, FALSE);
+    proto_tree_add_item (sub_tree, hf_sdc_audio_mode, tvb, 1, 1, FALSE);
+    proto_tree_add_item (sub_tree, hf_sdc_audio_sample_rate, tvb, 1, 1, FALSE);
+    proto_tree_add_item (sub_tree, hf_sdc_text_flag, tvb, 2, 1, FALSE);
+    proto_tree_add_item (sub_tree, hf_sdc_enhancement_flag, tvb, 2, 1, FALSE);
+    proto_tree_add_item (sub_tree, hf_sdc_coder_field, tvb, 2, 1, FALSE);
+  }
+}
+
+static void
+dissect_type10 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  proto_tree *sub_tree = NULL;
+  proto_item *ti = NULL;
+  if(tree) {
+    drm_data_t *data = (drm_data_t*)pinfo->private_data;
+    dissector_handle_t fac_handle;
+    guint8 version = 0;
+    if(data) {
+      version = data->sdc_bytes & 1;
+    }
+    ti = proto_tree_add_text (tree, tvb, 0, tvb_length(tvb),
+          "Type 10: FAC for %s configuration",
+          version?"the next":"this"
+          );
+    sub_tree = proto_item_add_subtree (ti, ett_type10);
+    fac_handle = find_dissector("FAC");
+    if(fac_handle)
+      call_dissector(fac_handle, tvb, pinfo, sub_tree);
+  }
+}
+
+/*
+• Short Id/Announcement Id flag 1 bit.
+• Short Id/Announcement Id field 2 bits.
+• Region/Schedule flag 1 bit.
+• Same Service flag 1 bit.
+• rfa 2 bits.
+• System Id 5 bits.
+• Region/Schedule field 0 bits or 8 bits.
+• Other Service Id 0 bits or 16 bits or 24 bits or 32 bits.
+• n frequencies n × (8 or 16) bits.
+
+DRM/AM frequency:
+each 16 bit field contains the following information:
+• rfu 1 bit.
+• frequency value 15 bits.
+rfu: this 1 bit is reserved for future use of the frequency value field and shall be set to zero until it is defined.
+frequency value: this 15 bit field is coded as an unsigned integer and gives the frequency in kHz.
+FM1 (87,5 MHz to 107,9 MHz) frequency:
+code meaning
+0 to 204: FM frequencies 87,5 MHz to 107,9 MHz (100 kHz step)
+FM2 (76,0 MHz to 90,0 MHz) frequency:
+code meaning
+0 to 140: FM frequencies 76,0 MHz to 90,0 MHz (100 kHz step)
+DAB [3] frequency:
+code meaning
+0 to 11: DAB channels 2A to 4D (Band I)
+64 to 95: DAB channels 5A to 12D (Band III)
+96 to 101: DAB channels 13A to 13F (Band III +)
+128 to 140: DAB channels (L-Band, European grid)
+160 to 182: DAB channels (L-Band, Canadian grid)
+
+*/
+
+static void
+dissect_type11 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  proto_tree *sub_tree = NULL;
+  proto_item *ti = NULL;
+  (void)pinfo;
+  if(tree) {
+    guint8 n = tvb_get_guint8(tvb, 0) & 0x0f;
+    guint8 sid = tvb_get_guint8(tvb, 1);
+    guint flen=255, offset;
+    guint idlen=0;
+	guint8 short_id = (n>>1)&0x3;
+    guint32 system_id, id;
+    const gchar* sys;
+    if(n&0x8)
+      ti = proto_tree_add_text (tree, tvb, 0, -1, 
+      "Type 11: Location of announcements with id %2d", short_id
+      );
+    else
+      ti = proto_tree_add_text (tree, tvb, 0, -1, 
+      "Type 11: Alternative frequencies for service %2d", short_id
+      );
+    sub_tree = proto_item_add_subtree (ti, ett_type11);
+    system_id = sid & 0x1f;
+    id = sid & 0x1f;
+    offset = 1;
+    sys = match_strval(system_id, system_id_vals);
+    proto_tree_add_text (sub_tree, tvb, offset, 1, 
+          "%s network carrying %s service",
+          sys,
+          (sid&0x80)?"the same":"an alternative"
+    );
+    offset += 1;
+    if(n&1) {
+      guint8 rs = tvb_get_guint8(tvb, offset);
+      proto_tree_add_text (sub_tree, tvb, offset, 1,
+        "restricted to region %d and schedule %d",
+        rs>>4, rs&0x0f
+        );
+      offset += 1;
+    }
+    switch(system_id){
+    case 0:
+       flen=2;
+       id = tvb_get_ntoh24(tvb, offset);
+       idlen = 3;
+       break;
+    case 1:
+       flen=2;
+       id = tvb_get_ntoh24(tvb, offset);
+       idlen = 3;
+       break;
+    case 2:
+       flen=2;
+       break;
+    case 3:
+       flen=1;
+       id = tvb_get_ntoh24(tvb, offset);
+       idlen = 3;
+       break;
+    case 4:
+       flen=1;
+       id = tvb_get_ntohs(tvb, offset);
+       idlen = 2;
+       break;
+    case 5:
+       flen=1;
+       break;
+    case 6:
+       flen=1;
+       id = tvb_get_ntoh24(tvb, offset);
+       idlen = 3;
+       break;
+    case 7:
+       flen=1;
+       id = tvb_get_ntohs(tvb, offset);
+       idlen = 2;
+       break;
+    case 8:
+       flen=1;
+       break;
+    case 9:
+       flen=1;
+       id = tvb_get_ntoh24(tvb, offset);
+       idlen = 3;
+       break;
+    case 10:
+       flen=1;
+       id = tvb_get_ntohs(tvb, offset);
+       idlen = 2;
+       break;
+    case 11:
+       flen=1;
+       id = tvb_get_ntohl(tvb, offset);
+       idlen = 4;
+       break;
+    }
+    if(idlen>0) {
+      proto_tree_add_text (sub_tree, tvb, offset, idlen, "with id %*X", idlen, id);
+      offset += idlen;
+    }
+	if(flen==255)
+	{
+      proto_tree_add_text (sub_tree, tvb, offset, 0, "bad system id, will not parse frequencies");
+	}
+    while(offset<tvb_length(tvb)){
+      guint16 f;
+      if(flen==1)
+         f = tvb_get_guint8(tvb, offset);
+      else
+        f = tvb_get_ntohs(tvb, offset);
+      switch(system_id) {
+        case 3:
+        case 4:
+        case 5:
+          {
+            double m = 87.5+((double)f)/10.0; /* NA & Europe Grid */
+            proto_tree_add_text (sub_tree, tvb, offset, flen, "%.1f MHz", m);
+          }
+          break;
+        case 6:
+        case 7:
+        case 8:
+          {
+            double m = 76.0+((double)f)/10.0; /* Asia Grid */
+            proto_tree_add_text (sub_tree, tvb, offset, flen, "%.1f MHz", m);
+          }
+          break;
+        case 9:
+        case 10:
+        case 11:
+          if(f<=11) {
+            guint channel = (f / 4) + 2;
+            guint sub_channel = (f % 4) + 'A';
+            proto_tree_add_text (sub_tree, tvb, offset, flen,
+               "Band I channel %d%c", channel, sub_channel);
+          } else if(64<= f && f <=95) {
+            guint channel = (f / 4) - 11;
+            guint sub_channel = (f % 4) + 'A';
+            proto_tree_add_text (sub_tree, tvb, offset, flen,
+               "Band III channel %d%c", channel, sub_channel);
+          } else if(96<= f && f <=101) {
+            guint channel = (f / 6) - 3;
+            guint sub_channel = (f % 6) + 'A';
+            proto_tree_add_text (sub_tree, tvb, offset, flen, 
+               "Band III+ channel %d%c", channel, sub_channel);
+          } else if(128<= f && f <=143) {
+            guint channel = f - 128;
+            double m = 1452.96+1.712*((double)channel);
+            proto_tree_add_text (sub_tree, tvb, offset, flen,
+                "European L-Band channel L%c, %.3f MHz", channel+'A', m);
+          } else if(160<= f && f <=182) {
+            guint channel = f - 159;
+            double m = 1451.072+1.744*((double)channel);
+            proto_tree_add_text (sub_tree, tvb, offset, flen,
+                "Canadian L-Band channel %d, %.3f MHz", channel, m);
+          } else {
+            proto_tree_add_text (sub_tree, tvb, offset, flen, "unknown channel %d", f);
+          }
+          break;
+        default:
+          proto_tree_add_text (sub_tree, tvb, offset, flen, "%d kHz", f);
+      }
+      offset += flen;
+    }
+  }
+}
+
+/*
+• Short Id 2 bits.
+• rfu 2 bits.
+• language code 24 bits.
+• country code 16 bits.
+*/
+
+static void
+dissect_type12 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  (void)pinfo;
+  if(tree) {
+    guint8 id = tvb_get_guint8(tvb, 0) & 0x0f;
+    guint8 *lang = tvb_get_string(tvb, 1, 3);
+    guint8 *cc = tvb_get_string(tvb, 4, 2);
+    proto_tree_add_text (tree, tvb, 0, -1, "Type 12: service %d %s, %s",
+    id>>2, lang, cc
+    );
+    g_free(lang);
+    g_free(cc);
+  }
+}
+
+static void
+dissect_sdci (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  guint len = tvb_length(tvb);
+
+  if(tree) {
+    if(len==0) /* in the RSCI, sdc_ can be empty when rx not synced */
+	{
+      proto_tree_add_text(tree, tvb, 0, 0, "sdci (not synced)");
+	}
+	else
+	{
+      proto_tree *sdci_tree = NULL;
+      proto_item *ti = NULL;
+      drm_data_t *data;    
+      ti = proto_tree_add_text (tree, tvb, 0, -1, "sdci");
+      sdci_tree = proto_item_add_subtree (ti, ett_sdci);
+      data = (drm_data_t*)pinfo->private_data;    
+      pinfo->private_data = 0;
+      dissect_type0(tvb, pinfo, sdci_tree);
+      pinfo->private_data = data;
+	}
+  }
+}
+
+void
+register_sdc_dissectors (int proto)
+{
+  proto_drm_sdc = proto;
+
+    sdc_handle = create_dissector_handle (dissect_sdc, proto_drm_sdc);
+    dissector_add_string("drm-di.tag", "sdc_", sdc_handle);
+
+    sdci_handle = create_dissector_handle (dissect_sdci, proto_drm_sdc);
+    dissector_add_string("drm-di.tag", "sdci", sdci_handle);
+
+    dissector_add("drm-sdc.type", 0, create_dissector_handle (dissect_type0, proto_drm_sdc));
+    dissector_add("drm-sdc.type", 1, create_dissector_handle (dissect_type1, proto_drm_sdc));
+    dissector_add("drm-sdc.type", 2, create_dissector_handle (dissect_type2, proto_drm_sdc));
+    dissector_add("drm-sdc.type", 3, create_dissector_handle (dissect_type3, proto_drm_sdc));
+    dissector_add("drm-sdc.type", 4, create_dissector_handle (dissect_type4, proto_drm_sdc));
+    dissector_add("drm-sdc.type", 5, create_dissector_handle (dissect_type5, proto_drm_sdc));
+    dissector_add("drm-sdc.type", 6, create_dissector_handle (dissect_type6, proto_drm_sdc));
+    dissector_add("drm-sdc.type", 7, create_dissector_handle (dissect_type7, proto_drm_sdc));
+    dissector_add("drm-sdc.type", 8, create_dissector_handle (dissect_type8, proto_drm_sdc));
+    dissector_add("drm-sdc.type", 9, create_dissector_handle (dissect_type9, proto_drm_sdc));
+    dissector_add("drm-sdc.type", 10, create_dissector_handle (dissect_type10, proto_drm_sdc));
+    dissector_add("drm-sdc.type", 11, create_dissector_handle (dissect_type11, proto_drm_sdc));
+    dissector_add("drm-sdc.type", 12, create_dissector_handle (dissect_type12, proto_drm_sdc));
+}
+
+void
+register_drm_sdc (int proto)
+{
+  static hf_register_info hf[] = {
+    {&hf_sdc_afs_index,
+     {"AFS Index", "drm-sdc.afs_index",
+      FT_UINT8, BASE_DEC, NULL, 0x0f,
+      "Pointer to the next identical SDC block", HFILL}
+     },
+    {&hf_sdc_length,
+     {"Length", "drm-sdc.length",
+      FT_UINT8, BASE_DEC, NULL, 0xfe,
+      "length of element", HFILL}
+     },
+    {&hf_sdc_version,
+     {"Version", "drm-sdc.version",
+      FT_UINT8, BASE_DEC, NULL, 0x01,
+      "element version, changes at reconfiguration, list change, ...", HFILL}
+     },
+    {&hf_sdc_type,
+     {"Type", "drm-sdc.type",
+      FT_UINT8, BASE_DEC, NULL, 0xf0,
+      "Element Type", HFILL}
+     },
+    {&hf_sdc_crc,
+     {"CRC", "drm-sdc.crc",
+      FT_UINT16, BASE_HEX, NULL, 0,
+      "CRC", HFILL}
+     },
+    {&hf_sdc_crc_ok,
+     {"CRC OK", "drm-sdc.crc_ok",
+      FT_BOOLEAN, BASE_NONE, NULL, 0,
+      "CRC OK", HFILL}
+     },
+    {&hf_sdc_short_id,
+     {"short id", "drm-sdc.short_id",
+      FT_UINT8, BASE_DEC, NULL, 0x0c,
+      "short id", HFILL}
+     },
+    {&hf_sdc_stream_id,
+     {"stream id", "drm-sdc.stream_id",
+      FT_UINT8, BASE_DEC, NULL, 0x03,
+      "stream id", HFILL}
+     },
+    {&hf_sdc_audio_coding,
+     {"audio coding", "drm-sdc.audio_coding",
+      FT_UINT8, BASE_DEC, VALS(fac_coder_vals), 0xc0,
+      "audio coding", HFILL}
+     },
+    {&hf_sdc_sbr_flag,
+     {"SBR", "drm-sdc.sbr_flag",
+      FT_BOOLEAN, BASE_NONE, NULL, 0x20,
+      "SBR", HFILL}
+     },
+    {&hf_sdc_audio_mode,
+     {"audio mode", "drm-sdc.audio_mode",
+      FT_UINT8, BASE_DEC, NULL, 0x18,
+      "audio mode", HFILL}
+     },
+    {&hf_sdc_audio_sample_rate,
+     {"audio sample rate", "drm-sdc.audio_sample_rate",
+      FT_UINT8, BASE_DEC, VALS(fac_sample_rate_vals), 0x07,
+      "audio sample rate", HFILL}
+     },
+    {&hf_sdc_text_flag,
+     {"Text Messages", "drm-sdc.text_flag",
+      FT_BOOLEAN, BASE_NONE, NULL, 0x80,
+      "Text Messages", HFILL}
+     },
+    {&hf_sdc_enhancement_flag,
+     {"enhancement flag", "drm-sdc.enhancement_flag",
+      FT_BOOLEAN, BASE_NONE, NULL, 0x40,
+      "enhancement flag", HFILL}
+     },
+    {&hf_sdc_coder_field,
+     {"coder field", "drm-sdc.coder_field",
+      FT_UINT8, BASE_HEX, NULL, 0x3e,
+      "coder field", HFILL}
+     },
+    {&hf_sdc_label,
+     {"Type 1: Label", "drm-sdc.label",
+      FT_STRING, BASE_NONE, NULL, 0,
+      "Label", HFILL}
+     },
+    {&hf_sdc_prot_a,
+     {"Protection (part A)", "drm-sdc.prot_a",
+      FT_UINT8, BASE_DEC, VALS(fac_protection_vals), 0x0c,
+      "protection level for part A", HFILL}
+     },
+    {&hf_sdc_prot_b,
+     {"Protection (part B)", "drm-sdc.prot_b",
+      FT_UINT8, BASE_DEC, VALS(fac_protection_vals), 0x03,
+      "protection level for part B", HFILL}
+     },
+    {&hf_sdc_data_len_a,
+     {"data length for part A", "drm-sdc.data_len_a",
+      FT_UINT24, BASE_DEC, NULL, 0xfff000,
+      "data length for part A", HFILL}
+     },
+    {&hf_sdc_data_len_b,
+     {"data length for part B", "drm-sdc.data_len_b",
+      FT_UINT24, BASE_DEC, NULL, 0xfff,
+      "data length for part B", HFILL}
+     },
+    {&hf_sdc_packet_flag,
+     {"Packet mode indicator", "drm-sdc.packet_flag",
+      FT_BOOLEAN, BASE_NONE, NULL, 0x80,
+      "Packet mode indicator", HFILL}
+     },
+    {&hf_sdc_app_data,
+     {"application data", "drm-sdc.app_data",
+      FT_BYTES, BASE_HEX, NULL, 0,
+      "application data", HFILL}
+     },
+    {&hf_sdc_data_unit_indicator,
+     {"Data unit indicator", "drm-sdc.data_unit_indicator",
+      FT_BOOLEAN, BASE_NONE, NULL, 0x40,
+      "Data unit indicator", HFILL}
+     },
+    {&hf_sdc_packet_id,
+     {"packet id", "drm-sdc.packet_id",
+      FT_UINT8, BASE_DEC, NULL, 0x30,
+      "packet id", HFILL}
+     },
+    {&hf_sdc_data_enhancement_flag,
+     {"enhancement flag", "drm-sdc.data_enhancement_flag",
+      FT_BOOLEAN, BASE_NONE, NULL, 0x08,
+      "enhancement flag", HFILL}
+     },
+    {&hf_sdc_application_domain,
+     {"application domain", "drm-sdc.application_domain",
+      FT_UINT8, BASE_DEC, VALS(app_domain_vals), 0x07,
+      "application domain", HFILL}
+     },
+    {&hf_sdc_packet_length,
+     {"packet length", "drm-sdc.packet_length",
+      FT_UINT8, BASE_DEC, NULL, 0,
+      "packet length", HFILL}
+     },
+    {&hf_sdc_ca_data,
+     {"CA data", "drm-sdc.ca_data",
+      FT_BYTES, BASE_HEX, NULL, 0,
+      "CA data", HFILL}
+     },
+    {&hf_sdc_ca_id,
+     {"CA System id", "drm-sdc.ca_id",
+      FT_UINT8, BASE_DEC, NULL, 0,
+      "CA System id", HFILL}
+     },
+    {&hf_sdc_region_id,
+     {"region id", "drm-sdc.region_id",
+      FT_UINT8, BASE_DEC, NULL, 0x0f,
+      "region id", HFILL}
+     },
+    {&hf_sdc_latitude,
+     {"Latitude", "drm-sdc.latitude",
+      FT_INT8, BASE_DEC, NULL, 0,
+      "Latitude of south corner", HFILL}
+     },
+    {&hf_sdc_longitude,
+     {"Longitude", "drm-sdc.longitude",
+      FT_INT16, BASE_DEC, NULL, 0,
+      "Longitude of west corner", HFILL}
+     },
+    {&hf_sdc_latx,
+     {"Latitude extent", "drm-sdc.latx",
+      FT_UINT16, BASE_DEC, NULL, 0x7f,
+      "Latitude extent", HFILL}
+     },
+    {&hf_sdc_longx,
+     {"Longitude extent", "drm-sdc.longx",
+      FT_UINT8, BASE_DEC, NULL, 0,
+      "Longitude extent", HFILL}
+     },
+    {&hf_sdc_zone,
+     {"CIRAF Zone", "drm-sdc.zone",
+      FT_UINT8, BASE_DEC, NULL, 0,
+      "CIRAF Zone", HFILL}
+     },
+    {&hf_sdc_ann_sup,
+     {"Possible announcements", "drm-sdc.ann_sup",
+      FT_UINT16, BASE_HEX, NULL, 0xffc00,
+      "Announcement support flags", HFILL}
+     },
+    {&hf_sdc_ann_sw,
+     {"Active announcements", "drm-sdc.ann_sw",
+      FT_UINT16, BASE_HEX, NULL, 0x3ff,
+      "Announcement switching flags", HFILL}
+     },
+    {&hf_sdc_mjd,
+     {"MJD", "drm-sdc.mjd",
+      FT_UINT32, BASE_DEC, NULL, 0x3fffe000,
+      "Modified Julian Day", HFILL}
+     },
+    {&hf_sdc_hour,
+     {"hour", "drm-sdc.hour",
+      FT_UINT32, BASE_DEC, NULL, 0x1fc0,
+      "UTC hour", HFILL}
+     },
+    {&hf_sdc_minute,
+     {"minute", "drm-sdc.minute",
+      FT_UINT32, BASE_DEC, NULL, 0x3f,
+      "UTC minute", HFILL}
+     }
+  };
+
+    
+/* Setup protocol subtree array */
+  static gint *ett[] = {
+    &ett_sdc,
+    &ett_sdci,
+    &ett_type0,
+    &ett_type2,
+    &ett_type3,
+    &ett_type5,
+    &ett_type7,
+    &ett_type6,
+    &ett_type9,
+    &ett_type10,
+    &ett_type11
+  };
+
+  proto_drm_sdc = proto;
+
+  proto_register_field_array (proto_drm_sdc, hf, array_length (hf));
+  proto_register_subtree_array (ett, array_length (ett));
+  /* subdissector code */
+  sdc_dissector_table = register_dissector_table("drm-sdc.type",
+	    "SDC Element Type", FT_UINT8, BASE_DEC);
+}
Index: epan/dissectors/packet-drm-sdc.h
===================================================================
--- epan/dissectors/packet-drm-sdc.h	(revision 0)
+++ epan/dissectors/packet-drm-sdc.h	(revision 0)
@@ -0,0 +1,36 @@
+/* packet-drm-sdc.h
+ * Routines for Digital Radio Mondiale Multiplex Distribution Interface Protocol 
+ * Copyright 2007, British Broadcasting Corporation 
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@xxxxxxxxxxxxx>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+ 
+#ifndef __PACKET_DRM_SDC_H
+#define __PACKET_DRM_SDC_H
+
+#include <epan/packet.h>
+
+void MjdToDate (guint32 Mjd, guint32 *Year, guint32 *Month, guint32 *Day);
+
+void register_drm_sdc (int);
+void register_sdc_dissectors(int);
+
+#endif
Index: epan/dissectors/packet-drm-fac.c
===================================================================
--- epan/dissectors/packet-drm-fac.c	(revision 0)
+++ epan/dissectors/packet-drm-fac.c	(revision 0)
@@ -0,0 +1,407 @@
+/* packet-drm-di-fac.c
+ * Routines for Digital Radio Mondiale Multiplex Distribution Interface Protocol 
+ * Fast Access Channel (FAC) elements
+ * Copyright 2007, British Broadcasting Corporation 
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@xxxxxxxxxxxxx>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+ 
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+#include <gmodule.h>
+#include <epan/packet.h>
+#include <epan/prefs.h>
+#include <epan/crcdrm.h>
+#include "packet-drm-fac.h"
+#include "drm-data.h"
+
+extern int proto_drm_di;
+static int hf_fac_layer = -1;
+static int hf_fac_identity = -1;
+static int hf_fac_spectrum_occupancy = -1;
+static int hf_fac_depth = -1;
+static int hf_fac_msc_mode = -1;
+static int hf_fac_sdc_mode = -1;
+static int hf_fac_nservices = -1;
+static int hf_fac_recon_index = -1;
+static int hf_fac_service_identifier = -1;
+static int hf_fac_short_id = -1;
+static int hf_fac_audio_ca = -1;
+static int hf_fac_language = -1;
+static int hf_fac_audio_data = -1;
+static int hf_fac_prog_type = -1;
+static int hf_fac_app_id = -1;
+static int hf_fac_data_ca = -1;
+static int hf_fac_frame_number = -1;
+static int hf_fac_afs_index_valid = -1;
+static int hf_fac_crc = -1;
+static int hf_fac_crc_ok = -1;
+
+
+/* Initialize the subtree pointers */
+static gint ett_fac = -1;
+
+static const value_string so_vals[] = {
+	{ 0,	"4.5 kHz" },
+	{ 1,	"5 kHz" },
+	{ 2,	"9 kHz" },
+	{ 3,	"10 kHz" },
+	{ 4,	"18 kHz" },
+	{ 5,	"20 kHz" },
+	{ 0,	NULL }
+};
+
+static const value_string depth_vals[] = {
+	{ 0,	"long" },
+	{ 1,	"short" },
+	{ 0,	NULL }
+};
+
+static const value_string msc_mode_vals[] = {
+	{ 0,	"64-QAM, no hierarchical" },
+	{ 1,	"64-QAM, hierarchical on I" },
+	{ 2,	"64-QAM, hierarchical on I&Q" },
+	{ 3,	"16-QAM" },
+	{ 0,	NULL }
+};
+
+static const value_string sdc_mode_vals[] = {
+	{ 0,	"16-QAM" },
+	{ 1,	"4-QAM" },
+	{ 0,	NULL }
+};
+
+static const value_string layer_vals[] = {
+	{ 0,	"base" },
+	{ 1,	"enhancement" },
+	{ 0,	NULL }
+};
+
+static const value_string nservices_vals[] = {
+	{ 0, "4 audio services" },
+	{ 1, "1 data service" },
+	{ 2, "2 data services" },
+	{ 3, "3 data services" },
+	{ 4, "1 audio service" },
+	{ 5, "1 audio service and 1 data service" },
+	{ 6, "1 audio service and 2 data services" },
+	{ 7, "1 audio service and 3 data services" },
+	{ 8, "2 audio services" },
+	{ 9, "2 audio services and 1 data service" },
+	{10, "2 audio services and 2 data services" },
+	{11, "reserved" },
+	{12, "3 audio services" },
+	{13, "3 audio services and 1 data service" },
+	{14, "reserved" },
+	{15, "4 data services" },
+	{ 0,	NULL }
+};
+
+static const value_string language_vals[] = {
+        { 0, "No language specified" },
+        { 1, "Arabic" },
+        { 2, "Bengali" },
+        { 3, "Chinese (Mandarin)" },
+        { 4, "Dutch" },
+        { 5, "English" },
+        { 8, "Hindi" },
+        { 6, "French" },
+        { 9, "Japanese" },
+        { 7, "German" },
+        { 10, "Javanese" },
+        { 11, "Korean" },
+        { 12, "Portuguese" },
+        { 13, "Russian" },
+        { 14, "Spanish" },
+        { 15, "Other language" },
+	{ 0,	NULL }
+};
+
+static const value_string prog_type_vals[] = {
+        { 0, "No programme type" },
+        { 1, "News" },
+        { 2, "Current Affairs" },
+        { 3, "Information" },
+        { 4, "Sport" },
+        { 5, "Education" },
+        { 6, "Drama" },
+        { 7, "Culture" },
+        { 8, "Science" },
+        { 9, "Varied" },
+        { 10, "Pop Music" },
+        { 11, "Rock Music" },
+        { 12, "Easy Listening Music" },
+        { 13, "Light Classical" },
+        { 14, "Serious Classical" },
+        { 15, "Other Music" },
+        { 16, "Weather/meteorology" },
+        { 17, "Finance/Business" },
+        { 18, "Children's programmes" },
+        { 19, "Social Affairs" },
+        { 20, "Religion" },
+        { 21, "Phone In" },
+        { 22, "Travel" },
+        { 23, "Leisure" },
+        { 24, "Jazz Music" },
+        { 25, "Country Music" },
+        { 26, "National Music" },
+        { 27, "Oldies Music" },
+        { 28, "Folk Music" },
+        { 29, "Documentary" },
+        { 30, "Not used" },
+        { 31, "Not used - skip indicator" },
+	{ 0,	NULL }
+};
+
+static const value_string app_id_vals[] = {
+        { 0, "Application Signalled in SDC" },
+        { 1, "reserved for future definition" },
+        { 2, "reserved for future definition" },
+        { 3, "reserved for future definition" },
+        { 4, "reserved for future definition" },
+        { 5, "reserved for future definition" },
+        { 6, "reserved for future definition" },
+        { 7, "reserved for future definition" },
+        { 8, "reserved for future definition" },
+        { 9, "reserved for future definition" },
+        { 10, "reserved for future definition" },
+        { 11, "reserved for future definition" },
+        { 12, "reserved for future definition" },
+        { 13, "reserved for future definition" },
+        { 14, "reserved for future definition" },
+        { 15, "reserved for future definition" },
+        { 16, "reserved for future definition" },
+        { 17, "reserved for future definition" },
+        { 18, "reserved for future definition" },
+        { 19, "reserved for future definition" },
+        { 20, "reserved for future definition" },
+        { 21, "reserved for future definition" },
+        { 22, "reserved for future definition" },
+        { 23, "reserved for future definition" },
+        { 24, "reserved for future definition" },
+        { 25, "reserved for future definition" },
+        { 26, "reserved for future definition" },
+        { 27, "reserved for future definition" },
+        { 28, "reserved for future definition" },
+        { 29, "reserved for future definition" },
+        { 30, "reserved for future definition" },
+        { 31, "Not used - skip indicator" },
+	{ 0,	NULL }
+};
+
+void
+dissect_fac (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+
+  proto_item *ti = NULL;
+  proto_item *ci = NULL;
+  proto_tree *fac_tree = NULL;
+  guint16 n;
+  guint len = tvb_length(tvb);
+  const guint8 *crc_buf;
+  unsigned long c;
+  drm_data_t *data;
+
+  if(len==0) /* in the RSCI, fac_ can be empty when rx not synced */
+  {
+    if (tree) {			/* we are being asked for details */
+      proto_tree_add_text(tree, tvb, 0, 0, "DRM Fast Access Channel (FAC) (not synced)");
+	}
+    return;
+  }
+
+  data = (drm_data_t*)pinfo->private_data;
+  crc_buf = tvb_get_ptr(tvb, 0, len);
+  n = tvb_get_guint8(tvb, 0);
+  data->spectral_occupancy = (n>>1)&0x0f;
+  data->interleaver_depth = n&1;
+  n = tvb_get_guint8(tvb, 1);
+  data->msc_mode = n>>6;
+  data->sdc_mode = (n>>5)&1;
+  c = crc_drm(crc_buf, len, 8, 0x11d, 1);
+
+  if (tree) {			/* we are being asked for details */
+    guint8 b;
+    guint32 frame_number;
+    guint16 identity;
+    gboolean afs_index_valid;
+    ti = proto_tree_add_text(tree, tvb, 0, -1, "DRM Fast Access Channel (FAC)");
+    fac_tree = proto_item_add_subtree (ti, ett_fac);
+    proto_tree_add_item (fac_tree, hf_fac_layer, tvb, 0, 1, FALSE);
+    proto_tree_add_item(fac_tree, hf_fac_identity, tvb, 0, 1, FALSE);
+    b = tvb_get_guint8(tvb, 0);
+    identity = (b >> 5) & 0x3;
+    if(identity==3){
+      frame_number = 0;
+      proto_tree_add_uint (fac_tree, hf_fac_frame_number, tvb, 0, 1, frame_number);
+      afs_index_valid = FALSE;
+    } else {
+      frame_number = identity;
+      afs_index_valid = (identity==0);
+    }
+    if(frame_number==0)
+      proto_tree_add_boolean (fac_tree, hf_fac_afs_index_valid, tvb, 0, 1, afs_index_valid);
+    proto_tree_add_item (fac_tree, hf_fac_spectrum_occupancy, tvb, 0, 1, FALSE);
+    proto_tree_add_item (fac_tree, hf_fac_depth, tvb, 0, 1, FALSE);
+    proto_tree_add_item (fac_tree, hf_fac_msc_mode, tvb, 1, 2, FALSE);
+    proto_tree_add_item (fac_tree, hf_fac_sdc_mode, tvb, 1, 2, FALSE);
+    proto_tree_add_item (fac_tree, hf_fac_nservices, tvb, 1, 2, FALSE);
+    proto_tree_add_item (fac_tree, hf_fac_recon_index, tvb, 1, 2, FALSE);
+    proto_tree_add_item (fac_tree, hf_fac_service_identifier, tvb, 2, 4, FALSE);
+    proto_tree_add_item (fac_tree, hf_fac_short_id, tvb, 5, 1, FALSE);
+    proto_tree_add_item (fac_tree, hf_fac_audio_ca, tvb, 5, 1, FALSE);
+    proto_tree_add_item (fac_tree, hf_fac_language, tvb, 5, 2, FALSE);
+    proto_tree_add_item (fac_tree, hf_fac_audio_data, tvb, 6, 1, FALSE);
+    if(tvb_get_guint8(tvb,6)&0x10)
+      proto_tree_add_item (fac_tree, hf_fac_app_id, tvb, 6, 2, FALSE);
+    else
+      proto_tree_add_item (fac_tree, hf_fac_prog_type, tvb, 6, 2, FALSE);
+    proto_tree_add_item (fac_tree, hf_fac_data_ca, tvb, 7, 1, FALSE);
+    ci = proto_tree_add_item (fac_tree, hf_fac_crc, tvb, 8, 1, FALSE);
+   	proto_item_append_text(ci, (c==0x3b)?" (Ok)":" (bad)");
+    proto_tree_add_boolean(fac_tree, hf_fac_crc_ok, tvb, 8, 1, c==0x3b);
+  }
+}
+
+void
+register_fac_dissectors(int proto)
+{
+    dissector_add_string("drm-di.tag", "fac_", create_dissector_handle (dissect_fac, proto));
+}
+
+void
+register_drm_fac (int proto)
+{
+  static hf_register_info hf[] = {
+    {&hf_fac_layer,
+     {"Layer", "drm-fac.layer",
+      FT_UINT8, BASE_DEC, VALS(layer_vals), 0x80,
+      "Base or Enhancement Layer", HFILL}
+     },
+    {&hf_fac_identity,
+     {"Identity", "drm-fac.identity",
+      FT_UINT8, BASE_DEC, NULL, 0x60,
+      "FAC frame identity", HFILL}
+     },
+    {&hf_fac_spectrum_occupancy,
+     {"Spectrum Occupancy", "drm-fac.spectrum_occupancy",
+      FT_UINT8, BASE_DEC, VALS(so_vals), 0x1e,
+      "Spectrum Occupancy", HFILL}
+     },
+    {&hf_fac_depth,
+     {"Interleaver Depth", "drm-fac.interleaver_depth",
+      FT_UINT8, BASE_DEC, VALS(depth_vals), 0x1,
+      "Interleaver Depth", HFILL}
+     },
+    {&hf_fac_msc_mode,
+     {"MSC Mode", "drm-fac.msc_mode",
+      FT_UINT16, BASE_DEC, VALS(msc_mode_vals), 0xc000,
+      "MSC Mode", HFILL}
+     },
+    {&hf_fac_sdc_mode,
+     {"SDC Mode", "drm-fac.sdc_mode",
+      FT_UINT16, BASE_DEC, VALS(sdc_mode_vals), 0x2000,
+      "SDC Mode", HFILL}
+     },
+    {&hf_fac_nservices,
+     {"Number of Services", "drm-fac.nservices",
+      FT_UINT16, BASE_DEC, VALS(nservices_vals), 0x1e00,
+      "Number of Services", HFILL}
+     },
+    {&hf_fac_recon_index,
+     {"Reconfiguration index", "drm-fac.recon_index",
+      FT_UINT16, BASE_DEC, NULL, 0x01c0,
+      "Reconfiguration index", HFILL}
+     },
+    {&hf_fac_service_identifier,
+     {"Service identifier", "drm-fac.service_identifier",
+      FT_UINT32, BASE_HEX, NULL, 0x0ffffff0,
+      "Service identifier", HFILL}
+     },
+    {&hf_fac_short_id,
+     {"short identifier", "drm-fac.short_id",
+      FT_UINT32, BASE_DEC, NULL, 0x0c,
+      "Service identifier", HFILL}
+     },
+    {&hf_fac_audio_ca,
+     {"Audio CA", "drm-fac.audio_ca",
+      FT_BOOLEAN, BASE_NONE, NULL, 0x02,
+      "Audio CA", HFILL}
+     },
+    {&hf_fac_language,
+     {"Language", "drm-fac.language",
+      FT_UINT16, BASE_DEC, VALS(language_vals), 0x01e0,
+      "Language", HFILL}
+     },
+    {&hf_fac_audio_data,
+     {"Data only", "drm-fac.is_data",
+      FT_BOOLEAN, BASE_NONE, NULL, 0x10,
+      "Audio or Data", HFILL}
+     },
+    {&hf_fac_prog_type,
+     {"Service Descriptor", "drm-fac.service_descriptor",
+      FT_UINT16, BASE_DEC, VALS(prog_type_vals), 0x0f80,
+      "Service Descriptor", HFILL}
+     },
+    {&hf_fac_app_id,
+     {"Service Descriptor", "drm-fac.service_descriptor",
+      FT_UINT16, BASE_DEC, VALS(app_id_vals), 0x0f80,
+      "Service Descriptor", HFILL}
+     },
+    {&hf_fac_data_ca,
+     {"Data CA", "drm-fac.data_ca",
+      FT_BOOLEAN, BASE_NONE, NULL, 0x60,
+      "Data CA", HFILL}
+     },
+     {&hf_fac_frame_number,
+     {"Frame Number", "drm-fac.frame_number",
+      FT_UINT8, BASE_DEC, NULL, 0,
+      "FAC Frame Number (SDC only in Frame 0)", HFILL}
+     },
+    {&hf_fac_afs_index_valid,
+     {"AFS Index Valid", "drm-fac.afs_index_valid",
+      FT_BOOLEAN, BASE_NONE, NULL, 0,
+      "Whether the AFS Index in the SDC in this frame can be used", HFILL}
+     },
+    {&hf_fac_crc,
+     {"CRC", "drm-fac.crc",
+      FT_UINT8, BASE_HEX, NULL, 0,
+      "CRC", HFILL}
+     },
+    {&hf_fac_crc_ok,
+     {"CRC OK", "drm-fac.crc_ok",
+      FT_BOOLEAN, BASE_NONE, NULL, 0,
+      "CRC OK", HFILL}
+     }
+  };
+
+/* Setup protocol subtree array */
+  static gint *ett[] = {
+    &ett_fac
+  };
+
+  proto_register_field_array (proto, hf, array_length (hf));
+  proto_register_subtree_array (ett, array_length (ett));
+
+}
Index: epan/dissectors/packet-drm-fac.h
===================================================================
--- epan/dissectors/packet-drm-fac.h	(revision 0)
+++ epan/dissectors/packet-drm-fac.h	(revision 0)
@@ -0,0 +1,34 @@
+/* packet-drm-fac.h
+ * Routines for Digital Radio Mondiale Multiplex Distribution Interface Protocol 
+ * Copyright 2007, British Broadcasting Corporation 
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@xxxxxxxxxxxxx>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+ 
+#ifndef __PACKET_DRM_FAC_H
+#define __PACKET_DRM_FAC_H
+
+#include <epan/packet.h>
+
+void register_fac_dissectors(int proto);
+void register_drm_fac (int proto);
+
+#endif
Index: epan/dissectors/packet-drm-packet-mode.c
===================================================================
--- epan/dissectors/packet-drm-packet-mode.c	(revision 0)
+++ epan/dissectors/packet-drm-packet-mode.c	(revision 0)
@@ -0,0 +1,377 @@
+/* packet-drm-msc-data.c
+ * Routines for Digital Radio Mondiale
+ * Copyright 2007, British Broadcasting Corporation 
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@xxxxxxxxxxxxx>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <string.h>
+#include <gmodule.h>
+#include <epan/emem.h>
+#include <epan/prefs.h>
+#include <epan/packet.h>
+#include <epan/crcdrm.h>
+#include <epan/reassemble2.h>
+#include "drm-data.h"
+
+int proto_drm_packet_mode = -1;
+
+gboolean
+dissect_msc_packet_data (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree);
+
+struct packet_data_t {
+	int seq_no;
+};
+
+struct packet_flow_data_t {
+  guint seq_no[256];
+  guint8* du;
+  guint du_size;
+};
+
+struct packet_stream_data_t {
+  gboolean visited;
+  struct packet_flow_data_t flow[4];
+};
+
+struct packet_frame_data_t {
+  struct packet_stream_data_t stream[4];
+};
+
+static reassembler reassembler_object[4][4];
+static guint8 packet_seq[4][4];
+
+/* Initialize the subtree pointers */
+static gint ett_msc_data = -1;
+static gint ett_msc_data_payload = -1;
+
+static heur_dissector_list_t msc_packet_heur_list = NULL;
+
+static int hf_packet_first = -1;
+static int hf_packet_last = -1;
+static int hf_packet_id = -1;
+static int hf_packet_ppi = -1;
+static int hf_packet_ci = -1;
+static int hf_packet_len = -1;
+static int hf_packet_crc = -1;
+static int hf_packet_data = -1;
+static int hf_packet_padding = -1;
+
+/*
+6.6.1 Packet structure
+The packet is made up as follows:
+- header 8 bits.
+- data field n bytes.
+- CRC 16 bits.
+The header contains information to describe the packet.
+The data field contains the data intended for a particular service. The length of the data field is indicated by use of data
+entity 5, see clause 6.4.3.6.
+Cyclic Redundancy Check (CRC): this 16-bit CRC shall be calculated on the header and the data field. It shall use the
+generator polynomial G16 (x) = x16 + x12 + x5 + 1 (see annex D).
+6.6.1.1 Header
+The header consists of the following fields:
+- first flag 1 bit.
+- last flag 1 bit.
+- packet Id 2 bits.
+- Padded Packet Indicator (PPI) 1 bit.
+- Continuity Index (CI) 3 bits.
+The following definitions apply:
+First flag, Last flag: these flags are used to identify particular packets which form a succession of packets. The flags
+are assigned as follows:
+First
+flag
+Last
+flag
+The packet is:
+0 0 : an intermediate packet;
+0 1 : the last packet of a data unit;
+1 0 : the first packet of a data unit;
+1 1 : the one and only packet of a data unit.
+Packet Id: this 2-bit field indicates the Packet Id of this packet.
+Padded Packet Indicator: this 1-bit flag indicates whether the data field carries padding or not, as follows:
+0: no padding is present: all data bytes in the data field are useful;
+1: padding is present: the first byte gives the number of useful data bytes in the data field.
+Continuity index: this 3-bit field shall increment by one modulo-8 for each packet with this packet Id.
+*/
+
+static void dissect_one_packet (tvbuff_t * tvb, packet_info * pinfo,
+ struct packet_stream_data_t *pd, guint packet_no,
+ proto_tree * tree)
+{
+  drm_data_t *data = (drm_data_t*)pinfo->private_data;
+  gint s = data->stream;
+  stream_t *stream_data = &data->stream_data[s];
+  guint8 header = tvb_get_guint8 (tvb, 0);
+  gboolean first = (header & 0x80)?1:0;
+  gboolean last = (header & 0x40)?1:0;
+  guint8 id = (header & 0x30) >> 4;
+  gboolean ppi = (header & 0x08)?1:0;
+  guint pad_bytes = 0;
+  guint8 ci = header & 0x07;
+  guint offset=0;
+  guint du_size=0;
+  guint payload_start, payload_len;
+  proto_tree *msc_payload_tree = NULL;
+  proto_item *ti;
+  proto_item *ri;
+  tvbuff_t *new_tvb = NULL;
+  const gint len = tvb_length(tvb);
+  const guint8 *crc_buf = tvb_get_ptr(tvb, 0, len);
+  guint16 crc = crc_drm(crc_buf, len, 16, 0x11021, 1);
+
+  if(ppi) {
+    payload_start = 2;
+    payload_len = tvb_get_guint8 (tvb, 1);
+    pad_bytes = stream_data->packet_length - payload_len - 1; /* -1 for the length byte! */
+  } else {
+    payload_start = 1;
+    payload_len = stream_data->packet_length;
+  }
+  if (tree) {
+    ti = proto_tree_add_text (tree, tvb, 0, -1, 
+            "packet with id=%d, first=%d last=%d ci=%d length=%d",
+            id, first, last, ci, payload_len);
+    msc_payload_tree = proto_item_add_subtree (ti, ett_msc_data_payload);
+    proto_tree_add_item (msc_payload_tree, hf_packet_first, tvb, offset, 1, FALSE);
+    proto_tree_add_item (msc_payload_tree, hf_packet_last, tvb, offset, 1, FALSE);
+    proto_tree_add_item (msc_payload_tree, hf_packet_id, tvb, offset, 1, FALSE);
+    proto_tree_add_item (msc_payload_tree, hf_packet_ppi, tvb, offset, 1, FALSE);
+    proto_tree_add_item (msc_payload_tree, hf_packet_ci, tvb, offset, 1, FALSE);
+    offset++;
+    if(ppi) {
+      proto_tree_add_item (msc_payload_tree, hf_packet_len, tvb, offset, 1, FALSE);
+      offset++;
+    } else {
+      proto_tree_add_uint (msc_payload_tree, hf_packet_len, tvb, 0, 1, payload_len);
+    }
+    proto_tree_add_item (msc_payload_tree, hf_packet_data, tvb, offset, payload_len, FALSE);
+    offset += payload_len;
+    if(pad_bytes>0) {
+      proto_tree_add_item (msc_payload_tree, hf_packet_padding, tvb, offset, pad_bytes, FALSE);
+      offset += pad_bytes;
+    }
+    ri = proto_tree_add_item (msc_payload_tree, hf_packet_crc, tvb, offset, 2, FALSE);
+    proto_item_append_text(ri, " (%s)", (crc==0xe2f0)?"Ok":"bad");
+  }
+  if(payload_len>0) {
+    if(stream_data->data_services[id].data_unit_indicator == 0) {
+      new_tvb = tvb_new_subset (tvb, payload_start, payload_len, payload_len);
+      du_size = payload_len;
+    } else {
+	  guint seq_no;
+	  if(pd->visited) {
+		seq_no = pd->flow[id].seq_no[packet_no];
+  	  } else {
+	    if(first) {
+		  packet_seq[s][id]=0;
+	    } else {
+		  packet_seq[s][id]++;
+	    }
+		seq_no = packet_seq[s][id];
+		pd->flow[id].seq_no[packet_no] = seq_no;
+	  }
+      if(first && last) {
+        new_tvb = tvb_new_subset (tvb, payload_start, payload_len, payload_len);
+        du_size = payload_len;
+      } else {
+		guint8* du = NULL;
+        if(pd->visited) {
+		  if(last) {
+		    du = pd->flow[id].du;
+		    du_size = pd->flow[id].du_size;
+ 		  }
+		} else {
+	      reassembler r=NULL;
+	      /* reassemble packets into data units */
+		  if(first) {
+			reassembler_object[s][id] = reassembler_new();
+		  }
+		  r = reassembler_object[s][id];
+		  if(r) {
+		    reassembler_add_segment(r, tvb_get_ptr(tvb, payload_start, payload_len), payload_len, seq_no, last);
+		    if(last) {
+			  if(reassembler_ready(r)) {
+			    du_size = reassembler_reassembled_size(r);
+			    du = se_alloc(du_size);
+			    (void)reassembler_reassemble(r, du, du_size);
+			    reassembler_delete(r);
+			    reassembler_object[s][id]=NULL;
+		        pd->flow[id].du = du;
+		        pd->flow[id].du_size = du_size;
+		      }
+		    }
+		  } else {
+            if (check_col (pinfo->cinfo, COL_INFO))
+              col_append_fstr (pinfo->cinfo, COL_INFO, " no reassembler object");
+ 		  }
+		}
+        if(du) {
+		  new_tvb = tvb_new_real_data(du, du_size, du_size);
+		  tvb_set_child_real_data_tvbuff(tvb, new_tvb);
+    	  add_new_data_source(pinfo, new_tvb, "Reassembled Data Unit");
+		}
+      }
+    }
+  }
+  fflush(stdout);
+
+  if(new_tvb) {
+    gboolean dissected;
+    proto_tree *ptree;
+    if(tree)
+      ptree = tree->parent->parent;
+    else
+      ptree = NULL;
+	pinfo->circuit_id = s << 2 & id;
+    dissected = dissector_try_heuristic(msc_packet_heur_list, new_tvb, pinfo, ptree);
+	if(!dissected)
+      proto_tree_add_text (ptree, new_tvb, 0, -1,
+	 		 "new DU len %d stream %d packet_id %d",
+	  		tvb_length(new_tvb), s, id);
+  }
+}
+
+gboolean
+dissect_msc_packet_data (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  int packet_len;
+  guint offset = 0;
+  guint packet_no = 0;
+  proto_item *ti = NULL;
+  struct packet_frame_data_t *pd;
+  proto_tree *packet_tree = NULL;
+  drm_data_t *data = (drm_data_t*)pinfo->private_data;
+  gint s = data->stream;
+  if(s<0 || s>3 || data->stream_data[s].valid==FALSE || data->stream_data[s].audio_not_data==TRUE)
+    return FALSE;
+  if(!data->stream_data[s].packet_mode_indicator)
+    return FALSE;
+
+  if (tree) {			/* we are being asked for details */
+      ti = proto_tree_add_text(tree, tvb, 0, -1, "DRM data in stream %d", s);
+      packet_tree = proto_item_add_subtree (ti, ett_msc_data);
+  }
+  pd = p_get_proto_data(pinfo->fd, proto_drm_packet_mode);
+  if(pd==NULL) {
+    pd = (struct packet_frame_data_t*)se_alloc0(sizeof(struct packet_frame_data_t));
+  }
+  packet_len = data->stream_data[s].packet_length+3;
+  if (tree)
+    proto_item_append_text(ti, " %d byte packets", packet_len);
+  while((offset+packet_len)<=tvb_length(tvb)) {
+    tvbuff_t *new_tvb = tvb_new_subset (tvb, offset, packet_len, packet_len);
+    dissect_one_packet(new_tvb, pinfo, &pd->stream[s], packet_no, packet_tree);
+    offset += packet_len;
+    packet_no++;
+  }
+  pd->stream[s].visited = TRUE;
+  p_add_proto_data(pinfo->fd, proto_drm_packet_mode, pd);
+  return TRUE;
+}
+
+void
+proto_reg_handoff_drm_packet_mode (void)
+{
+  static int Initialized = FALSE;
+  if (!Initialized) {
+	heur_dissector_add("drm-msc.str0.data", dissect_msc_packet_data, proto_drm_packet_mode);
+	heur_dissector_add("drm-msc.str1.data", dissect_msc_packet_data, proto_drm_packet_mode);
+	heur_dissector_add("drm-msc.str2.data", dissect_msc_packet_data, proto_drm_packet_mode);
+	heur_dissector_add("drm-msc.str3.data", dissect_msc_packet_data, proto_drm_packet_mode);
+	Initialized = TRUE;
+  }
+}
+
+void
+proto_register_drm_packet_mode (void)
+{
+  static hf_register_info hf[] = {
+    {&hf_packet_first,
+     {"first packet flag", "drm-packet.first_flag",
+      FT_BOOLEAN, BASE_NONE, NULL, 0x80,
+      "packet is first in a sequence", HFILL}
+     },
+    {&hf_packet_last,
+     {"last packet flag", "drm-packet.last_flag",
+      FT_BOOLEAN, BASE_NONE, NULL, 0x40,
+      "packet is last in a sequence", HFILL}
+     },
+    {&hf_packet_id,
+     {"id", "drm-packet.id",
+      FT_UINT8, BASE_DEC, NULL, 0x30,
+      "packet id", HFILL}
+     },
+    {&hf_packet_ppi,
+     {"padded packet indicator", "drm-packet.ppi_flag",
+      FT_BOOLEAN, BASE_NONE, NULL, 0x08,
+      "packet is shorted than max", HFILL}
+     },
+    {&hf_packet_ci,
+     {"continuity indicator", "drm-packet.ci",
+      FT_UINT8, BASE_DEC, NULL, 0x7,
+      "Packet Sequence Number", HFILL}
+     },
+    {&hf_packet_len,
+     {"length", "drm-packet.len",
+      FT_UINT8, BASE_DEC, NULL, 0,
+      "length in bytes of the payload", HFILL}
+     },
+    {&hf_packet_data,
+      {"data", "drm-packet.data",
+       FT_BYTES, BASE_HEX, NULL, 0,
+       "data", HFILL}
+     },
+    {&hf_packet_padding,
+      {"padding", "drm-packet.padding",
+       FT_BYTES, BASE_HEX, NULL, 0,
+       "padding", HFILL}
+     },
+    {&hf_packet_crc,
+     {"crc", "drm-packet.crc",
+      FT_UINT16, BASE_HEX, NULL, 0,
+      "crc", HFILL}
+     }
+    };
+
+/* Setup protocol subtree array */
+  static gint *ett[] = {
+    &ett_msc_data,
+    &ett_msc_data_payload
+  };
+
+  if (proto_drm_packet_mode == -1) {
+    proto_drm_packet_mode = proto_register_protocol ("DRM Packet Mode",	/* name */
+					 "DRM-Packet-Mode",	/* short name */
+					 "drm-packet-mode"	/* abbrev */
+      );
+  }
+  proto_register_field_array (proto_drm_packet_mode, hf, array_length (hf));
+  proto_register_subtree_array (ett, array_length (ett));
+
+  /* subdissector code */
+
+  register_heur_dissector_list("drm-packet-mode", &msc_packet_heur_list);
+
+}
Index: epan/dissectors/Makefile.common
===================================================================
--- epan/dissectors/Makefile.common	(revision 22076)
+++ epan/dissectors/Makefile.common	(working copy)
@@ -332,6 +332,12 @@
 	packet-dmp.c		\
 	packet-dnp.c		\
 	packet-dns.c		\
+	packet-drm-di.c		\
+	packet-drm-fac.c	\
+	packet-drm-msc.c	\
+	packet-drm-sdc.c	\
+	packet-drm-rsci.c	\
+	packet-drm-packet-mode.c	\
 	packet-drda.c 		\
 	packet-dsi.c		\
 	packet-dtls.c		\
@@ -827,6 +833,10 @@
 	packet-dis-pdus.h	\
 	packet-dns.h	\
 	packet-dop.h	\
+	packet-drm-fac.h	\
+	packet-drm-msc.h	\
+	packet-drm-sdc.h	\
+	packet-drm-rsci.h	\
 	packet-dsp.h	\
 	packet-dvmrp.h	\
 	packet-e164.h   \
Index: epan/dissectors/packet-drm-di.c
===================================================================
--- epan/dissectors/packet-drm-di.c	(revision 0)
+++ epan/dissectors/packet-drm-di.c	(revision 0)
@@ -0,0 +1,266 @@
+/* packet-drm-di.c
+ * Routines for Digital Radio Mondiale Multiplex Distribution Interface Protocol 
+ * Copyright 2007, British Broadcasting Corporation 
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@xxxxxxxxxxxxx>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Protocol info
+ * Ref: ETSI DRM MDI (ETSI TS 102 820)
+ *      ETSI DRM RSCI (ETSI TS 102 349)
+ */
+ 
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+#include <gmodule.h>
+#include <epan/packet.h>
+#include <epan/prefs.h>
+#include <epan/conversation.h>
+#include "packet-drm-fac.h"
+#include "packet-drm-sdc.h"
+#include "packet-drm-msc.h"
+#include "packet-drm-rsci.h"
+#include "drm-data.h"
+
+/* forward reference */
+static void dissect_di (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree);
+
+static dissector_table_t di_dissector_table;
+
+int proto_drm_di = -1;
+
+static dissector_handle_t di_handle;
+
+static int hf_di_tlv = -1;
+static int hf_di_robm = -1;
+static int hf_di_utco = -1;
+static int hf_di_tist = -1;
+static int hf_di_tist_s = -1;
+static int hf_di_tist_ms = -1;
+static int hf_di_dlfc = -1;
+
+/* Initialize the subtree pointers */
+static gint ett_di = -1;
+
+static const value_string robm_vals[] = {
+	{ 0x0,	"Mode A" },
+	{ 0x1,	"Mode B" },
+	{ 0x2,	"Mode C" },
+	{ 0x3,	"Mode D" },
+	{ 0,	NULL }
+};
+
+static void
+dissect_di (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  proto_tree *di_tree = NULL;
+  guint offset=0;
+  guint8 *prot;  
+  proto_item *ti = NULL;
+
+  conversation_t *conversation;
+  void* old_data;
+  drm_data_t *data;
+  drm_data_t data2;
+
+  /* look up the conversation */
+
+  conversation = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst,
+	pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
+
+  /* if conversation found get the data pointer that you stored */
+  if ( conversation) {
+    data = (drm_data_t*)conversation_get_proto_data(conversation,
+    	    proto_drm_di);
+	if(data==NULL)
+		data = &data2;
+  } else {
+
+    /* new conversation create local data structure */
+
+    data = malloc(sizeof(drm_data_t));
+
+    /*** add your code here to setup the new data structure ***/
+	memset(data, 0, sizeof(drm_data_t));
+
+    /* create the conversation with your data pointer  */
+
+    conversation = conversation_new(
+	    pinfo->fd->num,  &pinfo->src, &pinfo->dst, pinfo->ptype,
+	    pinfo->srcport, pinfo->destport, 0
+    );
+    conversation_add_proto_data(conversation, proto_drm_di, (void *) data);
+  }
+
+  pinfo->current_proto = "DRM-DI";
+  old_data = pinfo->private_data;
+  pinfo->private_data = data;
+  
+  /* Clear out stuff in the info column */
+  if (check_col (pinfo->cinfo, COL_INFO)) {
+    col_clear (pinfo->cinfo, COL_INFO);
+  }
+  
+  if (check_col (pinfo->cinfo, COL_PROTOCOL))
+    col_add_str (pinfo->cinfo, COL_PROTOCOL, "DRM-DI");
+
+  if(data==NULL && check_col (pinfo->cinfo, COL_PROTOCOL))
+    col_add_str (pinfo->cinfo, COL_PROTOCOL, " bad data pointer");
+
+  if(tree) {
+    ti = proto_tree_add_item (tree, proto_drm_di, tvb, 0, -1, FALSE);
+    di_tree = proto_item_add_subtree (ti, ett_di);
+  }
+  // let the sub-dissectors fill in our data structure
+  while(offset<tvb_length(tvb)) {
+    guint32 bits, bytes;
+    char *tag = (char*)tvb_get_string (tvb, offset, 4); offset += 4;
+    bits = tvb_get_ntohl(tvb, offset); offset += 4;
+    bytes = bits / 8;
+    if(bits % 8)
+      bytes++;
+    if(strcmp(tag, "*ptr")==0) {
+      data->maj = tvb_get_ntohs(tvb, offset+4);
+      data->min = tvb_get_ntohs(tvb, offset+6);
+      if(ti) {
+        prot = tvb_get_string (tvb, offset, 4);
+        proto_item_append_text(ti, " %s rev %d.%d", prot, data->maj, data->min);
+      }
+    } else if(strcmp(tag, "robm")==0) {
+	  if(bytes==1) {
+        data->robm = tvb_get_guint8(tvb, offset);
+        if(di_tree) {
+          proto_tree_add_item (di_tree, hf_di_robm, tvb, offset, 1, FALSE);
+        }
+	  } else {
+        if(di_tree) {
+          proto_tree_add_text (di_tree, tvb, offset, 0, "robm (not synced)");
+		}
+	  }
+    } else if(strcmp(tag, "tist")==0) {
+      if(di_tree) {
+        guint64 seconds = tvb_get_ntoh64(tvb, offset);
+        guint16 ms = seconds & 0x3ff;
+        seconds = (seconds >> 10) & 0xffffffffffULL;
+        data->tist.secs = seconds;
+        data->tist.nsecs = 1000000UL*ms;
+        proto_tree_add_item (di_tree, hf_di_utco, tvb, offset, 2, FALSE);
+        proto_tree_add_time (di_tree, hf_di_tist, tvb, offset, 8, &data->tist);
+      }
+    } else if(strcmp(tag, "dlfc")==0) {
+      if(di_tree) {
+        proto_tree_add_item (di_tree, hf_di_dlfc, tvb, offset, bytes, FALSE);
+	  }
+    } else {
+	  gboolean dissected;
+  	  tvbuff_t *next_tvb = NULL;
+      next_tvb = tvb_new_subset (tvb, offset, bytes, bytes);
+      dissected = dissector_try_string(di_dissector_table, tag, next_tvb, pinfo, di_tree);
+      if(di_tree && !dissected) {
+          proto_tree_add_text(di_tree, tvb, offset, bytes, "%s", tag);
+      }
+    }
+    offset += bytes;
+  }
+  pinfo->private_data = old_data;
+}
+
+void
+proto_reg_handoff_drm_di (void)
+{
+  static int Initialized = FALSE;
+  if (!Initialized) {
+    di_handle = create_dissector_handle (dissect_di, proto_drm_di);
+    dissector_add_string("dcp-tpl.ptr", "DMDI", di_handle);
+    dissector_add_string("dcp-tpl.ptr", "RSCI", di_handle);
+	register_fac_dissectors(proto_drm_di);
+	register_sdc_dissectors(proto_drm_di);
+	register_msc_dissectors(proto_drm_di);
+	register_rsci_dissectors(proto_drm_di);
+  }
+}
+
+void
+proto_register_drm_di (void)
+{
+  static hf_register_info hf[] = {
+    {&hf_di_tlv,
+     {"tag", "drm-di.tlv",
+      FT_BYTES, BASE_HEX, NULL, 0,
+      "Tag Packet", HFILL}
+     },
+    {&hf_di_robm,
+     {"Robustness", "drm-di.robm",
+      FT_UINT8, BASE_DEC, VALS(robm_vals), 0,
+      "Robustness Mode", HFILL}
+     },
+    {&hf_di_utco,
+     {"Leap seconds", "drm-di.utco",
+      FT_UINT16, BASE_DEC, NULL, 0xfffc,
+      "leap seconds since DRM t0", HFILL}
+     },
+    {&hf_di_tist,
+     {"tist", "drm-di.tist",
+      FT_RELATIVE_TIME, BASE_NONE, NULL, 0,
+      "time since DRM t0", HFILL}
+     },
+    {&hf_di_tist_s,
+     {"seconds", "drm-di.tist.seconds",
+      FT_UINT64, BASE_DEC, NULL, 0, /*0x0003fffffffffc00ULL, 64 bit masks not supported*/
+      "seconds since DRM t0", HFILL}
+     },
+    {&hf_di_tist_ms,
+     {"milliseconds", "drm-di.tist.ms",
+      FT_UINT16, BASE_DEC, NULL, 0x3ff,
+      "milliseconds", HFILL}
+     },
+    {&hf_di_dlfc,
+     {"Frame Number", "drm-di.dlfc",
+      FT_UINT32, BASE_DEC, NULL, 0,
+      "logical frame number", HFILL}
+     }
+  };
+
+/* Setup protocol subtree array */
+  static gint *ett[] = {
+    &ett_di
+  };
+
+  if (proto_drm_di == -1) {
+    proto_drm_di = proto_register_protocol ("DRM Distribution Interfaces, DI",	/* name */
+					 "DRM-DI",	/* short name */
+					 "drm-di"	/* abbrev */
+      );
+  }
+  proto_register_field_array (proto_drm_di, hf, array_length (hf));
+  proto_register_subtree_array (ett, array_length (ett));
+
+  /* subdissector code */
+  di_dissector_table = register_dissector_table("drm-di.tag", "DI Tag Packet", FT_STRING, BASE_NONE);
+
+  register_drm_fac(proto_drm_di);
+  register_drm_sdc(proto_drm_di);
+  register_drm_msc(proto_drm_di);
+  register_drm_rsci(proto_drm_di);
+}
+
Index: epan/dissectors/packet-drm-msc.c
===================================================================
--- epan/dissectors/packet-drm-msc.c	(revision 0)
+++ epan/dissectors/packet-drm-msc.c	(revision 0)
@@ -0,0 +1,250 @@
+/* packet-drm-mdi-msc.c
+ * Routines for Digital Radio Mondiale Multiplex Distribution Interface Protocol 
+ * Copyright 2007, British Broadcasting Corporation 
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@xxxxxxxxxxxxx>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+ 
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <string.h>
+#include <gmodule.h>
+#include <epan/packet.h>
+#include <epan/prefs.h>
+#include "packet-drm-msc.h"
+#include "drm-data.h"
+
+int proto_drm_msc = -1;
+
+static heur_dissector_list_t msc_heur_list = NULL;
+static heur_dissector_list_t msc_audio_heur_list = NULL;
+static heur_dissector_list_t msc_text_heur_list = NULL;
+static heur_dissector_list_t msc_data_heur_list = NULL;
+
+static int hf_audio[4] = {-1,-1,-1,-1};
+static int hf_text[4] = {-1,-1,-1,-1};
+static int hf_data[4] = {-1,-1,-1,-1};
+  
+static gboolean
+dissect_msc_audio (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  drm_data_t *data = (drm_data_t*)pinfo->private_data;
+  gint s = data->stream;
+  if(s<0 || s>3 || data->stream_data[s].valid==FALSE || data->stream_data[s].audio_not_data==FALSE)
+    return FALSE;
+  if (tree) {			/* we are being asked for details */
+    proto_tree_add_item (tree, hf_audio[s], tvb, 0, -1, FALSE);
+  }
+  return TRUE;
+}
+
+static gboolean
+dissect_msc_text (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  if (tree) {			/* we are being asked for details */
+    drm_data_t *data = (drm_data_t*)pinfo->private_data;
+    gint s = data->stream;
+    stream_t *stream_data = &data->stream_data[s];
+    if(0<=s && s<=3) {
+      if(stream_data->audio_not_data==FALSE) 
+        return FALSE;
+      if(stream_data->text_active==FALSE) 
+        return FALSE;
+      proto_tree_add_item (tree, hf_text[s], tvb, 0, -1, FALSE);
+    } else {
+      return FALSE;
+    }
+  }
+  return TRUE;
+}
+
+static gboolean
+dissect_msc_data (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+  drm_data_t *data = (drm_data_t*)pinfo->private_data;
+  gboolean done;
+
+  gint s = data->stream;
+  if(s<0 || s>3 || data->stream_data[s].valid==FALSE || data->stream_data[s].audio_not_data==TRUE)
+    return FALSE;
+
+  done = dissector_try_heuristic(msc_data_heur_list, tvb, pinfo, tree);
+  if(done)
+    return TRUE;
+
+  if (tree) {			/* we are being asked for details */
+      proto_tree_add_item (tree, hf_data[s], tvb, 0, -1, FALSE);
+  }
+  return TRUE;
+}
+
+static void
+dissect_msc (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree, int s)
+{
+  /* this allows external sub-dissectors for the msc */
+  gboolean done = dissector_try_heuristic(msc_heur_list, tvb, pinfo, tree);
+  drm_data_t *data;
+  if(done)
+    return;
+  data = (drm_data_t*)pinfo->private_data;
+  if(data==NULL)
+    return;
+  data->stream = s;
+  if(data->stream_data[s].audio_not_data) {
+    if(data->stream_data[s].text_active) {
+      guint bytes = tvb_length(tvb);
+      if(bytes<4) {
+        proto_tree_add_text(tree, tvb, 0, -1, "MSC audio+text but stream %d too small", s);
+      } else {
+        tvbuff_t *audio_tvb = tvb_new_subset (tvb, 0, bytes-4, bytes-4);
+        tvbuff_t *text_tvb = tvb_new_subset (tvb, bytes-4, 4, 4);
+        dissect_msc_audio(audio_tvb, pinfo, tree);
+        dissect_msc_text(text_tvb, pinfo, tree);
+      }
+    } else {
+      dissect_msc_audio(tvb, pinfo, tree);
+    }
+  } else {
+    dissect_msc_data(tvb, pinfo, tree);
+  }
+}
+
+static void
+dissect_msc0 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+    dissect_msc(tvb, pinfo, tree, 0);
+}
+
+static void
+dissect_msc1 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+    dissect_msc(tvb, pinfo, tree, 1);
+}
+
+static void
+dissect_msc2 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+    dissect_msc(tvb, pinfo, tree, 2);
+}
+
+static void
+dissect_msc3 (tvbuff_t * tvb, packet_info * pinfo, proto_tree * tree)
+{
+    dissect_msc(tvb, pinfo, tree, 3);
+}
+
+void
+register_drm_msc (int proto)
+{
+  static hf_register_info hf[] = {
+    {&hf_audio[0],
+     {"audio in stream 0", "drm-msc.str0.audio",
+      FT_BYTES, BASE_HEX, NULL, 0,
+      "audio in stream 0", HFILL}
+     },
+    {&hf_audio[1],
+     {"audio in stream 1", "drm-msc.str1.audio",
+      FT_BYTES, BASE_HEX, NULL, 0,
+      "audio in stream 1", HFILL}
+     },
+    {&hf_audio[2],
+     {"audio in stream 2", "drm-msc.str2.audio",
+      FT_BYTES, BASE_HEX, NULL, 0,
+      "audio in stream 2", HFILL}
+     },
+    {&hf_audio[3],
+     {"audio in stream 3", "drm-msc.str3.audio",
+      FT_BYTES, BASE_HEX, NULL, 0,
+      "audio in stream 3", HFILL}
+     },
+    {&hf_text[0],
+     {"text in stream 0", "drm-msc.str0.text",
+      FT_BYTES, BASE_HEX, NULL, 0,
+      "text in stream 0", HFILL}
+     },
+    {&hf_text[1],
+     {"text in stream 1", "drm-msc.str1.text",
+      FT_BYTES, BASE_HEX, NULL, 0,
+      "text in stream 1", HFILL}
+     },
+    {&hf_text[2],
+     {"text in stream 2", "drm-msc.str2.text",
+      FT_BYTES, BASE_HEX, NULL, 0,
+      "text in stream 2", HFILL}
+     },
+    {&hf_text[3],
+     {"text in stream 3", "drm-msc.str3.text",
+      FT_BYTES, BASE_HEX, NULL, 0,
+      "text in stream 3", HFILL}
+     },
+    {&hf_data[0],
+     {"data in stream 0", "drm-msc.str0.data",
+      FT_BYTES, BASE_HEX, NULL, 0,
+      "data in stream 0", HFILL}
+     },
+    {&hf_data[1],
+     {"data in stream 1", "drm-msc.str1.data",
+      FT_BYTES, BASE_HEX, NULL, 0,
+      "data in stream 1", HFILL}
+     },
+    {&hf_data[2],
+     {"data in stream 2", "drm-msc.str2.data",
+      FT_BYTES, BASE_HEX, NULL, 0,
+      "data in stream 2", HFILL}
+     },
+    {&hf_data[3],
+     {"data in stream 3", "drm-msc.str3.data",
+      FT_BYTES, BASE_HEX, NULL, 0,
+      "data in stream 3", HFILL}
+     }
+  };
+
+  proto_register_field_array (proto, hf, array_length (hf));
+
+  /* subdissector code */
+  register_heur_dissector_list("drm-msc", &msc_heur_list);
+  register_heur_dissector_list("drm-msc.str0.audio", &msc_audio_heur_list);
+  register_heur_dissector_list("drm-msc.str1.audio", &msc_audio_heur_list);
+  register_heur_dissector_list("drm-msc.str2.audio", &msc_audio_heur_list);
+  register_heur_dissector_list("drm-msc.str3.audio", &msc_audio_heur_list);
+  register_heur_dissector_list("drm-msc.str0.text", &msc_text_heur_list);
+  register_heur_dissector_list("drm-msc.str1.text", &msc_text_heur_list);
+  register_heur_dissector_list("drm-msc.str2.text", &msc_text_heur_list);
+  register_heur_dissector_list("drm-msc.str3.text", &msc_text_heur_list);
+  register_heur_dissector_list("drm-msc.str0.data", &msc_data_heur_list);
+  register_heur_dissector_list("drm-msc.str1.data", &msc_data_heur_list);
+  register_heur_dissector_list("drm-msc.str2.data", &msc_data_heur_list);
+  register_heur_dissector_list("drm-msc.str3.data", &msc_data_heur_list);
+}
+
+void
+register_msc_dissectors (int proto)
+{
+  static int Initialized = FALSE;
+  if (!Initialized) {
+    dissector_add_string("drm-di.tag", "str0", create_dissector_handle (dissect_msc0, proto));
+    dissector_add_string("drm-di.tag", "str1", create_dissector_handle (dissect_msc1, proto));
+    dissector_add_string("drm-di.tag", "str2", create_dissector_handle (dissect_msc2, proto));
+    dissector_add_string("drm-di.tag", "str3", create_dissector_handle (dissect_msc3, proto));
+  }
+}
Index: epan/dissectors/packet-drm-msc.h
===================================================================
--- epan/dissectors/packet-drm-msc.h	(revision 0)
+++ epan/dissectors/packet-drm-msc.h	(revision 0)
@@ -0,0 +1,34 @@
+/* packet-drm-msc.h
+ * Routines for Digital Radio Mondiale Multiplex Distribution Interface Protocol 
+ * Copyright 2007, British Broadcasting Corporation 
+ *
+ * $Id$
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@xxxxxxxxxxxxx>
+ * Copyright 1998 Gerald Combs
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+ 
+#ifndef __PACKET_DRM_MSC_H
+#define __PACKET_DRM_MSC_H
+
+#include <epan/packet.h>
+
+void register_drm_msc (int);
+void register_msc_dissectors(int);
+
+#endif
Index: epan/dissectors/drm-data.h
===================================================================
--- epan/dissectors/drm-data.h	(revision 0)
+++ epan/dissectors/drm-data.h	(revision 0)
@@ -0,0 +1,43 @@
+#ifndef _DRM_DATA_H
+#define _DRM_DATA_H
+
+#include <nstime.h>
+
+typedef struct _packet_stream_t {
+  gboolean data_unit_indicator;
+  guint8 application_domain;
+  guint8 *application_data;
+  gboolean valid;
+} packet_stream_t;
+
+typedef struct _stream_t {
+  guint16 part_a_len, part_b_len;
+  gboolean audio_not_data;
+  guint8 audio_coding;
+  gboolean sbr;
+  guint8 audio_mode;
+  guint8 sampling_rate_index;
+  gboolean text_active;
+  gboolean enhancement_flag;
+  guint8 coder_field;
+  gboolean packet_mode_indicator;
+  guint16 packet_length;
+  packet_stream_t data_services[4];
+  gboolean valid;
+} stream_t;
+
+typedef struct _drm_data_t {
+  guint16 maj, min;
+  guint8 robm, prot_a, prot_b, streams, stream;
+  nstime_t tist;
+  gint protection_a, protection_b;
+  gint VSPP;
+  gint spectral_occupancy;
+  gint interleaver_depth;
+  gint msc_mode;
+  gint sdc_mode;
+  stream_t stream_data[4];
+  guint sdc_bytes;
+} drm_data_t;
+
+#endif
Index: epan/reassemble2.c
===================================================================
--- epan/reassemble2.c	(revision 0)
+++ epan/reassemble2.c	(revision 0)
@@ -0,0 +1,216 @@
+/******************************************************************************\
+ * Copyright (c) 2007 British Broadcasting Corporation
+ *
+ * Author(s):
+ *	Julian Cable
+ *
+ * Description:
+ *	General Purpose Packet Reassemblers for data packet mode, MOT and PFT
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#include "reassemble2.h"
+#include <gmodule.h>
+#include <epan/emem.h>
+#include <string.h>
+
+typedef const void* cvoidp;
+
+struct _segment_tracker
+{
+	cvoidp* seg_data;
+	int segments;
+	int max_segments;
+};
+
+typedef struct _segment_tracker* segment_tracker;
+
+void segment_tracker_reset(segment_tracker s)
+{
+  if(s==NULL)
+    return;
+  if(s->seg_data) {
+	s->seg_data=NULL;
+  }
+  s->segments = s->max_segments = 0;
+}
+
+void segment_tracker_resize(segment_tracker s, int new_size)
+{
+  cvoidp* n;
+  if(s==NULL)
+    return;
+  if(s->max_segments<new_size) {
+    if(new_size<16)
+	  s->max_segments = 16;
+	else
+	  s->max_segments = 2*new_size;
+    n = (cvoidp*)se_alloc0(s->max_segments*sizeof(void*));
+    if(s->segments>0) {
+	   int i;
+	   for(i=0; i<s->segments; i++)
+	     n[i] = s->seg_data[i];
+    }
+	s->seg_data = n;
+  }
+}
+
+int segment_tracker_size(segment_tracker s)
+{
+  if(s==NULL)
+    return 0;
+  return s->segments;
+}
+
+int segment_tracker_ready(segment_tracker s)
+{
+  int i;
+  if(s==NULL)
+    return 0;
+  if (s->segments == 0)
+		return 0;
+  for ( i = 0; i < s->segments; i++)
+  {
+	if (s->seg_data[i] == 0)
+	{
+		return 0;
+	}
+  }
+  return 1;
+}
+
+void segment_tracker_add_segment(segment_tracker s, int seg, const void* data)
+{
+  if(s==NULL)
+    return;
+  if(seg >= s->max_segments)
+    segment_tracker_resize(s, seg + 1);
+  if(seg >= s->segments)
+    s->segments = seg+1;
+  s->seg_data[seg] = data;
+}
+
+int segment_tracker_have_segment (segment_tracker s, int seg)
+{
+  if(s==NULL)
+    return 0;
+  if (seg <  s->segments)
+    return s->seg_data[seg]?1:0;
+  return 0;
+}
+
+struct _reassembler
+{
+	int last_segment_num;
+	int last_segment_size;
+	size_t segment_size;
+	struct _segment_tracker tracker;
+	int ready;
+};
+
+reassembler reassembler_new()
+{
+  reassembler r = (reassembler)se_alloc0(sizeof(struct _reassembler));
+  r->last_segment_num=-1;
+  r->last_segment_size = -1;
+  r->segment_size=-1;
+  r->tracker.seg_data=NULL;
+  segment_tracker_reset(&r->tracker);
+  r->ready = 0;
+  return r;
+}
+
+void reassembler_delete(reassembler r)
+{
+ /* nothing to to? (using se_alloc routines) */
+ (void)r;
+}
+
+int reassembler_ready(reassembler r)
+{
+  if(r==NULL)
+    return 0;
+  return r->ready;
+}
+
+void reassembler_add_segment(
+  reassembler r, 
+  const void* data, int size,
+  int seg, int last)
+{
+  void *dest;
+  if(r==NULL)
+    return;
+  dest = se_alloc(size);
+  memcpy(dest, data, size);
+  segment_tracker_add_segment(&r->tracker, seg, dest);
+  if (last) {
+    if (r->last_segment_num == -1) {
+      r->last_segment_num = seg;
+	  r->last_segment_size = size;
+	  /* three cases:
+			   1: single segment - easy! (actually degenerate with case 3)
+			   2: multi-segment and the last segment came first.
+			   3: normal - some segment, not the last, came first, 
+			   we know the segment size
+       */
+      if (seg == 0) {	/* case 1,3 */
+        r->segment_size = size;
+      } else if (r->segment_size == 0) {/* case 2 */
+        r->last_segment_size = size;
+      }
+    } /* otherwise do nothing as we already have the last segment */
+  } else {
+    r->segment_size = size;
+  }
+
+  if ((r->last_segment_size != -1)	/* we have the last segment */
+		&& (r->ready == 0)	/* we haven't already completed reassembly */
+		&& segment_tracker_ready (&r->tracker)		/* there are no gaps */
+		)
+	{
+		r->ready = 1;
+	}
+}
+
+int reassembler_reassembled_size(reassembler r)
+{
+  if(r==NULL)
+    return -1;
+  return r->segment_size*r->last_segment_num+r->last_segment_size;
+}
+
+int reassembler_reassemble(reassembler r, void* data_out, int max_size)
+{
+  int i, size;
+  char *dest = (char*)data_out;
+  if(r==NULL)
+    return -1;
+  if(!r->ready)
+    return -1;
+  size = reassembler_reassembled_size(r);
+  if(max_size<size)
+    return -1;
+  for(i=0; i<r->last_segment_num; i++) {
+    memcpy(dest, r->tracker.seg_data[i], r->segment_size);
+    dest += r->segment_size;
+  }
+  memcpy(dest, r->tracker.seg_data[r->last_segment_num], r->last_segment_size);
+  return size;
+}
Index: epan/reassemble2.h
===================================================================
--- epan/reassemble2.h	(revision 0)
+++ epan/reassemble2.h	(revision 0)
@@ -0,0 +1,41 @@
+/******************************************************************************\
+ * Copyright (c) 2007 British Broadcasting Corporation
+ *
+ * Author(s):
+ *	Julian Cable
+ *
+ * Description:
+ *	See reassemble2.c
+ *
+ ******************************************************************************
+ *
+ * This program is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+\******************************************************************************/
+
+#ifndef REASSEMBLE2_H_INCLUDED
+#define REASSEMBLE2_H_INCLUDED
+
+typedef struct _reassembler* reassembler;
+
+reassembler reassembler_new();
+void reassembler_delete(reassembler r);
+int reassembler_ready(reassembler r);
+void reassembler_add_segment(reassembler r, 
+            const void* data, int size, int seg, int last);
+int reassembler_reassembled_size(reassembler r);
+int reassembler_reassemble(reassembler r, void* data_out, int max_size);
+
+#endif