aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Mathieson <martin.r.mathieson@googlemail.com>2015-10-10 15:53:45 -0700
committerMartin Mathieson <martin.r.mathieson@googlemail.com>2015-10-11 21:59:45 +0000
commit3221dbf542217cea5acd9ec764cf4779edaf65e8 (patch)
tree8999d3a38ee9a687737e35cdd39bde1ea7d18a2e
parenta6673b3fde1cac904a405cb1125d547d064d3aa7 (diff)
LTE RLC graphs - initial version
Change-Id: Ic5f2c353ae1f787ac19cb575a938cb093ff5f6dc Reviewed-on: https://code.wireshark.org/review/10930 Petri-Dish: Martin Mathieson <martin.r.mathieson@googlemail.com> Reviewed-by: Martin Mathieson <martin.r.mathieson@googlemail.com>
-rw-r--r--epan/dissectors/packet-rlc-lte.h4
-rw-r--r--ui/CMakeLists.txt1
-rw-r--r--ui/Makefile.common2
-rw-r--r--ui/gtk/rlc_lte_graph.c595
-rw-r--r--ui/qt/CMakeLists.txt3
-rw-r--r--ui/qt/Makefile.am2
-rw-r--r--ui/qt/Makefile.common4
-rw-r--r--ui/qt/Wireshark.pro3
-rw-r--r--ui/qt/lte_rlc_graph_dialog.cpp643
-rw-r--r--ui/qt/lte_rlc_graph_dialog.h118
-rw-r--r--ui/qt/lte_rlc_graph_dialog.ui275
-rw-r--r--ui/qt/main_window.cpp1
-rw-r--r--ui/qt/main_window.h1
-rw-r--r--ui/qt/main_window.ui11
-rw-r--r--ui/qt/main_window_slots.cpp7
-rw-r--r--ui/tap-rlc-graph.c309
-rw-r--r--ui/tap-rlc-graph.h103
17 files changed, 1631 insertions, 451 deletions
diff --git a/epan/dissectors/packet-rlc-lte.h b/epan/dissectors/packet-rlc-lte.h
index 4a5d162bd0..d945c03830 100644
--- a/epan/dissectors/packet-rlc-lte.h
+++ b/epan/dissectors/packet-rlc-lte.h
@@ -20,6 +20,9 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
+#ifndef PACKET_RLC_LTE_H
+#define PACKET_RLC_LTE_H
+
/* rlcMode */
#define RLC_TM_MODE 1
#define RLC_UM_MODE 2
@@ -153,4 +156,5 @@ void set_rlc_lte_drb_li_field(packet_info *pinfo, guint16 ueid, guint8 drbid, gb
continues until the end of the frame) */
#define RLC_LTE_PAYLOAD_TAG 0x01
+#endif
diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt
index 3a36920839..55831df520 100644
--- a/ui/CMakeLists.txt
+++ b/ui/CMakeLists.txt
@@ -54,6 +54,7 @@ set(COMMON_UI_SRC
tap-rtp-common.c
tap-sctp-analysis.c
tap-sequence-analysis.c
+ tap-rlc-graph.c
tap-tcp-stream.c
text_import.c
time_shift.c
diff --git a/ui/Makefile.common b/ui/Makefile.common
index 2e24319d42..fd17475a36 100644
--- a/ui/Makefile.common
+++ b/ui/Makefile.common
@@ -72,6 +72,7 @@ WIRESHARK_UI_SRC = \
ssl_key_export.c \
tap_export_pdu.c \
tap-iax2-analysis.c \
+ tap-rlc-graph.c \
tap-rtp-common.c \
tap-sctp-analysis.c \
tap-sequence-analysis.c \
@@ -116,6 +117,7 @@ noinst_HEADERS = \
ssl_key_export.h \
tap_export_pdu.h \
tap-iax2-analysis.h \
+ tap-rlc-graph.h \
tap-rtp-analysis.h \
tap-rtp-common.h \
tap-sctp-analysis.h \
diff --git a/ui/gtk/rlc_lte_graph.c b/ui/gtk/rlc_lte_graph.c
index b318800bb7..a352b21a20 100644
--- a/ui/gtk/rlc_lte_graph.c
+++ b/ui/gtk/rlc_lte_graph.c
@@ -32,6 +32,8 @@
# include <gdk/gdkkeysyms-compat.h>
#endif
+#include <ui/tap-rlc-graph.h>
+
#include <epan/packet.h>
#include <epan/epan_dissect.h>
#include <epan/dissectors/packet-rlc-lte.h>
@@ -63,28 +65,7 @@
extern int proto_rlc_lte;
-struct segment {
- struct segment *next;
- guint32 num; /* framenum */
- guint32 rel_secs;
- guint32 rel_usecs;
- guint32 abs_secs;
- guint32 abs_usecs;
-
- gboolean isControlPDU;
- guint16 SN;
- guint16 isResegmented;
- guint16 ACKNo;
- #define MAX_NACKs 128
- guint16 noOfNACKs;
- guint16 NACKs[MAX_NACKs];
-
- guint16 ueid;
- guint16 channelType;
- guint16 channelId;
- guint8 rlcMode;
- guint8 direction;
-};
+
struct line {
double x1, y1, x2, y2;
@@ -119,7 +100,7 @@ struct ellipse_params {
struct element {
ElementType type;
GdkRGBA *elment_color_p;
- struct segment *parent;
+ struct rlc_segment *parent;
union {
struct line_params line;
struct ellipse_params ellipse;
@@ -132,7 +113,7 @@ struct element_list {
};
struct axis {
- struct graph *g; /* which graph we belong to */
+ struct gtk_rlc_graph *g; /* which graph we belong to */
GtkWidget *drawing_area;
/* Double-buffering to avoid flicker */
#if GTK_CHECK_VERSION(2,22,0)
@@ -199,7 +180,9 @@ struct grab {
};
-struct graph {
+
+/* struct rlc_graph is shared between GTK and Qt implementations. */
+struct gtk_rlc_graph {
#define GRAPH_DESTROYED (1 << 0)
int flags;
GtkWidget *toplevel; /* keypress handler needs this */
@@ -239,18 +222,7 @@ struct graph {
struct cross cross;
struct axis *x_axis, *y_axis;
- /* List of segments to show */
- struct segment *segments;
-
- /* These are filled in with the channel/direction this graph is showing */
- guint16 ueid;
- guint16 channelType;
- guint16 channelId;
- guint8 rlcMode;
- guint8 direction;
-
- /* Lists of elements to draw */
- struct element_list *elists; /* element lists */
+ struct rlc_graph graph;
/* Colours, etc to be used in drawing */
struct style_rlc_lte style;
@@ -272,37 +244,31 @@ static int refnum = 0;
/*static int debugging = DBS_TPUT_ELMTS;*/
static int debugging = 0;
-static void create_gui(struct graph * );
-static void create_drawing_area(struct graph * );
+static void create_gui(struct gtk_rlc_graph * );
+static void create_drawing_area(struct gtk_rlc_graph * );
static void callback_toplevel_destroy(GtkWidget * , gpointer );
static void callback_create_help(GtkWidget * , gpointer );
static void get_mouse_position(GtkWidget *, int *pointer_x, int *pointer_y, GdkModifierType *mask);
-static rlc_lte_tap_info *select_rlc_lte_session(capture_file *, struct segment * );
-static int compare_headers(guint16 ueid1, guint16 channelType1, guint16 channelId1, guint8 rlcMode1, guint8 direction1,
- guint16 ueid2, guint16 channelType2, guint16 channelId2, guint8 rlcMode2, guint8 direction2,
- gboolean isControlFrame);
-static void get_data_control_counts(struct graph *g, int *data, int *acks, int *nacks);
-
-static struct graph *graph_new(void);
-static void graph_destroy(struct graph * );
-static void graph_initialize_values(struct graph * );
-static void graph_init_sequence(struct graph * );
-static void draw_element_line(struct graph * , struct element * , cairo_t * , GdkRGBA *new_color);
-static void draw_element_ellipse(struct graph * , struct element * , cairo_t *cr , GdkRGBA *new_color);
-static void graph_display(struct graph * );
-static void graph_pixmaps_create(struct graph * );
-static void graph_pixmaps_switch(struct graph * );
-static void graph_pixmap_draw(struct graph * );
-static void graph_pixmap_display(struct graph * );
-static void graph_element_lists_make(struct graph * );
-static void graph_element_lists_free(struct graph * );
-static void graph_element_lists_initialize(struct graph * );
-static void graph_title_pixmap_create(struct graph * );
-static void graph_title_pixmap_draw(struct graph * );
-static void graph_title_pixmap_display(struct graph * );
-static void graph_segment_list_get(struct graph *, gboolean channel_known );
-static void graph_segment_list_free(struct graph * );
-static void graph_select_segment(struct graph * , int , int );
+static void get_data_control_counts(struct gtk_rlc_graph *g, int *data, int *acks, int *nacks);
+
+static struct gtk_rlc_graph *graph_new(void);
+static void graph_destroy(struct gtk_rlc_graph * );
+static void graph_initialize_values(struct gtk_rlc_graph * );
+static void graph_init_sequence(struct gtk_rlc_graph * );
+static void draw_element_line(struct gtk_rlc_graph * , struct element * , cairo_t * , GdkRGBA *new_color);
+static void draw_element_ellipse(struct gtk_rlc_graph * , struct element * , cairo_t *cr , GdkRGBA *new_color);
+static void graph_display(struct gtk_rlc_graph * );
+static void graph_pixmaps_create(struct gtk_rlc_graph * );
+static void graph_pixmaps_switch(struct gtk_rlc_graph * );
+static void graph_pixmap_draw(struct gtk_rlc_graph * );
+static void graph_pixmap_display(struct gtk_rlc_graph * );
+static void graph_element_lists_make(struct gtk_rlc_graph * );
+static void graph_element_lists_free(struct gtk_rlc_graph * );
+static void graph_element_lists_initialize(struct gtk_rlc_graph * );
+static void graph_title_pixmap_create(struct gtk_rlc_graph * );
+static void graph_title_pixmap_draw(struct gtk_rlc_graph * );
+static void graph_title_pixmap_display(struct gtk_rlc_graph * );
+static void graph_select_segment(struct gtk_rlc_graph * , int , int );
static int line_detect_collision(struct element * , int , int );
static int ellipse_detect_collision(struct element *e, int x, int y);
static void axis_pixmaps_create(struct axis * );
@@ -318,15 +284,15 @@ static void axis_ticks_down(int * , int * );
static void axis_destroy(struct axis * );
static int get_label_dim(struct axis * , int , double );
-static void toggle_crosshairs(struct graph *);
-static void cross_draw(struct graph * , int x, int y);
-static void cross_erase(struct graph * );
-static void zoomrect_draw(struct graph * , int , int );
-static void zoomrect_erase(struct graph * );
+static void toggle_crosshairs(struct gtk_rlc_graph *);
+static void cross_draw(struct gtk_rlc_graph * , int x, int y);
+static void cross_erase(struct gtk_rlc_graph * );
+static void zoomrect_draw(struct gtk_rlc_graph * , int , int );
+static void zoomrect_erase(struct gtk_rlc_graph * );
static gboolean motion_notify_event(GtkWidget * , GdkEventMotion * , gpointer );
-static void toggle_time_origin(struct graph * );
-static void restore_initial_graph_view(struct graph *g);
+static void toggle_time_origin(struct gtk_rlc_graph * );
+static void restore_initial_graph_view(struct gtk_rlc_graph *g);
static gboolean configure_event(GtkWidget * , GdkEventConfigure * , gpointer );
#if GTK_CHECK_VERSION(3,0,0)
static gboolean draw_event(GtkWidget *widget, cairo_t *cr, gpointer user_data);
@@ -336,10 +302,10 @@ static gboolean expose_event(GtkWidget * , GdkEventExpose * , gpointer );
static gboolean button_press_event(GtkWidget * , GdkEventButton * , gpointer );
static gboolean button_release_event(GtkWidget * , GdkEventButton * , gpointer );
static gboolean key_press_event(GtkWidget * , GdkEventKey * , gpointer );
-static void graph_initialize(struct graph *);
-static void graph_get_bounds(struct graph *);
-static void graph_read_config(struct graph *);
-static void rlc_lte_make_elmtlist(struct graph *);
+static void graph_initialize(struct gtk_rlc_graph *);
+static void graph_get_bounds(struct gtk_rlc_graph *);
+static void graph_read_config(struct gtk_rlc_graph *);
+static void rlc_lte_make_elmtlist(struct gtk_rlc_graph *);
#if defined(_WIN32) && !defined(__MINGW32__) && (_MSC_VER < 1800)
/* Starting VS2013, rint already defined in math.h. No need to redefine */
@@ -407,13 +373,19 @@ static void unset_busy_cursor(GdkWindow *w)
void rlc_lte_graph_cb(GtkAction *action _U_, gpointer user_data _U_)
{
- struct segment current;
- struct graph *g;
+ struct rlc_segment current;
+ struct gtk_rlc_graph *g;
+ gchar *err_msg = NULL;
+ gboolean free_err_msg = FALSE;
debug(DBS_FENTRY) puts("rlc_lte_graph_cb()");
/* Can we choose an RLC channel from the selected frame? */
- if (!select_rlc_lte_session(&cfile, &current)) {
+ if (!select_rlc_lte_session(&cfile, &current, &err_msg, &free_err_msg)) {
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_msg);
+ if (free_err_msg) {
+ g_free(err_msg);
+ }
return;
}
@@ -425,7 +397,7 @@ void rlc_lte_graph_cb(GtkAction *action _U_, gpointer user_data _U_)
graph_initialize_values(g);
/* Get our list of segments from the packet list */
- graph_segment_list_get(g, FALSE);
+ rlc_graph_segment_list_get(&cfile, &(g->graph), FALSE, &err_msg, &free_err_msg);
create_gui(g);
graph_init_sequence(g);
@@ -435,9 +407,11 @@ void rlc_lte_graph_known_channel_launch(guint16 ueid, guint8 rlcMode,
guint16 channelType, guint16 channelId,
guint8 direction)
{
- struct graph *g;
+ struct gtk_rlc_graph *g;
+ gchar *err_msg = NULL;
+ gboolean free_err_msg = FALSE;
- debug(DBS_FENTRY) puts("rlc_lte_graph_known_channel()");
+ debug(DBS_FENTRY) puts("rlc_lte_graph_known_channel_launch()");
if (!(g = graph_new())) {
return;
@@ -447,28 +421,29 @@ void rlc_lte_graph_known_channel_launch(guint16 ueid, guint8 rlcMode,
graph_initialize_values(g);
/* Can set channel info for graph now */
- g->ueid = ueid;
- g->rlcMode = rlcMode;
- g->channelType = channelType;
- g->channelId = channelId;
- g->direction = direction;
+ g->graph.ueid = ueid;
+ g->graph.rlcMode = rlcMode;
+ g->graph.channelType = channelType;
+ g->graph.channelId = channelId;
+ g->graph.direction = direction;
+ g->graph.channelSet = TRUE;
/* Get our list of segments from the packet list */
- graph_segment_list_get(g, TRUE);
+ rlc_graph_segment_list_get(&cfile, &(g->graph), TRUE, &err_msg, &free_err_msg);
create_gui(g);
graph_init_sequence(g);
}
-static void create_gui(struct graph *g)
+static void create_gui(struct gtk_rlc_graph *g)
{
debug(DBS_FENTRY) puts("create_gui()");
/* create_text_widget(g); */
create_drawing_area(g);
}
-static void create_drawing_area(struct graph *g)
+static void create_drawing_area(struct gtk_rlc_graph *g)
{
#if GTK_CHECK_VERSION(3,0,0)
GtkStyleContext *context;
@@ -482,10 +457,10 @@ static void create_drawing_area(struct graph *g)
/* Set channel details in title */
g_snprintf(window_title, WINDOW_TITLE_LENGTH, "LTE RLC Graph %d: %s (UE-%u, chan=%s%u %s - %s)",
refnum, display_name,
- g->ueid, (g->channelType == CHANNEL_TYPE_SRB) ? "SRB" : "DRB",
- g->channelId,
- (g->direction == DIRECTION_UPLINK) ? "UL" : "DL",
- (g->rlcMode == RLC_UM_MODE) ? "UM" : "AM");
+ g->graph.ueid, (g->graph.channelType == CHANNEL_TYPE_SRB) ? "SRB" : "DRB",
+ g->graph.channelId,
+ (g->graph.direction == DIRECTION_UPLINK) ? "UL" : "DL",
+ (g->graph.rlcMode == RLC_UM_MODE) ? "UM" : "AM");
g_free(display_name);
g->toplevel = dlg_window_new("RLC Graph");
gtk_window_set_title(GTK_WINDOW(g->toplevel), window_title);
@@ -550,11 +525,11 @@ static void create_drawing_area(struct graph *g)
static void callback_toplevel_destroy(GtkWidget *widget _U_, gpointer data)
{
- struct graph *g = (struct graph * )data;
+ struct gtk_rlc_graph *g = (struct gtk_rlc_graph * )data;
if (!(g->flags & GRAPH_DESTROYED)) {
g->flags |= GRAPH_DESTROYED;
- graph_destroy((struct graph * )data);
+ graph_destroy((struct gtk_rlc_graph * )data);
}
}
@@ -608,11 +583,11 @@ static void get_mouse_position(GtkWidget *widget, int *pointer_x, int *pointer_y
#endif
}
-static struct graph *graph_new(void)
+static struct gtk_rlc_graph *graph_new(void)
{
- struct graph *g;
+ struct gtk_rlc_graph *g;
- g = (struct graph * )g_malloc0(sizeof(struct graph));
+ g = (struct gtk_rlc_graph * )g_malloc0(sizeof(struct gtk_rlc_graph));
graph_element_lists_initialize(g);
g->x_axis = (struct axis * )g_malloc0(sizeof(struct axis));
@@ -636,7 +611,7 @@ static struct graph *graph_new(void)
return g;
}
-static void graph_initialize_values(struct graph *g)
+static void graph_initialize_values(struct gtk_rlc_graph *g)
{
g->geom.width = g->wp.width = 750;
g->geom.height = g->wp.height = 550;
@@ -654,7 +629,7 @@ static void graph_initialize_values(struct graph *g)
g->grab.grabbed = 0;
}
-static void graph_init_sequence(struct graph *g)
+static void graph_init_sequence(struct gtk_rlc_graph *g)
{
debug(DBS_FENTRY) puts("graph_init_sequence()");
@@ -679,7 +654,7 @@ static void graph_init_sequence(struct graph *g)
axis_display(g->x_axis);
}
-static void graph_initialize(struct graph *g)
+static void graph_initialize(struct gtk_rlc_graph *g)
{
debug(DBS_FENTRY) puts("graph_initialize()");
graph_get_bounds(g);
@@ -691,7 +666,7 @@ static void graph_initialize(struct graph *g)
graph_read_config(g);
}
-static void graph_destroy(struct graph *g)
+static void graph_destroy(struct gtk_rlc_graph *g)
{
debug(DBS_FENTRY) puts("graph_destroy()");
@@ -716,318 +691,38 @@ static void graph_destroy(struct graph *g)
#endif /* GTK_CHECK_VERSION(2,22,0) */
g_free(g->x_axis);
g_free(g->y_axis);
- graph_segment_list_free(g);
+ rlc_graph_segment_list_free(&g->graph);
graph_element_lists_free(g);
g_free(g);
}
-
-typedef struct rlc_scan_t {
- struct graph *g;
- struct segment *last;
-} rlc_scan_t;
-
-
-static int
-tapall_rlc_lte_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
-{
- rlc_scan_t *ts = (rlc_scan_t *)pct;
- struct graph *g = ts->g;
- const rlc_lte_tap_info *rlchdr = (const rlc_lte_tap_info*)vip;
-
- /* See if this one matches current channel */
- if (compare_headers(g->ueid, g->channelType, g->channelId, g->rlcMode, g->direction,
- rlchdr->ueid, rlchdr->channelType, rlchdr->channelId, rlchdr->rlcMode, rlchdr->direction,
- rlchdr->isControlPDU)) {
-
- struct segment *segment = (struct segment *)g_malloc(sizeof(struct segment));
-
- /* It matches. Add to end of segment list */
- segment->next = NULL;
- segment->num = pinfo->fd->num;
- segment->rel_secs = (guint32) pinfo->rel_ts.secs;
- segment->rel_usecs = pinfo->rel_ts.nsecs/1000;
- segment->abs_secs = (guint32) pinfo->fd->abs_ts.secs;
- segment->abs_usecs = pinfo->fd->abs_ts.nsecs/1000;
-
- segment->ueid = rlchdr->ueid;
- segment->channelType = rlchdr->channelType;
- segment->channelId = rlchdr->channelId;
- segment->direction = rlchdr->direction;
-
- segment->isControlPDU = rlchdr->isControlPDU;
-
- if (!rlchdr->isControlPDU) {
- /* Data */
- segment->SN = rlchdr->sequenceNumber;
- segment->isResegmented = rlchdr->isResegmented;
- }
- else {
- /* Status PDU */
- gint n;
- segment->ACKNo = rlchdr->ACKNo;
- segment->noOfNACKs = rlchdr->noOfNACKs;
- for (n=0; n < rlchdr->noOfNACKs; n++) {
- segment->NACKs[n] = rlchdr->NACKs[n];
- }
- }
-
- /* Add to list */
- if (ts->g->segments) {
- /* Add to end of existing last element */
- ts->last->next = segment;
- } else {
- /* Make this the first (only) segment */
- ts->g->segments = segment;
- }
-
- /* This one is now the last one */
- ts->last = segment;
- }
-
- return 0;
-}
-
-
-/* Here we collect all the external data we will ever need */
-static void graph_segment_list_get(struct graph *g, gboolean channel_known)
-{
- struct segment current;
- GString *error_string;
- rlc_scan_t ts;
- current.ueid = 0;
- current.rlcMode = 0;
- current.channelType = 0;
- current.channelId = 0;
- current.isControlPDU = 0;
- current.direction = 0;
-
- debug(DBS_FENTRY) puts("graph_segment_list_get()");
-
- if (!channel_known) {
- select_rlc_lte_session(&cfile, &current);
-
- g->ueid = current.ueid;
- g->rlcMode = current.rlcMode;
- g->channelType = current.channelType;
- g->channelId = current.channelId;
- g->direction = (!current.isControlPDU) ? current.direction : !current.direction;
- }
-
- /* rescan all the packets and pick up all frames for this channel.
- * we only filter for LTE RLC here for speed and do the actual compare
- * in the tap listener
- */
-
- ts.g = g;
- ts.last = NULL;
- error_string = register_tap_listener("rlc-lte", &ts, "rlc-lte", 0, NULL, tapall_rlc_lte_packet, NULL);
- if (error_string){
- fprintf(stderr, "wireshark: Couldn't register rlc_lte_graph tap: %s\n",
- error_string->str);
- g_string_free(error_string, TRUE);
- exit(1);
- }
- cf_retap_packets(&cfile);
- remove_tap_listener(&ts);
-}
-
-
-typedef struct _th_t {
- int num_hdrs;
- #define MAX_SUPPORTED_CHANNELS 8
- rlc_lte_tap_info *rlchdrs[MAX_SUPPORTED_CHANNELS];
-} th_t;
-
-
-static int
-tap_lte_rlc_packet(void *pct, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *vip)
-{
- int n;
- gboolean is_unique = TRUE;
- th_t *th = (th_t *)pct;
- const rlc_lte_tap_info *header = (const rlc_lte_tap_info*)vip;
-
- /* Check new header details against any/all stored ones */
- for (n=0; n < th->num_hdrs; n++) {
- rlc_lte_tap_info *stored = th->rlchdrs[n];
-
- if (compare_headers(stored->ueid, stored->channelType, stored->channelId, stored->rlcMode, stored->direction,
- header->ueid, header->channelType, header->channelId, header->rlcMode, header->direction,
- header->isControlPDU)) {
- is_unique = FALSE;
- break;
- }
- }
-
- /* Add address if unique and have space for it */
- if (is_unique && (th->num_hdrs < MAX_SUPPORTED_CHANNELS)) {
- /* Copy the tap stuct in as next header */
- /* Need to take a deep copy of the tap struct, it may not be valid
- to read after this function returns? */
- th->rlchdrs[th->num_hdrs] = g_new(rlc_lte_tap_info,1);
- *(th->rlchdrs[th->num_hdrs]) = *header;
-
- /* Store in direction of data though... */
- if (th->rlchdrs[th->num_hdrs]->isControlPDU) {
- th->rlchdrs[th->num_hdrs]->direction = !th->rlchdrs[th->num_hdrs]->direction;
- }
- th->num_hdrs++;
- }
-
- return 0;
-}
-
-
-/* XXX should be enhanced so that if we have multiple RLC channels in the same MAC frame
- * then present the user with a dialog where the user can select WHICH RLC
- * channel to graph.
- */
-static rlc_lte_tap_info *select_rlc_lte_session(capture_file *cf, struct segment *hdrs)
-{
- frame_data *fdata;
- epan_dissect_t edt;
- dfilter_t *sfcode;
- gchar *err_msg;
- GString *error_string;
- nstime_t rel_ts;
- th_t th = {0, {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}};
-
- if (cf->state == FILE_CLOSED) {
- return NULL;
- }
-
- fdata = cf->current_frame;
-
- /* no real filter yet */
- if (!dfilter_compile("rlc-lte", &sfcode, &err_msg)) {
- simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", err_msg);
- g_free(err_msg);
- return NULL;
- }
-
- /* dissect the current record */
- if (!cf_read_record(cf, fdata)) {
- return NULL; /* error reading the record */
- }
-
- error_string = register_tap_listener("rlc-lte", &th, NULL, 0, NULL, tap_lte_rlc_packet, NULL);
- if (error_string){
- fprintf(stderr, "wireshark: Couldn't register rlc_lte_graph tap: %s\n",
- error_string->str);
- g_string_free(error_string, TRUE);
- exit(1);
- }
-
- epan_dissect_init(&edt, cf->epan, TRUE, FALSE);
- epan_dissect_prime_dfilter(&edt, sfcode);
- epan_dissect_run_with_taps(&edt, cf->cd_t, &cf->phdr, frame_tvbuff_new_buffer(fdata, &cf->buf), fdata, NULL);
- rel_ts = edt.pi.rel_ts;
- epan_dissect_cleanup(&edt);
- remove_tap_listener(&th);
-
- if (th.num_hdrs == 0){
- /* This "shouldn't happen", as our menu items shouldn't
- * even be enabled if the selected packet isn't an RLC PDU
- * as rlc_lte_graph_selected_packet_enabled() is used
- * to determine whether to enable any of our menu items. */
- simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
- "Selected packet doesn't have an RLC PDU");
- return NULL;
- }
- /* XXX fix this later, we should show a dialog allowing the user
- to select which session he wants here
- */
- if (th.num_hdrs>1){
- /* can only handle a single RLC channel yet */
- simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK,
- "The selected packet has more than one LTE RLC channel "
- "in it.");
- return NULL;
- }
-
- /* For now, still always choose the first/only one */
- hdrs->num = fdata->num;
- hdrs->rel_secs = (guint32) rel_ts.secs;
- hdrs->rel_usecs = rel_ts.nsecs/1000;
- hdrs->abs_secs = (guint32) fdata->abs_ts.secs;
- hdrs->abs_usecs = fdata->abs_ts.nsecs/1000;
-
- hdrs->ueid = th.rlchdrs[0]->ueid;
- hdrs->channelType = th.rlchdrs[0]->channelType;
- hdrs->channelId = th.rlchdrs[0]->channelId;
- hdrs->rlcMode = th.rlchdrs[0]->rlcMode;
- hdrs->isControlPDU = th.rlchdrs[0]->isControlPDU;
- hdrs->direction = !hdrs->isControlPDU ? th.rlchdrs[0]->direction : !th.rlchdrs[0]->direction;
-
- return th.rlchdrs[0];
-}
-
-static int compare_headers(guint16 ueid1, guint16 channelType1, guint16 channelId1, guint8 rlcMode1, guint8 direction1,
- guint16 ueid2, guint16 channelType2, guint16 channelId2, guint8 rlcMode2, guint8 direction2,
- gboolean frameIsControl)
-{
- /* Same direction, data - OK. */
- if (!frameIsControl) {
- return (direction1 == direction2) &&
- (ueid1 == ueid2) &&
- (channelType1 == channelType2) &&
- (channelId1 == channelId2) &&
- (rlcMode1 == rlcMode2);
- }
- else {
- if (frameIsControl && (rlcMode1 == RLC_AM_MODE) && (rlcMode2 == RLC_AM_MODE)) {
- return ((direction1 != direction2) &&
- (ueid1 == ueid2) &&
- (channelType1 == channelType2) &&
- (channelId1 == channelId2));
- }
- else {
- return FALSE;
- }
- }
-}
-
-/* Free all segments in the graph */
-static void graph_segment_list_free(struct graph *g)
-{
- struct segment *segment;
-
- while (g->segments) {
- segment = g->segments->next;
- g_free(g->segments);
- g->segments = segment;
- }
- g->segments = NULL;
-}
-
-static void graph_element_lists_initialize(struct graph *g)
+static void graph_element_lists_initialize(struct gtk_rlc_graph *g)
{
- g->elists = (struct element_list *)g_malloc0(sizeof(struct element_list));
- g->elists->elements = NULL;
- g->elists->next = NULL;
+ g->graph.elists = (struct element_list *)g_malloc0(sizeof(struct element_list));
+ g->graph.elists->elements = NULL;
+ g->graph.elists->next = NULL;
}
-static void graph_element_lists_make(struct graph *g)
+static void graph_element_lists_make(struct gtk_rlc_graph *g)
{
debug(DBS_FENTRY) puts("graph_element_lists_make()");
rlc_lte_make_elmtlist(g);
}
-static void graph_element_lists_free(struct graph *g)
+static void graph_element_lists_free(struct gtk_rlc_graph *g)
{
struct element_list *list, *next_list;
- for (list=g->elists; list; list=next_list) {
+ for (list=g->graph.elists; list; list=next_list) {
g_free(list->elements);
next_list = list->next;
g_free(list);
}
- g->elists = NULL; /* just to make debugging easier */
+ g->graph.elists = NULL; /* just to make debugging easier */
}
-static void graph_title_pixmap_create(struct graph *g)
+static void graph_title_pixmap_create(struct gtk_rlc_graph *g)
{
#if GTK_CHECK_VERSION(2,22,0)
if (g->title_surface){
@@ -1049,7 +744,7 @@ static void graph_title_pixmap_create(struct graph *g)
#endif
}
-static void graph_title_pixmap_draw(struct graph *g)
+static void graph_title_pixmap_draw(struct gtk_rlc_graph *g)
{
gint w, h;
PangoLayout *layout;
@@ -1073,7 +768,7 @@ static void graph_title_pixmap_draw(struct graph *g)
cairo_destroy(cr);
}
-static void graph_title_pixmap_display(struct graph *g)
+static void graph_title_pixmap_display(struct gtk_rlc_graph *g)
{
cairo_t *cr;
@@ -1088,7 +783,7 @@ static void graph_title_pixmap_display(struct graph *g)
cairo_destroy(cr);
}
-static void graph_pixmaps_create(struct graph *g)
+static void graph_pixmaps_create(struct gtk_rlc_graph *g)
{
debug(DBS_FENTRY) puts("graph_pixmaps_create()");
#if GTK_CHECK_VERSION(2,22,0)
@@ -1129,7 +824,7 @@ static void graph_pixmaps_create(struct graph *g)
}
-static void graph_display(struct graph *g)
+static void graph_display(struct gtk_rlc_graph *g)
{
set_busy_cursor(gtk_widget_get_window(g->drawing_area));
graph_pixmap_draw(g);
@@ -1138,7 +833,7 @@ static void graph_display(struct graph *g)
graph_pixmap_display(g);
}
-static void graph_pixmap_display(struct graph *g)
+static void graph_pixmap_display(struct gtk_rlc_graph *g)
{
cairo_t *cr;
@@ -1153,12 +848,12 @@ static void graph_pixmap_display(struct graph *g)
cairo_destroy(cr);
}
-static void graph_pixmaps_switch(struct graph *g)
+static void graph_pixmaps_switch(struct gtk_rlc_graph *g)
{
g->displayed = 1 ^ g->displayed;
}
-static void graph_pixmap_draw(struct graph *g)
+static void graph_pixmap_draw(struct gtk_rlc_graph *g)
{
struct element_list *list;
struct element *e;
@@ -1199,7 +894,7 @@ static void graph_pixmap_draw(struct graph *g)
cairo_set_line_width(cr_elements, 1.0);
/* Draw all elements */
- for (list=g->elists; list; list=list->next) {
+ for (list=g->graph.elists; list; list=list->next) {
for (e=list->elements; e->type != ELMT_NONE; e++) {
/* Work out if we need to change colour */
@@ -1244,7 +939,7 @@ static void graph_pixmap_draw(struct graph *g)
cairo_destroy(cr_elements);
}
-static void draw_element_line(struct graph *g, struct element *e, cairo_t *cr,
+static void draw_element_line(struct gtk_rlc_graph *g, struct element *e, cairo_t *cr,
GdkRGBA *new_color)
{
int xx1, xx2, yy1, yy2;
@@ -1282,7 +977,7 @@ static void draw_element_line(struct graph *g, struct element *e, cairo_t *cr,
cairo_line_to(cr, xx2+0.5, yy2+0.5);
}
-static void draw_element_ellipse(struct graph *g, struct element *e, cairo_t *cr,
+static void draw_element_ellipse(struct gtk_rlc_graph *g, struct element *e, cairo_t *cr,
GdkRGBA *new_color)
{
gdouble w = e->p.ellipse.dim.width;
@@ -1376,7 +1071,7 @@ static void axis_display(struct axis *axis)
/* These show sequence numbers. Avoid subdividing whole numbers. */
static void v_axis_pixmap_draw(struct axis *axis)
{
- struct graph *g = axis->g;
+ struct gtk_rlc_graph *g = axis->g;
int i;
double major_tick;
int not_disp, offset, imin, imax;
@@ -1476,7 +1171,7 @@ static void v_axis_pixmap_draw(struct axis *axis)
show 3 decimal places? */
static void h_axis_pixmap_draw(struct axis *axis)
{
- struct graph *g = axis->g;
+ struct gtk_rlc_graph *g = axis->g;
int i;
double major_tick, minor_tick;
int not_disp, rdigits, offset, imin, imax;
@@ -1759,7 +1454,7 @@ static double axis_zoom_get(struct axis *axis, int dir)
}
}
-static void graph_select_segment(struct graph *g, int x, int y)
+static void graph_select_segment(struct gtk_rlc_graph *g, int x, int y)
{
struct element_list *list;
struct element *e;
@@ -1772,7 +1467,7 @@ static void graph_select_segment(struct graph *g, int x, int y)
set_busy_cursor(gtk_widget_get_window(g->drawing_area));
- for (list=g->elists; list; list=list->next) {
+ for (list=g->graph.elists; list; list=list->next) {
for (e=list->elements; e->type != ELMT_NONE; e++) {
switch (e->type) {
case ELMT_LINE:
@@ -1854,7 +1549,7 @@ static int ellipse_detect_collision(struct element *e, int x, int y)
static gboolean configure_event(GtkWidget *widget _U_, GdkEventConfigure *event, gpointer user_data)
{
- struct graph *g = (struct graph *)user_data;
+ struct gtk_rlc_graph *g = (struct gtk_rlc_graph *)user_data;
struct zoom new_zoom;
int cur_g_width, cur_g_height;
int cur_wp_width, cur_wp_height;
@@ -1910,7 +1605,7 @@ static gboolean configure_event(GtkWidget *widget _U_, GdkEventConfigure *event,
static gboolean
draw_event(GtkWidget *widget _U_, cairo_t *cr, gpointer user_data)
{
- struct graph *g = (struct graph *)user_data;
+ struct gtk_rlc_graph *g = (struct gtk_rlc_graph *)user_data;
debug(DBS_FENTRY) puts("draw_event()");
@@ -1934,7 +1629,7 @@ draw_event(GtkWidget *widget _U_, cairo_t *cr, gpointer user_data)
#else
static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
{
- struct graph *g = (struct graph *)user_data;
+ struct gtk_rlc_graph *g = (struct gtk_rlc_graph *)user_data;
cairo_t *cr;
debug(DBS_FENTRY) puts("expose_event()");
@@ -1969,7 +1664,7 @@ static gboolean expose_event(GtkWidget *widget, GdkEventExpose *event, gpointer
static void
-perform_zoom(struct graph *g, struct zoomfactor *zf,
+perform_zoom(struct gtk_rlc_graph *g, struct zoomfactor *zf,
int origin_x, int origin_y)
{
int cur_width = g->geom.width, cur_height = g->geom.height;
@@ -2014,7 +1709,7 @@ perform_zoom(struct graph *g, struct zoomfactor *zf,
}
static void
-do_zoom_rectangle(struct graph *g, struct irect lcl_zoomrect)
+do_zoom_rectangle(struct gtk_rlc_graph *g, struct irect lcl_zoomrect)
{
int cur_width = g->wp.width, cur_height = g->wp.height;
/* Make copy of geom1 before working out zoom */
@@ -2110,7 +1805,7 @@ do_zoom_rectangle(struct graph *g, struct irect lcl_zoomrect)
/* Zoom because of keyboard or mouse press */
-static void do_zoom_common(struct graph *g, GdkEventButton *event,
+static void do_zoom_common(struct gtk_rlc_graph *g, GdkEventButton *event,
gboolean lock_vertical, gboolean lock_horizontal)
{
int cur_width = g->geom.width, cur_height = g->geom.height;
@@ -2218,21 +1913,21 @@ static void do_zoom_common(struct graph *g, GdkEventButton *event,
}
-static void do_zoom_keyboard(struct graph *g,
+static void do_zoom_keyboard(struct gtk_rlc_graph *g,
gboolean lock_vertical,
gboolean lock_horizontal)
{
do_zoom_common(g, NULL, lock_vertical, lock_horizontal);
}
-static void do_zoom_mouse(struct graph *g, GdkEventButton *event)
+static void do_zoom_mouse(struct gtk_rlc_graph *g, GdkEventButton *event)
{
do_zoom_common(g, event,
event->state & GDK_SHIFT_MASK,
event->state & GDK_CONTROL_MASK);
}
-static void do_zoom_in_keyboard(struct graph *g,
+static void do_zoom_in_keyboard(struct gtk_rlc_graph *g,
gboolean lock_vertical,
gboolean lock_horizontal)
{
@@ -2240,7 +1935,7 @@ static void do_zoom_in_keyboard(struct graph *g,
do_zoom_keyboard(g, lock_vertical, lock_horizontal);
}
-static void do_zoom_out_keyboard(struct graph *g,
+static void do_zoom_out_keyboard(struct gtk_rlc_graph *g,
gboolean lock_vertical,
gboolean lock_horizontal)
{
@@ -2248,7 +1943,7 @@ static void do_zoom_out_keyboard(struct graph *g,
do_zoom_keyboard(g, lock_vertical, lock_horizontal);
}
-static void do_key_motion(struct graph *g)
+static void do_key_motion(struct gtk_rlc_graph *g)
{
if (g->geom.x > g->wp.x) {
g->geom.x = g->wp.x;
@@ -2275,25 +1970,25 @@ static void do_key_motion(struct graph *g)
}
}
-static void do_key_motion_up(struct graph *g, int step)
+static void do_key_motion_up(struct gtk_rlc_graph *g, int step)
{
g->geom.y += step;
do_key_motion(g);
}
-static void do_key_motion_down(struct graph *g, int step)
+static void do_key_motion_down(struct gtk_rlc_graph *g, int step)
{
g->geom.y -= step;
do_key_motion(g);
}
-static void do_key_motion_left(struct graph *g, int step)
+static void do_key_motion_left(struct gtk_rlc_graph *g, int step)
{
g->geom.x += step;
do_key_motion(g);
}
-static void do_key_motion_right(struct graph *g, int step)
+static void do_key_motion_right(struct gtk_rlc_graph *g, int step)
{
g->geom.x -= step;
do_key_motion(g);
@@ -2301,7 +1996,7 @@ static void do_key_motion_right(struct graph *g, int step)
static gboolean button_press_event(GtkWidget *widget _U_, GdkEventButton *event, gpointer user_data)
{
- struct graph *g = (struct graph *)user_data;
+ struct gtk_rlc_graph *g = (struct gtk_rlc_graph *)user_data;
debug(DBS_FENTRY) puts("button_press_event()");
@@ -2327,7 +2022,7 @@ static gboolean button_press_event(GtkWidget *widget _U_, GdkEventButton *event,
static gboolean button_release_event(GtkWidget *widget _U_, GdkEventButton *event _U_, gpointer user_data)
{
- struct graph *g = (struct graph *)user_data;
+ struct gtk_rlc_graph *g = (struct gtk_rlc_graph *)user_data;
/* Turn off grab if right button released */
if (event->button == MOUSE_BUTTON_RIGHT) {
@@ -2363,7 +2058,7 @@ static gboolean button_release_event(GtkWidget *widget _U_, GdkEventButton *even
static gboolean motion_notify_event(GtkWidget *widget _U_, GdkEventMotion *event, gpointer user_data)
{
- struct graph *g = (struct graph *)user_data;
+ struct gtk_rlc_graph *g = (struct gtk_rlc_graph *)user_data;
int x, y;
GdkModifierType state;
@@ -2428,7 +2123,7 @@ static gboolean motion_notify_event(GtkWidget *widget _U_, GdkEventMotion *event
static gboolean key_press_event(GtkWidget *widget _U_, GdkEventKey *event, gpointer user_data)
{
- struct graph *g = (struct graph *)user_data;
+ struct gtk_rlc_graph *g = (struct gtk_rlc_graph *)user_data;
int step;
debug(DBS_FENTRY) puts("key_press_event()");
@@ -2518,7 +2213,7 @@ static gboolean key_press_event(GtkWidget *widget _U_, GdkEventKey *event, gpoin
return TRUE;
}
-static void toggle_crosshairs(struct graph *g)
+static void toggle_crosshairs(struct gtk_rlc_graph *g)
{
/* Toggle state */
g->cross.draw ^= 1;
@@ -2533,7 +2228,7 @@ static void toggle_crosshairs(struct graph *g)
}
}
-static void cross_draw(struct graph *g, int x, int y)
+static void cross_draw(struct gtk_rlc_graph *g, int x, int y)
{
/* Shouldn't draw twice onto the same position if haven't erased in the
meantime! */
@@ -2566,7 +2261,7 @@ static void cross_draw(struct graph *g, int x, int y)
g->cross.erase_needed = TRUE;
}
-static void cross_erase(struct graph *g)
+static void cross_erase(struct gtk_rlc_graph *g)
{
int x = g->cross.x;
int y = g->cross.y;
@@ -2581,7 +2276,7 @@ static void cross_erase(struct graph *g)
g->cross.erase_needed = FALSE;
}
-static void zoomrect_draw(struct graph *g, int x, int y)
+static void zoomrect_draw(struct gtk_rlc_graph *g, int x, int y)
{
if ((zoomrect.x > g->wp.x) && (zoomrect.x < g->wp.x + g->wp.width) &&
(zoomrect.y > g->wp.y) && (zoomrect.y < g->wp.y + g->wp.height) &&
@@ -2601,7 +2296,7 @@ static void zoomrect_draw(struct graph *g, int x, int y)
g->zoomrect_erase_needed = TRUE;
}
-static void zoomrect_erase(struct graph *g)
+static void zoomrect_erase(struct gtk_rlc_graph *g)
{
/* Just redraw what is in the pixmap buffer */
graph_pixmap_display(g);
@@ -2610,7 +2305,7 @@ static void zoomrect_erase(struct graph *g)
/* Toggle between showing the time starting at 0, or time in capture */
-static void toggle_time_origin(struct graph *g)
+static void toggle_time_origin(struct gtk_rlc_graph *g)
{
g->style.flags ^= TIME_ORIGIN;
@@ -2625,7 +2320,7 @@ static void toggle_time_origin(struct graph *g)
axis_display(g->x_axis);
}
-static void restore_initial_graph_view(struct graph *g)
+static void restore_initial_graph_view(struct gtk_rlc_graph *g)
{
g->geom.width = g->wp.width;
g->geom.height = g->wp.height;
@@ -2642,14 +2337,14 @@ static void restore_initial_graph_view(struct graph *g)
}
/* Walk the segment list, totalling up data PDUs, status ACKs and NACKs */
-static void get_data_control_counts(struct graph *g, int *data, int *acks, int *nacks)
+static void get_data_control_counts(struct gtk_rlc_graph *g, int *data, int *acks, int *nacks)
{
- struct segment *tmp;
+ struct rlc_segment *tmp;
*data = 0;
*acks = 0;
*nacks = 0;
- for (tmp=g->segments; tmp; tmp=tmp->next) {
+ for (tmp=g->graph.segments; tmp; tmp=tmp->next) {
if (tmp->isControlPDU) {
(*acks)++;
(*nacks) += tmp->noOfNACKs;
@@ -2665,9 +2360,9 @@ static void get_data_control_counts(struct graph *g, int *data, int *acks, int *
* Not currently trying to work out the upper bound of the window, as we
* don't reliably know the RLC channel state variables...
*/
-static void graph_get_bounds(struct graph *g)
+static void graph_get_bounds(struct gtk_rlc_graph *g)
{
- struct segment *tmp;
+ struct rlc_segment *tmp;
double tim;
gboolean data_frame_seen = FALSE;
double data_tim_low = 0;
@@ -2684,7 +2379,7 @@ static void graph_get_bounds(struct graph *g)
guint32 ack_seq_high = 0;
/* Go through all segments to determine "bounds" */
- for (tmp=g->segments; tmp; tmp=tmp->next) {
+ for (tmp=g->graph.segments; tmp; tmp=tmp->next) {
if (!tmp->isControlPDU) {
/* DATA frame */
@@ -2753,7 +2448,7 @@ static void graph_get_bounds(struct graph *g)
g->zoom.y = (g->geom.height -1) / g->bounds.height;
}
-static void graph_read_config(struct graph *g)
+static void graph_read_config(struct gtk_rlc_graph *g)
{
/* Black for PDUs */
g->style.seq_color.red = (double)0 / 65535.0;
@@ -2791,12 +2486,12 @@ static void graph_read_config(struct graph *g)
g->x_axis->label[1] = NULL;
}
-static void rlc_lte_make_elmtlist(struct graph *g)
+static void rlc_lte_make_elmtlist(struct gtk_rlc_graph *g)
{
- struct segment *tmp;
+ struct rlc_segment *tmp;
struct element *elements0, *e0; /* list of elmts showing control */
struct element *elements1, *e1; /* list of elmts showing data */
- struct segment *last_status_segment = NULL;
+ struct rlc_segment *last_status_segment = NULL;
double xx0, yy0;
gboolean ack_seen = FALSE;
guint32 seq_base;
@@ -2808,7 +2503,7 @@ static void rlc_lte_make_elmtlist(struct graph *g)
debug(DBS_FENTRY) puts("rlc_lte_make_elmtlist()");
/* Allocate all needed elements up-front */
- if (g->elists->elements == NULL) {
+ if (g->graph.elists->elements == NULL) {
get_data_control_counts(g, &data, &acks, &nacks);
/* Allocate elements for data */
@@ -2820,18 +2515,18 @@ static void rlc_lte_make_elmtlist(struct graph *g)
e1 = elements1 = (struct element *)g_malloc(n*sizeof(struct element));
/* Allocate container for 2nd list of elements */
- g->elists->next = (struct element_list *)g_malloc0(sizeof(struct element_list));
+ g->graph.elists->next = (struct element_list *)g_malloc0(sizeof(struct element_list));
} else {
- e0 = elements0 = g->elists->elements;
- e1 = elements1 = g->elists->next->elements;
+ e0 = elements0 = g->graph.elists->elements;
+ e1 = elements1 = g->graph.elists->next->elements;
}
xx0 = g->bounds.x0;
yy0 = g->bounds.y0;
seq_base = (guint32) yy0;
- for (tmp=g->segments; tmp; tmp=tmp->next) {
+ for (tmp=g->graph.segments; tmp; tmp=tmp->next) {
double secs;
double x, y;
@@ -2992,8 +2687,8 @@ static void rlc_lte_make_elmtlist(struct graph *g)
/* Complete both element lists */
e0->type = ELMT_NONE;
e1->type = ELMT_NONE;
- g->elists->elements = elements0;
- g->elists->next->elements = elements1;
+ g->graph.elists->elements = elements0;
+ g->graph.elists->next->elements = elements1;
}
#if defined(_WIN32) && !defined(__MINGW32__) && (_MSC_VER < 1800)
diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt
index d214b4826f..376ad38b87 100644
--- a/ui/qt/CMakeLists.txt
+++ b/ui/qt/CMakeLists.txt
@@ -81,6 +81,7 @@ set(WIRESHARK_QT_HEADERS
lbm_lbtru_transport_dialog.h
lbm_stream_dialog.h
lbm_uimflow_dialog.h
+ lte_rlc_graph_dialog.h
lte_mac_statistics_dialog.h
lte_rlc_statistics_dialog.h
main_status_bar.h
@@ -224,6 +225,7 @@ set(WIRESHARK_QT_SRC
lbm_lbtru_transport_dialog.cpp
lbm_stream_dialog.cpp
lbm_uimflow_dialog.cpp
+ lte_rlc_graph_dialog.cpp
lte_mac_statistics_dialog.cpp
lte_rlc_statistics_dialog.cpp
main_status_bar.cpp
@@ -365,6 +367,7 @@ set(WIRESHARK_QT_UI
lbm_lbtru_transport_dialog.ui
lbm_stream_dialog.ui
lbm_uimflow_dialog.ui
+ lte_rlc_graph_dialog.ui
main_welcome.ui
main_window.ui
main_window_preferences_frame.ui
diff --git a/ui/qt/Makefile.am b/ui/qt/Makefile.am
index 13bd0d2db7..fb7f2f592f 100644
--- a/ui/qt/Makefile.am
+++ b/ui/qt/Makefile.am
@@ -202,6 +202,8 @@ lbm_stream_dialog.$(OBJEXT): ui_lbm_stream_dialog.h
lbm_uimflow_dialog.$(OBJEXT): ui_lbm_uimflow_dialog.h
+lte_rlc_graph_dialog.$(OBJEXT): ui_lte_rlc_graph_dialog.h
+
main_welcome.$(OBJEXT): ui_main_welcome.h
main_window.$(OBJEXT): ui_main_window.h
diff --git a/ui/qt/Makefile.common b/ui/qt/Makefile.common
index 1af30ecbfb..409dca819c 100644
--- a/ui/qt/Makefile.common
+++ b/ui/qt/Makefile.common
@@ -66,6 +66,7 @@ NODIST_GENERATED_HEADER_FILES = \
ui_lbm_lbtru_transport_dialog.h \
ui_lbm_stream_dialog.h \
ui_lbm_uimflow_dialog.h \
+ ui_lte_rlc_graph_dialog.h \
ui_main_welcome.h \
ui_main_window.h \
ui_main_window_preferences_frame.h \
@@ -202,6 +203,7 @@ MOC_HDRS = \
lbm_stream_dialog.h \
lbm_uimflow_dialog.h \
lte_mac_statistics_dialog.h \
+ lte_rlc_graph_dialog.h \
lte_rlc_statistics_dialog.h \
main_status_bar.h \
main_welcome.h \
@@ -310,6 +312,7 @@ UI_FILES = \
lbm_lbtru_transport_dialog.ui \
lbm_stream_dialog.ui \
lbm_uimflow_dialog.ui \
+ lte_rlc_graph_dialog.ui \
main_welcome.ui \
main_window.ui \
main_window_preferences_frame.ui \
@@ -456,6 +459,7 @@ WIRESHARK_QT_SRC = \
lbm_stream_dialog.cpp \
lbm_uimflow_dialog.cpp \
lte_mac_statistics_dialog.cpp \
+ lte_rlc_graph_dialog.cpp \
lte_rlc_statistics_dialog.cpp \
main_status_bar.cpp \
main_welcome.cpp \
diff --git a/ui/qt/Wireshark.pro b/ui/qt/Wireshark.pro
index a056d5b6b9..a2361c9124 100644
--- a/ui/qt/Wireshark.pro
+++ b/ui/qt/Wireshark.pro
@@ -245,6 +245,7 @@ FORMS += \
lbm_lbtru_transport_dialog.ui \
lbm_stream_dialog.ui \
lbm_uimflow_dialog.ui \
+ lte_rlc_graph_dialog.ui \
main_welcome.ui \
main_window.ui \
main_window_preferences_frame.ui \
@@ -329,6 +330,7 @@ HEADERS += $$HEADERS_WS_C \
lbm_stream_dialog.h \
lbm_uimflow_dialog.h \
lte_mac_statistics_dialog.h \
+ lte_rlc_graph_dialog.h \
lte_rlc_statistics_dialog.h \
main_window_preferences_frame.h \
manage_interfaces_dialog.h \
@@ -722,6 +724,7 @@ SOURCES += \
lbm_stream_dialog.cpp \
lbm_uimflow_dialog.cpp \
lte_mac_statistics_dialog.cpp \
+ lte_rlc_graph_dialog.cpp \
lte_rlc_statistics_dialog.cpp \
main_status_bar.cpp \
main_welcome.cpp \
diff --git a/ui/qt/lte_rlc_graph_dialog.cpp b/ui/qt/lte_rlc_graph_dialog.cpp
new file mode 100644
index 0000000000..8b60df7e22
--- /dev/null
+++ b/ui/qt/lte_rlc_graph_dialog.cpp
@@ -0,0 +1,643 @@
+/* lte_rlc_graph_dialog.cpp
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "lte_rlc_graph_dialog.h"
+#include <ui_lte_rlc_graph_dialog.h>
+
+#include <epan/epan.h>
+#include <epan/epan_dissect.h>
+#include <epan/tap.h>
+#include <epan/stat_tap_ui.h>
+
+#include <epan/tvbuff-int.h>
+#include <epan/tvbuff.h>
+#include <frame_tvbuff.h>
+
+#include "tango_colors.h"
+
+#include <QMenu>
+#include <QRubberBand>
+
+#include "qt_ui_utils.h"
+#include "wireshark_application.h"
+#include "simple_dialog.h"
+
+#include "globals.h"
+
+#include <epan/dissectors/packet-rlc-lte.h>
+
+#include <ui/tap-rlc-graph.h>
+
+// TODO:
+// - better handling of zooming (select area like TCP and/or Jim's patch for 1 dimension at a time)
+// - get launched from RLC stats for a pre-known channel
+// - how to avoid panning or zooming out to -ve (x or y axis)
+// - goto packet functionality when click on segments
+
+const QRgb graph_color_seq = 0x000000; // Black.
+const QRgb graph_color_ack = tango_sky_blue_4; // Blue for ACK lines
+const QRgb graph_color_nack = tango_scarlet_red_3; // Red for NACKs
+const QRgb graph_color_resegmented = 0x888888; // Grey for resegmentations
+
+// Size of selectable packet points in the base graph
+const double pkt_point_size_ = 3.0;
+
+
+// Constructor.
+LteRlcGraphDialog::LteRlcGraphDialog(QWidget &parent, CaptureFile &cf) :
+ WiresharkDialog(parent, cf),
+ ui(new Ui::LteRlcGraphDialog),
+ mouse_drags_(true),
+ rubber_band_(NULL)
+{
+ ui->setupUi(this);
+
+ // XXX Use recent settings instead
+ resize(parent.width() * 4 / 5, parent.height() * 3 / 4);
+
+ QCustomPlot *rp = ui->rlcPlot;
+ rp->xAxis->setLabel(tr("Time"));
+ rp->yAxis->setLabel(tr("Sequence Number"));
+
+ // TODO: don't want all of these...
+ ctx_menu_ = new QMenu(this);
+ ctx_menu_->addAction(ui->actionZoomIn);
+ ctx_menu_->addAction(ui->actionZoomOut);
+ ctx_menu_->addAction(ui->actionReset);
+ ctx_menu_->addSeparator();
+ ctx_menu_->addAction(ui->actionMoveRight10);
+ ctx_menu_->addAction(ui->actionMoveLeft10);
+ ctx_menu_->addAction(ui->actionMoveUp10);
+ ctx_menu_->addAction(ui->actionMoveUp100);
+ ctx_menu_->addAction(ui->actionMoveDown10);
+ ctx_menu_->addAction(ui->actionMoveDown100);
+ ctx_menu_->addAction(ui->actionMoveRight1);
+ ctx_menu_->addAction(ui->actionMoveLeft1);
+ ctx_menu_->addAction(ui->actionMoveUp1);
+ ctx_menu_->addAction(ui->actionMoveDown1);
+// ctx_menu_.addSeparator();
+// ctx_menu_->addAction(ui->actionGoToPacket);
+ ctx_menu_->addSeparator();
+ ctx_menu_->addAction(ui->actionDragZoom);
+// ctx_menu_->addAction(ui->actionToggleTimeOrigin);
+ ctx_menu_->addAction(ui->actionCrosshairs);
+
+ // Zero out this struct.
+ memset(&graph_, 0, sizeof(graph_));
+
+ // If no channel chosen already, try to use currently selected frame.
+ findChannel();
+
+ // Set window title here.
+ if (graph_.channelSet) {
+ QString dlg_title = tr("LTE RLC Graph (UE=%1 chan=%2%3 %4 - %5)")
+ .arg(graph_.ueid)
+ .arg((graph_.channelType == CHANNEL_TYPE_SRB) ? "SRB" : "DRB")
+ .arg(graph_.channelId)
+ .arg((graph_.direction == DIRECTION_UPLINK) ? "UL" : "DL")
+ .arg((graph_.rlcMode == RLC_UM_MODE) ? "UM" : "AM");
+ setWindowTitle(dlg_title);
+ }
+ else {
+ setWindowTitle(tr("LTE RLC Graph - no channel selected"));
+ }
+
+ // Set colours/styles for each of the traces on the graph.
+ QCustomPlot *sp = ui->rlcPlot;
+ base_graph_ = sp->addGraph(); // All: Selectable segments
+ base_graph_->setPen(QPen(QBrush(graph_color_seq), 0.25));
+
+ reseg_graph_ = sp->addGraph();
+ reseg_graph_->setPen(QPen(QBrush(graph_color_resegmented), 0.25));
+
+ acks_graph_ = sp->addGraph();
+ acks_graph_->setPen(QPen(QBrush(graph_color_ack), 1.0));
+
+ nacks_graph_ = sp->addGraph();
+ nacks_graph_->setPen(QPen(QBrush(graph_color_nack), 0.25));
+
+ connect(rp, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(graphClicked(QMouseEvent*)));
+ connect(rp, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoved(QMouseEvent*)));
+ connect(rp, SIGNAL(mouseRelease(QMouseEvent*)), this, SLOT(mouseReleased(QMouseEvent*)));
+
+ // Extract the data that the graph can use.
+ fillGraph();
+}
+
+// Destructor
+LteRlcGraphDialog::~LteRlcGraphDialog()
+{
+ delete ui;
+}
+
+// See if the given segment matches the channel this graph is plotting.
+bool LteRlcGraphDialog::compareHeaders(rlc_segment *seg)
+{
+ return compare_rlc_headers(graph_.ueid, graph_.channelType,
+ graph_.channelId, graph_.rlcMode, graph_.direction,
+ seg->ueid, seg->channelType,
+ seg->channelId, seg->rlcMode, seg->direction,
+ seg->isControlPDU);
+}
+
+// Look for channel to plot based upon currently selected frame.
+void LteRlcGraphDialog::findChannel()
+{
+ char *err_string = NULL;
+ gboolean free_err_string = FALSE;
+ rlc_graph_segment_list_free(&graph_);
+ if (!rlc_graph_segment_list_get(cap_file_.capFile(), &graph_, graph_.channelSet,
+ &err_string, &free_err_string)) {
+ // Pop up an error box to report error.
+ simple_error_message_box("%s", err_string);
+ if (free_err_string) {
+ g_free(err_string);
+ }
+ }
+}
+
+// Fill in graph data based upon what was read into the rlc_graph struct.
+void LteRlcGraphDialog::fillGraph()
+{
+ QCustomPlot *sp = ui->rlcPlot;
+
+ // We should always have 4 graphs, but cover case if no channel was chosen.
+ if (sp->graphCount() < 1) {
+ return;
+ }
+
+ base_graph_->setLineStyle(QCPGraph::lsNone); // dot
+ reseg_graph_->setLineStyle(QCPGraph::lsNone); // dot
+ acks_graph_->setLineStyle(QCPGraph::lsStepLeft); // to get step effect...
+ nacks_graph_->setLineStyle(QCPGraph::lsNone); // dot, but bigger.
+
+ // Will show all graphs with data we find.
+ for (int i = 0; i < sp->graphCount(); i++) {
+ sp->graph(i)->clearData();
+ sp->graph(i)->setVisible(true);
+ }
+
+ // NACKs are shown bigger than others.
+ base_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, pkt_point_size_));
+ reseg_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, pkt_point_size_));
+ acks_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, pkt_point_size_));
+ nacks_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, pkt_point_size_*2));
+
+
+ ts_offset_ = 0;
+ seq_offset_ = 0; // TODO: needed?
+ bool first = true;
+
+ // Map timestamps -> segments in first pass.
+ time_stamp_map_.clear();
+ for (struct rlc_segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
+ if (!compareHeaders(seg)) {
+ continue;
+ }
+ double ts = seg->rel_secs + seg->rel_usecs / 1000000.0;
+ if (first) {
+ // Take note of first sequence number seen.
+ if (seq_origin_zero_) {
+ seq_offset_ = seg->SN;
+ }
+ first = false;
+ }
+ time_stamp_map_.insertMulti(ts - ts_offset_, seg);
+ }
+
+ // Now sequence numbers.
+ QVector<double> seq_time, seq,
+ reseg_seq_time, reseg_seq,
+ acks_time, acks,
+ nacks_time, nacks;
+ for (struct rlc_segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
+ double ts = (seg->rel_secs + seg->rel_usecs / 1000000.0) - ts_offset_;
+ if (compareHeaders(seg)) {
+ if (!seg->isControlPDU) {
+ // Data
+ if (seg->isResegmented) {
+ reseg_seq_time.append(ts);
+ reseg_seq.append(seg->SN - seq_offset_);
+ }
+ else {
+ seq_time.append(ts);
+ seq.append(seg->SN - seq_offset_);
+ }
+ }
+ else {
+ // Status (ACKs/NACKs)
+ acks_time.append(ts);
+ acks.append(seg->ACKNo-1);
+ for (int n=0; n < seg->noOfNACKs; n++) {
+ nacks_time.append(ts);
+ nacks.append(seg->NACKs[n]);
+ }
+ }
+ }
+ }
+
+ // Add the data from the graphs.
+ base_graph_->setData(seq_time, seq);
+ reseg_graph_->setData(reseg_seq_time, reseg_seq);
+ acks_graph_->setData(acks_time, acks);
+ nacks_graph_->setData(nacks_time, nacks);
+
+ sp->setEnabled(true);
+
+ // Auto-size...
+ mouseMoved(NULL);
+ resetAxes();
+
+ // XXX QCustomPlot doesn't seem to draw any sort of focus indicator.
+ sp->setFocus();
+}
+
+// Copied from TCP graphs, seems like a kludge to get the graph resized immediately after it is built...
+void LteRlcGraphDialog::showEvent(QShowEvent *)
+{
+ resetAxes();
+}
+
+// Respond to a key press.
+void LteRlcGraphDialog::keyPressEvent(QKeyEvent *event)
+{
+ int pan_pixels = event->modifiers() & Qt::ShiftModifier ? 1 : 10;
+
+ switch(event->key()) {
+ case Qt::Key_Minus:
+ case Qt::Key_Underscore: // Shifted minus on U.S. keyboards
+ case Qt::Key_O: // GTK+
+ case Qt::Key_R:
+ zoomAxes(false);
+ break;
+ case Qt::Key_Plus:
+ case Qt::Key_Equal: // Unshifted plus on U.S. keyboards
+ case Qt::Key_I: // GTK+
+ zoomAxes(true);
+ break;
+
+ case Qt::Key_Right:
+ case Qt::Key_L:
+ panAxes(pan_pixels, 0);
+ break;
+ case Qt::Key_Left:
+ case Qt::Key_H:
+ panAxes(-1 * pan_pixels, 0);
+ break;
+ case Qt::Key_Up:
+ case Qt::Key_K:
+ panAxes(0, pan_pixels);
+ break;
+ case Qt::Key_Down:
+ case Qt::Key_J:
+ panAxes(0, -1 * pan_pixels);
+ break;
+
+ case Qt::Key_PageUp:
+ panAxes(0, 20 * pan_pixels);
+ break;
+ case Qt::Key_PageDown:
+ panAxes(0, -20 * pan_pixels);
+ break;
+
+ case Qt::Key_Space:
+// toggleTracerStyle();
+ break;
+
+ case Qt::Key_0:
+ case Qt::Key_ParenRight: // Shifted 0 on U.S. keyboards
+ case Qt::Key_Home:
+ resetAxes();
+ break;
+
+ case Qt::Key_G:
+// on_actionGoToPacket_triggered();
+ break;
+ case Qt::Key_T:
+// on_actionToggleTimeOrigin_triggered();
+ break;
+ case Qt::Key_Z:
+ on_actionDragZoom_triggered();
+ break;
+ }
+
+ WiresharkDialog::keyPressEvent(event);
+}
+
+void LteRlcGraphDialog::zoomAxes(bool in)
+{
+ QCustomPlot *rp = ui->rlcPlot;
+ double h_factor = rp->axisRect()->rangeZoomFactor(Qt::Horizontal);
+ double v_factor = rp->axisRect()->rangeZoomFactor(Qt::Vertical);
+
+ if (!in) {
+ h_factor = pow(h_factor, -1);
+ v_factor = pow(v_factor, -1);
+ }
+
+ rp->xAxis->scaleRange(h_factor, rp->xAxis->range().center());
+ rp->yAxis->scaleRange(v_factor, rp->yAxis->range().center());
+ rp->replot();
+}
+
+void LteRlcGraphDialog::panAxes(int x_pixels, int y_pixels)
+{
+ QCustomPlot *rp = ui->rlcPlot;
+ double h_pan = 0.0;
+ double v_pan = 0.0;
+
+ h_pan = rp->xAxis->range().size() * x_pixels / rp->xAxis->axisRect()->width();
+ v_pan = rp->yAxis->range().size() * y_pixels / rp->yAxis->axisRect()->height();
+ // The GTK+ version won't pan unless we're zoomed. Should we do the same here?
+ if (h_pan) {
+ rp->xAxis->moveRange(h_pan);
+ rp->replot();
+ }
+ if (v_pan) {
+ rp->yAxis->moveRange(v_pan);
+ rp->replot();
+ }
+}
+
+// Don't accidentally zoom into a 1x1 rect if you happen to click on the graph
+// in zoom mode.
+const int min_zoom_pixels_ = 20;
+QRectF LteRlcGraphDialog::getZoomRanges(QRect zoom_rect)
+{
+ QRectF zoom_ranges = QRectF();
+
+ if (zoom_rect.width() < min_zoom_pixels_ && zoom_rect.height() < min_zoom_pixels_) {
+ return zoom_ranges;
+ }
+
+ QCustomPlot *rp = ui->rlcPlot;
+ QRect zr = zoom_rect.normalized();
+ QRect ar = rp->axisRect()->rect();
+ if (ar.intersects(zr)) {
+ QRect zsr = ar.intersected(zr);
+ zoom_ranges.setX(rp->xAxis->range().lower
+ + rp->xAxis->range().size() * (zsr.left() - ar.left()) / ar.width());
+ zoom_ranges.setWidth(rp->xAxis->range().size() * zsr.width() / ar.width());
+
+ // QRects grow down
+ zoom_ranges.setY(rp->yAxis->range().lower
+ + rp->yAxis->range().size() * (ar.bottom() - zsr.bottom()) / ar.height());
+ zoom_ranges.setHeight(rp->yAxis->range().size() * zsr.height() / ar.height());
+ }
+ return zoom_ranges;
+}
+
+void LteRlcGraphDialog::graphClicked(QMouseEvent *event)
+{
+ QCustomPlot *rp = ui->rlcPlot;
+
+ if (event->button() == Qt::RightButton) {
+ // XXX We should find some way to get rlcPlot to handle a
+ // contextMenuEvent instead.
+ ctx_menu_->exec(event->globalPos());
+ } else if (mouse_drags_) {
+ if (rp->axisRect()->rect().contains(event->pos())) {
+ rp->setCursor(QCursor(Qt::ClosedHandCursor));
+ }
+// on_actionGoToPacket_triggered();
+ } else {
+ if (!rubber_band_) {
+ rubber_band_ = new QRubberBand(QRubberBand::Rectangle, rp);
+ }
+ rb_origin_ = event->pos();
+ rubber_band_->setGeometry(QRect(rb_origin_, QSize()));
+ rubber_band_->show();
+ }
+ rp->setFocus();
+}
+
+void LteRlcGraphDialog::mouseMoved(QMouseEvent *event)
+{
+ QCustomPlot *rp = ui->rlcPlot;
+ QString hint;
+ Qt::CursorShape shape = Qt::ArrowCursor;
+
+ if (event) {
+ if (event->buttons().testFlag(Qt::LeftButton)) {
+ if (mouse_drags_) {
+ shape = Qt::ClosedHandCursor;
+ } else {
+ shape = Qt::CrossCursor;
+ }
+ } else if (rp->axisRect()->rect().contains(event->pos())) {
+ if (mouse_drags_) {
+ shape = Qt::OpenHandCursor;
+ } else {
+ shape = Qt::CrossCursor;
+ }
+ }
+ rp->setCursor(QCursor(shape));
+ }
+
+ if (mouse_drags_) {
+// double ts = 0;
+// packet_num_ = 0;
+// int interval_packet = -1;
+
+// if (event && tracer_->graph()) {
+// tracer_->setGraphKey(rp->xAxis->pixelToCoord(event->pos().x()));
+// ts = tracer_->position->key();
+
+// QTreeWidgetItem *ti = ui->graphTreeWidget->topLevelItem(0);
+// IOGraph *iog = NULL;
+// if (ti) {
+// iog = ti->data(name_col_, Qt::UserRole).value<IOGraph *>();
+// interval_packet = iog->packetFromTime(ts);
+// }
+// }
+
+// if (interval_packet < 0) {
+// hint += tr("Hover over the graph for details.");
+// } else {
+// QString msg = tr("No packets in interval");
+// QString val;
+// if (interval_packet > 0) {
+// packet_num_ = (guint32) interval_packet;
+// msg = tr("%1 %2")
+// .arg(!file_closed_ ? tr("Click to select packet") : tr("Packet"))
+// .arg(packet_num_);
+// val = " = " + QString::number(tracer_->position->value(), 'g', 4);
+// }
+// hint += tr("%1 (%2s%3).")
+// .arg(msg)
+// .arg(QString::number(ts, 'g', 4))
+// .arg(val);
+// }
+ rp->replot();
+ } else {
+ if (event && rubber_band_ && rubber_band_->isVisible()) {
+ rubber_band_->setGeometry(QRect(rb_origin_, event->pos()).normalized());
+ QRectF zoom_ranges = getZoomRanges(QRect(rb_origin_, event->pos()));
+ if (zoom_ranges.width() > 0.0 && zoom_ranges.height() > 0.0) {
+ hint += tr("Release to zoom, x = %1 to %2, y = %3 to %4")
+ .arg(zoom_ranges.x())
+ .arg(zoom_ranges.x() + zoom_ranges.width())
+ .arg(zoom_ranges.y())
+ .arg(zoom_ranges.y() + zoom_ranges.height());
+ } else {
+ hint += tr("Unable to select range.");
+ }
+ } else {
+ hint += tr("Click to select a portion of the graph.");
+ }
+ }
+
+ hint.prepend("<small><i>");
+ hint.append("</i></small>");
+ ui->hintLabel->setText(hint);
+}
+
+void LteRlcGraphDialog::mouseReleased(QMouseEvent *event)
+{
+ QCustomPlot *rp = ui->rlcPlot;
+ if (rubber_band_) {
+ rubber_band_->hide();
+ if (!mouse_drags_) {
+ QRectF zoom_ranges = getZoomRanges(QRect(rb_origin_, event->pos()));
+ if (zoom_ranges.width() > 0.0 && zoom_ranges.height() > 0.0) {
+ rp->xAxis->setRangeLower(zoom_ranges.x());
+ rp->xAxis->setRangeUpper(zoom_ranges.x() + zoom_ranges.width());
+ rp->yAxis->setRangeLower(zoom_ranges.y());
+ rp->yAxis->setRangeUpper(zoom_ranges.y() + zoom_ranges.height());
+ rp->replot();
+ }
+ }
+ } else if (rp->cursor().shape() == Qt::ClosedHandCursor) {
+ rp->setCursor(QCursor(Qt::OpenHandCursor));
+ }
+}
+
+void LteRlcGraphDialog::resetAxes()
+{
+ QCustomPlot *rp = ui->rlcPlot;
+
+ QCPRange x_range = rp->xAxis->scaleType() == QCPAxis::stLogarithmic ?
+ rp->xAxis->range().sanitizedForLogScale() : rp->xAxis->range();
+
+ double pixel_pad = 10.0; // per side
+
+ rp->rescaleAxes(true);
+ base_graph_->rescaleValueAxis(false, true);
+
+ double axis_pixels = rp->xAxis->axisRect()->width();
+ rp->xAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, x_range.center());
+
+ axis_pixels = rp->yAxis->axisRect()->height();
+ rp->yAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, rp->yAxis->range().center());
+
+ rp->replot();
+}
+
+void LteRlcGraphDialog::on_actionReset_triggered()
+{
+ resetAxes();
+}
+
+void LteRlcGraphDialog::on_actionZoomIn_triggered()
+{
+ zoomAxes(true);
+}
+
+void LteRlcGraphDialog::on_actionZoomOut_triggered()
+{
+ zoomAxes(false);
+}
+
+void LteRlcGraphDialog::on_actionMoveUp10_triggered()
+{
+ panAxes(0, 10);
+}
+
+void LteRlcGraphDialog::on_actionMoveUp100_triggered()
+{
+ panAxes(0, 100);
+}
+
+void LteRlcGraphDialog::on_actionMoveLeft10_triggered()
+{
+ panAxes(-10, 0);
+}
+
+void LteRlcGraphDialog::on_actionMoveRight10_triggered()
+{
+ panAxes(10, 0);
+}
+
+void LteRlcGraphDialog::on_actionMoveDown10_triggered()
+{
+ panAxes(0, -10);
+}
+
+void LteRlcGraphDialog::on_actionMoveDown100_triggered()
+{
+ panAxes(0, -100);
+}
+
+void LteRlcGraphDialog::on_actionMoveUp1_triggered()
+{
+ panAxes(0, 1);
+}
+
+void LteRlcGraphDialog::on_actionMoveLeft1_triggered()
+{
+ panAxes(-1, 0);
+}
+
+void LteRlcGraphDialog::on_actionMoveRight1_triggered()
+{
+ panAxes(1, 0);
+}
+
+void LteRlcGraphDialog::on_actionMoveDown1_triggered()
+{
+ panAxes(0, -1);
+}
+
+void LteRlcGraphDialog::on_actionDragZoom_triggered()
+{
+// if (mouse_drags_) {
+// ui->zoomRadioButton->toggle();
+// } else {
+// ui->dragRadioButton->toggle();
+// }
+}
+
+
+// No need to register tap listeners here. This is done
+// in calls to the common functions in ui/tap-rlc-graph.c
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/qt/lte_rlc_graph_dialog.h b/ui/qt/lte_rlc_graph_dialog.h
new file mode 100644
index 0000000000..eaadfc4b85
--- /dev/null
+++ b/ui/qt/lte_rlc_graph_dialog.h
@@ -0,0 +1,118 @@
+/* lte_rlc_graph_dialog.h
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef LTE_RLC_GRAPH_DIALOG_H
+#define LTE_RLC_GRAPH_DIALOG_H
+
+#include "wireshark_dialog.h"
+#include <ui/tap-rlc-graph.h>
+
+#include "qcustomplot.h"
+
+class QMenu;
+class QRubberBand;
+
+namespace Ui {
+class LteRlcGraphDialog;
+}
+
+class LteRlcGraphDialog : public WiresharkDialog
+{
+ Q_OBJECT
+
+public:
+ // TODO: will need to add another constructor option to give channel explicitly,
+ // rather than find in currently selected packet, for when launch graph from
+ // RLC statistics dialog.
+ explicit LteRlcGraphDialog(QWidget &parent, CaptureFile &cf);
+ ~LteRlcGraphDialog();
+
+protected:
+ void showEvent(QShowEvent *event);
+ void keyPressEvent(QKeyEvent *event);
+
+private:
+ Ui::LteRlcGraphDialog *ui;
+ bool mouse_drags_;
+ QRubberBand *rubber_band_;
+ QPoint rb_origin_;
+ QMenu *ctx_menu_;
+
+ // Data gleaned directly from tapping packets (shared with gtk impl)
+ struct rlc_graph graph_;
+
+ // Data
+ QMap<double, struct rlc_segment *> time_stamp_map_;
+ double ts_offset_;
+ QMap<double, struct rlc_segment *> sequence_num_map_;
+ double seq_offset_;
+ bool seq_origin_zero_;
+
+ QCPGraph *base_graph_; // Clickable packets
+ QCPGraph *reseg_graph_;
+ QCPGraph *acks_graph_;
+ QCPGraph *nacks_graph_;
+
+ bool compareHeaders(rlc_segment *seg);
+
+ void findChannel();
+ void fillGraph();
+
+ void zoomAxes(bool in);
+ void panAxes(int x_pixels, int y_pixels);
+ QRectF getZoomRanges(QRect zoom_rect);
+
+private slots:
+ void graphClicked(QMouseEvent *event);
+ void mouseMoved(QMouseEvent *event);
+ void mouseReleased(QMouseEvent *event);
+ void resetAxes();
+
+ void on_actionReset_triggered();
+ void on_actionZoomIn_triggered();
+ void on_actionZoomOut_triggered();
+ void on_actionMoveUp10_triggered();
+ void on_actionMoveLeft10_triggered();
+ void on_actionMoveRight10_triggered();
+ void on_actionMoveDown10_triggered();
+ void on_actionMoveUp1_triggered();
+ void on_actionMoveLeft1_triggered();
+ void on_actionMoveRight1_triggered();
+ void on_actionMoveDown1_triggered();
+ void on_actionDragZoom_triggered();
+ void on_actionMoveUp100_triggered();
+ void on_actionMoveDown100_triggered();
+};
+
+#endif // LTE_RLC_GRAPH_DIALOG_H
+
+/*
+ * Editor modelines
+ *
+ * Local Variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * ex: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/ui/qt/lte_rlc_graph_dialog.ui b/ui/qt/lte_rlc_graph_dialog.ui
new file mode 100644
index 0000000000..827a1bb62b
--- /dev/null
+++ b/ui/qt/lte_rlc_graph_dialog.ui
@@ -0,0 +1,275 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>LteRlcGraphDialog</class>
+ <widget class="QDialog" name="LteRlcGraphDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>660</width>
+ <height>447</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Dialog</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout" stretch="1,0,0">
+ <item>
+ <widget class="QCustomPlot" name="rlcPlot" native="true"/>
+ </item>
+ <item>
+ <widget class="QLabel" name="hintLabel">
+ <property name="toolTip">
+ <string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;
+
+&lt;h3&gt;Valuable and amazing time-saving keyboard shortcuts&lt;/h3&gt;
+&lt;table&gt;&lt;tbody&gt;
+
+&lt;tr&gt;&lt;th&gt;+&lt;/th&gt;&lt;td&gt;Zoom in&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;-&lt;/th&gt;&lt;td&gt;Zoom out&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;0&lt;/th&gt;&lt;td&gt;Reset graph to its initial state&lt;/td&gt;&lt;/th&gt;
+
+&lt;tr&gt;&lt;th&gt;→&lt;/th&gt;&lt;td&gt;Move right 10 pixels&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;←&lt;/th&gt;&lt;td&gt;Move left 10 pixels&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;↑&lt;/th&gt;&lt;td&gt;Move up 10 pixels&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;↓&lt;/th&gt;&lt;td&gt;Move down 10 pixels&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;&lt;i&gt;Shift+&lt;/i&gt;→&lt;/th&gt;&lt;td&gt;Move right 1 pixel&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;&lt;i&gt;Shift+&lt;/i&gt;←&lt;/th&gt;&lt;td&gt;Move left 1 pixel&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;&lt;i&gt;Shift+&lt;/i&gt;↑&lt;/th&gt;&lt;td&gt;Move up 1 pixel&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;&lt;i&gt;Shift+&lt;/i&gt;↓&lt;/th&gt;&lt;td&gt;Move down 1 pixel&lt;/td&gt;&lt;/th&gt;
+
+&lt;tr&gt;&lt;th&gt;g&lt;/th&gt;&lt;td&gt;Go to packet under cursor&lt;/td&gt;&lt;/th&gt;
+
+&lt;tr&gt;&lt;th&gt;z&lt;/th&gt;&lt;td&gt;Toggle mouse drag / zoom&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;t&lt;/th&gt;&lt;td&gt;Toggle capture / session time origin&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;Space&lt;/th&gt;&lt;td&gt;Toggle crosshairs&lt;/td&gt;&lt;/th&gt;
+
+&lt;/tbody&gt;&lt;/table&gt;
+&lt;/body&gt;&lt;/html&gt;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Close|QDialogButtonBox::Help|QDialogButtonBox::Save</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ <action name="actionReset">
+ <property name="text">
+ <string>Reset Graph</string>
+ </property>
+ <property name="toolTip">
+ <string>Reset the graph to its initial state.</string>
+ </property>
+ <property name="shortcut">
+ <string>0</string>
+ </property>
+ </action>
+ <action name="actionZoomIn">
+ <property name="text">
+ <string>Zoom In</string>
+ </property>
+ <property name="toolTip">
+ <string>Zoom In</string>
+ </property>
+ <property name="shortcut">
+ <string>+</string>
+ </property>
+ </action>
+ <action name="actionZoomOut">
+ <property name="text">
+ <string>Zoom Out</string>
+ </property>
+ <property name="toolTip">
+ <string>Zoom Out</string>
+ </property>
+ <property name="shortcut">
+ <string>-</string>
+ </property>
+ </action>
+ <action name="actionMoveUp10">
+ <property name="text">
+ <string>Move Up 10 Pixels</string>
+ </property>
+ <property name="toolTip">
+ <string>Move Up 10 Pixels</string>
+ </property>
+ <property name="shortcut">
+ <string>Up</string>
+ </property>
+ </action>
+ <action name="actionMoveLeft10">
+ <property name="text">
+ <string>Move Left 10 Pixels</string>
+ </property>
+ <property name="toolTip">
+ <string>Move Left 10 Pixels</string>
+ </property>
+ <property name="shortcut">
+ <string>Left</string>
+ </property>
+ </action>
+ <action name="actionMoveRight10">
+ <property name="text">
+ <string>Move Right 10 Pixels</string>
+ </property>
+ <property name="toolTip">
+ <string>Move Right 10 Pixels</string>
+ </property>
+ <property name="shortcut">
+ <string>Right</string>
+ </property>
+ </action>
+ <action name="actionMoveDown10">
+ <property name="text">
+ <string>Move Down 10 Pixels</string>
+ </property>
+ <property name="toolTip">
+ <string>Move Down 10 Pixels</string>
+ </property>
+ <property name="shortcut">
+ <string>Down</string>
+ </property>
+ </action>
+ <action name="actionMoveUp1">
+ <property name="text">
+ <string>Move Up 1 Pixel</string>
+ </property>
+ <property name="toolTip">
+ <string>Move Up 1 Pixel</string>
+ </property>
+ <property name="shortcut">
+ <string>Shift+Up</string>
+ </property>
+ </action>
+ <action name="actionMoveLeft1">
+ <property name="text">
+ <string>Move Left 1 Pixel</string>
+ </property>
+ <property name="toolTip">
+ <string>Move Left 1 Pixel</string>
+ </property>
+ <property name="shortcut">
+ <string>Shift+Left</string>
+ </property>
+ </action>
+ <action name="actionMoveRight1">
+ <property name="text">
+ <string>Move Right 1 Pixel</string>
+ </property>
+ <property name="toolTip">
+ <string>Move Right 1 Pixel</string>
+ </property>
+ <property name="shortcut">
+ <string>Shift+Right</string>
+ </property>
+ </action>
+ <action name="actionMoveDown1">
+ <property name="text">
+ <string>Move Down 1 Pixel</string>
+ </property>
+ <property name="toolTip">
+ <string>Move down 1 Pixel</string>
+ </property>
+ <property name="shortcut">
+ <string>Shift+Down</string>
+ </property>
+ </action>
+ <action name="actionDragZoom">
+ <property name="text">
+ <string>Drag / Zoom</string>
+ </property>
+ <property name="toolTip">
+ <string>Toggle mouse drag / zoom behavior</string>
+ </property>
+ <property name="shortcut">
+ <string>Z</string>
+ </property>
+ </action>
+ <action name="actionCrosshairs">
+ <property name="text">
+ <string>Crosshairs</string>
+ </property>
+ <property name="toolTip">
+ <string>Toggle crosshairs</string>
+ </property>
+ <property name="shortcut">
+ <string>Space</string>
+ </property>
+ </action>
+ <action name="actionMoveUp100">
+ <property name="text">
+ <string>Move Up 100 Pixels</string>
+ </property>
+ <property name="toolTip">
+ <string>Move Up 100 Pixels</string>
+ </property>
+ <property name="shortcut">
+ <string>PgUp</string>
+ </property>
+ </action>
+ <action name="actionMoveDown100">
+ <property name="text">
+ <string>Move Up 100 Pixels</string>
+ </property>
+ <property name="toolTip">
+ <string>Move Up 100 Pixels</string>
+ </property>
+ <property name="shortcut">
+ <string>PgDown</string>
+ </property>
+ </action>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>QCustomPlot</class>
+ <extends>QWidget</extends>
+ <header>qcustomplot.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>LteRlcGraphDialog</receiver>
+ <slot>accept()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>248</x>
+ <y>254</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>157</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>LteRlcGraphDialog</receiver>
+ <slot>reject()</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>316</x>
+ <y>260</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>286</x>
+ <y>274</y>
+ </hint>
+ </hints>
+ </connection>
+ </connections>
+</ui>
diff --git a/ui/qt/main_window.cpp b/ui/qt/main_window.cpp
index b053c0c1cf..7c2d2c05bd 100644
--- a/ui/qt/main_window.cpp
+++ b/ui/qt/main_window.cpp
@@ -2156,6 +2156,7 @@ void MainWindow::addDynamicMenus()
wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_GSM, main_ui_->actionTelephonyGsmMapSummary);
wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteMacStatistics);
wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcStatistics);
+ wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_LTE, main_ui_->actionTelephonyLteRlcGraph);
wsApp->addDynamicMenuGroupItem(REGISTER_STAT_GROUP_TELEPHONY_MTP3, main_ui_->actionTelephonyMtp3Summary);
// Fill in each menu
diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h
index 8f25f1e334..dd883ffcac 100644
--- a/ui/qt/main_window.h
+++ b/ui/qt/main_window.h
@@ -540,6 +540,7 @@ private slots:
void on_actionTelephonyLteRlcStatistics_triggered();
void statCommandLteRlcStatistics(const char *arg, void *);
void on_actionTelephonyLteMacStatistics_triggered();
+ void on_actionTelephonyLteRlcGraph_triggered();
void on_actionTelephonyIax2StreamAnalysis_triggered();
void on_actionTelephonyISUPMessages_triggered();
void on_actionTelephonyMtp3Summary_triggered();
diff --git a/ui/qt/main_window.ui b/ui/qt/main_window.ui
index 72fc47ea43..b9baa1d738 100644
--- a/ui/qt/main_window.ui
+++ b/ui/qt/main_window.ui
@@ -2449,7 +2449,16 @@
<property name="toolTip">
<string>LTE RLC statistics</string>
</property>
- </action> <action name="actionTelephonyMtp3Summary">
+ </action>
+ <action name="actionTelephonyLteRlcGraph">
+ <property name="text">
+ <string>RLC Graph</string>
+ </property>
+ <property name="toolTip">
+ <string>LTE RLC graph</string>
+ </property>
+ </action>
+ <action name="actionTelephonyMtp3Summary">
<property name="text">
<string>MTP3 Summary</string>
</property>
diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp
index 2579f02169..395df9faf7 100644
--- a/ui/qt/main_window_slots.cpp
+++ b/ui/qt/main_window_slots.cpp
@@ -114,6 +114,7 @@
#include "lbm_lbtru_transport_dialog.h"
#include "lte_mac_statistics_dialog.h"
#include "lte_rlc_statistics_dialog.h"
+#include "lte_rlc_graph_dialog.h"
#include "mtp3_summary_dialog.h"
#include "multicast_statistics_dialog.h"
#include "packet_comment_dialog.h"
@@ -3025,6 +3026,12 @@ void MainWindow::on_actionTelephonyLteRlcStatistics_triggered()
statCommandLteRlcStatistics(NULL, NULL);
}
+void MainWindow::on_actionTelephonyLteRlcGraph_triggered()
+{
+ LteRlcGraphDialog *lrg_dialog = new LteRlcGraphDialog(*this, capture_file_);
+ lrg_dialog->show();
+}
+
void MainWindow::on_actionTelephonyMtp3Summary_triggered()
{
Mtp3SummaryDialog *mtp3s_dialog = new Mtp3SummaryDialog(*this, capture_file_);
diff --git a/ui/tap-rlc-graph.c b/ui/tap-rlc-graph.c
new file mode 100644
index 0000000000..f5945a7b56
--- /dev/null
+++ b/ui/tap-rlc-graph.c
@@ -0,0 +1,309 @@
+/* tap-rlc-stream.c
+ * LTE RLC stream statistics
+ *
+ * Originally from tcp_graph.c by Pavel Mores <pvl@uh.cz>
+ * Win32 port: rwh@unifiedtech.com
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include "config.h"
+
+#include "tap-rlc-graph.h"
+
+#include <file.h>
+#include <frame_tvbuff.h>
+
+#include <epan/epan.h>
+#include <epan/epan_dissect.h>
+#include <epan/packet.h>
+#include <epan/tap.h>
+
+
+int compare_rlc_headers(guint16 ueid1, guint16 channelType1, guint16 channelId1, guint8 rlcMode1, guint8 direction1,
+ guint16 ueid2, guint16 channelType2, guint16 channelId2, guint8 rlcMode2, guint8 direction2,
+ gboolean frameIsControl)
+{
+ /* Same direction, data - OK. */
+ if (!frameIsControl) {
+ return (direction1 == direction2) &&
+ (ueid1 == ueid2) &&
+ (channelType1 == channelType2) &&
+ (channelId1 == channelId2) &&
+ (rlcMode1 == rlcMode2);
+ }
+ else {
+ if (frameIsControl && (rlcMode1 == RLC_AM_MODE) && (rlcMode2 == RLC_AM_MODE)) {
+ return ((direction1 != direction2) &&
+ (ueid1 == ueid2) &&
+ (channelType1 == channelType2) &&
+ (channelId1 == channelId2));
+ }
+ else {
+ return FALSE;
+ }
+ }
+}
+
+
+static int
+tap_lte_rlc_packet(void *pct, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *vip)
+{
+ int n;
+ gboolean is_unique = TRUE;
+ th_t *th = (th_t *)pct;
+ const rlc_lte_tap_info *header = (const rlc_lte_tap_info*)vip;
+
+ /* Check new header details against any/all stored ones */
+ for (n=0; n < th->num_hdrs; n++) {
+ rlc_lte_tap_info *stored = th->rlchdrs[n];
+
+ if (compare_rlc_headers(stored->ueid, stored->channelType, stored->channelId, stored->rlcMode, stored->direction,
+ header->ueid, header->channelType, header->channelId, header->rlcMode, header->direction,
+ header->isControlPDU)) {
+ is_unique = FALSE;
+ break;
+ }
+ }
+
+ /* Add address if unique and have space for it */
+ if (is_unique && (th->num_hdrs < MAX_SUPPORTED_CHANNELS)) {
+ /* Copy the tap stuct in as next header */
+ /* Need to take a deep copy of the tap struct, it may not be valid
+ to read after this function returns? */
+ th->rlchdrs[th->num_hdrs] = g_new(rlc_lte_tap_info,1);
+ *(th->rlchdrs[th->num_hdrs]) = *header;
+
+ /* Store in direction of data though... */
+ if (th->rlchdrs[th->num_hdrs]->isControlPDU) {
+ th->rlchdrs[th->num_hdrs]->direction = !th->rlchdrs[th->num_hdrs]->direction;
+ }
+ th->num_hdrs++;
+ }
+
+ return 0;
+}
+
+/* Return an array of tap_info structs that were found while dissecting the current frame
+ in the packet list */
+rlc_lte_tap_info *select_rlc_lte_session(capture_file *cf,
+ struct rlc_segment *hdrs,
+ gchar **err_msg, gboolean *free_err_msg)
+{
+ frame_data *fdata;
+ epan_dissect_t edt;
+ dfilter_t *sfcode;
+
+ GString *error_string;
+ nstime_t rel_ts;
+ /* Initialised to no known channels */
+ th_t th = {0, {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}};
+
+ if (cf->state == FILE_CLOSED) {
+ return NULL;
+ }
+
+ fdata = cf->current_frame;
+
+ /* No real filter yet */
+ if (!dfilter_compile("rlc-lte", &sfcode, err_msg)) {
+ *free_err_msg = TRUE;
+ return NULL;
+ }
+
+ /* Dissect the current record */
+ if (!cf_read_record(cf, fdata)) {
+ return NULL; /* error reading the record */
+ }
+
+ /* Set tap listener that will populate th. */
+ error_string = register_tap_listener("rlc-lte", &th, NULL, 0, NULL, tap_lte_rlc_packet, NULL);
+ if (error_string){
+ fprintf(stderr, "wireshark: Couldn't register rlc_lte_graph tap: %s\n",
+ error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1);
+ }
+
+ epan_dissect_init(&edt, cf->epan, TRUE, FALSE);
+ epan_dissect_prime_dfilter(&edt, sfcode);
+ epan_dissect_run_with_taps(&edt, cf->cd_t, &cf->phdr, frame_tvbuff_new_buffer(fdata, &cf->buf), fdata, NULL);
+ rel_ts = edt.pi.rel_ts;
+ epan_dissect_cleanup(&edt);
+ remove_tap_listener(&th);
+
+ if (th.num_hdrs == 0){
+ /* This "shouldn't happen", as our menu items shouldn't
+ * even be enabled if the selected packet isn't an RLC PDU
+ * as rlc_lte_graph_selected_packet_enabled() is used
+ * to determine whether to enable any of our menu items. */
+ *err_msg = (char*)"Selected packet doesn't have an RLC PDU";
+ *free_err_msg = FALSE;
+ return NULL;
+ }
+ /* XXX fix this later, we should show a dialog allowing the user
+ to select which session he wants here
+ */
+ if (th.num_hdrs>1){
+ /* Can only handle a single RLC channel yet */
+ *err_msg = (char*)"The selected packet has more than one LTE RLC channel in it.";
+ *free_err_msg = FALSE;
+ return NULL;
+ }
+
+ /* For now, still always choose the first/only one */
+ hdrs->num = fdata->num;
+ hdrs->rel_secs = (guint32) rel_ts.secs;
+ hdrs->rel_usecs = rel_ts.nsecs/1000;
+ hdrs->abs_secs = (guint32) fdata->abs_ts.secs;
+ hdrs->abs_usecs = fdata->abs_ts.nsecs/1000;
+
+ hdrs->ueid = th.rlchdrs[0]->ueid;
+ hdrs->channelType = th.rlchdrs[0]->channelType;
+ hdrs->channelId = th.rlchdrs[0]->channelId;
+ hdrs->rlcMode = th.rlchdrs[0]->rlcMode;
+ hdrs->isControlPDU = th.rlchdrs[0]->isControlPDU;
+ hdrs->direction = !hdrs->isControlPDU ? th.rlchdrs[0]->direction : !th.rlchdrs[0]->direction;
+
+ return th.rlchdrs[0];
+}
+
+
+int rlc_lte_tap_for_graph_data(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, const void *vip)
+{
+ struct rlc_graph *graph = (struct rlc_graph *)pct;
+ const rlc_lte_tap_info *rlchdr = (const rlc_lte_tap_info*)vip;
+
+ /* See if this one matches current channel */
+ if (compare_rlc_headers(graph->ueid, graph->channelType, graph->channelId, graph->rlcMode, graph->direction,
+ rlchdr->ueid, rlchdr->channelType, rlchdr->channelId, rlchdr->rlcMode, rlchdr->direction,
+ rlchdr->isControlPDU)) {
+
+ struct rlc_segment *segment = (struct rlc_segment *)g_malloc(sizeof(struct rlc_segment));
+
+ /* It matches. Add to end of segment list */
+ segment->next = NULL;
+ segment->num = pinfo->fd->num;
+ segment->rel_secs = (guint32) pinfo->rel_ts.secs;
+ segment->rel_usecs = pinfo->rel_ts.nsecs/1000;
+ segment->abs_secs = (guint32) pinfo->fd->abs_ts.secs;
+ segment->abs_usecs = pinfo->fd->abs_ts.nsecs/1000;
+
+ segment->ueid = rlchdr->ueid;
+ segment->channelType = rlchdr->channelType;
+ segment->channelId = rlchdr->channelId;
+ segment->direction = rlchdr->direction;
+ segment->rlcMode = rlchdr->rlcMode;
+
+ segment->isControlPDU = rlchdr->isControlPDU;
+
+ if (!rlchdr->isControlPDU) {
+ /* Data */
+ segment->SN = rlchdr->sequenceNumber;
+ segment->isResegmented = rlchdr->isResegmented;
+ }
+ else {
+ /* Status PDU */
+ gint n;
+ segment->ACKNo = rlchdr->ACKNo;
+ segment->noOfNACKs = rlchdr->noOfNACKs;
+ for (n=0; n < rlchdr->noOfNACKs; n++) {
+ segment->NACKs[n] = rlchdr->NACKs[n];
+ }
+ }
+
+ /* Add to list */
+ if (graph->segments) {
+ /* Add to end of existing last element */
+ graph->last_segment->next = segment;
+ } else {
+ /* Make this the first (only) segment */
+ graph->segments = segment;
+ }
+
+ /* This one is now the last one */
+ graph->last_segment = segment;
+ }
+
+ return 0;
+}
+
+/* If don't have a channel, try to get one from current frame, then read all frames looking for data
+ for that channel. */
+gboolean rlc_graph_segment_list_get(capture_file *cf, struct rlc_graph *g, gboolean stream_known,
+ char **err_string, gboolean *free_err_string)
+{
+ struct rlc_segment current;
+ GString *error_string;
+
+ g_log(NULL, G_LOG_LEVEL_DEBUG, "graph_segment_list_get()");
+
+ if (!cf || !g) {
+ /* Really shouldn't happen */
+ return FALSE;
+ }
+
+ if (!stream_known) {
+ struct rlc_lte_tap_info *header = select_rlc_lte_session(cf, &current, err_string, free_err_string);
+ if (!header) {
+ /* Didn't have a channel, and current frame didn't provide one */
+ return FALSE;
+ }
+ g->channelSet = TRUE;
+ g->ueid = header->ueid;
+ g->channelType = header->channelType;
+ g->channelId = header->channelId;
+ g->rlcMode = header->rlcMode;
+ g->direction = header->direction;
+ }
+
+
+ /* rescan all the packets and pick up all interesting RLC headers.
+ * we only filter for rlc-lte here for speed and do the actual compare
+ * in the tap listener
+ */
+
+ g->last_segment = NULL;
+ error_string = register_tap_listener("rlc-lte", g, "rlc-lte", 0, NULL, rlc_lte_tap_for_graph_data, NULL);
+ if (error_string) {
+ fprintf(stderr, "wireshark: Couldn't register rlc_graph tap: %s\n",
+ error_string->str);
+ g_string_free(error_string, TRUE);
+ exit(1); /* XXX: fix this */
+ }
+ cf_retap_packets(cf);
+ remove_tap_listener(g);
+
+ return TRUE;
+}
+
+/* Free and zero the segments list of an rlc_graph struct */
+void rlc_graph_segment_list_free(struct rlc_graph * g)
+{
+ struct rlc_segment *segment;
+
+ /* Free all segments */
+ while (g->segments) {
+ segment = g->segments->next;
+ g_free(g->segments);
+ g->segments = segment;
+ }
+ /* Set head of list to NULL too */
+ g->segments = NULL;
+}
diff --git a/ui/tap-rlc-graph.h b/ui/tap-rlc-graph.h
new file mode 100644
index 0000000000..1312ef12c2
--- /dev/null
+++ b/ui/tap-rlc-graph.h
@@ -0,0 +1,103 @@
+/* tap-rlc-stream.h
+ * LTE RLC stream statistics
+ *
+ * Wireshark - Network traffic analyzer
+ * By Gerald Combs <gerald@wireshark.org>
+ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __TAP_RLC_GRAPH_H__
+#define __TAP_RLC_GRAPH_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include <epan/epan.h>
+#include <epan/packet.h>
+#include <wiretap/wtap.h>
+#include <cfile.h>
+#include <epan/dissectors/packet-rlc-lte.h>
+
+struct rlc_segment {
+ struct rlc_segment *next;
+ guint32 num; /* framenum */
+ guint32 rel_secs;
+ guint32 rel_usecs;
+ guint32 abs_secs;
+ guint32 abs_usecs;
+
+ gboolean isControlPDU;
+ guint16 SN;
+ guint16 isResegmented;
+ guint16 ACKNo;
+ #define MAX_NACKs 128
+ guint16 noOfNACKs;
+ guint16 NACKs[MAX_NACKs];
+
+ guint16 ueid;
+ guint16 channelType;
+ guint16 channelId;
+ guint8 rlcMode;
+ guint8 direction;
+};
+
+/* A collection of channels that may be found in one frame. Used when working out
+ which channel(s) are present in a frame. */
+typedef struct _th_t {
+ int num_hdrs;
+ #define MAX_SUPPORTED_CHANNELS 8
+ rlc_lte_tap_info *rlchdrs[MAX_SUPPORTED_CHANNELS];
+} th_t;
+
+struct rlc_graph {
+ /* List of segments to show */
+ struct rlc_segment *segments;
+ struct rlc_segment *last_segment;
+
+ /* These are filled in with the channel/direction this graph is showing */
+ gboolean channelSet;
+ guint16 ueid;
+ guint16 channelType;
+ guint16 channelId;
+ guint8 rlcMode;
+ guint8 direction;
+
+ /* Lists of elements to draw. N.B. GTK version only. */
+ struct element_list *elists;
+};
+
+gboolean rlc_graph_segment_list_get(capture_file *cf, struct rlc_graph *tg, gboolean stream_known,
+ char **err_string, gboolean *free_err_string);
+void rlc_graph_segment_list_free(struct rlc_graph * );
+
+
+
+int compare_rlc_headers(guint16 ueid1, guint16 channelType1, guint16 channelId1, guint8 rlcMode1, guint8 direction1,
+ guint16 ueid2, guint16 channelType2, guint16 channelId2, guint8 rlcMode2, guint8 direction2,
+ gboolean isControlFrame);
+rlc_lte_tap_info *select_rlc_lte_session(capture_file *cf, struct rlc_segment *hdrs,
+ gchar **err_msg, gboolean *free_err_msg);
+int rlc_lte_tap_for_graph_data(void *pct, packet_info *pinfo _U_, epan_dissect_t *edt _U_, const void *vip);
+
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+
+#endif