msm: sdio: Unit test for sdio close feature.

- no sdio_close() for stream channels
- test 27: host sender with open/close Diag, CIQ and RPC
- test 28: close channel & LPM Test (Host wakes the Client)

Signed-off-by: Konstantin Dorfman <kdorfman@codeaurora.org>
diff --git a/arch/arm/mach-msm/sdio_al_test.c b/arch/arm/mach-msm/sdio_al_test.c
index c2425a0..454d877 100644
--- a/arch/arm/mach-msm/sdio_al_test.c
+++ b/arch/arm/mach-msm/sdio_al_test.c
@@ -77,6 +77,7 @@
 	SDIO_TEST_LPM_CLIENT_WAKER,
 	SDIO_TEST_LPM_RANDOM,
 	SDIO_TEST_HOST_SENDER_NO_LP,
+	SDIO_TEST_CLOSE_CHANNEL,
 	/* The following tests are not part of the 9k tests and should be
 	 * kept last in case new tests are added
 	 */
@@ -208,6 +209,7 @@
 	int channel_mask_id;
 	int modem_result_per_chan;
 	int notify_counter_per_chan;
+	int max_burst_size;        /* number of writes before close/open */
 };
 
 struct sdio_al_test_debug {
@@ -1132,6 +1134,257 @@
 	lpm_test(test_ch);
 }
 
+static void notify(void *priv, unsigned channel_event);
+
+/**
+  * Writes number of packets into test channel
+  * @test_ch: test channel control struct
+  * @burst_size: number of packets to send
+  */
+static int write_packet_burst(struct test_channel *test_ch,
+		int burst_size)
+{
+	int ret = 0;
+	int packet_count = 0;
+	unsigned int random_num = 0;
+	int size = test_ch->packet_length; /* first packet size */
+	u32 write_avail = 0;
+
+	while (packet_count < burst_size) {
+		/* wait for data ready event */
+		write_avail = sdio_write_avail(test_ch->ch);
+		TEST_DBG(TEST_MODULE_NAME ":%s write_avail=%d,size=%d on chan"
+				" %s\n", __func__,
+				write_avail, size, test_ch->name);
+		if (write_avail < size) {
+			TEST_DBG(TEST_MODULE_NAME ":%s wait for event on"
+					" chan %s\n", __func__, test_ch->name);
+			wait_event(test_ch->wait_q,
+					atomic_read(&test_ch->tx_notify_count));
+			atomic_dec(&test_ch->tx_notify_count);
+		}
+		write_avail = sdio_write_avail(test_ch->ch);
+		if (write_avail < size) {
+			pr_info(TEST_MODULE_NAME ":%s not enough write"
+					" avail %d, need %d on chan %s\n",
+					__func__, write_avail, size,
+					test_ch->name);
+			continue;
+		}
+		ret = sdio_write(test_ch->ch, test_ch->buf, size);
+		if (ret) {
+			pr_err(TEST_MODULE_NAME ":%s sdio_write "
+					"failed (%d) on chan %s\n", __func__,
+					ret, test_ch->name);
+			break;
+		}
+		udelay(1000); /*low bus usage while running number of channels*/
+		TEST_DBG(TEST_MODULE_NAME ":%s() successfully write %d bytes"
+				", packet_count=%d on chan %s\n", __func__,
+				size, packet_count, test_ch->name);
+		test_ch->tx_bytes += size;
+		packet_count++;
+		/* get next packet size */
+		random_num = get_random_int();
+		size = (random_num % test_ch->packet_length) + 1;
+	}
+	return ret;
+}
+/**
+  * Reads packet from test channel and checks that packet number
+  * encoded into the packet is equal to packet_counter
+  *
+  * @test_ch: test channel
+  * @size: expected packet size
+  * @packet_counter: number to validate readed packet
+  */
+static int read_data_from_channel(struct test_channel *test_ch,
+				unsigned int size,
+				int packet_counter)
+{
+	u32 read_avail = 0;
+	int ret = 0;
+
+	if (!test_ch->ch->is_packet_mode) {
+		pr_err(TEST_MODULE_NAME
+				":%s:not packet mode ch %s\n",
+				__func__, test_ch->name);
+		return -EINVAL;
+	}
+	read_avail = sdio_read_avail(test_ch->ch);
+	/* wait for read data ready event */
+	if (read_avail < size) {
+		TEST_DBG(TEST_MODULE_NAME ":%s() wait for rx data on "
+				"chan %s\n", __func__, test_ch->name);
+		wait_event(test_ch->wait_q,
+				atomic_read(&test_ch->rx_notify_count));
+		atomic_dec(&test_ch->rx_notify_count);
+	}
+	read_avail = sdio_read_avail(test_ch->ch);
+	TEST_DBG(TEST_MODULE_NAME ":%s read_avail=%d bytes on chan %s\n",
+			__func__, read_avail, test_ch->name);
+
+	if (read_avail != size) {
+		pr_err(TEST_MODULE_NAME
+				":read_avail size %d for chan %s not as "
+				"expected size %d\n",
+				read_avail, test_ch->name, size);
+		return -EINVAL;
+	}
+
+	ret = sdio_read(test_ch->ch, test_ch->buf, read_avail);
+	if (ret) {
+		pr_err(TEST_MODULE_NAME ":%s() sdio_read for chan %s (%d)\n",
+				__func__, test_ch->name, -ret);
+		return ret;
+	}
+	if ((test_ch->buf[0] != packet_counter) && (size != 1)) {
+		pr_err(TEST_MODULE_NAME ":Read WRONG DATA"
+				" for chan %s, size=%d\n",
+				test_ch->name, size);
+		return -EINVAL;
+	}
+	return 0;
+}
+
+/**
+ *   Test close channel feature:
+ *   1. write random packet number into channel
+ *   2. read some data from channel (do this only for second half of
+ *   requested packets to send).
+ *   3. close && re-open then repeat 1.
+ *
+ *   Total packets to send: test_ch->config_msg.num_packets.
+ *   Burst size is random in [1..test_ch->max_burst_size] range
+ *   Packet size is random in [1..test_ch->packet_length]
+ */
+static void open_close_test(struct test_channel *test_ch)
+{
+	int ret = 0;
+	u32 read_avail = 0;
+	int total_packet_count = 0;
+	int size = test_ch->packet_length;
+	u16 *buf16 = (u16 *) test_ch->buf;
+	int i;
+	int max_packet_count = 0;
+	unsigned int random_num = 0;
+	int curr_burst_size = test_ch->max_burst_size;
+
+	/* the test sends configured number of packets in
+	   2 portions: first without reading between write bursts,
+	   second with it */
+	max_packet_count = test_ch->config_msg.num_packets / 2;
+
+	pr_info(TEST_MODULE_NAME ":%s channel %s, total packets:%d,"
+			" max packet size %d, max burst size:%d\n",
+			__func__, test_ch->name,
+			test_ch->config_msg.num_packets, test_ch->packet_length,
+			test_ch->max_burst_size);
+	for (i = 0 ; i < size / 2 ; i++)
+		buf16[i] = (u16) (i & 0xFFFF);
+
+	for (i = 0; i < 2 ; i++) {
+		total_packet_count = 0;
+		while (total_packet_count < max_packet_count) {
+			if (test_ctx->exit_flag) {
+				pr_info(TEST_MODULE_NAME ":%s exit test\n",
+						__func__);
+				return;
+			}
+			test_ch->buf[0] = total_packet_count;
+			random_num = get_random_int();
+			curr_burst_size = (random_num %
+					test_ch->max_burst_size) + 1;
+
+			/* limit burst size to send
+			 * no more than configured packets */
+			if (curr_burst_size + total_packet_count >
+					max_packet_count) {
+				curr_burst_size = max_packet_count -
+					total_packet_count;
+			}
+			TEST_DBG(TEST_MODULE_NAME ":%s Current burst size:%d"
+					" on chan %s\n", __func__,
+					curr_burst_size, test_ch->name);
+			ret = write_packet_burst(test_ch, curr_burst_size);
+			if (ret) {
+				pr_err(TEST_MODULE_NAME ":%s write burst failed (%d), ch %s\n",
+						__func__, ret, test_ch->name);
+				goto exit_err;
+			}
+			if (i > 0) {
+				/* read from channel */
+				ret = read_data_from_channel(test_ch, size,
+						total_packet_count);
+				if (ret) {
+					pr_err(TEST_MODULE_NAME ":%s read"
+							" failed:%d, chan %s\n",
+							__func__, ret,
+							test_ch->name);
+					goto exit_err;
+				}
+			}
+			TEST_DBG(TEST_MODULE_NAME ":%s before close, ch %s\n",
+					__func__, test_ch->name);
+			ret = sdio_close(test_ch->ch);
+			if (ret) {
+				pr_err(TEST_MODULE_NAME":%s close channel %s"
+						" failed (%d)\n",
+						__func__, test_ch->name, ret);
+				goto exit_err;
+			} else {
+				TEST_DBG(TEST_MODULE_NAME":%s close channel %s"
+						" success\n", __func__,
+						test_ch->name);
+				total_packet_count += curr_burst_size;
+				atomic_set(&test_ch->rx_notify_count, 0);
+				atomic_set(&test_ch->tx_notify_count, 0);
+				atomic_set(&test_ch->any_notify_count, 0);
+			}
+			TEST_DBG(TEST_MODULE_NAME ":%s before open, ch %s\n",
+					__func__, test_ch->name);
+			ret = sdio_open(test_ch->name , &test_ch->ch,
+					test_ch, notify);
+			if (ret) {
+				pr_err(TEST_MODULE_NAME":%s open channel %s"
+						" failed (%d)\n",
+						__func__, test_ch->name, ret);
+				goto exit_err;
+			} else {
+				read_avail = sdio_read_avail(test_ch->ch);
+				if (read_avail > 0) {
+					pr_err(TEST_MODULE_NAME": after open"
+						" ch %s read_availis not zero"
+						" (%d bytes)\n",
+						test_ch->name, read_avail);
+					goto exit_err;
+				}
+			}
+			TEST_DBG(TEST_MODULE_NAME ":%s total tx = %d,"
+					" packet# = %d, size = %d for ch %s\n",
+					__func__, test_ch->tx_bytes,
+					total_packet_count, size,
+					test_ch->name);
+		} /* end of while */
+	}
+	pr_info(TEST_MODULE_NAME ":%s Test end: total rx bytes = 0x%x,"
+			" total tx bytes = 0x%x for chan %s\n", __func__,
+			test_ch->rx_bytes, test_ch->tx_bytes, test_ch->name);
+	pr_info(TEST_MODULE_NAME ":%s TEST PASS for chan %s.\n", __func__,
+			test_ch->name);
+	test_ch->test_completed = 1;
+	test_ch->test_result = TEST_PASSED;
+	check_test_completion();
+	return;
+exit_err:
+	pr_info(TEST_MODULE_NAME ":%s TEST FAIL for chan %s.\n", __func__,
+			test_ch->name);
+	test_ch->test_completed = 1;
+	test_ch->test_result = TEST_FAILED;
+	check_test_completion();
+	return;
+}
+
 /**
  * sender Test
  */
@@ -1656,7 +1909,7 @@
 	u16 *buf16 = (u16 *) test_ch->buf;
 	int i;
 	int max_packet_count = 10000;
-	int random_num = 0;
+	unsigned int random_num = 0;
 
 	max_packet_count = test_ch->config_msg.num_packets;
 
@@ -1789,6 +2042,9 @@
 	case SDIO_TEST_RTT:
 		a2_rtt_test(test_ch);
 		break;
+	case SDIO_TEST_CLOSE_CHANNEL:
+		open_close_test(test_ch);
+		break;
 	default:
 		pr_err(TEST_MODULE_NAME ":Bad Test type = %d.\n",
 			(int) test_type);
@@ -2317,6 +2573,22 @@
 	wait_event(test_ctx->wait_q, test_ctx->test_completed);
 	check_test_result();
 
+	for (i = 0; i < SDIO_MAX_CHANNELS; i++) {
+		struct test_channel *tch = test_ctx->test_ch_arr[i];
+
+		if ((!tch) || (!tch->is_used) || (!tch->ch_ready) ||
+		    (tch->ch_id == SDIO_SMEM) || (!tch->ch->is_packet_mode))
+			continue;
+		ret = sdio_close(tch->ch);
+		if (ret) {
+			pr_err(TEST_MODULE_NAME":%s close channel %s"
+					" failed\n", __func__, tch->name);
+		} else {
+			pr_info(TEST_MODULE_NAME":%s close channel %s"
+					" success\n", __func__, tch->name);
+			tch->ch_ready = false;
+		}
+	}
 	return 0;
 }
 
@@ -2340,7 +2612,33 @@
 
 	return 0;
 }
-
+static int set_params_loopback_9k_close(struct test_channel *tch)
+{
+	if (!tch) {
+		pr_err(TEST_MODULE_NAME ":NULL channel\n");
+		return -EINVAL;
+	}
+	tch->is_used = 1;
+	tch->test_type = SDIO_TEST_CLOSE_CHANNEL;
+	tch->config_msg.signature = TEST_CONFIG_SIGNATURE;
+	tch->config_msg.test_case = SDIO_TEST_LOOPBACK_CLIENT;
+	tch->config_msg.num_packets = 5000;
+	tch->config_msg.num_iterations = 1;
+	tch->max_burst_size = 10;
+	switch (tch->ch_id) {
+	case SDIO_DUN:
+	case SDIO_RPC:
+		tch->packet_length = 128; /* max is 2K*/
+		break;
+	case SDIO_CIQ:
+	case SDIO_DIAG:
+	case SDIO_RMNT:
+	default:
+		tch->packet_length = 512; /* max is 4k */
+	}
+	tch->timer_interval_ms = 0;
+	return 0;
+}
 static int set_params_a2_perf(struct test_channel *tch)
 {
 	if (!tch) {
@@ -2508,6 +2806,69 @@
 		   __func__, test_ctx->lpm_pseudo_random_seed);
 }
 
+/*
+   for each channel
+   1. open channel
+   2. close channel
+   3. Run lpm Host waker test on packet channel
+*/
+#define MAX_LPM_CH_ARR 4
+static int close_channel_lpm_test(void)
+{
+	int ret = 0;
+	struct test_channel *tch = NULL;
+	int i;
+	int lpm_ch_arr[] = { SDIO_RPC,
+			SDIO_QMI,
+			SDIO_DIAG,
+			SDIO_CIQ};
+
+	for (i = 0; i < MAX_LPM_CH_ARR; i++) {
+		tch = test_ctx->test_ch_arr[lpm_ch_arr[i]];
+
+		/* sdio_close is not implemented for stream ch */
+		if ((!tch) ||
+			(tch->ch_id == SDIO_DUN) ||
+			(tch->ch_id == SDIO_RMNT))
+			continue;
+
+		if (!tch->ch_ready) {
+			ret = sdio_open(tch->name , &tch->ch, tch, notify);
+			if (ret) {
+				pr_err(TEST_MODULE_NAME":%s open channel %s"
+					" failed\n", __func__, tch->name);
+				return ret;
+			} else {
+				pr_info(TEST_MODULE_NAME":%s open channel %s"
+					" success\n", __func__, tch->name);
+			}
+		}
+		ret = sdio_close(tch->ch);
+		if (ret) {
+			pr_err(TEST_MODULE_NAME":%s close channel %s"
+					" failed\n", __func__, tch->name);
+			return ret;
+		} else {
+			pr_info(TEST_MODULE_NAME":%s close channel %s"
+					" success\n", __func__, tch->name);
+			tch->ch_ready = false;
+		}
+		if (tch->ch->is_packet_mode) {
+			set_params_lpm_test(tch, SDIO_TEST_LPM_HOST_WAKER, 120);
+
+			ret = test_start();
+			if (ret) {
+				pr_err(TEST_MODULE_NAME ":test_start failed, ret = %d.\n",
+						ret);
+				return ret;
+			}
+		}
+		tch->is_used = 0;
+
+	}
+	return ret;
+}
+
 /**
  * Write File.
  *
@@ -2520,6 +2881,7 @@
 {
 	int ret = 0;
 	int i;
+	struct test_channel **ch_arr = test_ctx->test_ch_arr;
 
 	for (i = 0 ; i < MAX_NUM_OF_SDIO_DEVICES ; ++i)
 			test_ctx->test_dev_arr[i].sdio_al_device = NULL;
@@ -2617,6 +2979,26 @@
 		if (set_params_rtt(test_ctx->test_ch_arr[SDIO_RMNT]))
 			return size;
 		break;
+	case 27:
+		pr_info(TEST_MODULE_NAME " -- host sender with open/close for "
+				"Diag, CIQ and RPC --");
+		if (set_params_loopback_9k_close(ch_arr[SDIO_DIAG]) ||
+		    set_params_loopback_9k_close(ch_arr[SDIO_CIQ]) ||
+		    set_params_loopback_9k_close(ch_arr[SDIO_RPC]))
+			return size;
+		break;
+	case 28:
+		pr_info(TEST_MODULE_NAME " -- Close channel & LPM Test "
+			"Host wakes the Client --\n");
+		ret = close_channel_lpm_test();
+		if (ret) {
+			pr_err(TEST_MODULE_NAME " -- Close channel & LPM Test "
+					"FAILED: %d --\n", ret);
+		} else {
+			pr_err(TEST_MODULE_NAME " -- Close channel & LPM Test "
+					"PASSED\n");
+		}
+		return size;
 	case 111:
 		pr_info(TEST_MODULE_NAME " --LPM Test For Device 1. Client "
 			"wakes the Host --.\n");
@@ -2670,7 +3052,27 @@
 	if (ret) {
 		pr_err(TEST_MODULE_NAME ":test_start failed, ret = %d.\n",
 			ret);
-
+		return size;
+	}
+	/* combined test cases */
+	switch (test_ctx->testcase) {
+	case 27:
+		pr_info(TEST_MODULE_NAME " -- correctness test for"
+				"DIAG, CIQ and DUN ");
+		if (set_params_loopback_9k(ch_arr[SDIO_DIAG]) ||
+		    set_params_loopback_9k(ch_arr[SDIO_CIQ]) ||
+		    set_params_loopback_9k(ch_arr[SDIO_RPC]))
+			return size;
+		break;
+	default:
+		pr_debug(TEST_MODULE_NAME ":2nd test not defined for %ld\n",
+				test_ctx->testcase);
+		return size;
+	}
+	ret = test_start();
+	if (ret) {
+		pr_err(TEST_MODULE_NAME ":2nd round test failed (%d)\n",
+				ret);
 	}
 	return size;
 }