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