Merge branch 'zecke/jolly-lapdm-fixes'

* Squashed fix and testcase into one.
diff --git a/src/gsm/lapd_core.c b/src/gsm/lapd_core.c
index b33cf6e..f351308 100644
--- a/src/gsm/lapd_core.c
+++ b/src/gsm/lapd_core.c
@@ -820,7 +820,12 @@
 				"frame established state\n");
 			/* If link is lost on the remote side, we start over
 			 * and send DL-ESTABLISH indication again. */
-			if (dl->v_send != dl->v_recv) {
+			/* Additionally, continue in case of content resoltion
+			 * (GSM network). This happens, if the mobile has not
+			 * yet received UA or another mobile (collision) tries
+			 * to establish connection. The mobile must receive
+			 * UA again. */
+			if (!dl->cont_res && dl->v_send != dl->v_recv) {
 				LOGP(DLLAPD, LOGL_INFO, "Remote reestablish\n");
 				mdl_error(MDL_CAUSE_SABM_MF, lctx);
 				break;
@@ -831,7 +836,8 @@
 #ifdef TEST_CONTENT_RESOLUTION_NETWORK
 				dl->cont_res->data[0] ^= 0x01;
 #endif
-				if (memcmp(dl->cont_res, msg->data, length)) {
+				if (memcmp(dl->cont_res->data, msg->data,
+								length)) {
 					LOGP(DLLAPD, LOGL_INFO, "Another SABM "
 						"with diffrent content - "
 						"ignoring!\n");
diff --git a/src/gsm/lapdm.c b/src/gsm/lapdm.c
index 2bda48a..71045aa 100644
--- a/src/gsm/lapdm.c
+++ b/src/gsm/lapdm.c
@@ -687,7 +687,8 @@
 	if (oph->sap != SAP_GSM_PH) {
 		LOGP(DLLAPD, LOGL_ERROR, "primitive for unknown SAP %u\n",
 			oph->sap);
-		return -ENODEV;
+		rc = -ENODEV;
+		goto free;
 	}
 
 	switch (oph->primitive) {
@@ -695,7 +696,8 @@
 		if (oph->operation != PRIM_OP_INDICATION) {
 			LOGP(DLLAPD, LOGL_ERROR, "PH_DATA is not INDICATION %u\n",
 				oph->operation);
-			return -ENODEV;
+			rc = -ENODEV;
+			goto free;
 		}
 		rc = l2_ph_data_ind(oph->msg, le, pp->u.data.chan_nr,
 				    pp->u.data.link_id);
@@ -704,7 +706,8 @@
 		if (oph->operation != PRIM_OP_INDICATION) {
 			LOGP(DLLAPD, LOGL_ERROR, "PH_RTS is not INDICATION %u\n",
 				oph->operation);
-			return -ENODEV;
+			rc = -ENODEV;
+			goto free;
 		}
 		rc = l2_ph_data_conf(oph->msg, le);
 		break;
@@ -718,12 +721,22 @@
 			rc = l2_ph_chan_conf(oph->msg, le, pp->u.rach_ind.fn);
 			break;
 		default:
-			return -EIO;
+			rc = -EIO;
+			goto free;
 		}
 		break;
+	default:
+		LOGP(DLLAPD, LOGL_ERROR, "Unknown primitive %u\n",
+			oph->primitive);
+		rc = -EINVAL;
+		goto free;
 	}
 
 	return rc;
+
+free:
+	msgb_free(oph->msg);
+	return rc;
 }
 
 
diff --git a/tests/lapd/lapd_test.c b/tests/lapd/lapd_test.c
index a60c45d..49b7eef 100644
--- a/tests/lapd/lapd_test.c
+++ b/tests/lapd/lapd_test.c
@@ -67,6 +67,12 @@
 	0x2b, 0x2b, 0x2b, 0x2b
 };
 
+static const uint8_t ua[] = {
+	0x01, 0x73, 0x41, 0x05, 0x24, 0x31, 0x03, 0x50,
+	0x18, 0x93, 0x08, 0x29, 0x47, 0x80, 0x00, 0x00,
+	0x00, 0x00, 0x80, 0x2b, 0x2b, 0x2b, 0x2b
+};
+
 static const uint8_t mm[] = {
 	0x00, 0x0c, 0x00, 0x03, 0x01, 0x01, 0x20, 0x02,
 	0x00, 0x0b, 0x00, 0x03, 0x05, 0x04, 0x0d
@@ -153,6 +159,32 @@
 	return 0;
 }
 
+static int send_sabm(struct lapdm_channel *chan, int second_ms)
+{
+	struct osmo_phsap_prim pp;
+	struct msgb *msg;
+	int rc;
+
+	msg = msgb_alloc_headroom(128, 64, "PH-DATA.ind");
+	osmo_prim_init(&pp.oph, SAP_GSM_PH, PRIM_PH_DATA,
+			PRIM_OP_INDICATION, msg);
+	/* copy over actual MAC block */
+	msg->l2h = msgb_put(msg, 23);
+	msg->l2h[0] = 0x01;
+	msg->l2h[1] = 0x3f;
+	msg->l2h[2] = 0x01 | (sizeof(cm) << 2);
+	memcpy(msg->l2h + 3, cm_padded, sizeof(cm_padded));
+	msg->l2h[3] += second_ms; /* alter message, for second mobile */
+
+	/* LAPDm requires those... */
+	pp.u.data.chan_nr = 0;
+	pp.u.data.link_id = 0;
+        /* feed into the LAPDm code of libosmogsm */
+        rc = lapdm_phsap_up(&pp.oph, &chan->lapdm_dcch);
+	OSMO_ASSERT(rc == 0 || rc == -EBUSY);
+	return 0;
+}
+
 /*
  * I get called from the LAPDm code when something was sent my way...
  */
@@ -348,12 +380,56 @@
 	lapdm_channel_exit(&bts_to_ms_channel);
 }
 
+static void test_lapdm_contention_resolution()
+{
+	printf("I test contention resultion by having two mobiles collide and "
+		"first mobile repeating SABM.\n");
+
+	int rc;
+	struct lapdm_polling_state test_state;
+	struct osmo_phsap_prim pp;
+
+	/* Configure LAPDm on both sides */
+	struct lapdm_channel bts_to_ms_channel;
+	memset(&bts_to_ms_channel, 0, sizeof(bts_to_ms_channel));
+
+	memset(&test_state, 0, sizeof(test_state));
+	test_state.bts = &bts_to_ms_channel;
+
+	/* BTS to MS in polling mode */
+	lapdm_channel_init(&bts_to_ms_channel, LAPDM_MODE_BTS);
+	lapdm_channel_set_flags(&bts_to_ms_channel, LAPDM_ENT_F_POLLING_ONLY);
+	lapdm_channel_set_l1(&bts_to_ms_channel, NULL, &test_state);
+	lapdm_channel_set_l3(&bts_to_ms_channel, bts_to_ms_tx_cb, &test_state);
+
+	/* Send SABM MS 1, we must get UA */
+	send_sabm(&bts_to_ms_channel, 0);
+	rc = lapdm_phsap_dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp);
+	CHECK_RC(rc);
+	OSMO_ASSERT(memcmp(pp.oph.msg->l2h, ua, ARRAY_SIZE(ua)) == 0);
+
+	/* Send SABM MS 2, we must get nothing, due to collision */
+	send_sabm(&bts_to_ms_channel, 1);
+	rc = lapdm_phsap_dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp);
+	OSMO_ASSERT(rc == -ENODEV);
+
+	/* Send SABM MS 1 again, we must get UA gain */
+	send_sabm(&bts_to_ms_channel, 0);
+	rc = lapdm_phsap_dequeue_prim(&bts_to_ms_channel.lapdm_dcch, &pp);
+	CHECK_RC(rc);
+	OSMO_ASSERT(memcmp(pp.oph.msg->l2h, ua, ARRAY_SIZE(ua)) == 0);
+
+	/* clean up */
+	lapdm_channel_exit(&bts_to_ms_channel);
+}
+
 int main(int argc, char **argv)
 {
 	osmo_init_logging(&info);
 
 	test_lapdm_polling();
 	test_lapdm_early_release();
+	test_lapdm_contention_resolution();
 	printf("Success.\n");
 
 	return 0;
diff --git a/tests/lapd/lapd_test.ok b/tests/lapd/lapd_test.ok
index f1b990e..aa92708 100644
--- a/tests/lapd/lapd_test.ok
+++ b/tests/lapd/lapd_test.ok
@@ -18,4 +18,7 @@
 bts_to_ms_tx_cb: MS->BTS(us) message 14
 BTS: Verifying dummy message.
 I test RF channel release of an unestablished channel.
+I test contention resultion by having two mobiles collide and first mobile repeating SABM.
+bts_to_ms_tx_cb: MS->BTS(us) message 29
+BTS: Verifying CM request.
 Success.