e1_input: add 'port_nr' field and support DAHDI T1 cards

the "e1_line <0-255> port <0-255>" vty command allows the user to
set which physical port/card number should be represented by the
given virtual e1_line.

Furthermore, we now actually query the DAHDI hardware to determine the
number of ports of a given span (e.g. only 24 in case of T1) instead of
blindly assuming there are 31 timeslots on each port.

This specifically will fix T1 timeslot (/dev/dahdi/%u) calculation in
setups with multiple DAHDI spans/ports and a T1 span != span 1.
diff --git a/include/osmocom/abis/e1_input.h b/include/osmocom/abis/e1_input.h
index 42b1758..b304254 100644
--- a/include/osmocom/abis/e1_input.h
+++ b/include/osmocom/abis/e1_input.h
@@ -157,10 +157,12 @@
 
 	unsigned int num;
 	const char *name;
+	unsigned int port_nr;
 	struct rate_ctr_group *rate_ctr;
 
 	/* array of timestlots */
 	struct e1inp_ts ts[NUM_E1_TS];
+	unsigned int num_ts;
 
 	const struct e1inp_line_ops *ops;
 
diff --git a/src/e1_input.c b/src/e1_input.c
index ad0778a..cad39aa 100644
--- a/src/e1_input.c
+++ b/src/e1_input.c
@@ -337,7 +337,8 @@
 
 	line->rate_ctr = rate_ctr_group_alloc(line, &e1inp_ctr_g_d, line->num);
 
-	for (i = 0; i < NUM_E1_TS; i++) {
+	line->num_ts = NUM_E1_TS;
+	for (i = 0; i < line->num_ts; i++) {
 		line->ts[i].num = i+1;
 		line->ts[i].line = line;
 	}
diff --git a/src/e1_input_vty.c b/src/e1_input_vty.c
index bcc0251..10c4687 100644
--- a/src/e1_input_vty.c
+++ b/src/e1_input_vty.c
@@ -69,6 +69,25 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN(cfg_e1line_port, cfg_e1_line_port_cmd,
+	"e1_line <0-255> port <0-255>"
+	E1_LINE_HELP, "Set physical port/span/card number\n"
+	"E1/T1 Port/Span/Card number\n")
+{
+	struct e1inp_line *line;
+	int e1_nr = atoi(argv[0]);
+
+	line = e1inp_line_find(e1_nr);
+	if (!line) {
+		vty_out(vty, "%% Line %d doesn't exist%s", e1_nr, VTY_NEWLINE);
+		return CMD_WARNING;
+	}
+
+	line->port_nr = atoi(argv[1]);
+
+	return CMD_SUCCESS;
+}
+
 DEFUN(cfg_e1line_name, cfg_e1_line_name_cmd,
 	"e1_line <0-255> name .LINE",
 	E1_LINE_HELP "Set name for this line\n" "Human readable name\n")
@@ -111,6 +130,8 @@
 	llist_for_each_entry(line, &e1inp_line_list, list) {
 		vty_out(vty, " e1_line %u driver %s%s", line->num,
 			line->driver->name, VTY_NEWLINE);
+		vty_out(vty, " e1_line %u port %u%s", line->num,
+			line->port_nr, VTY_NEWLINE);
 		if (line->name)
 			vty_out(vty, " e1_line %u name %s%s", line->num,
 				line->name, VTY_NEWLINE);
@@ -200,7 +221,7 @@
 
 	if (argc == 0) {
 		llist_for_each_entry(line, &e1inp_line_list, list) {
-			for (ts_nr = 0; ts_nr < NUM_E1_TS; ts_nr++) {
+			for (ts_nr = 0; ts_nr < line->num_ts; ts_nr++) {
 				ts = &line->ts[ts_nr];
 				e1ts_dump_vty(vty, ts);
 			}
@@ -224,7 +245,7 @@
 	}
 	if (argc >= 2) {
 		ts_nr = atoi(argv[1]);
-		if (ts_nr >= NUM_E1_TS) {
+		if (ts_nr >= line->num_ts) {
 			vty_out(vty, "E1 timeslot %s is invalid%s",
 				argv[1], VTY_NEWLINE);
 			return CMD_WARNING;
@@ -233,7 +254,7 @@
 		e1ts_dump_vty(vty, ts);
 		return CMD_SUCCESS;
 	} else {
-		for (ts_nr = 0; ts_nr < NUM_E1_TS; ts_nr++) {
+		for (ts_nr = 0; ts_nr < line->num_ts; ts_nr++) {
 			ts = &line->ts[ts_nr];
 			e1ts_dump_vty(vty, ts);
 		}
@@ -254,6 +275,7 @@
 	install_element(CONFIG_NODE, &cfg_e1inp_cmd);
 	install_node(&e1inp_node, e1inp_config_write);
 	install_element(L_E1INP_NODE, &cfg_e1_line_driver_cmd);
+	install_element(L_E1INP_NODE, &cfg_e1_line_port_cmd);
 	install_element(L_E1INP_NODE, &cfg_e1_line_name_cmd);
 
 	install_element_ve(&show_e1drv_cmd);
diff --git a/src/input/dahdi.c b/src/input/dahdi.c
index 66bf53f..75331cc 100644
--- a/src/input/dahdi.c
+++ b/src/input/dahdi.c
@@ -51,6 +51,62 @@
 
 #define TS1_ALLOC_SIZE	300
 
+struct span_cfg {
+	struct llist_head list;
+
+	unsigned int span_nr;
+	unsigned int chan_base;
+	unsigned int chan_num;
+};
+
+static struct span_cfg *span_cfgs[DAHDI_MAX_SPANS];
+
+static int reread_span_cfgs(void)
+{
+	struct dahdi_spaninfo si;
+	unsigned int basechan = 1;
+	int i;
+	int fd;
+
+	if ((fd = open("/dev/dahdi/ctl", O_RDWR)) < 0) {
+		LOGP(DLMI, LOGL_ERROR, "Unable to open DAHDI ctl: %s\n",
+			strerror(errno));
+		return -EIO;
+	}
+
+	for (i = 1; i < DAHDI_MAX_SPANS; i++) {
+		struct span_cfg *scfg;
+
+		/* clear any old cached information */
+		if (span_cfgs[i]) {
+			talloc_free(span_cfgs[i]);
+			span_cfgs[i] = NULL;
+		}
+
+		memset(&si, 0, sizeof(si));
+		si.spanno = i;
+		if (ioctl(fd, DAHDI_SPANSTAT, &si))
+			continue;
+
+		/* create and link new span_cfg */
+		scfg = talloc_zero(NULL, struct span_cfg);
+		if (!scfg) {
+			close(fd);
+			return -ENOMEM;
+		}
+		scfg->span_nr = i;
+		scfg->chan_num = si.totalchans;
+		scfg->chan_base = basechan;
+		span_cfgs[i] = scfg;
+
+		basechan += si.totalchans;
+	}
+
+	close(fd);
+
+	return 0;
+}
+
 /* Corresponds to dahdi/user.h, only PRI related events */
 static const struct value_string dahdi_evt_names[] = {
 	{ DAHDI_EVENT_NONE,		"NONE" },
@@ -383,10 +439,19 @@
 
 static int dahdi_e1_setup(struct e1inp_line *line)
 {
+	struct span_cfg *scfg;
 	int ts, ret;
 
+	reread_span_cfgs();
+
+	scfg = span_cfgs[line->port_nr];
+	if (!scfg)
+		return -EIO;
+
+	line->num_ts = scfg->chan_num;
+
 	/* TS0 is CRC4, don't need any fd for it */
-	for (ts = 1; ts < NUM_E1_TS; ts++) {
+	for (ts = 1; ts <= scfg->chan_num; ts++) {
 		unsigned int idx = ts-1;
 		char openstr[128];
 		struct e1inp_ts *e1i_ts = &line->ts[idx];
@@ -400,7 +465,7 @@
 		/* DAHDI device names/numbers just keep incrementing
 		 * even over multiple boards.  So TS1 of the second
 		 * board will be 32 */
-		dev_nr = line->num * (NUM_E1_TS-1) + ts;
+		dev_nr = scfg->chan_base + idx;
 
 		bfd->data = line;
 		bfd->priv_nr = ts;
diff --git a/src/input/misdn.c b/src/input/misdn.c
index 716c1e7..fce6150 100644
--- a/src/input/misdn.c
+++ b/src/input/misdn.c
@@ -495,7 +495,7 @@
 
 		memset(&addr, 0, sizeof(addr));
 		addr.family = AF_ISDN;
-		addr.dev = line->num;
+		addr.dev = line->port_nr;
 		switch (e1i_ts->type) {
 		case E1INP_TS_TYPE_SIGN:
 			if (mline->use_userspace_lapd) {
@@ -579,11 +579,11 @@
 	//DEBUGP(DLMI,"%d device%s found\n", cnt, (cnt==1)?"":"s");
 	printf("%d device%s found\n", cnt, (cnt==1)?"":"s");
 #if 1
-	devinfo.id = line->num;
+	devinfo.id = line->port_nr;
 	ret = ioctl(sk, IMGETDEVINFO, &devinfo);
 	if (ret < 0) {
 		fprintf(stdout, "error getting info for device %d: %s\n",
-			line->num, strerror(errno));
+			line->port_nr, strerror(errno));
 		return -ENODEV;
 	}
 	fprintf(stdout, "        id:             %d\n", devinfo.id);