multislot: Extracted "slot allocation algorithm" from tbf allocator

The current available algorithm only supports selecting a single slot
for downlink/uplink. (In the future, a multislot algorithm will follow.)
diff --git a/src/gprs_rlcmac.cpp b/src/gprs_rlcmac.cpp
index ab04aea..3db405f 100644
--- a/src/gprs_rlcmac.cpp
+++ b/src/gprs_rlcmac.cpp
@@ -176,17 +176,16 @@
 }
 
 struct gprs_rlcmac_tbf *tbf_alloc(enum gprs_rlcmac_tbf_direction dir,
-	uint8_t tfi, uint8_t trx, uint8_t first_ts, uint8_t num_ts)
+	uint8_t tfi, uint8_t trx, uint8_t first_ts, uint8_t ms_class)
 {
 	struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
-	struct gprs_rlcmac_pdch *pdch;
 	struct gprs_rlcmac_tbf *tbf;
-	uint8_t ts_count, ts;
-	int8_t usf, tsc = -1; /* both must be signed */
+	int rc;
 
 	LOGP(DRLCMAC, LOGL_DEBUG, "********** TBF starts here **********\n");
-	LOGP(DRLCMAC, LOGL_INFO, "Allocating %s TBF with TFI=%d on TRX=%d.\n",
-		(dir == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tfi, trx);
+	LOGP(DRLCMAC, LOGL_INFO, "Allocating %s TBF: TFI=%d TRX=%d "
+		"MS_CLASS=%d\n", (dir == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL",
+		tfi, trx, ms_class);
 
 	if (trx >= 8 || first_ts >= 8 || tfi >= 32)
 		return NULL;
@@ -199,12 +198,34 @@
 	tbf->tfi = tfi;
 	tbf->trx = trx;
 	tbf->arfcn = bts->trx[trx].arfcn;
-	/* assign free TS to TBF, where TFI is free
-	 * for uplink: assign free USF to each uplink TS
-	 * Note that the first TS must be free, because it was selected by
-	 * tfi_alloc(). */
-	for (ts_count = 0, ts = first_ts; ts < 8; ts++) {
-		pdch = &bts->trx[trx].pdch[ts];
+	tbf->first_ts = first_ts;
+	tbf->ms_class = ms_class;
+	tbf->ws = 64;
+	tbf->sns = 128;
+	/* select algorithm according to multislot class */
+	if (ms_class)
+		rc = bts->alloc_algorithm(tbf);
+	else
+		rc = alloc_algorithm_a(tbf);
+	/* if no ressource */
+	if (rc < 0) {
+		talloc_free(tbf);
+		return NULL;
+	}
+
+	INIT_LLIST_HEAD(&tbf->llc_queue);
+	if (dir == GPRS_RLCMAC_UL_TBF)
+		llist_add(&tbf->list, &gprs_rlcmac_ul_tbfs);
+	else
+		llist_add(&tbf->list, &gprs_rlcmac_dl_tbfs);
+
+	return tbf;
+}
+
+#if 0
+int alloc_algorithm_b(struct gprs_rlcmac_tbf *tbf)
+{
+		pdch = &bts->trx[tbf->trx].pdch[ts];
 		if (!pdch->enable)
 			continue;
 		if (tsc < 0)
@@ -214,55 +235,59 @@
 				"because it has different TSC than lower TS "
 				"of TRX. In order to allow multislot, all "
 				"slots must be configured with the same TSC!\n",
-				ts, trx);
+				ts, tbf->trx);
 			continue;
 		}
-		if (dir == GPRS_RLCMAC_UL_TBF) {
-			/* if TFI is free on TS */
-			if (!pdch->ul_tbf[tfi]) {
-				/* if USF available */
-				usf = find_free_usf(pdch, ts);
-				if (usf >= 0) {
-					LOGP(DRLCMAC, LOGL_DEBUG, " Assign "
-						"uplink TS=%d USF=%d\n",
-						ts, usf);
-					pdch->ul_tbf[tfi] = tbf;
-					tbf->pdch[ts] = pdch;
-					ts_count++;
-				} else
-					LOGP(DRLCMAC, LOGL_DEBUG, " Skipping "
-						"TS=%d, no USF available\n",
-						ts);
+#endif
+
+int alloc_algorithm_a(struct gprs_rlcmac_tbf *tbf)
+{
+	struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+	struct gprs_rlcmac_pdch *pdch;
+	uint8_t ts = tbf->first_ts;
+	int8_t usf; /* must be signed */
+
+	pdch = &bts->trx[tbf->trx].pdch[ts];
+	if (!pdch->enable) {
+		LOGP(DRLCMAC, LOGL_ERROR, "TS=%d not enabled.", ts);
+			return -EIO;
+	}
+	tbf->tsc = pdch->tsc;
+	if (tbf->direction == GPRS_RLCMAC_UL_TBF) {
+		/* if TFI is free on TS */
+		if (!pdch->ul_tbf[tbf->tfi]) {
+			/* if USF available */
+			usf = find_free_usf(pdch, ts);
+			if (usf >= 0) {
+				LOGP(DRLCMAC, LOGL_DEBUG, " Assign uplink "
+					"TS=%d USF=%d\n", ts, usf);
+				pdch->ul_tbf[tbf->tfi] = tbf;
+				tbf->pdch[ts] = pdch;
+			} else {
+				LOGP(DRLCMAC, LOGL_NOTICE, " Failed allocating "
+					"TS=%d, no USF available\n", ts);
+				return -EBUSY;
 			}
 		} else {
-			/* if TFI is free on TS */
-			if (!pdch->dl_tbf[tfi]) {
-				LOGP(DRLCMAC, LOGL_DEBUG, " Assign downlink "
-					"TS=%d\n", ts);
-				pdch->dl_tbf[tfi] = tbf;
-				tbf->pdch[ts] = pdch;
-				ts_count++;
-			}
+			LOGP(DRLCMAC, LOGL_NOTICE, " Failed allocating "
+				"TS=%d, TFI is not available\n", ts);
+			return -EBUSY;
 		}
-		if (ts_count == num_ts)
-			break;
-	}
-	if (!ts_count) { /* implies that direction is uplink */
-		LOGP(DRLCMAC, LOGL_NOTICE, "No USF available\n");
-		talloc_free(tbf);
-		return NULL;
+	} else {
+		/* if TFI is free on TS */
+		if (!pdch->dl_tbf[tbf->tfi]) {
+			LOGP(DRLCMAC, LOGL_DEBUG, " Assign downlink TS=%d\n",
+				ts);
+			pdch->dl_tbf[tbf->tfi] = tbf;
+			tbf->pdch[ts] = pdch;
+		} else {
+			LOGP(DRLCMAC, LOGL_NOTICE, " Failed allocating "
+				"TS=%d, TFI is not available\n", ts);
+			return -EBUSY;
+		}
 	}
 
-	tbf->first_ts = first_ts;
-	tbf->ws = 64;
-	tbf->sns = 128;
-	INIT_LLIST_HEAD(&tbf->llc_queue);
-	if (dir == GPRS_RLCMAC_UL_TBF)
-		llist_add(&tbf->list, &gprs_rlcmac_ul_tbfs);
-	else
-		llist_add(&tbf->list, &gprs_rlcmac_dl_tbfs);
-
-	return tbf;
+	return 0;
 }
 
 void tbf_free(struct gprs_rlcmac_tbf *tbf)
diff --git a/src/gprs_rlcmac.h b/src/gprs_rlcmac.h
index db0e9ab..71edf3d 100644
--- a/src/gprs_rlcmac.h
+++ b/src/gprs_rlcmac.h
@@ -71,6 +71,7 @@
 	uint8_t n3103;
 	uint8_t n3105;
 	struct gprs_rlcmac_trx trx[8];
+	int (*alloc_algorithm)(struct gprs_rlcmac_tbf *tbf);
 };
 
 extern struct gprs_rlcmac_bts *gprs_rlcmac_bts;
@@ -135,6 +136,7 @@
 	uint16_t arfcn;
 	uint8_t tsc;
 	uint8_t first_ts;
+	uint8_t ms_class;
 	struct gprs_rlcmac_pdch *pdch[8]; /* list of PDCHs allocated to TBF */
 	uint16_t ta;
 	uint8_t llc_frame[LLC_MAX_LEN]; /* current DL or UL frame */
@@ -196,7 +198,9 @@
 	uint8_t use_trx, uint8_t first_ts);
 
 struct gprs_rlcmac_tbf *tbf_alloc(enum gprs_rlcmac_tbf_direction dir,
-	uint8_t tfi, uint8_t trx, uint8_t first_ts, uint8_t num_ts);
+	uint8_t tfi, uint8_t trx, uint8_t first_ts, uint8_t ms_class);
+
+int alloc_algorithm_a(struct gprs_rlcmac_tbf *tbf);
 
 struct gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi, uint8_t trx, uint8_t ts,
         enum gprs_rlcmac_tbf_direction dir);
diff --git a/src/gprs_rlcmac_data.cpp b/src/gprs_rlcmac_data.cpp
index 0e8aca3..1b936fc 100644
--- a/src/gprs_rlcmac_data.cpp
+++ b/src/gprs_rlcmac_data.cpp
@@ -207,9 +207,9 @@
 				/* FIXME: send reject */
 				break;
 			}
-			/* FIXME: set number of downlink slots according to
-			 * multislot class */
-			ul_tbf = tbf_alloc(GPRS_RLCMAC_UL_TBF, tfi, trx, ts, 1);
+			/* use multislot class of downlink TBF */
+			ul_tbf = tbf_alloc(GPRS_RLCMAC_UL_TBF, tfi, trx, ts,
+				tbf->ms_class);
 			if (!ul_tbf) {
 				LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n");
 				/* FIXME: send reject */
@@ -825,8 +825,8 @@
 		/* FIXME: send reject */
 		return -EBUSY;
 	}
-	/* select only one TS, since we don't know the multislot class yet */
-	tbf = tbf_alloc(GPRS_RLCMAC_UL_TBF, tfi, trx, ts, 1);
+	/* set class to 0, since we don't know the multislot class yet */
+	tbf = tbf_alloc(GPRS_RLCMAC_UL_TBF, tfi, trx, ts, 0);
 	if (!tbf) {
 		LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n");
 		/* FIXME: send reject */
diff --git a/src/pcu_main.cpp b/src/pcu_main.cpp
index 2c6d763..9fd0455 100644
--- a/src/pcu_main.cpp
+++ b/src/pcu_main.cpp
@@ -183,6 +183,9 @@
 		exit(1);
 	}
 
+	if (!bts->alloc_algorithm)
+		bts->alloc_algorithm = alloc_algorithm_a;
+
 	rc = pcu_l1if_open();
 
 	if (rc < 0)