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