Add commo-channel stats.
[obnox/wireshark/wip.git] / gtk / rlc_lte_stat_dlg.c
1 /* rlc_lte_stat_dlg.c
2  * Copyright 2010 Martin Mathieson
3  *
4  * $Id$
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
23  */
24
25
26 /* TODO:
27    - per-channel graph tap
28    - apply top-level filter (e.g. to tap only one sector)
29    - common channel stats
30 */
31
32 #ifdef HAVE_CONFIG_H
33 #include "config.h"
34 #endif
35
36 #ifdef HAVE_SYS_TYPES_H
37 #include <sys/types.h>
38 #endif
39
40 #include <string.h>
41
42 #include <gtk/gtk.h>
43
44 #include "gtk/gtkglobals.h"
45
46 #include <epan/packet.h>
47 #include <epan/packet_info.h>
48 #include <epan/tap.h>
49 #include <epan/dissectors/packet-rlc-lte.h>
50
51 #include "../register.h"
52 #include "../simple_dialog.h"
53 #include "../stat_menu.h"
54
55 #include "gtk/dlg_utils.h"
56 #include "gtk/gui_stat_menu.h"
57 #include "gtk/gui_utils.h"
58 #include "gtk/help_dlg.h"
59 #include "gtk/main.h"
60
61 /**********************************************/
62 /* Table column identifiers and title strings */
63
64 enum {
65     UEID_COLUMN,
66     UL_FRAMES_COLUMN,
67     UL_BYTES_COLUMN,
68     UL_NACKS_COLUMN,
69     DL_FRAMES_COLUMN,
70     DL_BYTES_COLUMN,
71     DL_NACKS_COLUMN,
72     UE_TABLE_COLUMN,
73     NUM_UE_COLUMNS
74 };
75
76 enum {
77     CHANNEL_NAME,
78     CHANNEL_MODE,
79     CHANNEL_UL_FRAMES,
80     CHANNEL_UL_BYTES,
81     CHANNEL_UL_ACKS,
82     CHANNEL_UL_NACKS,
83     CHANNEL_DL_FRAMES,
84     CHANNEL_DL_BYTES,
85     CHANNEL_DL_ACKS,
86     CHANNEL_DL_NACKS,
87     CHANNEL_TABLE_COLUMN,
88     NUM_CHANNEL_COLUMNS
89 };
90
91 static const gchar *ue_titles[] = { "UEId",
92                                     "UL Frames", "UL Bytes", "UL NACKs",
93                                     "DL Frames", "DL Bytes", "DL NACKs"};
94
95 static const gchar *channel_titles[] = { "", "Mode",
96                                          "UL Frames", "UL Bytes", "UL ACKs", "UL NACKs",
97                                          "DL Frames", "DL Bytes", "DL ACKs", "DL NACKs"};
98
99 /* Stats kept for one channel */
100 typedef struct rlc_channel_stats {
101     guint8   inUse;
102     guint8   rlcMode;
103     guint16  channelType;
104     guint16  channelId;
105
106     guint32 UL_frames;
107     guint32 UL_bytes;
108     guint32 DL_frames;
109     guint32 DL_bytes;
110
111     guint32 UL_acks;
112     guint32 UL_nacks;
113
114     guint32 DL_acks;
115     guint32 DL_nacks;
116
117     GtkTreeIter iter;
118     gboolean iter_valid;
119 } rlc_channel_stats;
120
121 /* Stats for one UE */
122 typedef struct rlc_lte_row_data {
123     /* Key for matching this row */
124     guint16 ueid;
125
126     gboolean is_predefined_data;
127
128     guint32 UL_frames;
129     guint32 UL_total_bytes;
130     guint32 UL_total_nacks;
131
132     guint32 DL_frames;
133     guint32 DL_total_bytes;
134     guint32 DL_total_nacks;
135
136     rlc_channel_stats CCCH_stats;
137     rlc_channel_stats srb_stats[2];
138     rlc_channel_stats drb_stats[32];
139 } rlc_lte_row_data;
140
141
142 /* Common channel stats */
143 typedef struct rlc_lte_common_stats {
144     guint32 bcch_frames;
145     guint32 bcch_bytes;
146     guint32 pcch_frames;
147     guint32 pcch_bytes;
148 } rlc_lte_common_stats;
149
150 static rlc_lte_common_stats common_stats;
151
152 static GtkWidget *rlc_lte_common_bcch_frames;
153 static GtkWidget *rlc_lte_common_bcch_bytes;
154 static GtkWidget *rlc_lte_common_pcch_frames;
155 static GtkWidget *rlc_lte_common_pcch_bytes;
156
157 /* One row/UE in the UE table */
158 typedef struct rlc_lte_ep {
159     struct rlc_lte_ep* next;
160     struct rlc_lte_row_data stats;
161     GtkTreeIter iter;                                         
162     gboolean iter_valid;
163 } rlc_lte_ep_t;
164
165
166 /* Top-level dialog and labels */
167 static GtkWidget  *rlc_lte_stat_dlg_w = NULL;
168 static GtkWidget  *rlc_lte_stat_common_channel_lb = NULL;
169 static GtkWidget  *rlc_lte_stat_ues_lb = NULL;
170 static GtkWidget  *rlc_lte_stat_channels_lb = NULL;
171 static GtkWidget  *rlc_lte_stat_filter_buttons_lb = NULL;
172
173 GtkWidget         *ul_filter_bt;
174 GtkWidget         *dl_filter_bt;
175 GtkWidget         *uldl_filter_bt;
176
177 GtkWidget         *show_only_control_pdus_cb;
178 GtkWidget         *sn_filter_lb;
179 GtkWidget         *sn_filter_te;
180
181 gboolean          s_show_mac = FALSE;
182
183
184
185 /* Used to keep track of whole RLC LTE statistics window */
186 typedef struct rlc_lte_stat_t {
187     GtkTreeView   *ue_table;
188     rlc_lte_ep_t  *ep_list;
189     guint32       total_frames;
190
191     GtkTreeView   *channel_table;
192 } rlc_lte_stat_t;
193
194
195 /* Show filter controls appropriate to current selection */
196 static void enable_filter_controls(guint8 enabled)
197 {
198     gtk_widget_set_sensitive(ul_filter_bt, enabled);
199     gtk_widget_set_sensitive(dl_filter_bt, enabled);
200     gtk_widget_set_sensitive(uldl_filter_bt, enabled);
201 }
202
203
204
205 /* Reset the statistics window */
206 static void
207 rlc_lte_stat_reset(void *phs)
208 {
209     rlc_lte_stat_t* rlc_lte_stat = (rlc_lte_stat_t *)phs;
210     rlc_lte_ep_t* list = rlc_lte_stat->ep_list;
211     gchar title[256];
212     GtkListStore *store;
213
214     /* Set the title */
215     if (rlc_lte_stat_dlg_w != NULL) {
216         g_snprintf(title, sizeof(title), "Wireshark: LTE RLC Traffic Statistics: %s",
217                    cf_get_display_name(&cfile));
218         gtk_window_set_title(GTK_WINDOW(rlc_lte_stat_dlg_w), title);
219     }
220
221     g_snprintf(title, sizeof(title), "0 UEs");
222     gtk_frame_set_label(GTK_FRAME(rlc_lte_stat_ues_lb), title);
223
224     rlc_lte_stat->total_frames = 0;
225     memset(&common_stats, 0, sizeof(common_stats));
226
227     /* Remove all entries from the UE list */
228     store = GTK_LIST_STORE(gtk_tree_view_get_model(rlc_lte_stat->ue_table));
229     gtk_list_store_clear(store);
230
231     if (!list) {
232         return;
233     }
234
235     rlc_lte_stat->ep_list = NULL;
236 }
237
238
239 /* Allocate a rlc_lte_ep_t struct to store info for new UE */
240 static rlc_lte_ep_t* alloc_rlc_lte_ep(struct rlc_lte_tap_info *si, packet_info *pinfo _U_)
241 {
242     rlc_lte_ep_t* ep;
243     int n;
244
245     if (!si) {
246         return NULL;
247     }
248
249     if (!(ep = g_malloc(sizeof(rlc_lte_ep_t)))) {
250         return NULL;
251     }
252
253     /* Copy SI data into ep->stats */
254     ep->stats.ueid = si->ueid;
255
256     /* Counts for new UE are all 0 */
257     ep->stats.UL_frames = 0;
258     ep->stats.DL_frames = 0;
259     ep->stats.UL_total_bytes = 0;
260     ep->stats.DL_total_bytes = 0;
261     ep->stats.UL_total_nacks = 0;
262     ep->stats.DL_total_nacks = 0;
263
264     memset(&ep->stats.CCCH_stats, 0, sizeof(rlc_channel_stats));
265     for (n=0; n < 2; n++) {
266         memset(&ep->stats.srb_stats[n], 0, sizeof(rlc_channel_stats));
267     }
268     for (n=0; n < 32; n++) {
269         memset(&ep->stats.drb_stats[n], 0, sizeof(rlc_channel_stats));
270     }
271
272     ep->next = NULL;
273     ep->iter_valid = FALSE;
274
275     return ep;
276 }
277
278
279 /* Return string for RLC mode for display */
280 static const char *print_rlc_channel_mode(guint8 mode)
281 {
282     static char unknown[16];
283
284     switch (mode) {
285         case RLC_TM_MODE:  return "TM";
286         case RLC_UM_MODE:  return "UM";
287         case RLC_AM_MODE:  return "AM";
288         case RLC_PREDEF:   return "Predef";
289
290         default:
291             g_snprintf(unknown, 32, "Unknown (%u)", mode);
292             return unknown;
293     }
294 }
295
296
297 /* Process stat struct for a RLC LTE frame */
298 static int
299 rlc_lte_stat_packet(void *phs, packet_info *pinfo, epan_dissect_t *edt _U_,
300                     const void *phi)
301 {
302     /* Get reference to stat window instance */
303     rlc_lte_stat_t *hs = (rlc_lte_stat_t *)phs;
304     rlc_lte_ep_t *tmp = NULL, *te = NULL;
305     rlc_channel_stats *channel_stats = NULL;
306
307     /* Cast tap info struct */
308     struct rlc_lte_tap_info *si = (struct rlc_lte_tap_info *)phi;
309
310     /* Need this */
311     if (!hs) {
312         return 0;
313     }
314
315     /* Are we ignoring RLC frames that were found in MAC frames, or only those
316        that were logged separately? */
317     if (!s_show_mac && si->loggedInMACFrame) {
318         return 0;
319     }
320
321     /* Inc top-level frame count */
322     hs->total_frames++;
323
324     /* Common channel stats */
325     switch (si->channelType) {
326         case CHANNEL_TYPE_BCCH:
327             common_stats.bcch_frames++;
328             common_stats.bcch_bytes += si->pduLength;
329             return 1;
330
331         case CHANNEL_TYPE_PCCH:
332             common_stats.pcch_frames++;
333             common_stats.pcch_bytes += si->pduLength;
334             return 1;
335
336         default:
337             break;
338     }
339
340     /* For per-UE data, must create a new row if none already existing */
341     if (!hs->ep_list) {
342         /* Allocate new list */
343         hs->ep_list = alloc_rlc_lte_ep(si, pinfo);
344         /* Make it the first/only entry */
345         te = hs->ep_list;
346     } else {
347         /* Look among existing rows for this UEId */
348         for (tmp = hs->ep_list;(tmp != NULL); tmp = tmp->next) {
349             if (tmp->stats.ueid == si->ueid) {
350                 te = tmp;
351                 break;
352             }
353         }
354
355         /* Not found among existing, so create a new one anyway */
356         if (te == NULL) {
357             if ((te = alloc_rlc_lte_ep(si, pinfo))) {
358                 /* Add new item to end of list */
359                 rlc_lte_ep_t *p = hs->ep_list;
360                 while (p->next) {
361                     p = p->next;
362                 }
363                 p->next = te;
364                 te->next = NULL;
365             }
366         }
367     }
368
369     /* Really should have a row pointer by now */
370     if (!te) {
371         return 0;
372     }
373
374     /* Update entry with details from si */
375     te->stats.ueid = si->ueid;
376
377     /* Top-level traffic stats */
378     if (si->direction == DIRECTION_UPLINK) {
379         te->stats.UL_frames++;
380         te->stats.UL_total_bytes += si->pduLength;
381     }
382     else {
383         te->stats.DL_frames++;
384         te->stats.DL_total_bytes += si->pduLength;
385     }
386
387     /* Find channel struct */
388     switch (si->channelType) {
389         case CHANNEL_TYPE_CCCH:
390             channel_stats = &te->stats.CCCH_stats;
391             break;
392
393         case CHANNEL_TYPE_SRB:
394             channel_stats = &te->stats.srb_stats[si->channelId-1];
395             break;
396
397         case CHANNEL_TYPE_DRB:
398             channel_stats = &te->stats.drb_stats[si->channelId-1];
399             break;
400
401         default:
402             /* Shouldn't get here... */
403             return 0;
404     }
405
406     if (channel_stats != NULL) {
407         channel_stats->inUse = TRUE;
408         channel_stats->iter_valid = FALSE;
409         channel_stats->rlcMode = si->rlcMode;
410         channel_stats->channelType = si->channelType;
411         channel_stats->channelId = si->channelId;
412     }
413
414     if (si->direction == DIRECTION_UPLINK) {
415         channel_stats->UL_frames++;
416         channel_stats->UL_bytes += si->pduLength;
417         channel_stats->UL_nacks += si->noOfNACKs;
418         if (si->isControlPDU) {
419             channel_stats->UL_acks++;
420         }
421         te->stats.UL_total_nacks += si->noOfNACKs;
422     }
423     else {
424         channel_stats->DL_frames++;
425         channel_stats->DL_bytes += si->pduLength;
426         channel_stats->DL_nacks += si->noOfNACKs;
427         if (si->isControlPDU) {
428             channel_stats->DL_acks++;
429         }
430         te->stats.DL_total_nacks += si->noOfNACKs;
431     }
432
433     return 1;
434 }
435
436
437 /* The channels for any UE would need to be re-added to the list */
438 static void invalidate_channel_iters(rlc_lte_stat_t *hs)
439 {
440     gint n;
441     rlc_lte_ep_t *ep = hs->ep_list;
442
443     while (ep) {
444         ep->stats.CCCH_stats.iter_valid = FALSE;
445         for (n=0; n < 2; n++) {
446             ep->stats.srb_stats[n].iter_valid = FALSE;
447         }
448         for (n=0; n < 32; n++) {
449             ep->stats.drb_stats[n].iter_valid = FALSE;
450         }
451
452         ep = ep->next;
453     }
454 }
455
456
457 /* Draw the channels table according to the current UE selection */
458 static void
459 rlc_lte_channels(rlc_lte_ep_t *rlc_stat_ep, rlc_lte_stat_t *hs)
460 {
461     GtkListStore *channels_store = GTK_LIST_STORE(gtk_tree_view_get_model(hs->channel_table));
462     rlc_channel_stats *channel_stats;
463     char buff[32];
464     int n;
465
466     /* Clear any existing rows */
467     gtk_list_store_clear(channels_store);
468     invalidate_channel_iters(hs);
469
470     if (rlc_stat_ep == NULL) {
471         return;
472     }
473
474     /* Add one row for each channel */
475
476     /* CCCH */
477     channel_stats = &rlc_stat_ep->stats.CCCH_stats;
478     if (channel_stats->inUse) {
479
480         if (!channel_stats->iter_valid) {
481             /* Add to list control if not drawn this UE before */
482             gtk_list_store_append(channels_store, &channel_stats->iter);
483             channel_stats->iter_valid = TRUE;
484         }
485
486         /* Set each column for this row */
487         gtk_list_store_set(channels_store, &channel_stats->iter,
488                            CHANNEL_NAME, "CCCH",
489                            CHANNEL_MODE, print_rlc_channel_mode(channel_stats->rlcMode),
490                            CHANNEL_UL_FRAMES, channel_stats->UL_frames,
491                            CHANNEL_UL_BYTES, channel_stats->UL_bytes,
492                            CHANNEL_DL_FRAMES, channel_stats->DL_frames,
493                            CHANNEL_DL_BYTES, channel_stats->DL_bytes,
494                            CHANNEL_TABLE_COLUMN, channel_stats,
495                            -1);
496     }
497
498
499     /* SRB */
500     for (n=0; n < 2; n++) {
501         channel_stats = &rlc_stat_ep->stats.srb_stats[n];
502         if (channel_stats->inUse) {
503
504             if (!channel_stats->iter_valid) {
505                 /* Add to list control if not drawn this UE before */
506                 gtk_list_store_append(channels_store, &channel_stats->iter);
507                 channel_stats->iter_valid = TRUE;
508             }
509
510             g_snprintf(buff, 32, "SRB-%u", n+1);
511
512             /* Set each column for this row */
513             gtk_list_store_set(channels_store, &channel_stats->iter,
514                                CHANNEL_NAME, buff,
515                                CHANNEL_MODE, print_rlc_channel_mode(channel_stats->rlcMode),
516                                CHANNEL_UL_FRAMES, channel_stats->UL_frames,
517                                CHANNEL_UL_BYTES, channel_stats->UL_bytes,
518                                CHANNEL_UL_ACKS, channel_stats->UL_acks,
519                                CHANNEL_UL_NACKS, channel_stats->UL_nacks,
520                                CHANNEL_DL_FRAMES, channel_stats->DL_frames,
521                                CHANNEL_DL_BYTES, channel_stats->DL_bytes,
522                                CHANNEL_DL_ACKS, channel_stats->DL_acks,
523                                CHANNEL_DL_NACKS, channel_stats->DL_nacks,
524                                CHANNEL_TABLE_COLUMN, channel_stats,
525                                -1);
526         }
527     }
528
529
530     /* DRB */
531     for (n=0; n < 32; n++) {
532         channel_stats = &rlc_stat_ep->stats.drb_stats[n];
533         if (channel_stats->inUse) {
534
535             if (!channel_stats->iter_valid) {
536                 /* Add to list control if not drawn this UE before */
537                 gtk_list_store_append(channels_store, &channel_stats->iter);
538                 channel_stats->iter_valid = TRUE;
539             }
540
541             g_snprintf(buff, 32, "DRB-%u", n+1);
542
543             /* Set each column for this row */
544             gtk_list_store_set(channels_store, &channel_stats->iter,
545                                CHANNEL_NAME, buff,
546                                CHANNEL_MODE, print_rlc_channel_mode(channel_stats->rlcMode),
547                                CHANNEL_UL_FRAMES, channel_stats->UL_frames,
548                                CHANNEL_UL_BYTES, channel_stats->UL_bytes,
549                                CHANNEL_UL_ACKS, channel_stats->UL_acks,
550                                CHANNEL_UL_NACKS, channel_stats->UL_nacks,
551                                CHANNEL_DL_FRAMES, channel_stats->DL_frames,
552                                CHANNEL_DL_BYTES, channel_stats->DL_bytes,
553                                CHANNEL_DL_ACKS, channel_stats->DL_acks,
554                                CHANNEL_DL_NACKS, channel_stats->DL_nacks,
555                                CHANNEL_TABLE_COLUMN, channel_stats,
556                                -1);
557         }
558     }
559 }
560
561
562
563 /* (Re)draw the whole dialog window */
564 static void
565 rlc_lte_stat_draw(void *phs)
566 {
567     gchar   buff[32];
568     guint16 number_of_ues = 0;
569     gchar title[256];
570
571     /* Look up the statistics window */
572     rlc_lte_stat_t *hs = (rlc_lte_stat_t *)phs;
573     rlc_lte_ep_t* list = hs->ep_list, *tmp = 0;
574
575     GtkListStore *ues_store;
576     GtkTreeSelection *sel;
577     GtkTreeModel *model;
578     GtkTreeIter iter;
579
580     /* Common channel data */
581     g_snprintf(buff, sizeof(buff), "BCCH Frames: %u", common_stats.bcch_frames);
582     gtk_label_set_text(GTK_LABEL(rlc_lte_common_bcch_frames), buff);
583     g_snprintf(buff, sizeof(buff), "BCCH Bytes: %u", common_stats.bcch_bytes);
584     gtk_label_set_text(GTK_LABEL(rlc_lte_common_bcch_bytes), buff);
585     g_snprintf(buff, sizeof(buff), "PCCH Frames: %u", common_stats.pcch_frames);
586     gtk_label_set_text(GTK_LABEL(rlc_lte_common_pcch_frames), buff);
587     g_snprintf(buff, sizeof(buff), "PCCH Bytes: %u", common_stats.pcch_bytes);
588     gtk_label_set_text(GTK_LABEL(rlc_lte_common_pcch_bytes), buff);
589
590     /* Per-UE table entries */
591     ues_store = GTK_LIST_STORE(gtk_tree_view_get_model(hs->ue_table));
592
593     /* Set title that shows how many UEs currently in table */
594     for (tmp = list; (tmp!=NULL); tmp=tmp->next, number_of_ues++);
595     g_snprintf(title, sizeof(title), "%u UEs", number_of_ues);
596     gtk_frame_set_label(GTK_FRAME(rlc_lte_stat_ues_lb), title);
597
598     /* Update title to include number of UEs and frames */
599     g_snprintf(title, sizeof(title), "Wireshark: LTE RLC Traffic Statistics: %s (%u UEs, %u frames)",
600                cf_get_display_name(&cfile),
601                number_of_ues,
602                hs->total_frames);
603     gtk_window_set_title(GTK_WINDOW(rlc_lte_stat_dlg_w), title);
604
605
606     /* For each row/UE in the model */
607     for (tmp = list; tmp; tmp=tmp->next) {
608         if (tmp->iter_valid != TRUE) {
609             /* Add to list control if not drawn this UE before */
610             gtk_list_store_append(ues_store, &tmp->iter);
611             tmp->iter_valid = TRUE;
612         }
613
614         /* Set each column for this row */
615         gtk_list_store_set(ues_store, &tmp->iter,
616                            UEID_COLUMN, tmp->stats.ueid,
617                            UL_FRAMES_COLUMN, tmp->stats.UL_frames,
618                            UL_BYTES_COLUMN, tmp->stats.UL_total_bytes,
619                            UL_NACKS_COLUMN, tmp->stats.UL_total_nacks,
620                            DL_FRAMES_COLUMN, tmp->stats.DL_frames,
621                            DL_BYTES_COLUMN, tmp->stats.DL_total_bytes,
622                            DL_NACKS_COLUMN, tmp->stats.DL_total_nacks,
623                            UE_TABLE_COLUMN, tmp,
624                            -1);
625     }
626
627     /* If there is a UE selected, update its counters in details window */
628     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(hs->ue_table));
629     if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
630         rlc_lte_ep_t *ep;
631
632         gtk_tree_model_get(model, &iter, UE_TABLE_COLUMN, &ep, -1);
633         rlc_lte_channels(ep, hs);
634     }
635 }
636
637
638 /* What to do when a UE list item is selected/unselected */
639 static void rlc_lte_select_ue_cb(GtkTreeSelection *sel, gpointer data)
640 {
641     rlc_lte_ep_t   *ep;
642     GtkTreeModel   *model;
643     GtkTreeIter    iter;
644
645     if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
646         /* Show details of selected UE */
647         gtk_tree_model_get(model, &iter, UE_TABLE_COLUMN, &ep, -1);
648         rlc_lte_channels(ep, (rlc_lte_stat_t*)data);
649     }
650     else {
651         rlc_lte_channels(NULL, (rlc_lte_stat_t*)data);
652     }
653
654     /* Channel will be deselected */
655     enable_filter_controls(FALSE);
656 }
657
658
659 /* What to do when a channel list item is selected/unselected */
660 static void rlc_lte_select_channel_cb(GtkTreeSelection *sel, gpointer data _U_)
661 {
662     rlc_lte_ep_t   *ep;
663     GtkTreeModel   *model;
664     GtkTreeIter    iter;
665
666     if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
667         /* Enable buttons */
668         enable_filter_controls(TRUE);
669
670         /* Show details of selected UE */
671         gtk_tree_model_get(model, &iter, UE_TABLE_COLUMN, &ep, -1);
672     }
673     else {
674         /* Disable buttons */
675         enable_filter_controls(FALSE);
676     }
677 }
678
679
680 /* Destroy the stats window */
681 static void win_destroy_cb(GtkWindow *win _U_, gpointer data)
682 {
683     rlc_lte_stat_t *hs = (rlc_lte_stat_t *)data;
684
685     protect_thread_critical_region();
686     remove_tap_listener(hs);
687     unprotect_thread_critical_region();
688
689     if (rlc_lte_stat_dlg_w != NULL) {
690         window_destroy(rlc_lte_stat_dlg_w);
691         rlc_lte_stat_dlg_w = NULL;
692     }
693     rlc_lte_stat_reset(hs);
694     g_free(hs);
695 }
696
697
698
699 static void
700 toggle_show_mac(GtkWidget *widget, gpointer data _U_)
701 {
702     /* Read state */
703     s_show_mac = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
704
705     /* Retap */
706     cf_retap_packets(&cfile);
707 }
708
709
710
711 /* Check that a UE / channel is currently selected.  If so, fill in out
712    parameters with details of channel.
713    Return TRUE if a channel is selected */
714 static int get_channel_selection(rlc_lte_stat_t *hs,
715                                  guint16 *ueid, guint8 *rlcMode,
716                                  guint16 *channelType, guint16 *channelId)
717 {
718     GtkTreeModel *model;
719     GtkTreeIter iter;
720
721     /* Check UE selection */
722     GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(hs->ue_table));
723     if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
724         rlc_lte_ep_t *ep;
725
726         gtk_tree_model_get(model, &iter, UE_TABLE_COLUMN, &ep, -1);
727         *ueid = ep->stats.ueid;
728
729         /* Check channel selection */
730         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(hs->channel_table));
731         if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
732             /* Find details of selected channel */
733             rlc_channel_stats *channel_stats;
734             gtk_tree_model_get(model, &iter, CHANNEL_TABLE_COLUMN, &channel_stats, -1);
735             *rlcMode = channel_stats->rlcMode;
736             *channelType = channel_stats->channelType;
737             *channelId = channel_stats->channelId;
738         }
739         else {
740             return FALSE;
741         }
742     }
743     else {
744         return FALSE;
745     }
746
747     return TRUE;
748 }
749
750
751 /* Build and set a display filter to match the given channel settings */
752 typedef enum ChannelDirection_t {UL_Only, DL_Only, UL_and_DL} ChannelDirection_t;
753 static void set_channel_filter_expression(guint16  ueid,
754                                           guint8   rlcMode,
755                                           guint16  channelType,
756                                           guint16  channelId,
757                                           ChannelDirection_t channelDirection,
758                                           gint     filterOnSN,
759                                           gint     statusOnlyPDUs)
760 {
761     #define MAX_FILTER_LEN 1024
762     static char buffer[MAX_FILTER_LEN];
763     int offset = 0;
764
765     /* Should we exclude MAC frames? */
766     if (!s_show_mac) {
767         offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, "not mac-lte and ");
768     }
769
770     /* UEId */
771     offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, "(rlc-lte.ueid == %u) and ", ueid);
772     offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, "(rlc-lte.channel-type == %u)", channelType);
773
774     /* Channel-id for srb/drb */
775     if ((channelType == CHANNEL_TYPE_SRB) || (channelType == CHANNEL_TYPE_DRB)) {
776         offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, " and (rlc-lte.channel-id == %u)", channelId);
777     }
778
779     /* Direction (also depends upon RLC mode) */
780     switch (channelDirection) {
781         case UL_Only:
782             if (rlcMode == RLC_AM_MODE) {
783                 /* Always filter status PDUs */
784                 offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
785                                      " and (rlc-lte.direction == 1 and rlc-lte.am.frame_type == 0)");
786                 if (!statusOnlyPDUs) {
787                     /* Also filter data */
788                     offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
789                                          " or (rlc-lte.direction == 0 and rlc-lte.am.frame_type == 1)");
790                 }
791             }
792             else {
793                 offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, " and (rlc-lte.direction == 0)");
794             }
795             break;
796         case DL_Only:
797             if (rlcMode == RLC_AM_MODE) {
798                 /* Always filter status PDs */
799                 offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
800                                      " and (rlc-lte.direction == 0 and rlc-lte.am.frame_type == 0)");
801                 if (!statusOnlyPDUs) {
802                     /* Also filter data */
803                     offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
804                                          " or (rlc-lte.direction == 1 and rlc-lte.am.frame_type == 1)");
805                 }
806             }
807             else {
808                 offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, " and (rlc-lte.direction == 1)");
809             }
810             break;
811         case UL_and_DL:
812             if (rlcMode == RLC_AM_MODE) {
813                 if (statusOnlyPDUs) {
814                     g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, " and (rlc-lte.am.frame_type == 0)");
815                 }
816             }
817
818         default:
819             break;
820     }
821
822     /* Filter on a specific sequence number */
823     if (filterOnSN != -1) {
824         switch (rlcMode) {
825             case RLC_AM_MODE:
826                 offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
827                                      " and ((rlc-lte.am.fixed.sn == %u) or "
828                                      "(rlc-lte.am.ack-sn == %u) or "
829                                      "(rlc-lte.am.nack-sn == %u))",
830                                      filterOnSN, filterOnSN, filterOnSN);
831                 break;
832             case RLC_UM_MODE:
833                 offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
834                                      " and (rlc-lte.um.sn == %u)", filterOnSN);
835                 break;
836
837             default:
838                 break;
839         }
840     }
841
842     /* Set its value to our new string */
843     gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), buffer);
844
845     /* Run the filter */
846     main_filter_packets(&cfile, buffer, TRUE);
847 }
848
849 /* Respond to UL filter button being clicked by building and using filter */
850 static void ul_filter_clicked(GtkWindow *win _U_, rlc_lte_stat_t* hs)
851 {
852     guint16  ueid;
853     guint8   rlcMode;
854     guint16  channelType;
855     guint16  channelId;
856     int      sn = -1;
857     const gchar *sn_string = "";
858
859     /* Read SN to filter on (if present) */
860     sn_string = gtk_entry_get_text(GTK_ENTRY(sn_filter_te));
861     if (strlen(sn_string) > 0) {
862         sn = atoi(sn_string);
863     }
864
865     if (!get_channel_selection(hs, &ueid, &rlcMode, &channelType, &channelId)) {
866         return;
867     }
868
869     set_channel_filter_expression(ueid, rlcMode, channelType, channelId, UL_Only, sn,
870                                   gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(show_only_control_pdus_cb)));
871 }
872
873 /* Respond to DL filter button being clicked by building and using filter */
874 static void dl_filter_clicked(GtkWindow *win _U_, rlc_lte_stat_t* hs)
875 {
876     guint16  ueid;
877     guint8   rlcMode;
878     guint16  channelType;
879     guint16  channelId;
880     int      sn = -1;
881     const gchar *sn_string = "";
882
883     /* Read SN to filter on (if present) */
884     sn_string = gtk_entry_get_text(GTK_ENTRY(sn_filter_te));
885     if (strlen(sn_string) > 0) {
886         sn = atoi(sn_string);
887     }
888
889     if (!get_channel_selection(hs, &ueid, &rlcMode, &channelType, &channelId)) {
890         return;
891     }
892
893     set_channel_filter_expression(ueid, rlcMode, channelType, channelId, DL_Only, sn,
894                                   gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(show_only_control_pdus_cb)));
895 }
896
897 /* Respond to UL/DL filter button being clicked by building and using filter */
898 static void uldl_filter_clicked(GtkWindow *win _U_, rlc_lte_stat_t* hs)
899 {
900     guint16  ueid;
901     guint8   rlcMode;
902     guint16  channelType;
903     guint16  channelId;
904     int      sn = -1;
905     const gchar *sn_string = "";
906
907     /* Read SN to filter on (if present) */
908     sn_string = gtk_entry_get_text(GTK_ENTRY(sn_filter_te));
909     if (strlen(sn_string) > 0) {
910         sn = atoi(sn_string);
911     }
912
913     if (!get_channel_selection(hs, &ueid, &rlcMode, &channelType, &channelId)) {
914         return;
915     }
916
917     set_channel_filter_expression(ueid, rlcMode, channelType, channelId, UL_and_DL, sn,
918                                   gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(show_only_control_pdus_cb)));
919 }
920
921
922 /* Create a new RLC LTE stats dialog */
923 static void rlc_lte_stat_dlg_create(void)
924 {
925     rlc_lte_stat_t    *hs;
926     GString           *error_string;
927     GtkWidget         *ues_scrolled_window;
928     GtkWidget         *channels_scrolled_window;
929     GtkWidget         *bbox;
930     GtkWidget         *top_level_vbox;
931
932     GtkWidget         *common_row_hbox;
933     GtkWidget         *show_mac_cb;
934     GtkWidget         *ues_vb;
935     GtkWidget         *channels_vb;
936     GtkWidget         *filter_vb;
937     GtkWidget         *filter_buttons_hb;
938     GtkWidget         *sn_filter_hb;
939
940     GtkWidget         *close_bt;
941     GtkWidget         *help_bt;
942
943     GtkListStore      *store;
944
945     GtkTreeView       *tree_view;
946     GtkCellRenderer   *renderer;
947     GtkTreeViewColumn *column;
948     GtkTreeSelection  *sel;
949     gchar title[256];
950     gint i;
951
952     /* Create dialog */
953     hs = g_malloc(sizeof(rlc_lte_stat_t));
954     hs->ep_list = NULL;
955
956     /* Set title */
957     g_snprintf(title, sizeof(title), "Wireshark: LTE RLC Statistics: %s",
958                cf_get_display_name(&cfile));
959     rlc_lte_stat_dlg_w = window_new_with_geom(GTK_WINDOW_TOPLEVEL, title, "LTE RLC Statistics");
960
961     /* Window size */
962     gtk_window_set_default_size(GTK_WINDOW(rlc_lte_stat_dlg_w), 750, 300);
963
964     /* Will stack widgets vertically inside dlg */
965     top_level_vbox = gtk_vbox_new(FALSE, 3);       /* FALSE = not homogeneous */
966     gtk_container_add(GTK_CONTAINER(rlc_lte_stat_dlg_w), top_level_vbox);
967
968     gtk_container_set_border_width(GTK_CONTAINER(top_level_vbox), 6);
969     gtk_widget_show(top_level_vbox);
970
971     /**********************************************/
972     /* Exclude-MAC checkbox                       */
973     show_mac_cb = gtk_check_button_new_with_mnemonic("Show RLC PDUs found inside logged MAC frames");
974     gtk_container_add(GTK_CONTAINER(top_level_vbox), show_mac_cb);
975     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(show_mac_cb), FALSE);
976     /* TODO: add tooltip */
977     g_signal_connect(show_mac_cb, "toggled", G_CALLBACK(toggle_show_mac), hs);
978
979
980     /**********************************************/
981     /* Common Channel data                        */
982     /**********************************************/
983     rlc_lte_stat_common_channel_lb = gtk_frame_new("Common Channel Data");
984
985     /* Will add BCCH and PCCH counters into one row */
986     common_row_hbox = gtk_hbox_new(FALSE, 0);
987     gtk_container_add(GTK_CONTAINER(rlc_lte_stat_common_channel_lb), common_row_hbox);
988     gtk_container_set_border_width(GTK_CONTAINER(common_row_hbox), 5);
989
990     gtk_box_pack_start(GTK_BOX(top_level_vbox), rlc_lte_stat_common_channel_lb, FALSE, FALSE, 0);
991
992     /* Create labels (that will hold label and counter value) */
993     rlc_lte_common_bcch_frames = gtk_label_new("BCCH Frames:");
994     gtk_misc_set_alignment(GTK_MISC(rlc_lte_common_bcch_frames), 0.0f, .5f);
995     gtk_container_add(GTK_CONTAINER(common_row_hbox), rlc_lte_common_bcch_frames);
996     gtk_widget_show(rlc_lte_common_bcch_frames);
997
998     rlc_lte_common_bcch_bytes = gtk_label_new("BCCH Bytes:");
999     gtk_misc_set_alignment(GTK_MISC(rlc_lte_common_bcch_bytes), 0.0f, .5f);
1000     gtk_container_add(GTK_CONTAINER(common_row_hbox), rlc_lte_common_bcch_bytes);
1001     gtk_widget_show(rlc_lte_common_bcch_bytes);
1002
1003     rlc_lte_common_pcch_frames = gtk_label_new("PCCH Frames:");
1004     gtk_misc_set_alignment(GTK_MISC(rlc_lte_common_pcch_frames), 0.0f, .5f);
1005     gtk_container_add(GTK_CONTAINER(common_row_hbox), rlc_lte_common_pcch_frames);
1006     gtk_widget_show(rlc_lte_common_pcch_frames);
1007
1008     rlc_lte_common_pcch_bytes = gtk_label_new("PCCH Bytes:");
1009     gtk_misc_set_alignment(GTK_MISC(rlc_lte_common_pcch_bytes), 0.0f, .5f);
1010     gtk_container_add(GTK_CONTAINER(common_row_hbox), rlc_lte_common_pcch_bytes);
1011     gtk_widget_show(rlc_lte_common_pcch_bytes);
1012
1013
1014     /**********************************************/
1015     /* UE List                                    */
1016     /**********************************************/
1017
1018     rlc_lte_stat_ues_lb = gtk_frame_new("UE Data (0 UEs)");
1019     ues_vb = gtk_vbox_new(FALSE, 0);
1020     gtk_container_add(GTK_CONTAINER(rlc_lte_stat_ues_lb), ues_vb);
1021     gtk_container_set_border_width(GTK_CONTAINER(ues_vb), 5);
1022
1023     ues_scrolled_window = scrolled_window_new(NULL, NULL);
1024     gtk_box_pack_start(GTK_BOX(ues_vb), ues_scrolled_window, TRUE, TRUE, 0);
1025     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(ues_scrolled_window),
1026                                         GTK_SHADOW_IN);
1027
1028     /* Create the table of UE data */
1029     store = gtk_list_store_new(NUM_UE_COLUMNS, G_TYPE_INT,
1030                                G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, /* UL */
1031                                G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, /* DL */
1032                                G_TYPE_POINTER);
1033     hs->ue_table = GTK_TREE_VIEW(tree_view_new(GTK_TREE_MODEL(store)));
1034     gtk_container_add(GTK_CONTAINER (ues_scrolled_window), GTK_WIDGET(hs->ue_table));
1035     g_object_unref(G_OBJECT(store));
1036
1037     tree_view = hs->ue_table;
1038     gtk_tree_view_set_headers_visible(tree_view, TRUE);
1039     gtk_tree_view_set_headers_clickable(tree_view, TRUE);
1040
1041     /* Create the titles for each column of the per-UE table */
1042     for (i = 0; i < UE_TABLE_COLUMN; i++) {
1043         renderer = gtk_cell_renderer_text_new();
1044         column = gtk_tree_view_column_new_with_attributes(ue_titles[i], renderer,
1045                                                           "text", i, NULL);
1046         gtk_tree_view_column_set_sort_column_id(column, i);
1047
1048         if (i == 0) {
1049             /* Expand first column (RNTI, which is Key) */
1050             gtk_tree_view_column_set_expand(column, TRUE);
1051         } else {
1052             /* For other columns, set all of the free space to be on the left */
1053             g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1054         }
1055         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1056         gtk_tree_view_column_set_resizable(column, TRUE);
1057         gtk_tree_view_append_column(tree_view, column);
1058     }
1059
1060     /* Set callback function for selecting a row in the UE table */
1061     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(hs->ue_table));
1062     gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
1063     g_signal_connect(sel, "changed", G_CALLBACK(rlc_lte_select_ue_cb), hs);
1064
1065     gtk_box_pack_start(GTK_BOX(top_level_vbox), rlc_lte_stat_ues_lb, TRUE, TRUE, 0);
1066
1067
1068     /**********************************************/
1069     /* Channels of selected UE                    */
1070     /**********************************************/
1071     rlc_lte_stat_channels_lb = gtk_frame_new("Channels of selected UE");
1072
1073     channels_vb = gtk_vbox_new(FALSE, 6);
1074     gtk_container_add(GTK_CONTAINER(rlc_lte_stat_channels_lb), channels_vb);
1075     gtk_container_set_border_width(GTK_CONTAINER(channels_vb), 5);
1076
1077     channels_scrolled_window = scrolled_window_new(NULL, NULL);
1078     gtk_box_pack_start(GTK_BOX(channels_vb), channels_scrolled_window, TRUE, TRUE, 0);
1079     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(channels_scrolled_window),
1080                                         GTK_SHADOW_IN);
1081
1082     /* Create the table of UE data */
1083     store = gtk_list_store_new(NUM_CHANNEL_COLUMNS,
1084                                G_TYPE_STRING, G_TYPE_STRING, /* name & type */
1085                                G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, /* UL */
1086                                G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, /* DL */
1087                                G_TYPE_POINTER);
1088     hs->channel_table = GTK_TREE_VIEW(tree_view_new(GTK_TREE_MODEL(store)));
1089     gtk_container_add(GTK_CONTAINER (channels_scrolled_window), GTK_WIDGET(hs->channel_table));
1090     g_object_unref(G_OBJECT(store));
1091
1092     tree_view = hs->channel_table;
1093     gtk_tree_view_set_headers_visible(tree_view, TRUE);
1094     gtk_tree_view_set_headers_clickable(tree_view, TRUE);
1095
1096     /* Create the titles for each column of the per-UE table */
1097     for (i = 0; i < CHANNEL_TABLE_COLUMN; i++) {
1098         renderer = gtk_cell_renderer_text_new();
1099         column = gtk_tree_view_column_new_with_attributes(channel_titles[i], renderer,
1100                                                           "text", i, NULL);
1101         gtk_tree_view_column_set_sort_column_id(column, i);
1102
1103         if (i == 0) {
1104             /* Expand first column (Type) */
1105             gtk_tree_view_column_set_expand(column, TRUE);
1106         } else {
1107             /* For other columns, set all of the free space to be on the left */
1108             g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1109         }
1110         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1111         gtk_tree_view_column_set_resizable(column, TRUE);
1112         gtk_tree_view_append_column(tree_view, column);
1113     }
1114
1115     /* Set callback function for selecting a row in the channel table */
1116     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(hs->channel_table));
1117     gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
1118     g_signal_connect(sel, "changed", G_CALLBACK(rlc_lte_select_channel_cb), hs);
1119
1120     gtk_box_pack_start(GTK_BOX(top_level_vbox), rlc_lte_stat_channels_lb, TRUE, TRUE, 0);
1121
1122
1123     /**********************************************/
1124     /* Channel filters                            */
1125     /**********************************************/
1126
1127     rlc_lte_stat_filter_buttons_lb = gtk_frame_new("Filter on selected channel");
1128
1129     filter_vb = gtk_vbox_new(FALSE, 3);
1130     gtk_container_add(GTK_CONTAINER(rlc_lte_stat_filter_buttons_lb), filter_vb);
1131
1132     /* Horizontal row of filter buttons */
1133     filter_buttons_hb = gtk_hbox_new(FALSE, 6);
1134     gtk_container_add(GTK_CONTAINER(filter_vb), filter_buttons_hb);
1135     gtk_container_set_border_width(GTK_CONTAINER(filter_buttons_hb), 2);
1136
1137     /* UL only */
1138     ul_filter_bt = gtk_button_new_with_label("Set UL display filter for this channel");
1139     gtk_box_pack_start(GTK_BOX(filter_buttons_hb), ul_filter_bt, TRUE, TRUE, 0);
1140     g_signal_connect(ul_filter_bt, "clicked", G_CALLBACK(ul_filter_clicked), hs);
1141     gtk_widget_show(ul_filter_bt);
1142
1143     /* DL only */
1144     dl_filter_bt = gtk_button_new_with_label("Set DL display filter for this channel");
1145     gtk_box_pack_start(GTK_BOX(filter_buttons_hb), dl_filter_bt, TRUE, TRUE, 0);
1146     g_signal_connect(dl_filter_bt, "clicked", G_CALLBACK(dl_filter_clicked), hs);
1147     gtk_widget_show(dl_filter_bt);
1148
1149     /* UL and DL */
1150     uldl_filter_bt = gtk_button_new_with_label("Set UL / DL display filter for this channel");
1151     gtk_box_pack_start(GTK_BOX(filter_buttons_hb), uldl_filter_bt, TRUE, TRUE, 0);
1152     g_signal_connect(uldl_filter_bt, "clicked", G_CALLBACK(uldl_filter_clicked), hs);
1153     gtk_widget_show(uldl_filter_bt);
1154
1155     /* Allow filtering only to select status PDUs for AM */
1156     show_only_control_pdus_cb = gtk_check_button_new_with_mnemonic("Show only status PDUs (for AM)");
1157     gtk_container_add(GTK_CONTAINER(filter_vb), show_only_control_pdus_cb);
1158     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(show_only_control_pdus_cb), FALSE);
1159
1160
1161     /* Allow filtering on specific SN number. */
1162     /* Row with label and text entry control  */
1163     sn_filter_hb = gtk_hbox_new(FALSE, 3);
1164     gtk_container_add(GTK_CONTAINER(filter_vb), sn_filter_hb);
1165     gtk_widget_show(sn_filter_hb);
1166
1167     sn_filter_lb = gtk_label_new("Sequence number to filter on:");
1168     gtk_box_pack_start(GTK_BOX(sn_filter_hb), sn_filter_lb, TRUE, TRUE, 0);
1169     gtk_widget_show(sn_filter_lb);
1170
1171     sn_filter_te = gtk_entry_new();
1172     gtk_box_pack_start(GTK_BOX(sn_filter_hb), sn_filter_te, TRUE, TRUE, 0);
1173     gtk_widget_show(sn_filter_te);
1174
1175
1176     /* Add filters box to top-level window */
1177     gtk_box_pack_start(GTK_BOX(top_level_vbox), rlc_lte_stat_filter_buttons_lb, TRUE, TRUE, 0);
1178
1179     enable_filter_controls(FALSE);
1180
1181     /**********************************************/
1182     /* Register the tap listener                  */
1183     /**********************************************/
1184
1185     error_string = register_tap_listener("rlc-lte", hs, NULL, 0,
1186                                          rlc_lte_stat_reset,
1187                                          rlc_lte_stat_packet,
1188                                          rlc_lte_stat_draw);
1189     if (error_string) {
1190         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
1191         g_string_free(error_string, TRUE);
1192         g_free(hs);
1193         return;
1194     }
1195
1196
1197     /************************************/
1198     /* Bottom button row.                */
1199     /************************************/
1200
1201     bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_HELP, NULL);
1202     gtk_box_pack_end(GTK_BOX(top_level_vbox), bbox, FALSE, FALSE, 0);
1203
1204     /* Add the close button */
1205     close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
1206     window_set_cancel_button(rlc_lte_stat_dlg_w, close_bt, window_cancel_button_cb);
1207
1208     help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
1209     g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_LTE_RLC_TRAFFIC_DIALOG);
1210
1211     /* Set callbacks */
1212     g_signal_connect(rlc_lte_stat_dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1213     g_signal_connect(rlc_lte_stat_dlg_w, "destroy", G_CALLBACK(win_destroy_cb), hs);
1214
1215     /* Show the window */
1216     gtk_widget_show_all(rlc_lte_stat_dlg_w);
1217     window_present(rlc_lte_stat_dlg_w);
1218
1219     /* Retap */
1220     cf_retap_packets(&cfile);
1221     gdk_window_raise(rlc_lte_stat_dlg_w->window);
1222 }
1223
1224
1225 /* Show window, creating if necessary */
1226 static void rlc_lte_stat_launch(GtkWidget *w _U_, gpointer data _U_)
1227 {
1228     if (rlc_lte_stat_dlg_w) {
1229         reactivate_window(rlc_lte_stat_dlg_w);
1230     } else {
1231         rlc_lte_stat_dlg_create();
1232     }
1233 }
1234
1235 /* Register this tap listener (need void on own so line register function found) */
1236 void
1237 register_tap_listener_rlc_lte_stat(void)
1238 {
1239     register_stat_menu_item("_LTE RLC...", REGISTER_STAT_GROUP_TELEPHONY,
1240                             rlc_lte_stat_launch, NULL, NULL, NULL);
1241 }
1242