Try to cast away windows buildbot problems.
[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 */
29
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33
34 #ifdef HAVE_SYS_TYPES_H
35 #include <sys/types.h>
36 #endif
37
38 #include <string.h>
39
40 #include <gtk/gtk.h>
41
42 #include "gtk/gtkglobals.h"
43
44 #include <epan/packet.h>
45 #include <epan/packet_info.h>
46 #include <epan/tap.h>
47 #include <epan/dissectors/packet-rlc-lte.h>
48
49 #include "../simple_dialog.h"
50 #include "../stat_menu.h"
51
52 #include "gtk/dlg_utils.h"
53 #include "gtk/gui_stat_menu.h"
54 #include "gtk/tap_dfilter_dlg.h"
55 #include "gtk/gui_utils.h"
56 #include "gtk/help_dlg.h"
57 #include "gtk/main.h"
58
59 /**********************************************/
60 /* Table column identifiers and title strings */
61
62 enum {
63     UEID_COLUMN,
64     UL_FRAMES_COLUMN,
65     UL_BYTES_COLUMN,
66     UL_BW_COLUMN,
67     UL_NACKS_COLUMN,
68     UL_MISSING_COLUMN,
69     DL_FRAMES_COLUMN,
70     DL_BYTES_COLUMN,
71     DL_BW_COLUMN,
72     DL_NACKS_COLUMN,
73     DL_MISSING_COLUMN,
74     UE_TABLE_COLUMN,
75     NUM_UE_COLUMNS
76 };
77
78 enum {
79     CHANNEL_NAME,
80     CHANNEL_MODE,
81     CHANNEL_UL_FRAMES,
82     CHANNEL_UL_BYTES,
83     CHANNEL_UL_BW,
84     CHANNEL_UL_ACKS,
85     CHANNEL_UL_NACKS,
86     CHANNEL_UL_MISSING,
87     CHANNEL_DL_FRAMES,
88     CHANNEL_DL_BYTES,
89     CHANNEL_DL_BW,
90     CHANNEL_DL_ACKS,
91     CHANNEL_DL_NACKS,
92     CHANNEL_DL_MISSING,
93     CHANNEL_TABLE_COLUMN,
94     NUM_CHANNEL_COLUMNS
95 };
96
97 static const gchar *ue_titles[] = { "UEId",
98                                     "UL Frames", "UL Bytes", "UL Mbs", "UL NACKs", "UL Missing",
99                                     "DL Frames", "DL Bytes", "DL Mbs", "DL NACKs", "DL Missing"};
100
101 static const gchar *channel_titles[] = { "", "Mode",
102                                          "UL Frames", "UL Bytes", "UL Mbs", "UL ACKs", "UL NACKs", "UL Missing",
103                                          "DL Frames", "DL Bytes", "DL Mbs", "DL ACKs", "DL NACKs", "DL Missing"};
104
105 /* Stats kept for one channel */
106 typedef struct rlc_channel_stats {
107     guint8   inUse;
108     guint8   rlcMode;
109     guint16  channelType;
110     guint16  channelId;
111
112     guint32  UL_frames;
113     guint32  UL_bytes;
114     nstime_t UL_time_start;
115     nstime_t UL_time_stop;
116
117     guint32  DL_frames;
118     guint32  DL_bytes;
119     nstime_t DL_time_start;
120     nstime_t DL_time_stop;
121
122     guint32  UL_acks;
123     guint32  UL_nacks;
124
125     guint32  DL_acks;
126     guint32  DL_nacks;
127
128     guint32  UL_missing;
129     guint32  DL_missing;
130
131     GtkTreeIter iter;
132     gboolean iter_valid;
133 } rlc_channel_stats;
134
135
136 /* Stats for one UE */
137 typedef struct rlc_lte_row_data {
138     /* Key for matching this row */
139     guint16  ueid;
140
141     gboolean is_predefined_data;
142
143     guint32  UL_frames;
144     guint32  UL_total_bytes;
145     nstime_t UL_time_start;
146     nstime_t UL_time_stop;
147     guint32  UL_total_nacks;
148     guint32  UL_total_missing;
149
150     guint32  DL_frames;
151     guint32  DL_total_bytes;
152     nstime_t DL_time_start;
153     nstime_t DL_time_stop;
154     guint32  DL_total_nacks;
155     guint32  DL_total_missing;
156
157     rlc_channel_stats CCCH_stats;
158     rlc_channel_stats srb_stats[2];
159     rlc_channel_stats drb_stats[32];
160 } rlc_lte_row_data;
161
162
163 /* Common channel stats */
164 typedef struct rlc_lte_common_stats {
165     guint32 bcch_frames;
166     guint32 bcch_bytes;
167     guint32 pcch_frames;
168     guint32 pcch_bytes;
169 } rlc_lte_common_stats;
170
171
172 /* One row/UE in the UE table */
173 typedef struct rlc_lte_ep {
174     struct rlc_lte_ep* next;
175     struct rlc_lte_row_data stats;
176     GtkTreeIter iter;
177     gboolean iter_valid;
178 } rlc_lte_ep_t;
179
180
181 /* Used to keep track of whole RLC LTE statistics window */
182 typedef struct rlc_lte_stat_t {
183     GtkTreeView   *ue_table;
184     rlc_lte_ep_t  *ep_list;
185     guint32       total_frames;
186
187     char          *filter;
188
189     /* Top-level dialog and labels */
190     GtkWidget  *dlg_w;
191     GtkWidget  *ues_lb;
192
193     /* Other widgets */
194     GtkWidget  *ul_filter_bt;
195     GtkWidget  *dl_filter_bt;
196     GtkWidget  *uldl_filter_bt;
197     GtkWidget  *show_only_control_pdus_cb;
198     GtkWidget  *show_dct_errors_cb;
199     GtkWidget  *sn_filter_lb;
200     GtkWidget  *sn_filter_te;
201
202     /* Common stats */
203     rlc_lte_common_stats common_stats;
204     GtkWidget *common_bcch_frames;
205     GtkWidget *common_bcch_bytes;
206     GtkWidget *common_pcch_frames;
207     GtkWidget *common_pcch_bytes;
208
209     gboolean  show_mac;
210
211     /* State used to attempt to re-select chosen UE/channel */
212     guint16   reselect_ue;
213     guint16   reselect_channel_type;
214     guint16   reselect_channel_id;
215
216     GtkTreeView   *channel_table;
217 } rlc_lte_stat_t;
218
219
220 static int get_channel_selection(rlc_lte_stat_t *hs,
221                                  guint16 *ueid, guint8 *rlcMode,
222                                  guint16 *channelType, guint16 *channelId);
223
224 /* Show filter controls appropriate to current selection */
225 static void enable_filter_controls(guint8 enabled, guint8 rlcMode, rlc_lte_stat_t *hs)
226 {
227     gtk_widget_set_sensitive(hs->ul_filter_bt, enabled);
228     gtk_widget_set_sensitive(hs->dl_filter_bt, enabled);
229     gtk_widget_set_sensitive(hs->uldl_filter_bt, enabled);
230     gtk_widget_set_sensitive(hs->show_dct_errors_cb, enabled);
231
232     switch (rlcMode) {
233         case RLC_TM_MODE:
234             gtk_widget_set_sensitive(hs->show_only_control_pdus_cb, FALSE);
235             gtk_widget_set_sensitive(hs->sn_filter_lb, FALSE);
236             gtk_widget_set_sensitive(hs->sn_filter_te, FALSE);
237             break;
238         case RLC_UM_MODE:
239             gtk_widget_set_sensitive(hs->show_only_control_pdus_cb, FALSE);
240             gtk_widget_set_sensitive(hs->sn_filter_lb, TRUE);
241             gtk_widget_set_sensitive(hs->sn_filter_te, TRUE);
242             break;
243         case RLC_AM_MODE:
244             gtk_widget_set_sensitive(hs->show_only_control_pdus_cb, TRUE);
245             gtk_widget_set_sensitive(hs->sn_filter_lb, TRUE);
246             gtk_widget_set_sensitive(hs->sn_filter_te, TRUE);
247             break;
248
249         default:
250             gtk_widget_set_sensitive(hs->show_only_control_pdus_cb, FALSE);
251             gtk_widget_set_sensitive(hs->sn_filter_lb, FALSE);
252             gtk_widget_set_sensitive(hs->sn_filter_te, FALSE);
253             break;
254     }
255 }
256
257
258
259 /* Reset the statistics window */
260 static void
261 rlc_lte_stat_reset(void *phs)
262 {
263     rlc_lte_stat_t* rlc_lte_stat = (rlc_lte_stat_t *)phs;
264     rlc_lte_ep_t* list = rlc_lte_stat->ep_list;
265     gchar title[256];
266     GtkListStore *store;
267
268     /* Set the title */
269     if (rlc_lte_stat->dlg_w != NULL) {
270         g_snprintf(title, sizeof(title), "Wireshark: LTE RLC Traffic Statistics: %s (filter=\"%s\")",
271                    cf_get_display_name(&cfile),
272                    rlc_lte_stat->filter ? rlc_lte_stat->filter : "none");
273         gtk_window_set_title(GTK_WINDOW(rlc_lte_stat->dlg_w), title);
274     }
275
276     g_snprintf(title, sizeof(title), "0 UEs");
277     gtk_frame_set_label(GTK_FRAME(rlc_lte_stat->ues_lb), title);
278
279     rlc_lte_stat->total_frames = 0;
280     memset(&rlc_lte_stat->common_stats, 0, sizeof(rlc_lte_common_stats));
281
282     /* Remove all entries from the UE list */
283     store = GTK_LIST_STORE(gtk_tree_view_get_model(rlc_lte_stat->ue_table));
284     gtk_list_store_clear(store);
285
286     if (!list) {
287         return;
288     }
289
290     rlc_lte_stat->ep_list = NULL;
291 }
292
293
294 /* Allocate a rlc_lte_ep_t struct to store info for new UE */
295 static rlc_lte_ep_t* alloc_rlc_lte_ep(struct rlc_lte_tap_info *si, packet_info *pinfo _U_)
296 {
297     rlc_lte_ep_t* ep;
298     int n;
299
300     if (!si) {
301         return NULL;
302     }
303
304     if (!(ep = g_malloc(sizeof(rlc_lte_ep_t)))) {
305         return NULL;
306     }
307
308     /* Copy SI data into ep->stats */
309     ep->stats.ueid = si->ueid;
310
311     /* Counts for new UE are all 0 */
312     ep->stats.UL_frames = 0;
313     ep->stats.DL_frames = 0;
314     ep->stats.UL_total_bytes = 0;
315     ep->stats.DL_total_bytes = 0;
316     memset(&ep->stats.DL_time_start, 0, sizeof(nstime_t));
317     memset(&ep->stats.DL_time_stop, 0, sizeof(nstime_t));
318     ep->stats.UL_total_nacks = 0;
319     ep->stats.DL_total_nacks = 0;
320     ep->stats.UL_total_missing = 0;
321     ep->stats.DL_total_missing = 0;
322
323     memset(&ep->stats.CCCH_stats, 0, sizeof(rlc_channel_stats));
324     for (n=0; n < 2; n++) {
325         memset(&ep->stats.srb_stats[n], 0, sizeof(rlc_channel_stats));
326     }
327     for (n=0; n < 32; n++) {
328         memset(&ep->stats.drb_stats[n], 0, sizeof(rlc_channel_stats));
329     }
330
331     ep->next = NULL;
332     ep->iter_valid = FALSE;
333
334     return ep;
335 }
336
337
338 /* Return string for RLC mode for display */
339 static const char *print_rlc_channel_mode(guint8 mode)
340 {
341     static char unknown[16];
342
343     switch (mode) {
344         case RLC_TM_MODE:  return "TM";
345         case RLC_UM_MODE:  return "UM";
346         case RLC_AM_MODE:  return "AM";
347         case RLC_PREDEF:   return "Predef";
348
349         default:
350             g_snprintf(unknown, 32, "Unknown (%u)", mode);
351             return unknown;
352     }
353 }
354
355
356 /* Process stat struct for a RLC LTE frame */
357 static int
358 rlc_lte_stat_packet(void *phs, packet_info *pinfo, epan_dissect_t *edt _U_,
359                     const void *phi)
360 {
361     /* Get reference to stat window instance */
362     rlc_lte_stat_t *hs = (rlc_lte_stat_t *)phs;
363     rlc_lte_ep_t *tmp = NULL, *te = NULL;
364     rlc_channel_stats *channel_stats = NULL;
365
366     /* Cast tap info struct */
367     struct rlc_lte_tap_info *si = (struct rlc_lte_tap_info *)phi;
368
369     /* Need this */
370     if (!hs) {
371         return 0;
372     }
373
374     /* Are we ignoring RLC frames that were found in MAC frames, or only those
375        that were logged separately? */
376     if ((!hs->show_mac && si->loggedInMACFrame) ||
377         (hs->show_mac && !si->loggedInMACFrame)) {
378         return 0;
379     }
380
381     /* Inc top-level frame count */
382     hs->total_frames++;
383
384     /* Common channel stats */
385     switch (si->channelType) {
386         case CHANNEL_TYPE_BCCH:
387             hs->common_stats.bcch_frames++;
388             hs->common_stats.bcch_bytes += si->pduLength;
389             return 1;
390
391         case CHANNEL_TYPE_PCCH:
392             hs->common_stats.pcch_frames++;
393             hs->common_stats.pcch_bytes += si->pduLength;
394             return 1;
395
396         default:
397             break;
398     }
399
400     /* For per-UE data, must create a new row if none already existing */
401     if (!hs->ep_list) {
402         /* Allocate new list */
403         hs->ep_list = alloc_rlc_lte_ep(si, pinfo);
404         /* Make it the first/only entry */
405         te = hs->ep_list;
406     } else {
407         /* Look among existing rows for this UEId */
408         for (tmp = hs->ep_list; (tmp != NULL); tmp = tmp->next) {
409             if (tmp->stats.ueid == si->ueid) {
410                 te = tmp;
411                 break;
412             }
413         }
414
415         /* Not found among existing, so create a new one anyway */
416         if (te == NULL) {
417             if ((te = alloc_rlc_lte_ep(si, pinfo))) {
418                 /* Add new item to end of list */
419                 rlc_lte_ep_t *p = hs->ep_list;
420                 while (p->next) {
421                     p = p->next;
422                 }
423                 p->next = te;
424                 te->next = NULL;
425             }
426         }
427     }
428
429     /* Really should have a row pointer by now */
430     if (!te) {
431         return 0;
432     }
433
434     /* Update entry with details from si */
435     te->stats.ueid = si->ueid;
436
437     /* Top-level traffic stats */
438     if (si->direction == DIRECTION_UPLINK) {
439         /* Update start/stop time as appropriate */
440         if (te->stats.UL_frames == 0) {
441             te->stats.UL_time_start = si->time;
442         }
443         else {
444             te->stats.UL_time_stop = si->time;
445         }
446         te->stats.UL_frames++;
447         te->stats.UL_total_bytes += si->pduLength;
448     }
449     else {
450         /* Update start/stop time as appropriate */
451         if (te->stats.DL_frames == 0) {
452             te->stats.DL_time_start = si->time;
453         }
454         else {
455             te->stats.DL_time_stop = si->time;
456         }
457         te->stats.DL_frames++;
458         te->stats.DL_total_bytes += si->pduLength;
459     }
460
461     /* Find channel struct */
462     switch (si->channelType) {
463         case CHANNEL_TYPE_CCCH:
464             channel_stats = &te->stats.CCCH_stats;
465             break;
466
467         case CHANNEL_TYPE_SRB:
468             channel_stats = &te->stats.srb_stats[si->channelId-1];
469             break;
470
471         case CHANNEL_TYPE_DRB:
472             channel_stats = &te->stats.drb_stats[si->channelId-1];
473             break;
474
475         default:
476             /* Shouldn't get here... */
477             return 0;
478     }
479
480     if (channel_stats != NULL) {
481         channel_stats->inUse = TRUE;
482         channel_stats->iter_valid = FALSE;
483         channel_stats->rlcMode = si->rlcMode;
484         channel_stats->channelType = si->channelType;
485         channel_stats->channelId = si->channelId;
486     }
487     else {
488         /* Giving up if no channel found... */
489         return 0;
490     }
491
492     if (si->direction == DIRECTION_UPLINK) {
493         if (channel_stats->UL_frames == 0) {
494             channel_stats->UL_time_start = si->time;
495         }
496         else {
497             channel_stats->UL_time_stop = si->time;
498         }
499         channel_stats->UL_frames++;
500         channel_stats->UL_bytes += si->pduLength;
501         channel_stats->UL_nacks += si->noOfNACKs;
502         channel_stats->UL_missing += si->missingSNs;
503         if (si->isControlPDU) {
504             channel_stats->UL_acks++;
505         }
506         te->stats.UL_total_nacks += si->noOfNACKs;
507         te->stats.UL_total_missing += si->missingSNs;
508     }
509     else {
510         if (channel_stats->DL_frames == 0) {
511             channel_stats->DL_time_start = si->time;
512         }
513         else {
514             channel_stats->DL_time_stop = si->time;
515         }
516         channel_stats->DL_frames++;
517         channel_stats->DL_bytes += si->pduLength;
518         channel_stats->DL_nacks += si->noOfNACKs;
519         channel_stats->DL_missing += si->missingSNs;
520         if (si->isControlPDU) {
521             channel_stats->DL_acks++;
522         }
523         te->stats.DL_total_nacks += si->noOfNACKs;
524         te->stats.DL_total_missing += si->missingSNs;
525     }
526
527     return 1;
528 }
529
530
531 /* The channels for any UE would need to be re-added to the list */
532 static void invalidate_channel_iters(rlc_lte_stat_t *hs)
533 {
534     gint n;
535     rlc_lte_ep_t *ep = hs->ep_list;
536
537     while (ep) {
538         ep->stats.CCCH_stats.iter_valid = FALSE;
539         for (n=0; n < 2; n++) {
540             ep->stats.srb_stats[n].iter_valid = FALSE;
541         }
542         for (n=0; n < 32; n++) {
543             ep->stats.drb_stats[n].iter_valid = FALSE;
544         }
545
546         ep = ep->next;
547     }
548 }
549
550
551 /* Calculate and return a bandwidth figure, in Mbs */
552 static float calculate_bw(nstime_t *start_time, nstime_t *stop_time, guint32 bytes)
553 {
554     if (memcmp(start_time, stop_time, sizeof(nstime_t)) != 0) {
555         float elapsed_ms = (((float)stop_time->secs - (float)start_time->secs) * 1000) +
556                            (((float)stop_time->nsecs - (float)start_time->nsecs) / 1000000);
557         return ((bytes * 8) / elapsed_ms) / 1000;
558     }
559     else {
560         return 0.0;
561     }
562 }
563
564
565
566 /* Draw the channels table according to the current UE selection */
567 static void
568 rlc_lte_channels(rlc_lte_ep_t *rlc_stat_ep, rlc_lte_stat_t *hs)
569 {
570     GtkListStore *channels_store = GTK_LIST_STORE(gtk_tree_view_get_model(hs->channel_table));
571     rlc_channel_stats *channel_stats;
572     char buff[32];
573     int n;
574
575     /* Clear any existing rows */
576     gtk_list_store_clear(channels_store);
577     invalidate_channel_iters(hs);
578
579     if (rlc_stat_ep == NULL) {
580         return;
581     }
582
583     /* Add one row for each channel */
584
585     /* CCCH */
586     channel_stats = &rlc_stat_ep->stats.CCCH_stats;
587     if (channel_stats->inUse) {
588
589         if (!channel_stats->iter_valid) {
590             /* Add to list control if not drawn this UE before */
591             gtk_list_store_append(channels_store, &channel_stats->iter);
592             channel_stats->iter_valid = TRUE;
593         }
594
595         /* Set each column for this row */
596         gtk_list_store_set(channels_store, &channel_stats->iter,
597                            CHANNEL_NAME, "CCCH",
598                            CHANNEL_MODE, print_rlc_channel_mode(channel_stats->rlcMode),
599                            CHANNEL_UL_FRAMES, channel_stats->UL_frames,
600                            CHANNEL_UL_BYTES, channel_stats->UL_bytes,
601                            CHANNEL_DL_FRAMES, channel_stats->DL_frames,
602                            CHANNEL_DL_BYTES, channel_stats->DL_bytes,
603                            CHANNEL_TABLE_COLUMN, channel_stats,
604                            -1);
605     }
606
607
608     /* SRB */
609     for (n=0; n < 2; n++) {
610         channel_stats = &rlc_stat_ep->stats.srb_stats[n];
611         if (channel_stats->inUse) {
612
613             /* Calculate bandwidth */
614             float UL_bw = calculate_bw(&channel_stats->UL_time_start,
615                                        &channel_stats->UL_time_stop,
616                                        channel_stats->UL_bytes);
617             float DL_bw = calculate_bw(&channel_stats->DL_time_start,
618                                        &channel_stats->DL_time_stop,
619                                        channel_stats->DL_bytes);
620
621             if (!channel_stats->iter_valid) {
622                 /* Add to list control if not drawn this UE before */
623                 gtk_list_store_append(channels_store, &channel_stats->iter);
624                 channel_stats->iter_valid = TRUE;
625             }
626
627             g_snprintf(buff, 32, "SRB-%u", n+1);
628
629             /* Set each column for this row */
630             gtk_list_store_set(channels_store, &channel_stats->iter,
631                                CHANNEL_NAME, buff,
632                                CHANNEL_MODE, print_rlc_channel_mode(channel_stats->rlcMode),
633                                CHANNEL_UL_FRAMES, channel_stats->UL_frames,
634                                CHANNEL_UL_BYTES, channel_stats->UL_bytes,
635                                CHANNEL_UL_BW, UL_bw,
636                                CHANNEL_UL_ACKS, channel_stats->UL_acks,
637                                CHANNEL_UL_NACKS, channel_stats->UL_nacks,
638                                CHANNEL_UL_MISSING, channel_stats->UL_missing,
639                                CHANNEL_DL_FRAMES, channel_stats->DL_frames,
640                                CHANNEL_DL_BYTES, channel_stats->DL_bytes,
641                                CHANNEL_DL_BW, DL_bw,
642                                CHANNEL_DL_ACKS, channel_stats->DL_acks,
643                                CHANNEL_DL_NACKS, channel_stats->DL_nacks,
644                                CHANNEL_DL_MISSING, channel_stats->DL_missing,
645                                CHANNEL_TABLE_COLUMN, channel_stats,
646                                -1);
647         }
648     }
649
650
651     /* DRB */
652     for (n=0; n < 32; n++) {
653         channel_stats = &rlc_stat_ep->stats.drb_stats[n];
654         if (channel_stats->inUse) {
655
656             /* Calculate bandwidth */
657             float UL_bw = calculate_bw(&channel_stats->UL_time_start,
658                                        &channel_stats->UL_time_stop,
659                                        channel_stats->UL_bytes);
660             float DL_bw = calculate_bw(&channel_stats->DL_time_start,
661                                        &channel_stats->DL_time_stop,
662                                        channel_stats->DL_bytes);
663
664             if (!channel_stats->iter_valid) {
665                 /* Add to list control if not drawn this UE before */
666                 gtk_list_store_append(channels_store, &channel_stats->iter);
667                 channel_stats->iter_valid = TRUE;
668             }
669
670             g_snprintf(buff, 32, "DRB-%u", n+1);
671
672             /* Set each column for this row */
673             gtk_list_store_set(channels_store, &channel_stats->iter,
674                                CHANNEL_NAME, buff,
675                                CHANNEL_MODE, print_rlc_channel_mode(channel_stats->rlcMode),
676                                CHANNEL_UL_FRAMES, channel_stats->UL_frames,
677                                CHANNEL_UL_BYTES, channel_stats->UL_bytes,
678                                CHANNEL_UL_BW, UL_bw,
679                                CHANNEL_UL_ACKS, channel_stats->UL_acks,
680                                CHANNEL_UL_NACKS, channel_stats->UL_nacks,
681                                CHANNEL_UL_MISSING, channel_stats->UL_missing,
682                                CHANNEL_DL_FRAMES, channel_stats->DL_frames,
683                                CHANNEL_DL_BYTES, channel_stats->DL_bytes,
684                                CHANNEL_DL_BW, DL_bw,
685                                CHANNEL_DL_ACKS, channel_stats->DL_acks,
686                                CHANNEL_DL_NACKS, channel_stats->DL_nacks,
687                                CHANNEL_DL_MISSING, channel_stats->DL_missing,
688                                CHANNEL_TABLE_COLUMN, channel_stats,
689                                -1);
690         }
691     }
692 }
693
694
695
696 /* (Re)draw the whole dialog window */
697 static void
698 rlc_lte_stat_draw(void *phs)
699 {
700     gchar   buff[32];
701     guint16 number_of_ues = 0;
702     gchar title[256];
703
704     /* Look up the statistics window */
705     rlc_lte_stat_t *hs = (rlc_lte_stat_t *)phs;
706     rlc_lte_ep_t* list = hs->ep_list, *tmp = 0;
707
708     GtkListStore *ues_store;
709     GtkTreeSelection *sel;
710     GtkTreeModel *model;
711     GtkTreeIter iter;
712     rlc_channel_stats *channel_stats = NULL;
713
714     /* Common channel data */
715     g_snprintf(buff, sizeof(buff), "BCCH Frames: %u", hs->common_stats.bcch_frames);
716     gtk_label_set_text(GTK_LABEL(hs->common_bcch_frames), buff);
717     g_snprintf(buff, sizeof(buff), "BCCH Bytes: %u", hs->common_stats.bcch_bytes);
718     gtk_label_set_text(GTK_LABEL(hs->common_bcch_bytes), buff);
719     g_snprintf(buff, sizeof(buff), "PCCH Frames: %u", hs->common_stats.pcch_frames);
720     gtk_label_set_text(GTK_LABEL(hs->common_pcch_frames), buff);
721     g_snprintf(buff, sizeof(buff), "PCCH Bytes: %u", hs->common_stats.pcch_bytes);
722     gtk_label_set_text(GTK_LABEL(hs->common_pcch_bytes), buff);
723
724     /* Per-UE table entries */
725     ues_store = GTK_LIST_STORE(gtk_tree_view_get_model(hs->ue_table));
726
727     /* Set title that shows how many UEs currently in table */
728     for (tmp = list; (tmp!=NULL); tmp=tmp->next, number_of_ues++);
729     g_snprintf(title, sizeof(title), "%u UEs", number_of_ues);
730     gtk_frame_set_label(GTK_FRAME(hs->ues_lb), title);
731
732     /* Update title to include number of UEs and frames */
733     g_snprintf(title, sizeof(title), "Wireshark: LTE RLC Traffic Statistics: %s (%u UEs, %u frames) (filter=\"%s\")",
734                cf_get_display_name(&cfile),
735                number_of_ues,
736                hs->total_frames,
737                hs->filter ? hs->filter : "none");
738     gtk_window_set_title(GTK_WINDOW(hs->dlg_w), title);
739
740
741     /* For each row/UE in the model */
742     for (tmp = list; tmp; tmp=tmp->next) {
743         /* Calculate bandwidth */
744         float UL_bw = calculate_bw(&tmp->stats.UL_time_start,
745                                    &tmp->stats.UL_time_stop,
746                                    tmp->stats.UL_total_bytes);
747         float DL_bw = calculate_bw(&tmp->stats.DL_time_start,
748                                    &tmp->stats.DL_time_stop,
749                                    tmp->stats.DL_total_bytes);
750
751         if (tmp->iter_valid != TRUE) {
752             /* Add to list control if not drawn this UE before */
753             gtk_list_store_append(ues_store, &tmp->iter);
754             tmp->iter_valid = TRUE;
755         }
756
757         /* Set each column for this row */
758         gtk_list_store_set(ues_store, &tmp->iter,
759                            UEID_COLUMN, tmp->stats.ueid,
760                            UL_FRAMES_COLUMN, tmp->stats.UL_frames,
761                            UL_BYTES_COLUMN, tmp->stats.UL_total_bytes,
762                            UL_BW_COLUMN, UL_bw,
763                            UL_NACKS_COLUMN, tmp->stats.UL_total_nacks,
764                            UL_MISSING_COLUMN, tmp->stats.UL_total_missing,
765                            DL_FRAMES_COLUMN, tmp->stats.DL_frames,
766                            DL_BYTES_COLUMN, tmp->stats.DL_total_bytes,
767                            DL_BW_COLUMN, DL_bw,
768                            DL_NACKS_COLUMN, tmp->stats.DL_total_nacks,
769                            DL_MISSING_COLUMN, tmp->stats.DL_total_missing,
770                            UE_TABLE_COLUMN, tmp,
771                            -1);
772     }
773
774     /* Reselect UE? */
775     if (hs->reselect_ue != 0) {
776         GtkTreeIter *ue_iter = NULL;
777         rlc_lte_ep_t *ep = hs->ep_list;
778         while (ep != NULL) {
779             if (ep->stats.ueid == hs->reselect_ue) {
780                 ue_iter = &ep->iter;
781                 break;
782             }
783             ep = ep->next;
784         }
785         if (ue_iter != NULL) {
786             gtk_tree_selection_select_iter(gtk_tree_view_get_selection(hs->ue_table), ue_iter);
787         }
788     }
789
790     /* If there is a UE selected, update its counters in details window */
791     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(hs->ue_table));
792     if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
793         rlc_lte_ep_t *ep;
794
795         gtk_tree_model_get(model, &iter, UE_TABLE_COLUMN, &ep, -1);
796         rlc_lte_channels(ep, hs);
797
798         /* Reselect channel? */
799         switch (hs->reselect_channel_type) {
800             case CHANNEL_TYPE_CCCH:
801                 channel_stats = &(ep->stats.CCCH_stats);
802                 break;
803             case CHANNEL_TYPE_DRB:
804                 channel_stats = &(ep->stats.drb_stats[hs->reselect_channel_id-1]);
805                 break;
806             case CHANNEL_TYPE_SRB:
807                 channel_stats = &(ep->stats.srb_stats[hs->reselect_channel_id-1]);
808                 break;
809             default:
810                 break;
811         }
812
813         if ((channel_stats != NULL) && channel_stats->inUse && channel_stats->iter_valid) {
814             gtk_tree_selection_select_iter(gtk_tree_view_get_selection(hs->channel_table), &channel_stats->iter);
815         }
816     }
817 }
818
819
820 /* What to do when a UE list item is selected/unselected */
821 static void rlc_lte_select_ue_cb(GtkTreeSelection *sel, gpointer data)
822 {
823     rlc_lte_ep_t   *ep;
824     GtkTreeModel   *model;
825     GtkTreeIter    iter;
826     rlc_lte_stat_t *hs = (rlc_lte_stat_t*)data;
827
828     if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
829         /* Show details of selected UE */
830         gtk_tree_model_get(model, &iter, UE_TABLE_COLUMN, &ep, -1);
831         hs->reselect_ue = ep->stats.ueid;
832         rlc_lte_channels(ep, hs);
833     }
834     else {
835         rlc_lte_channels(NULL, hs);
836     }
837
838     /* Channel will be deselected */
839     enable_filter_controls(FALSE, 0, hs);
840 }
841
842
843 /* What to do when a channel list item is selected/unselected */
844 static void rlc_lte_select_channel_cb(GtkTreeSelection *sel, gpointer data)
845 {
846     GtkTreeModel   *model;
847     GtkTreeIter    iter;
848     rlc_lte_stat_t *hs = (rlc_lte_stat_t *)data;
849
850     if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
851         guint16  ueid;
852         guint8   rlcMode;
853
854         /* Remember selected channel */
855         get_channel_selection(hs, &ueid, &rlcMode,
856                               &(hs->reselect_channel_type), &(hs->reselect_channel_id));
857
858         /* Enable buttons */
859         enable_filter_controls(TRUE, rlcMode, hs);
860
861     }
862     else {
863         /* No channel selected - disable buttons */
864         enable_filter_controls(FALSE, 0, hs);
865     }
866 }
867
868
869 /* Destroy the stats window */
870 static void win_destroy_cb(GtkWindow *win _U_, gpointer data)
871 {
872     rlc_lte_stat_t *hs = (rlc_lte_stat_t *)data;
873
874     protect_thread_critical_region();
875     remove_tap_listener(hs);
876     unprotect_thread_critical_region();
877
878     if (hs->dlg_w != NULL) {
879         window_destroy(hs->dlg_w);
880         hs->dlg_w = NULL;
881     }
882     rlc_lte_stat_reset(hs);
883     g_free(hs);
884 }
885
886
887
888 static void
889 toggle_show_mac(GtkWidget *widget, gpointer data)
890 {
891     rlc_lte_stat_t *hs = (rlc_lte_stat_t *)data;
892
893     /* Read state */
894     hs->show_mac = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON (widget));
895
896     /* Retap */
897     cf_retap_packets(&cfile);
898 }
899
900
901
902 /* Check that a UE / channel is currently selected.  If so, fill in out
903    parameters with details of channel.
904    Return TRUE if a channel is selected */
905 static int get_channel_selection(rlc_lte_stat_t *hs,
906                                  guint16 *ueid, guint8 *rlcMode,
907                                  guint16 *channelType, guint16 *channelId)
908 {
909     GtkTreeModel *model;
910     GtkTreeIter iter;
911
912     /* Check UE selection */
913     GtkTreeSelection *sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(hs->ue_table));
914     if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
915         rlc_lte_ep_t *ep;
916
917         gtk_tree_model_get(model, &iter, UE_TABLE_COLUMN, &ep, -1);
918         *ueid = ep->stats.ueid;
919
920         /* Check channel selection */
921         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(hs->channel_table));
922         if (gtk_tree_selection_get_selected(sel, &model, &iter)) {
923             /* Find details of selected channel */
924             rlc_channel_stats *channel_stats;
925             gtk_tree_model_get(model, &iter, CHANNEL_TABLE_COLUMN, &channel_stats, -1);
926             *rlcMode = channel_stats->rlcMode;
927             *channelType = channel_stats->channelType;
928             *channelId = channel_stats->channelId;
929         }
930         else {
931             return FALSE;
932         }
933     }
934     else {
935         return FALSE;
936     }
937
938     return TRUE;
939 }
940
941
942 /* Build and set a display filter to match the given channel settings */
943 typedef enum ChannelDirection_t {UL_Only, DL_Only, UL_and_DL} ChannelDirection_t;
944 static void set_channel_filter_expression(guint16  ueid,
945                                           guint8   rlcMode,
946                                           guint16  channelType,
947                                           guint16  channelId,
948                                           ChannelDirection_t channelDirection,
949                                           gint     filterOnSN,
950                                           gint     statusOnlyPDUs,
951                                           gint     showDCTErrors,
952                                           rlc_lte_stat_t *hs)
953 {
954     #define MAX_FILTER_LEN 1024
955     static char buffer[MAX_FILTER_LEN];
956     int offset = 0;
957
958     /* Show DCT errors */
959     if (showDCTErrors) {
960         offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
961                              "dct2000.error-comment or (");
962     }
963
964     /* Include dialog filter */
965     if (hs->filter) {
966         offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, "%s and ", hs->filter);
967     }
968
969     /* Should we exclude MAC frames? */
970     if (!hs->show_mac) {
971         offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, "not mac-lte and ");
972     }
973     else {
974         offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, "mac-lte and ");
975     }
976
977     /* UEId */
978     offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, "(rlc-lte.ueid == %u) and ", ueid);
979     offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, "(rlc-lte.channel-type == %u)", channelType);
980
981     /* Channel-id for srb/drb */
982     if ((channelType == CHANNEL_TYPE_SRB) || (channelType == CHANNEL_TYPE_DRB)) {
983         offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, " and (rlc-lte.channel-id == %u)", channelId);
984     }
985
986     /* Direction (also depends upon RLC mode) */
987     switch (channelDirection) {
988         case UL_Only:
989             if (rlcMode == RLC_AM_MODE) {
990                 /* Always filter status PDUs */
991                 offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
992                                      " and (rlc-lte.direction == 1 and rlc-lte.am.frame_type == 0)");
993                 if (!statusOnlyPDUs) {
994                     /* Also filter data */
995                     offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
996                                          " or (rlc-lte.direction == 0 and rlc-lte.am.frame_type == 1)");
997                 }
998             }
999             else {
1000                 offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, " and (rlc-lte.direction == 0)");
1001             }
1002             break;
1003         case DL_Only:
1004             if (rlcMode == RLC_AM_MODE) {
1005                 /* Always filter status PDs */
1006                 offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
1007                                      " and (rlc-lte.direction == 0 and rlc-lte.am.frame_type == 0)");
1008                 if (!statusOnlyPDUs) {
1009                     /* Also filter data */
1010                     offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
1011                                          " or (rlc-lte.direction == 1 and rlc-lte.am.frame_type == 1)");
1012                 }
1013             }
1014             else {
1015                 offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, " and (rlc-lte.direction == 1)");
1016             }
1017             break;
1018         case UL_and_DL:
1019             if (rlcMode == RLC_AM_MODE) {
1020                 if (statusOnlyPDUs) {
1021                     g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, " and (rlc-lte.am.frame_type == 0)");
1022                 }
1023             }
1024
1025         default:
1026             break;
1027     }
1028
1029     /* Filter on a specific sequence number */
1030     if (filterOnSN != -1) {
1031         switch (rlcMode) {
1032             case RLC_AM_MODE:
1033                 offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
1034                                      " and ((rlc-lte.am.fixed.sn == %u) or "
1035                                      "(rlc-lte.am.ack-sn == %u) or "
1036                                      "(rlc-lte.am.nack-sn == %u))",
1037                                      filterOnSN, (filterOnSN+1) % 1024, filterOnSN);
1038                 break;
1039             case RLC_UM_MODE:
1040                 offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset,
1041                                      " and (rlc-lte.um.sn == %u)", filterOnSN);
1042                 break;
1043
1044             default:
1045                 break;
1046         }
1047     }
1048
1049     /* Close () if open */
1050     if (showDCTErrors) {
1051         offset += g_snprintf(buffer+offset, MAX_FILTER_LEN-offset, ")");
1052     }
1053
1054
1055     /* Set its value to our new string */
1056     gtk_entry_set_text(GTK_ENTRY(main_display_filter_widget), buffer);
1057
1058     /* Run the filter */
1059     main_filter_packets(&cfile, buffer, TRUE);
1060 }
1061
1062 /* Respond to UL filter button being clicked by building and using filter */
1063 static void ul_filter_clicked(GtkWindow *win _U_, rlc_lte_stat_t* hs)
1064 {
1065     guint16  ueid;
1066     guint8   rlcMode;
1067     guint16  channelType;
1068     guint16  channelId;
1069     int      sn = -1;
1070     const gchar *sn_string = "";
1071
1072     /* Read SN to filter on (if present) */
1073     sn_string = gtk_entry_get_text(GTK_ENTRY(hs->sn_filter_te));
1074     if (strlen(sn_string) > 0) {
1075         sn = atoi(sn_string);
1076     }
1077
1078     if (!get_channel_selection(hs, &ueid, &rlcMode, &channelType, &channelId)) {
1079         return;
1080     }
1081
1082     set_channel_filter_expression(ueid, rlcMode, channelType, channelId, UL_Only, sn,
1083                                   gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hs->show_only_control_pdus_cb)),
1084                                   gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hs->show_dct_errors_cb)),
1085                                   hs);
1086 }
1087
1088 /* Respond to DL filter button being clicked by building and using filter */
1089 static void dl_filter_clicked(GtkWindow *win _U_, rlc_lte_stat_t* hs)
1090 {
1091     guint16  ueid;
1092     guint8   rlcMode;
1093     guint16  channelType;
1094     guint16  channelId;
1095     int      sn = -1;
1096     const gchar *sn_string = "";
1097
1098     /* Read SN to filter on (if present) */
1099     sn_string = gtk_entry_get_text(GTK_ENTRY(hs->sn_filter_te));
1100     if (strlen(sn_string) > 0) {
1101         sn = atoi(sn_string);
1102     }
1103
1104     if (!get_channel_selection(hs, &ueid, &rlcMode, &channelType, &channelId)) {
1105         return;
1106     }
1107
1108     set_channel_filter_expression(ueid, rlcMode, channelType, channelId, DL_Only, sn,
1109                                   gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hs->show_only_control_pdus_cb)),
1110                                   gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hs->show_dct_errors_cb)),
1111                                   hs);
1112 }
1113
1114 /* Respond to UL/DL filter button being clicked by building and using filter */
1115 static void uldl_filter_clicked(GtkWindow *win _U_, rlc_lte_stat_t* hs)
1116 {
1117     guint16  ueid;
1118     guint8   rlcMode;
1119     guint16  channelType;
1120     guint16  channelId;
1121     int      sn = -1;
1122     const gchar *sn_string = "";
1123
1124     /* Read SN to filter on (if present) */
1125     sn_string = gtk_entry_get_text(GTK_ENTRY(hs->sn_filter_te));
1126     if (strlen(sn_string) > 0) {
1127         sn = atoi(sn_string);
1128     }
1129
1130     if (!get_channel_selection(hs, &ueid, &rlcMode, &channelType, &channelId)) {
1131         return;
1132     }
1133
1134     set_channel_filter_expression(ueid, rlcMode, channelType, channelId, UL_and_DL, sn,
1135                                   gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hs->show_only_control_pdus_cb)),
1136                                   gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(hs->show_dct_errors_cb)),
1137                                   hs);
1138 }
1139
1140
1141 /* Create a new RLC LTE stats dialog */
1142 static void gtk_rlc_lte_stat_init(const char *optarg, void *userdata _U_)
1143 {
1144     rlc_lte_stat_t    *hs;
1145     const char        *filter = NULL;
1146     GString           *error_string;
1147     GtkWidget         *ues_scrolled_window;
1148     GtkWidget         *channels_scrolled_window;
1149     GtkWidget         *bbox;
1150     GtkWidget         *top_level_vbox;
1151
1152     GtkWidget         *pdu_source_lb;
1153     GtkWidget         *common_channel_lb;
1154     GtkWidget         *channels_lb;
1155     GtkWidget         *filter_buttons_lb;
1156
1157     GtkWidget         *common_row_hbox;
1158     GtkWidget         *show_mac_cb;
1159     GtkWidget         *ues_vb;
1160     GtkWidget         *channels_vb;
1161     GtkWidget         *filter_vb;
1162     GtkWidget         *filter_buttons_hb;
1163     GtkWidget         *sn_filter_hb;
1164
1165     GtkWidget         *close_bt;
1166     GtkWidget         *help_bt;
1167     GtkTooltips       *tooltips = gtk_tooltips_new();
1168     GtkListStore      *store;
1169
1170     GtkTreeView       *tree_view;
1171     GtkCellRenderer   *renderer;
1172     GtkTreeViewColumn *column;
1173     GtkTreeSelection  *sel;
1174     gchar title[256];
1175     gint i;
1176
1177     /* Check for a filter string */
1178     if (strncmp(optarg, "rlc-lte,stat,", 13) == 0) {
1179         /* Skip those characters from filter to display */
1180         filter = optarg + 13;
1181     }
1182     else {
1183         /* No filter */
1184         filter = NULL;
1185     }
1186
1187
1188     /* Create dialog */
1189     hs = g_malloc(sizeof(rlc_lte_stat_t));
1190     hs->ep_list = NULL;
1191
1192     /* Copy filter (so can be used for window title at reset) */
1193     if (filter) {
1194         hs->filter = g_strdup(filter);
1195     }
1196     else {
1197         hs->filter = NULL;
1198     }
1199
1200
1201     /* Set title */
1202     g_snprintf(title, sizeof(title), "Wireshark: LTE RLC Statistics: %s",
1203                cf_get_display_name(&cfile));
1204     hs->dlg_w = window_new_with_geom(GTK_WINDOW_TOPLEVEL, title, "LTE RLC Statistics");
1205
1206     /* Window size */
1207     gtk_window_set_default_size(GTK_WINDOW(hs->dlg_w), 750, 300);
1208
1209     /* Will stack widgets vertically inside dlg */
1210     top_level_vbox = gtk_vbox_new(FALSE, 3);       /* FALSE = not homogeneous */
1211     gtk_container_add(GTK_CONTAINER(hs->dlg_w), top_level_vbox);
1212     gtk_container_set_border_width(GTK_CONTAINER(top_level_vbox), 6);
1213     gtk_widget_show(top_level_vbox);
1214
1215     /**********************************************/
1216     /* Exclude-MAC checkbox                       */
1217     pdu_source_lb = gtk_frame_new("PDUs to use");
1218     show_mac_cb = gtk_check_button_new_with_mnemonic("Show RLC PDUs found inside logged MAC frames");
1219     gtk_container_add(GTK_CONTAINER(pdu_source_lb), show_mac_cb);
1220     gtk_tooltips_set_tip(tooltips, show_mac_cb, "Can either use separately-logged RLC PDUs, OR find them "
1221                          "decoded inside MAC PDUs (enabled in MAC dissector preferences)", NULL);
1222
1223
1224     /* MAC on by default */
1225     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(show_mac_cb), TRUE);
1226     hs->show_mac = TRUE;
1227     gtk_box_pack_start(GTK_BOX(top_level_vbox), pdu_source_lb, FALSE, FALSE, 0);
1228     /* TODO: add tooltips... */
1229     g_signal_connect(show_mac_cb, "toggled", G_CALLBACK(toggle_show_mac), hs);
1230
1231
1232     /**********************************************/
1233     /* Common Channel data                        */
1234     /**********************************************/
1235     common_channel_lb = gtk_frame_new("Common Channel Data");
1236
1237     /* Will add BCCH and PCCH counters into one row */
1238     common_row_hbox = gtk_hbox_new(FALSE, 0);
1239     gtk_container_add(GTK_CONTAINER(common_channel_lb), common_row_hbox);
1240     gtk_container_set_border_width(GTK_CONTAINER(common_row_hbox), 5);
1241     gtk_box_pack_start(GTK_BOX(top_level_vbox), common_channel_lb, FALSE, FALSE, 0);
1242
1243     /* Create labels (that will hold label and counter value) */
1244     hs->common_bcch_frames = gtk_label_new("BCCH Frames:");
1245     gtk_misc_set_alignment(GTK_MISC(hs->common_bcch_frames), 0.0f, .5f);
1246     gtk_container_add(GTK_CONTAINER(common_row_hbox), hs->common_bcch_frames);
1247     gtk_widget_show(hs->common_bcch_frames);
1248
1249     hs->common_bcch_bytes = gtk_label_new("BCCH Bytes:");
1250     gtk_misc_set_alignment(GTK_MISC(hs->common_bcch_bytes), 0.0f, .5f);
1251     gtk_container_add(GTK_CONTAINER(common_row_hbox), hs->common_bcch_bytes);
1252     gtk_widget_show(hs->common_bcch_bytes);
1253
1254     hs->common_pcch_frames = gtk_label_new("PCCH Frames:");
1255     gtk_misc_set_alignment(GTK_MISC(hs->common_pcch_frames), 0.0f, .5f);
1256     gtk_container_add(GTK_CONTAINER(common_row_hbox), hs->common_pcch_frames);
1257     gtk_widget_show(hs->common_pcch_frames);
1258
1259     hs->common_pcch_bytes = gtk_label_new("PCCH Bytes:");
1260     gtk_misc_set_alignment(GTK_MISC(hs->common_pcch_bytes), 0.0f, .5f);
1261     gtk_container_add(GTK_CONTAINER(common_row_hbox), hs->common_pcch_bytes);
1262     gtk_widget_show(hs->common_pcch_bytes);
1263
1264
1265     /**********************************************/
1266     /* UE List                                    */
1267     /**********************************************/
1268
1269     hs->ues_lb = gtk_frame_new("UE Data (0 UEs)");
1270     ues_vb = gtk_vbox_new(FALSE, 0);
1271     gtk_container_add(GTK_CONTAINER(hs->ues_lb), ues_vb);
1272     gtk_container_set_border_width(GTK_CONTAINER(ues_vb), 5);
1273
1274     ues_scrolled_window = scrolled_window_new(NULL, NULL);
1275     gtk_box_pack_start(GTK_BOX(ues_vb), ues_scrolled_window, TRUE, TRUE, 0);
1276     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(ues_scrolled_window),
1277                                         GTK_SHADOW_IN);
1278
1279     /* Create the table of UE data */
1280     store = gtk_list_store_new(NUM_UE_COLUMNS, G_TYPE_INT,
1281                                G_TYPE_INT, G_TYPE_INT, G_TYPE_FLOAT, G_TYPE_INT, G_TYPE_INT, /* UL */
1282                                G_TYPE_INT, G_TYPE_INT, G_TYPE_FLOAT, G_TYPE_INT, G_TYPE_INT, /* DL */
1283                                G_TYPE_POINTER);
1284     hs->ue_table = GTK_TREE_VIEW(tree_view_new(GTK_TREE_MODEL(store)));
1285     gtk_container_add(GTK_CONTAINER (ues_scrolled_window), GTK_WIDGET(hs->ue_table));
1286     g_object_unref(G_OBJECT(store));
1287
1288     tree_view = hs->ue_table;
1289     gtk_tree_view_set_headers_visible(tree_view, TRUE);
1290     gtk_tree_view_set_headers_clickable(tree_view, TRUE);
1291
1292     /* Create the titles for each column of the per-UE table */
1293     for (i = 0; i < UE_TABLE_COLUMN; i++) {
1294         renderer = gtk_cell_renderer_text_new();
1295         column = gtk_tree_view_column_new_with_attributes(ue_titles[i], renderer,
1296                                                           "text", i, NULL);
1297         gtk_tree_view_column_set_sort_column_id(column, i);
1298
1299         if (i == 0) {
1300             /* Expand first column (RNTI, which is Key) */
1301             gtk_tree_view_column_set_expand(column, TRUE);
1302         } else {
1303             /* For other columns, set all of the free space to be on the left */
1304             g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1305         }
1306         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1307         gtk_tree_view_column_set_resizable(column, TRUE);
1308         gtk_tree_view_append_column(tree_view, column);
1309     }
1310
1311     /* Set callback function for selecting a row in the UE table */
1312     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(hs->ue_table));
1313     gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
1314     g_signal_connect(sel, "changed", G_CALLBACK(rlc_lte_select_ue_cb), hs);
1315
1316     gtk_box_pack_start(GTK_BOX(top_level_vbox), hs->ues_lb, TRUE, TRUE, 0);
1317
1318
1319     /**********************************************/
1320     /* Channels of selected UE                    */
1321     /**********************************************/
1322     channels_lb = gtk_frame_new("Channels of selected UE");
1323
1324     channels_vb = gtk_vbox_new(FALSE, 6);
1325     gtk_container_add(GTK_CONTAINER(channels_lb), channels_vb);
1326     gtk_container_set_border_width(GTK_CONTAINER(channels_vb), 5);
1327
1328     channels_scrolled_window = scrolled_window_new(NULL, NULL);
1329     gtk_box_pack_start(GTK_BOX(channels_vb), channels_scrolled_window, TRUE, TRUE, 0);
1330     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(channels_scrolled_window),
1331                                         GTK_SHADOW_IN);
1332
1333     /* Create the table of UE data */
1334     store = gtk_list_store_new(NUM_CHANNEL_COLUMNS,
1335                                G_TYPE_STRING, G_TYPE_STRING, /* name & type */
1336                                G_TYPE_INT, G_TYPE_INT, G_TYPE_FLOAT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, /* UL */
1337                                G_TYPE_INT, G_TYPE_INT, G_TYPE_FLOAT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, /* DL */
1338                                G_TYPE_POINTER);
1339     hs->channel_table = GTK_TREE_VIEW(tree_view_new(GTK_TREE_MODEL(store)));
1340     gtk_container_add(GTK_CONTAINER (channels_scrolled_window), GTK_WIDGET(hs->channel_table));
1341     g_object_unref(G_OBJECT(store));
1342
1343     tree_view = hs->channel_table;
1344     gtk_tree_view_set_headers_visible(tree_view, TRUE);
1345     gtk_tree_view_set_headers_clickable(tree_view, TRUE);
1346
1347     /* Create the titles for each column of the per-UE table */
1348     for (i = 0; i < CHANNEL_TABLE_COLUMN; i++) {
1349         renderer = gtk_cell_renderer_text_new();
1350         column = gtk_tree_view_column_new_with_attributes(channel_titles[i], renderer,
1351                                                           "text", i, NULL);
1352         gtk_tree_view_column_set_sort_column_id(column, i);
1353
1354         if (i == 0) {
1355             /* Expand first column (Type) */
1356             gtk_tree_view_column_set_expand(column, TRUE);
1357         } else {
1358             /* For other columns, set all of the free space to be on the left */
1359             g_object_set(G_OBJECT(renderer), "xalign", 1.0, NULL);
1360         }
1361         gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_AUTOSIZE);
1362         gtk_tree_view_column_set_resizable(column, TRUE);
1363         gtk_tree_view_append_column(tree_view, column);
1364     }
1365
1366     /* Set callback function for selecting a row in the channel table */
1367     sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(hs->channel_table));
1368     gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
1369     g_signal_connect(sel, "changed", G_CALLBACK(rlc_lte_select_channel_cb), hs);
1370
1371     gtk_box_pack_start(GTK_BOX(top_level_vbox), channels_lb, TRUE, TRUE, 0);
1372
1373
1374     /**********************************************/
1375     /* Channel filters                            */
1376     /**********************************************/
1377
1378     filter_buttons_lb = gtk_frame_new("Filter on selected channel");
1379
1380     filter_vb = gtk_vbox_new(FALSE, 3);
1381     gtk_container_add(GTK_CONTAINER(filter_buttons_lb), filter_vb);
1382
1383     /* Horizontal row of filter buttons */
1384     filter_buttons_hb = gtk_hbox_new(FALSE, 6);
1385     gtk_container_add(GTK_CONTAINER(filter_vb), filter_buttons_hb);
1386     gtk_container_set_border_width(GTK_CONTAINER(filter_buttons_hb), 2);
1387
1388     /* UL only */
1389     hs->ul_filter_bt = gtk_button_new_with_label("Set UL display filter for this channel");
1390     gtk_box_pack_start(GTK_BOX(filter_buttons_hb), hs->ul_filter_bt, TRUE, TRUE, 0);
1391     g_signal_connect(hs->ul_filter_bt, "clicked", G_CALLBACK(ul_filter_clicked), hs);
1392     gtk_widget_show(hs->ul_filter_bt);
1393     gtk_tooltips_set_tip(tooltips, hs->ul_filter_bt, "Generate and set a display filter to show frames "
1394                          "associated with the channel, in the UL direction only. "
1395                          "N.B. DL Status PDUs sent on this channel will also be shown for AM", NULL);
1396
1397     /* DL only */
1398     hs->dl_filter_bt = gtk_button_new_with_label("Set DL display filter for this channel");
1399     gtk_box_pack_start(GTK_BOX(filter_buttons_hb), hs->dl_filter_bt, TRUE, TRUE, 0);
1400     g_signal_connect(hs->dl_filter_bt, "clicked", G_CALLBACK(dl_filter_clicked), hs);
1401     gtk_widget_show(hs->dl_filter_bt);
1402     gtk_tooltips_set_tip(tooltips, hs->dl_filter_bt, "Generate and set a display filter to show frames "
1403                          "associated with the channel, in the DL direction only. "
1404                          "N.B. UL Status PDUs sent on this channel will also be shown for AM", NULL);
1405
1406     /* UL and DL */
1407     hs->uldl_filter_bt = gtk_button_new_with_label("Set UL / DL display filter for this channel");
1408     gtk_box_pack_start(GTK_BOX(filter_buttons_hb), hs->uldl_filter_bt, TRUE, TRUE, 0);
1409     g_signal_connect(hs->uldl_filter_bt, "clicked", G_CALLBACK(uldl_filter_clicked), hs);
1410     gtk_widget_show(hs->uldl_filter_bt);
1411     gtk_tooltips_set_tip(tooltips, hs->uldl_filter_bt, "Generate and set a display filter to show frames "
1412                          "associated with the channel, in UL and DL", NULL);
1413
1414     /* Allow filtering on specific SN number. */
1415     /* Row with label and text entry control  */
1416     sn_filter_hb = gtk_hbox_new(FALSE, 3);
1417     gtk_container_add(GTK_CONTAINER(filter_vb), sn_filter_hb);
1418     gtk_widget_show(sn_filter_hb);
1419
1420     /* Allow filtering only to select status PDUs for AM */
1421     hs->show_only_control_pdus_cb = gtk_check_button_new_with_mnemonic("Show only status PDUs");
1422     gtk_container_add(GTK_CONTAINER(sn_filter_hb), hs->show_only_control_pdus_cb);
1423     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(hs->show_only_control_pdus_cb), FALSE);
1424     gtk_tooltips_set_tip(tooltips, hs->show_only_control_pdus_cb, "Generated filters will only show AM status PDUs "
1425                          "(i.e. if you filter on UL you'll see ACKs/NACK replies sent in the DL)", NULL);
1426
1427     /* Allow DCT errors to be shown... */
1428     hs->show_dct_errors_cb = gtk_check_button_new_with_mnemonic("Show DCT2000 error strings");
1429     gtk_container_add(GTK_CONTAINER(sn_filter_hb), hs->show_dct_errors_cb);
1430     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(hs->show_dct_errors_cb), FALSE);
1431     gtk_tooltips_set_tip(tooltips, hs->show_dct_errors_cb, "When checked, generated filters will "
1432                          "include DCT2000 error strings", NULL);
1433
1434     /* Allow filtering of a particular sequence number */
1435     hs->sn_filter_te = gtk_entry_new();
1436     gtk_box_pack_end(GTK_BOX(sn_filter_hb), hs->sn_filter_te, FALSE, FALSE, 0);
1437     gtk_widget_show(hs->sn_filter_te);
1438     gtk_tooltips_set_tip(tooltips, hs->sn_filter_te, "Can limit generated filters to a given sequence number (0-1023). "
1439                          "Will also include relevant AM status PDUs", NULL);
1440
1441     hs->sn_filter_lb = gtk_label_new("Sequence number to filter on:");
1442     gtk_box_pack_end(GTK_BOX(sn_filter_hb), hs->sn_filter_lb, FALSE, FALSE, 0);
1443     gtk_widget_show(hs->sn_filter_lb);
1444
1445
1446     /* Add filters box to top-level window */
1447     gtk_box_pack_start(GTK_BOX(top_level_vbox), filter_buttons_lb, FALSE, FALSE, 0);
1448
1449     enable_filter_controls(FALSE, 0, hs);
1450
1451     /**********************************************/
1452     /* Register the tap listener                  */
1453     /**********************************************/
1454
1455     error_string = register_tap_listener("rlc-lte", hs,
1456                                          filter, 0,
1457                                          rlc_lte_stat_reset,
1458                                          rlc_lte_stat_packet,
1459                                          rlc_lte_stat_draw);
1460     if (error_string) {
1461         simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", error_string->str);
1462         g_string_free(error_string, TRUE);
1463         g_free(hs);
1464         return;
1465     }
1466
1467
1468     /************************************/
1469     /* Bottom button row.                */
1470     /************************************/
1471
1472     bbox = dlg_button_row_new(GTK_STOCK_CLOSE, GTK_STOCK_HELP, NULL);
1473     gtk_box_pack_end(GTK_BOX(top_level_vbox), bbox, FALSE, FALSE, 0);
1474
1475     /* Add the close button */
1476     close_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_CLOSE);
1477     window_set_cancel_button(hs->dlg_w, close_bt, window_cancel_button_cb);
1478
1479     help_bt = g_object_get_data(G_OBJECT(bbox), GTK_STOCK_HELP);
1480     g_signal_connect(help_bt, "clicked", G_CALLBACK(topic_cb), (gpointer)HELP_STATS_LTE_RLC_TRAFFIC_DIALOG);
1481
1482     /* Set callbacks */
1483     g_signal_connect(hs->dlg_w, "delete_event", G_CALLBACK(window_delete_event_cb), NULL);
1484     g_signal_connect(hs->dlg_w, "destroy", G_CALLBACK(win_destroy_cb), hs);
1485
1486     /* Show the window */
1487     gtk_widget_show_all(hs->dlg_w);
1488     window_present(hs->dlg_w);
1489
1490     /* Retap */
1491     cf_retap_packets(&cfile);
1492     gdk_window_raise(hs->dlg_w->window);
1493 }
1494
1495
1496 static tap_dfilter_dlg rlc_lte_stat_dlg = {
1497     "LTE RLC Stats",
1498     "rlc-lte,stat",
1499     gtk_rlc_lte_stat_init,
1500     -1
1501 };
1502
1503
1504 /* Register this tap listener (need void on own so line register function found) */
1505 void
1506 register_tap_listener_rlc_lte_stat(void)
1507 {
1508     register_dfilter_stat(&rlc_lte_stat_dlg, "_LTE RLC...", REGISTER_STAT_GROUP_TELEPHONY);
1509 }
1510