Display current channel usage load in 'show bts' and 'show network'

This is just the load at one given instant.  We definitely also want to see
some averages and record the measurements in a database later.
diff --git a/openbsc/src/chan_alloc.c b/openbsc/src/chan_alloc.c
index c42b60b..6328608 100644
--- a/openbsc/src/chan_alloc.c
+++ b/openbsc/src/chan_alloc.c
@@ -121,6 +121,7 @@
 	[GSM_PCHAN_TCH_F] = 1,
 	[GSM_PCHAN_TCH_H] = 2,
 	[GSM_PCHAN_SDCCH8_SACCH8C] = 8,
+	/* FIXME: what about dynamic TCH_F_TCH_H ? */
 };
 
 static struct gsm_lchan *
@@ -329,3 +330,51 @@
 
 	return NULL;
 }
+
+void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts)
+{
+	struct gsm_bts_trx *trx;
+
+	llist_for_each_entry(trx, &bts->trx_list, list) {
+		int i;
+
+		/* skip administratively deactivated tranxsceivers */
+		if (trx->nm_state.availability != NM_AVSTATE_OK ||
+		    trx->bb_transc.nm_state.availability != NM_AVSTATE_OK)
+			continue;
+
+		for (i = 0; i < ARRAY_SIZE(trx->ts); i++) {
+			struct gsm_bts_trx_ts *ts = &trx->ts[i];
+			struct load_counter *pl = &cl->pchan[ts->pchan];
+			int j;
+
+			/* skip administratively deactivated timeslots */
+			if (ts->nm_state.availability != NM_AVSTATE_OK)
+				continue;
+
+			for (j = 0; j < subslots_per_pchan[ts->pchan]; j++) {
+				struct gsm_lchan *lchan = &ts->lchan[j];
+
+				pl->total++;
+
+				switch (lchan->state) {
+				case LCHAN_S_NONE:
+					break;
+				default:
+					pl->used++;
+					break;
+				}
+			}
+		}
+	}
+}
+
+void network_chan_load(struct pchan_load *pl, struct gsm_network *net)
+{
+	struct gsm_bts *bts;
+
+	memset(pl, 0, sizeof(*pl));
+
+	llist_for_each_entry(bts, &net->bts_list, list)
+		bts_chan_load(pl, bts);
+}
diff --git a/openbsc/src/vty_interface.c b/openbsc/src/vty_interface.c
index fdc678a..077e1d0 100644
--- a/openbsc/src/vty_interface.c
+++ b/openbsc/src/vty_interface.c
@@ -33,6 +33,7 @@
 #include <openbsc/e1_input.h>
 #include <openbsc/abis_nm.h>
 #include <openbsc/gsm_utils.h>
+#include <openbsc/chan_alloc.h>
 #include <openbsc/db.h>
 #include <openbsc/talloc.h>
 
@@ -74,8 +75,30 @@
 		nm_avail_name(nms->availability), VTY_NEWLINE);
 }
 
+static void dump_pchan_load_vty(struct vty *vty, char *prefix,
+				const struct pchan_load *pl)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(pl->pchan); i++) {
+		const struct load_counter *lc = &pl->pchan[i];
+		unsigned int percent;
+
+		if (lc->total == 0)
+			continue;
+
+		percent = (lc->used * 100) / lc->total;
+
+		vty_out(vty, "%s%20s: %3u%% (%u/%u)%s", prefix,
+			gsm_pchan_name(i), percent, lc->used, lc->total,
+			VTY_NEWLINE);
+	}
+}
+
 static void net_dump_vty(struct vty *vty, struct gsm_network *net)
 {
+	struct pchan_load pl;
+
 	vty_out(vty, "BSC is on Country Code %u, Network Code %u "
 		"and has %u BTS%s", net->country_code, net->network_code,
 		net->num_bts, VTY_NEWLINE);
@@ -97,6 +120,9 @@
 		VTY_NEWLINE);
 	vty_out(vty, "  Handover: %s%s", net->handover.active ? "On" : "Off",
 		VTY_NEWLINE);
+	network_chan_load(&pl, net);
+	vty_out(vty, "  Current Channel Load:%s", VTY_NEWLINE);
+	dump_pchan_load_vty(vty, "    ", &pl);
 }
 
 DEFUN(show_net, show_net_cmd, "show network",
@@ -128,6 +154,8 @@
 
 static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts)
 {
+	struct pchan_load pl;
+
 	vty_out(vty, "BTS %u is of %s type in band %s, has CI %u LAC %u, "
 		"BSIC %u, TSC %u and %u TRX%s",
 		bts->nr, btstype2str(bts->type), gsm_band_name(bts->band),
@@ -157,6 +185,10 @@
 		e1isl_dump_vty(vty, bts->oml_link);
 	}
 	/* FIXME: oml_link, chan_desc */
+	memset(&pl, 0, sizeof(pl));
+	bts_chan_load(&pl, bts);
+	vty_out(vty, "  Current Channel Load:%s", VTY_NEWLINE);
+	dump_pchan_load_vty(vty, "    ", &pl);
 }
 
 DEFUN(show_bts, show_bts_cmd, "show bts [number]",