blob: cd6e76435cb097df694f21caaa5d993648e1fbbb [file] [log] [blame]
/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/*
* SDIO-Abstraction-Layer Test Module.
*
*/
#include <linux/module.h>
#include <linux/device.h>
#include <linux/workqueue.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/random.h>
#include <linux/platform_device.h>
#include <mach/sdio_smem.h>
#include <linux/wakelock.h>
#include <linux/uaccess.h>
#include <sdio_al_private.h>
#include <linux/debugfs.h>
#include <linux/kthread.h>
enum lpm_test_msg_type {
LPM_NO_MSG, /* 0 */
LPM_MSG_SEND, /* 1 */
LPM_MSG_REC, /* 2 */
LPM_SLEEP, /* 3 */
LPM_WAKEUP, /* 4 */
LPM_NOTIFY /* 5 */
};
#define LPM_NO_MSG_NAME "LPM No Event"
#define LPM_MSG_SEND_NAME "LPM Send Msg Event"
#define LPM_MSG_REC_NAME "LPM Receive Msg Event"
#define LPM_SLEEP_NAME "LPM Sleep Event"
#define LPM_WAKEUP_NAME "LPM Wakeup Event"
/** Module name string */
#define TEST_MODULE_NAME "sdio_al_test"
#define TEST_SIGNATURE 0x12345678
#define TEST_CONFIG_SIGNATURE 0xBEEFCAFE
#define MAX_XFER_SIZE (16*1024)
#define SMEM_MAX_XFER_SIZE 0xBC000
#define A2_MIN_PACKET_SIZE 5
#define DUN_PACKET_SIZE (2*1024)
#define TEST_DBG(x...) if (test_ctx->runtime_debug) pr_info(x)
#define LPM_TEST_NUM_OF_PACKETS 100
#define LPM_MAX_OPEN_CHAN_PER_DEV 4
#define LPM_ARRAY_SIZE (7*LPM_TEST_NUM_OF_PACKETS*LPM_MAX_OPEN_CHAN_PER_DEV)
#define SDIO_LPM_TEST "sdio_lpm_test_reading_task"
#define LPM_TEST_CONFIG_SIGNATURE 0xDEADBABE
#define LPM_MSG_NAME_SIZE 20
#define MAX_STR_SIZE 10
#define A2_HEADER_OVERHEAD 8
enum sdio_test_case_type {
SDIO_TEST_LOOPBACK_HOST,
SDIO_TEST_LOOPBACK_CLIENT,
SDIO_TEST_LPM_HOST_WAKER,
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
*/
SDIO_TEST_PERF,
SDIO_TEST_RTT,
SDIO_TEST_MODEM_RESET,
};
struct lpm_task {
struct task_struct *lpm_task;
const char *task_name;
};
struct lpm_entry_type {
enum lpm_test_msg_type msg_type;
char msg_name[LPM_MSG_NAME_SIZE];
u32 counter;
u32 current_ms;
u32 read_avail_mask;
char chan_name[CHANNEL_NAME_SIZE];
};
struct lpm_msg {
u32 signature;
u32 counter;
u32 reserve1;
u32 reserve2;
};
struct test_config_msg {
u32 signature;
u32 test_case;
u32 test_param;
u32 num_packets;
u32 num_iterations;
};
struct test_result_msg {
u32 signature;
u32 is_successful;
};
struct test_work {
struct work_struct work;
struct test_channel *test_ch;
};
enum sdio_channels_ids {
SDIO_RPC,
SDIO_QMI,
SDIO_RMNT,
SDIO_DIAG,
SDIO_DUN,
SDIO_SMEM,
SDIO_CIQ,
SDIO_MAX_CHANNELS
};
enum sdio_test_results {
TEST_NO_RESULT,
TEST_FAILED,
TEST_PASSED
};
enum sdio_lpm_vote_state {
SDIO_NO_VOTE,
SDIO_VOTE_FOR_SLEEP,
SDIO_VOTE_AGAINST_SLEEP
};
struct sdio_test_device {
int open_channels_counter_to_recv;
int open_channels_counter_to_send;
struct lpm_entry_type *lpm_arr;
int array_size;
void *sdio_al_device;
spinlock_t lpm_array_lock;
unsigned long lpm_array_lock_flags;
u32 next_avail_entry_in_array;
struct lpm_task lpm_test_task;
u32 next_mask_id;
u32 read_avail_mask;
int modem_result_per_dev;
int final_result_per_dev;
};
struct test_channel {
struct sdio_channel *ch;
char name[CHANNEL_NAME_SIZE];
int ch_id;
struct sdio_test_device *test_device;
u32 *buf;
u32 buf_size;
struct workqueue_struct *workqueue;
struct test_work test_work;
u32 rx_bytes;
u32 tx_bytes;
wait_queue_head_t wait_q;
atomic_t rx_notify_count;
atomic_t tx_notify_count;
atomic_t any_notify_count;
atomic_t wakeup_client;
atomic_t card_detected_event;
int wait_counter;
int is_used;
int test_type;
int ch_ready;
struct test_config_msg config_msg;
int test_completed;
int test_result;
struct timer_list timer;
int timer_interval_ms;
struct timer_list timeout_timer;
int timeout_ms;
void *sdio_al_device;
int is_ok_to_sleep;
unsigned int packet_length;
int random_packet_size;
int next_index_in_sent_msg_per_chan;
int channel_mask_id;
int modem_result_per_chan;
int notify_counter_per_chan;
int max_burst_size; /* number of writes before close/open */
int card_removed;
};
struct sdio_al_test_debug {
u32 dun_throughput;
u32 rmnt_throughput;
struct dentry *debug_root;
struct dentry *debug_test_result;
struct dentry *debug_dun_throughput;
struct dentry *debug_rmnt_throughput;
struct dentry *rpc_sender_test;
struct dentry *rpc_qmi_diag_sender_test;
struct dentry *smem_test;
struct dentry *smem_rpc_test;
struct dentry *rmnet_a2_perf_test;
struct dentry *dun_a2_perf_test;
struct dentry *rmnet_dun_a2_perf_test;
struct dentry *rpc_sender_rmnet_a2_perf_test;
struct dentry *all_channels_test;
struct dentry *host_sender_no_lp_diag_test;
struct dentry *host_sender_no_lp_diag_rpc_ciq_test;
struct dentry *rmnet_small_packets_test;
struct dentry *rmnet_rtt_test;
struct dentry *modem_reset_rpc_test;
struct dentry *modem_reset_rmnet_test;
struct dentry *modem_reset_channels_4bit_dev_test;
struct dentry *modem_reset_channels_8bit_dev_test;
struct dentry *modem_reset_all_channels_test;
struct dentry *open_close_test;
struct dentry *open_close_dun_rmnet_test;
struct dentry *close_chan_lpm_test;
struct dentry *lpm_test_client_wakes_host_test;
struct dentry *lpm_test_host_wakes_client_test;
struct dentry *lpm_test_random_single_channel_test;
struct dentry *lpm_test_random_multi_channel_test;
};
struct test_context {
dev_t dev_num;
struct device *dev;
struct cdev *cdev;
int number_of_active_devices;
int max_number_of_devices;
struct sdio_test_device test_dev_arr[MAX_NUM_OF_SDIO_DEVICES];
struct test_channel *test_ch;
struct test_channel *test_ch_arr[SDIO_MAX_CHANNELS];
long testcase;
const char *name;
int exit_flag;
u32 signature;
int runtime_debug;
struct platform_device *smem_pdev;
struct sdio_smem_client *sdio_smem;
int smem_was_init;
u8 *smem_buf;
uint32_t smem_counter;
struct platform_device *ciq_app_pdev;
wait_queue_head_t wait_q;
int test_completed;
int test_result;
struct sdio_al_test_debug debug;
struct wake_lock wake_lock;
unsigned int lpm_pseudo_random_seed;
};
/* FORWARD DECLARATIONS */
static int set_params_loopback_9k(struct test_channel *tch);
static int set_params_smem_test(struct test_channel *tch);
static int set_params_a2_perf(struct test_channel *tch);
static int set_params_8k_sender_no_lp(struct test_channel *tch);
static int set_params_a2_small_pkts(struct test_channel *tch);
static int set_params_rtt(struct test_channel *tch);
static int set_params_loopback_9k_close(struct test_channel *tch);
static int close_channel_lpm_test(int channel_num);
static int set_params_lpm_test(struct test_channel *tch,
enum sdio_test_case_type test,
int timer_interval_ms);
static void set_pseudo_random_seed(void);
static int set_params_modem_reset(struct test_channel *tch);
static int test_start(void);
static void rx_cleanup(struct test_channel *test_ch, int *rx_packet_count);
static void sdio_al_test_cleanup_channels(void);
static void notify(void *priv, unsigned channel_event);
#ifdef CONFIG_MSM_SDIO_SMEM
static int sdio_smem_open(struct sdio_smem_client *sdio_smem);
#endif
/*
* Seed for pseudo random time sleeping in Random LPM test.
* If not set, current time in jiffies is used.
*/
static unsigned int seed;
module_param(seed, int, 0);
static struct test_context *test_ctx;
static void sdio_al_test_initial_dev_and_chan(struct test_context *test_ctx)
{
int i = 0;
for (i = 0 ; i < MAX_NUM_OF_SDIO_DEVICES ; ++i)
test_ctx->test_dev_arr[i].sdio_al_device = NULL;
for (i = 0; i < SDIO_MAX_CHANNELS; i++) {
struct test_channel *tch = test_ctx->test_ch_arr[i];
if (!tch)
continue;
tch->is_used = 0;
}
sdio_al_test_cleanup_channels();
}
#ifdef CONFIG_DEBUG_FS
static int message_repeat;
static int sdio_al_test_extract_number(const char __user *buf,
size_t count)
{
int ret = 0;
int number = -1;
char local_buf[MAX_STR_SIZE] = {0};
char *start = NULL;
if (count > MAX_STR_SIZE) {
pr_err(TEST_MODULE_NAME ": %s - MAX_STR_SIZE(%d) < count(%d). "
"Please choose smaller number\n",
__func__, MAX_STR_SIZE, (int)count);
return -EINVAL;
}
if (copy_from_user(local_buf, buf, count)) {
pr_err(TEST_MODULE_NAME ": %s - copy_from_user() failed\n",
__func__);
return -EINVAL;
}
/* adding null termination to the string */
local_buf[count] = '\0';
/* stripping leading and trailing white spaces */
start = strstrip(local_buf);
ret = kstrtoint(start, 10, &number);
if (ret) {
pr_err(TEST_MODULE_NAME " : %s - kstrtoint() failed\n",
__func__);
return ret;
}
return number;
}
static int sdio_al_test_open(struct inode *inode, struct file *file)
{
file->private_data = inode->i_private;
message_repeat = 1;
return 0;
}
static void sdio_al_test_cleanup_channels(void)
{
int channel_num;
int dummy = 0;
for (channel_num = 0 ; channel_num < SDIO_MAX_CHANNELS ;
++channel_num) {
if (channel_num == SDIO_SMEM)
continue;
rx_cleanup(test_ctx->test_ch_arr[channel_num], &dummy);
}
return;
}
/* RPC SENDER TEST */
static ssize_t rpc_sender_test_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
int i = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "-- RPC SENDER TEST --\n");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_RPC]);
ret = test_start();
if (ret)
break;
}
return count;
}
static ssize_t rpc_sender_test_read(struct file *file,
char __user *buffer,
size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nRPC_SENDER_TEST\n"
"===============\n"
"Description:\n"
"TBD\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations rpc_sender_test_ops = {
.open = sdio_al_test_open,
.write = rpc_sender_test_write,
.read = rpc_sender_test_read,
};
/* RPC, QMI & DIAG SENDER TEST */
static ssize_t rpc_qmi_diag_sender_test_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
int i = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "-- RPC, QMI AND DIAG SENDER TEST --\n");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_RPC]);
set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_QMI]);
set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_DIAG]);
ret = test_start();
if (ret)
break;
}
return count;
}
static ssize_t rpc_qmi_diag_sender_test_read(struct file *file,
char __user
*buffer, size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nRPC_QMI_DIAG_SENDER_TEST\n"
"========================\n"
"Description:\n"
"TBD\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations rpc_qmi_diag_sender_test_ops = {
.open = sdio_al_test_open,
.write = rpc_qmi_diag_sender_test_write,
.read = rpc_qmi_diag_sender_test_read,
};
/* SMEM TEST */
static ssize_t smem_test_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
int i = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "-- SMEM TEST --\n");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
set_params_smem_test(test_ctx->test_ch_arr[SDIO_SMEM]);
ret = test_start();
if (ret)
break;
}
return count;
}
static ssize_t smem_test_read(struct file *file,
char __user *buffer,
size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nSMEM_TEST\n"
"=========\n"
"Description:\n"
"TBD\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations smem_test_ops = {
.open = sdio_al_test_open,
.write = smem_test_write,
.read = smem_test_read,
};
/* SMEM & RPC TEST */
static ssize_t smem_rpc_test_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
int i = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "-- SMEM AND RPC TEST --\n");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_RPC]);
set_params_smem_test(test_ctx->test_ch_arr[SDIO_SMEM]);
ret = test_start();
if (ret)
break;
}
return count;
}
static ssize_t smem_rpc_test_read(struct file *file,
char __user *buffer,
size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nSMEM_RPC_TEST\n"
"=============\n"
"Description:\n"
"TBD\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations smem_rpc_test_ops = {
.open = sdio_al_test_open,
.write = smem_rpc_test_write,
.read = smem_rpc_test_read,
};
/* RMNET A2 PERFORMANCE TEST */
static ssize_t rmnet_a2_perf_test_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
int i = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "-- RMNET A2 PERFORMANCE TEST --\n");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
set_params_a2_perf(test_ctx->test_ch_arr[SDIO_RMNT]);
ret = test_start();
if (ret)
break;
}
return count;
}
static ssize_t rmnet_a2_perf_test_read(struct file *file,
char __user *buffer,
size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nRMNET_A2_PERFORMANCE_TEST\n"
"=========================\n"
"Description:\n"
"TBD\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations rmnet_a2_perf_test_ops = {
.open = sdio_al_test_open,
.write = rmnet_a2_perf_test_write,
.read = rmnet_a2_perf_test_read,
};
/* DUN A2 PERFORMANCE TEST */
static ssize_t dun_a2_perf_test_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
int i = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "-- DUN A2 PERFORMANCE TEST --\n");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
set_params_a2_perf(test_ctx->test_ch_arr[SDIO_DUN]);
ret = test_start();
if (ret)
break;
}
return count;
}
static ssize_t dun_a2_perf_test_read(struct file *file,
char __user *buffer,
size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nDUN_A2_PERFORMANCE_TEST\n"
"=======================\n"
"Description:\n"
"TBD\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations dun_a2_perf_test_ops = {
.open = sdio_al_test_open,
.write = dun_a2_perf_test_write,
.read = dun_a2_perf_test_read,
};
/* RMNET DUN A2 PERFORMANCE TEST */
static ssize_t rmnet_dun_a2_perf_test_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
int i = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "-- RMNET AND DUN A2 PERFORMANCE TEST --\n");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
set_params_a2_perf(test_ctx->test_ch_arr[SDIO_RMNT]);
set_params_a2_perf(test_ctx->test_ch_arr[SDIO_DUN]);
ret = test_start();
if (ret)
break;
}
return count;
}
static ssize_t rmnet_dun_a2_perf_test_read(struct file *file,
char __user *buffer,
size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nRMNET_DUN_A2_PERFORMANCE_TEST\n"
"=============================\n"
"Description:\n"
"TBD\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations rmnet_dun_a2_perf_test_ops = {
.open = sdio_al_test_open,
.write = rmnet_dun_a2_perf_test_write,
.read = rmnet_dun_a2_perf_test_read,
};
/* RPC SENDER & RMNET A2 PERFORMANCE TEST */
static ssize_t rpc_sender_rmnet_a2_perf_test_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
int i = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "--RPC SENDER AND RMNET A2 "
"PERFORMANCE --\n");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_RPC]);
set_params_a2_perf(test_ctx->test_ch_arr[SDIO_RMNT]);
ret = test_start();
if (ret)
break;
}
return count;
}
static ssize_t rpc_sender_rmnet_a2_perf_test_read(struct file *file,
char __user *buffer,
size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nRPC_SENDER_RMNET_A2_PERFORMANCE_TEST\n"
"====================================\n"
"Description:\n"
"TBD\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations rpc_sender_rmnet_a2_perf_test_ops = {
.open = sdio_al_test_open,
.write = rpc_sender_rmnet_a2_perf_test_write,
.read = rpc_sender_rmnet_a2_perf_test_read,
};
/* ALL CHANNELS TEST */
static ssize_t all_channels_test_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
int i = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "-- ALL THE CHANNELS TEST --\n");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_RPC]);
set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_QMI]);
set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_DIAG]);
set_params_a2_perf(test_ctx->test_ch_arr[SDIO_RMNT]);
set_params_a2_perf(test_ctx->test_ch_arr[SDIO_DUN]);
set_params_smem_test(test_ctx->test_ch_arr[SDIO_SMEM]);
set_params_loopback_9k(test_ctx->test_ch_arr[SDIO_CIQ]);
ret = test_start();
if (ret)
break;
}
return count;
}
static ssize_t all_channels_test_read(struct file *file,
char __user *buffer,
size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nALL_CHANNELS_TEST\n"
"=================\n"
"Description:\n"
"TBD\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations all_channels_test_ops = {
.open = sdio_al_test_open,
.write = all_channels_test_write,
.read = all_channels_test_read,
};
/* HOST SENDER NO LP DIAG TEST */
static ssize_t host_sender_no_lp_diag_test_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
int i = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "-- HOST SENDER NO LP FOR DIAG TEST --");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
set_params_8k_sender_no_lp(test_ctx->test_ch_arr[SDIO_DIAG]);
ret = test_start();
if (ret)
break;
}
return count;
}
static ssize_t host_sender_no_lp_diag_test_read(struct file *file,
char __user *buffer,
size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nHOST_SENDER_NO_LP_DIAG_TEST\n"
"===========================\n"
"Description:\n"
"TBD\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations host_sender_no_lp_diag_test_ops = {
.open = sdio_al_test_open,
.write = host_sender_no_lp_diag_test_write,
.read = host_sender_no_lp_diag_test_read,
};
/* HOST SENDER NO LP DIAG, RPC, CIQ TEST */
static ssize_t host_sender_no_lp_diag_rpc_ciq_test_write(
struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
int i = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "-- HOST SENDER NO LP FOR DIAG, RPC, "
"CIQ TEST --");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
set_params_8k_sender_no_lp(test_ctx->test_ch_arr[SDIO_DIAG]);
set_params_8k_sender_no_lp(test_ctx->test_ch_arr[SDIO_CIQ]);
set_params_8k_sender_no_lp(test_ctx->test_ch_arr[SDIO_RPC]);
ret = test_start();
if (ret)
break;
}
return count;
}
static ssize_t host_sender_no_lp_diag_rpc_ciq_test_read(
struct file *file,
char __user *buffer,
size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nHOST_SENDER_NO_LP_DIAG_RPC_CIQ_TEST\n"
"===================================\n"
"Description:\n"
"TBD\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations host_sender_no_lp_diag_rpc_ciq_test_ops = {
.open = sdio_al_test_open,
.write = host_sender_no_lp_diag_rpc_ciq_test_write,
.read = host_sender_no_lp_diag_rpc_ciq_test_read,
};
/* RMNET SMALL PACKETS TEST */
static ssize_t rmnet_small_packets_test_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
int i = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "-- RMNET SMALL PACKETS (5-128) TEST --");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
set_params_a2_small_pkts(test_ctx->test_ch_arr[SDIO_RMNT]);
ret = test_start();
if (ret)
break;
}
return count;
}
static ssize_t rmnet_small_packets_test_read(struct file *file,
char __user *buffer,
size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nRMNET_SMALL_PACKETS_TEST\n"
"========================\n"
"Description:\n"
"TBD\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations rmnet_small_packets_test_ops = {
.open = sdio_al_test_open,
.write = rmnet_small_packets_test_write,
.read = rmnet_small_packets_test_read,
};
/* RMNET RTT TEST */
static ssize_t rmnet_rtt_test_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
int i = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "-- RMNET RTT TEST --");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
set_params_rtt(test_ctx->test_ch_arr[SDIO_RMNT]);
ret = test_start();
if (ret)
break;
}
return count;
}
static ssize_t rmnet_rtt_test_read(struct file *file,
char __user *buffer,
size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nRMNET_RTT_TEST\n"
"==============\n"
"Description:\n"
"TBD\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations rmnet_rtt_test_ops = {
.open = sdio_al_test_open,
.write = rmnet_rtt_test_write,
.read = rmnet_rtt_test_read,
};
/* MODEM RESET RPC TEST */
static ssize_t modem_reset_rpc_test_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
int i = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "-- MODEM RESET - RPC CHANNEL TEST --");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
set_params_modem_reset(test_ctx->test_ch_arr[SDIO_RPC]);
ret = test_start();
if (ret)
break;
}
return count;
}
static ssize_t modem_reset_rpc_test_read(struct file *file,
char __user *buffer,
size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nMODEM_RESET_RPC_TEST\n"
"====================\n"
"Description:\n"
"TBD\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations modem_reset_rpc_test_ops = {
.open = sdio_al_test_open,
.write = modem_reset_rpc_test_write,
.read = modem_reset_rpc_test_read,
};
/* MODEM RESET RMNET TEST */
static ssize_t modem_reset_rmnet_test_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
int i = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "-- MODEM RESET - RMNT CHANNEL TEST --");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
set_params_modem_reset(test_ctx->test_ch_arr[SDIO_RMNT]);
ret = test_start();
if (ret)
break;
}
return count;
}
static ssize_t modem_reset_rmnet_test_read(struct file *file,
char __user *buffer,
size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nMODEM_RESET_RMNET_TEST\n"
"======================\n"
"Description:\n"
"TBD\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations modem_reset_rmnet_test_ops = {
.open = sdio_al_test_open,
.write = modem_reset_rmnet_test_write,
.read = modem_reset_rmnet_test_read,
};
/* MODEM RESET - CHANNELS IN 4BIT DEVICE TEST */
static ssize_t modem_reset_channels_4bit_dev_test_write(
struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
int i = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "-- MODEM RESET - ALL CHANNELS IN "
"4BIT DEVICE TEST --");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
set_params_modem_reset(test_ctx->test_ch_arr[SDIO_RPC]);
set_params_modem_reset(test_ctx->test_ch_arr[SDIO_QMI]);
set_params_modem_reset(test_ctx->test_ch_arr[SDIO_DIAG]);
ret = test_start();
if (ret)
break;
}
return count;
}
static ssize_t modem_reset_channels_4bit_dev_test_read(
struct file *file,
char __user *buffer,
size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nMODEM_RESET_CHANNELS_4BIT_DEV_TEST\n"
"==================================\n"
"Description:\n"
"TBD\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations modem_reset_channels_4bit_dev_test_ops = {
.open = sdio_al_test_open,
.write = modem_reset_channels_4bit_dev_test_write,
.read = modem_reset_channels_4bit_dev_test_read,
};
/* MODEM RESET - CHANNELS IN 8BIT DEVICE TEST */
static ssize_t modem_reset_channels_8bit_dev_test_write(
struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
int i = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "-- MODEM RESET - ALL CHANNELS IN "
"8BIT DEVICE TEST --");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
set_params_modem_reset(test_ctx->test_ch_arr[SDIO_RMNT]);
set_params_modem_reset(test_ctx->test_ch_arr[SDIO_DUN]);
set_params_modem_reset(test_ctx->test_ch_arr[SDIO_CIQ]);
ret = test_start();
if (ret)
break;
}
return count;
}
static ssize_t modem_reset_channels_8bit_dev_test_read(
struct file *file,
char __user *buffer,
size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nMODEM_RESET_CHANNELS_8BIT_DEV_TEST\n"
"==================================\n"
"Description:\n"
"TBD\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations modem_reset_channels_8bit_dev_test_ops = {
.open = sdio_al_test_open,
.write = modem_reset_channels_8bit_dev_test_write,
.read = modem_reset_channels_8bit_dev_test_read,
};
/* MODEM RESET - ALL CHANNELS TEST */
static ssize_t modem_reset_all_channels_test_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
int i = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "-- MODEM RESET - ALL CHANNELS TEST --");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
set_params_modem_reset(test_ctx->test_ch_arr[SDIO_RPC]);
set_params_modem_reset(test_ctx->test_ch_arr[SDIO_QMI]);
set_params_modem_reset(test_ctx->test_ch_arr[SDIO_DIAG]);
set_params_modem_reset(test_ctx->test_ch_arr[SDIO_RMNT]);
set_params_modem_reset(test_ctx->test_ch_arr[SDIO_DUN]);
set_params_modem_reset(test_ctx->test_ch_arr[SDIO_CIQ]);
ret = test_start();
if (ret)
break;
}
return count;
}
static ssize_t modem_reset_all_channels_test_read(struct file *file,
char __user *buffer,
size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nMODEM_RESET_ALL_CHANNELS_TEST\n"
"=============================\n"
"Description:\n"
"TBD\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations modem_reset_all_channels_test_ops = {
.open = sdio_al_test_open,
.write = modem_reset_all_channels_test_write,
.read = modem_reset_all_channels_test_read,
};
/* HOST SENDER WITH OPEN/CLOSE TEST */
static ssize_t open_close_test_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
struct test_channel **ch_arr = test_ctx->test_ch_arr;
int i = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "-- HOST SENDER WITH OPEN/CLOSE TEST --");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
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]);
set_params_loopback_9k_close(ch_arr[SDIO_SMEM]);
set_params_loopback_9k_close(ch_arr[SDIO_QMI]);
set_params_loopback_9k_close(ch_arr[SDIO_RMNT]);
set_params_loopback_9k_close(ch_arr[SDIO_DUN]);
ret = test_start();
if (ret)
break;
pr_info(TEST_MODULE_NAME " -- correctness test for"
"DIAG, CIQ ");
set_params_loopback_9k(ch_arr[SDIO_DIAG]);
set_params_loopback_9k(ch_arr[SDIO_CIQ]);
ret = test_start();
if (ret)
break;
}
return count;
}
static ssize_t open_close_test_read(struct file *file,
char __user *buffer,
size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nOPEN_CLOSE_TEST\n"
"============================\n"
"Description:\n"
"In this test the host sends 5k packets to the modem in the "
"following sequence: Send a random burst of packets on "
"Diag, CIQ and Rmnet channels, read 0 or a random number "
"of packets, close and re-open the channel. At the end of the "
"test, the channel is verified by running a loopback test\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations open_close_test_ops = {
.open = sdio_al_test_open,
.write = open_close_test_write,
.read = open_close_test_read,
};
/* HOST SENDER WITH OPEN/CLOSE FOR DUN & RMNET TEST */
static ssize_t open_close_dun_rmnet_test_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
struct test_channel **ch_arr = test_ctx->test_ch_arr;
int i = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "-- HOST SENDER WITH OPEN/CLOSE FOR "
"DUN AND RMNET TEST --");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
set_params_loopback_9k_close(ch_arr[SDIO_DUN]);
set_params_loopback_9k_close(ch_arr[SDIO_RMNT]);
ret = test_start();
if (ret)
break;
}
return count;
}
static ssize_t open_close_dun_rmnet_test_read(struct file *file,
char __user *buffer,
size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nOPEN_CLOSE_DUN_RMNET_TEST\n"
"============================\n"
"Description:\n"
"In this test the host sends 5k packets to the modem in the "
"following sequence: Send a random burst of packets on "
"DUN and Rmnet channels, read 0 or a random number "
"of packets, close and re-open the channel.\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations open_close_dun_rmnet_test_ops = {
.open = sdio_al_test_open,
.write = open_close_dun_rmnet_test_write,
.read = open_close_dun_rmnet_test_read,
};
/* CLOSE CHANNEL & LPM TEST HOST WAKES THE CLIENT TEST */
static ssize_t close_chan_lpm_test_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
int i = 0;
int channel_num = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "-- CLOSE CHANNEL & LPM TEST "
"HOST WAKES THE CLIENT TEST --\n");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
for (channel_num = 0 ; channel_num < SDIO_MAX_CHANNELS ;
channel_num++) {
ret = close_channel_lpm_test(channel_num);
if (ret)
break;
set_params_lpm_test(test_ctx->test_ch_arr[SDIO_RPC],
SDIO_TEST_LPM_HOST_WAKER, 120);
ret = test_start();
if (ret)
break;
}
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 count;
}
static ssize_t close_chan_lpm_test_read(struct file *file,
char __user *buffer,
size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nCLOSE_CHAN_LPM_TEST\n"
"===================\n"
"Description:\n"
"TBD\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations close_chan_lpm_test_ops = {
.open = sdio_al_test_open,
.write = close_chan_lpm_test_write,
.read = close_chan_lpm_test_read,
};
/* LPM TEST FOR DEVICE 1. CLIENT WAKES THE HOST TEST */
static ssize_t lpm_test_client_wakes_host_test_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
int i = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "-- LPM TEST FOR DEVICE 1. CLIENT "
"WAKES THE HOST TEST --\n");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
set_params_lpm_test(test_ctx->test_ch_arr[SDIO_RPC],
SDIO_TEST_LPM_CLIENT_WAKER, 90);
ret = test_start();
if (ret)
break;
}
return count;
}
static ssize_t lpm_test_client_wakes_host_test_read(struct file *file,
char __user *buffer,
size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nLPM_TEST_CLIENT_WAKES_HOST_TEST\n"
"===============================\n"
"Description:\n"
"In this test, the HOST is going into LPM mode,\n"
"and the CLIENT is responsible to send it a message\n"
"in order to wake it up\n\n"
"END OF DESCRIPTION\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations lpm_test_client_wakes_host_test_ops = {
.open = sdio_al_test_open,
.write = lpm_test_client_wakes_host_test_write,
.read = lpm_test_client_wakes_host_test_read,
};
/* LPM TEST FOR DEVICE 1. HOST WAKES THE CLIENT TEST */
static ssize_t lpm_test_host_wakes_client_test_write(struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
int i = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "-- LPM TEST FOR DEVICE 1. HOST "
"WAKES THE CLIENT TEST --\n");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
set_params_lpm_test(test_ctx->test_ch_arr[SDIO_RPC],
SDIO_TEST_LPM_HOST_WAKER, 120);
ret = test_start();
if (ret)
break;
}
return count;
}
static ssize_t lpm_test_host_wakes_client_test_read(struct file *file,
char __user *buffer,
size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nLPM_TEST_HOST_WAKES_CLIENT_TEST\n"
"===============================\n"
"Description:\n"
"In this test, the CLIENT goes into LPM mode, and the\n"
"HOST is responsible to send it a message\n"
"in order to wake it up\n\n"
"END OF DESCRIPTION\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations lpm_test_host_wakes_client_test_ops = {
.open = sdio_al_test_open,
.write = lpm_test_host_wakes_client_test_write,
.read = lpm_test_host_wakes_client_test_read,
};
/* LPM TEST RANDOM, SINGLE CHANNEL TEST */
static ssize_t lpm_test_random_single_channel_test_write(
struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
int i = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "-- LPM TEST RANDOM SINGLE "
"CHANNEL TEST --\n");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
set_pseudo_random_seed();
set_params_lpm_test(test_ctx->test_ch_arr[SDIO_RPC],
SDIO_TEST_LPM_RANDOM, 0);
ret = test_start();
if (ret)
break;
}
return count;
}
static ssize_t lpm_test_random_single_channel_test_read(
struct file *file,
char __user *buffer,
size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nLPM_TEST_RANDOM_SINGLE_CHANNEL_TEST\n"
"===================================\n"
"Description:\n"
"In this test, the HOST and CLIENT "
"send messages to each other,\n"
"random in time, over RPC channel only.\n"
"All events are being recorded, and later on,\n"
"they are being analysed by the HOST and by the CLIENT\n,"
"in order to check if the LPM mechanism worked properly,\n"
"meaning:"
" When all the relevant conditions are met, a device should:\n"
"1. Go to sleep\n"
"2. Wake up\n"
"3. Stay awake\n\n"
"END OF DESCRIPTION\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations lpm_test_random_single_channel_test_ops = {
.open = sdio_al_test_open,
.write = lpm_test_random_single_channel_test_write,
.read = lpm_test_random_single_channel_test_read,
};
/* LPM TEST RANDOM, MULTI CHANNEL TEST */
static ssize_t lpm_test_random_multi_channel_test_write(
struct file *file,
const char __user *buf,
size_t count,
loff_t *ppos)
{
int ret = 0;
int i = 0;
int number = -1;
pr_info(TEST_MODULE_NAME "-- LPM TEST RANDOM MULTI CHANNEL TEST --\n");
number = sdio_al_test_extract_number(buf, count);
if (number < 0) {
pr_err(TEST_MODULE_NAME " : %s - sdio_al_test_extract_number() "
"failed. number = %d\n", __func__, number);
return count;
}
for (i = 0 ; i < number ; ++i) {
pr_info(TEST_MODULE_NAME " - Cycle # %d / %d\n", i+1, number);
pr_info(TEST_MODULE_NAME " ===================");
sdio_al_test_initial_dev_and_chan(test_ctx);
set_pseudo_random_seed();
set_params_lpm_test(test_ctx->test_ch_arr[SDIO_RPC],
SDIO_TEST_LPM_RANDOM, 0);
set_params_lpm_test(test_ctx->test_ch_arr[SDIO_CIQ],
SDIO_TEST_LPM_RANDOM, 0);
set_params_lpm_test(test_ctx->test_ch_arr[SDIO_DIAG],
SDIO_TEST_LPM_RANDOM, 0);
set_params_lpm_test(test_ctx->test_ch_arr[SDIO_QMI],
SDIO_TEST_LPM_RANDOM, 0);
ret = test_start();
if (ret)
break;
}
return count;
}
static ssize_t lpm_test_random_multi_channel_test_read(
struct file *file,
char __user *buffer,
size_t count,
loff_t *offset)
{
memset((void *)buffer, 0, count);
snprintf(buffer, count,
"\nLPM_TEST_RANDOM_MULTI_CHANNEL_TEST\n"
"==================================\n"
"Description:\n"
"In this test, the HOST and CLIENT "
"send messages to each other,\n"
"random in time, over RPC, QMI, DIAG AND CIQ channels\n"
"(i.e, on both SDIO devices).\n"
"All events are being recorded, and later on,\n"
"they are being analysed by the HOST and by the CLIENT,\n"
"in order to check if the LPM mechanism worked properly,\n"
"meaning:"
" When all the relevant conditions are met, a device should:\n"
"1. Go to sleep\n"
"2. Wake up\n"
"3. Stay awake\n\n"
"END OF DESCRIPTION\n");
if (message_repeat == 1) {
message_repeat = 0;
return strnlen(buffer, count);
} else {
return 0;
}
}
const struct file_operations lpm_test_random_multi_channel_test_ops = {
.open = sdio_al_test_open,
.write = lpm_test_random_multi_channel_test_write,
.read = lpm_test_random_multi_channel_test_read,
};
static int sdio_al_test_debugfs_init(void)
{
test_ctx->debug.debug_root = debugfs_create_dir("sdio_al_test",
NULL);
if (!test_ctx->debug.debug_root)
return -ENOENT;
test_ctx->debug.debug_test_result = debugfs_create_u32(
"test_result",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
&test_ctx->test_result);
test_ctx->debug.debug_dun_throughput = debugfs_create_u32(
"dun_throughput",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
&test_ctx->debug.dun_throughput);
test_ctx->debug.debug_rmnt_throughput = debugfs_create_u32(
"rmnt_throughput",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
&test_ctx->debug.rmnt_throughput);
test_ctx->debug.rpc_sender_test =
debugfs_create_file("10_rpc_sender_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&rpc_sender_test_ops);
test_ctx->debug.rpc_qmi_diag_sender_test =
debugfs_create_file("20_rpc_qmi_diag_sender_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&rpc_qmi_diag_sender_test_ops);
test_ctx->debug.smem_test =
debugfs_create_file("40_smem_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&smem_test_ops);
test_ctx->debug.smem_rpc_test =
debugfs_create_file("50_smem_rpc_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&smem_rpc_test_ops);
test_ctx->debug.rmnet_a2_perf_test =
debugfs_create_file("60_rmnet_a2_perf_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&rmnet_a2_perf_test_ops);
test_ctx->debug.dun_a2_perf_test =
debugfs_create_file("70_dun_a2_perf_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&dun_a2_perf_test_ops);
test_ctx->debug.rmnet_dun_a2_perf_test =
debugfs_create_file("80_rmnet_dun_a2_perf_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&rmnet_dun_a2_perf_test_ops);
test_ctx->debug.rpc_sender_rmnet_a2_perf_test =
debugfs_create_file("90_rpc_sender_rmnet_a2_perf_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&rpc_sender_rmnet_a2_perf_test_ops);
test_ctx->debug.all_channels_test =
debugfs_create_file("100_all_channels_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&all_channels_test_ops);
test_ctx->debug.host_sender_no_lp_diag_test =
debugfs_create_file("160_host_sender_no_lp_diag_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&host_sender_no_lp_diag_test_ops);
test_ctx->debug.host_sender_no_lp_diag_rpc_ciq_test =
debugfs_create_file("170_host_sender_no_lp_diag_rpc_ciq_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&host_sender_no_lp_diag_rpc_ciq_test_ops);
test_ctx->debug.rmnet_small_packets_test =
debugfs_create_file("180_rmnet_small_packets_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&rmnet_small_packets_test_ops);
test_ctx->debug.rmnet_rtt_test =
debugfs_create_file("190_rmnet_rtt_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&rmnet_rtt_test_ops);
test_ctx->debug.modem_reset_rpc_test =
debugfs_create_file("220_modem_reset_rpc_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&modem_reset_rpc_test_ops);
test_ctx->debug.modem_reset_rmnet_test =
debugfs_create_file("230_modem_reset_rmnet_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&modem_reset_rmnet_test_ops);
test_ctx->debug.modem_reset_channels_4bit_dev_test =
debugfs_create_file("240_modem_reset_channels_4bit_dev_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&modem_reset_channels_4bit_dev_test_ops);
test_ctx->debug.modem_reset_channels_8bit_dev_test =
debugfs_create_file("250_modem_reset_channels_8bit_dev_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&modem_reset_channels_8bit_dev_test_ops);
test_ctx->debug.modem_reset_all_channels_test =
debugfs_create_file("260_modem_reset_all_channels_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&modem_reset_all_channels_test_ops);
test_ctx->debug.open_close_test =
debugfs_create_file("270_open_close_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&open_close_test_ops);
test_ctx->debug.open_close_dun_rmnet_test =
debugfs_create_file("271_open_close_dun_rmnet_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&open_close_dun_rmnet_test_ops);
test_ctx->debug.close_chan_lpm_test =
debugfs_create_file("280_close_chan_lpm_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&close_chan_lpm_test_ops);
test_ctx->debug.lpm_test_client_wakes_host_test =
debugfs_create_file("600_lpm_test_client_wakes_host_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&lpm_test_client_wakes_host_test_ops);
test_ctx->debug.lpm_test_host_wakes_client_test =
debugfs_create_file("610_lpm_test_host_wakes_client_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&lpm_test_host_wakes_client_test_ops);
test_ctx->debug.lpm_test_random_single_channel_test =
debugfs_create_file("620_lpm_test_random_single_channel_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&lpm_test_random_single_channel_test_ops);
test_ctx->debug.lpm_test_random_multi_channel_test =
debugfs_create_file("630_lpm_test_random_multi_channel_test",
S_IRUGO | S_IWUGO,
test_ctx->debug.debug_root,
NULL,
&lpm_test_random_multi_channel_test_ops);
if ((!test_ctx->debug.debug_dun_throughput) &&
(!test_ctx->debug.debug_rmnt_throughput)) {
debugfs_remove_recursive(test_ctx->debug.debug_root);
test_ctx->debug.debug_root = NULL;
return -ENOENT;
}
return 0;
}
static void sdio_al_test_debugfs_cleanup(void)
{
debugfs_remove(test_ctx->debug.debug_dun_throughput);
debugfs_remove(test_ctx->debug.debug_rmnt_throughput);
debugfs_remove(test_ctx->debug.debug_root);
}
#endif
static int channel_name_to_id(char *name)
{
pr_info(TEST_MODULE_NAME "%s: channel name %s\n",
__func__, name);
if (!strncmp(name, "SDIO_RPC_TEST",
strnlen("SDIO_RPC_TEST", CHANNEL_NAME_SIZE)))
return SDIO_RPC;
else if (!strncmp(name, "SDIO_QMI_TEST",
strnlen("SDIO_QMI_TEST", TEST_CH_NAME_SIZE)))
return SDIO_QMI;
else if (!strncmp(name, "SDIO_RMNT_TEST",
strnlen("SDIO_RMNT_TEST", TEST_CH_NAME_SIZE)))
return SDIO_RMNT;
else if (!strncmp(name, "SDIO_DIAG_TEST",
strnlen("SDIO_DIAG", TEST_CH_NAME_SIZE)))
return SDIO_DIAG;
else if (!strncmp(name, "SDIO_DUN_TEST",
strnlen("SDIO_DUN_TEST", TEST_CH_NAME_SIZE)))
return SDIO_DUN;
else if (!strncmp(name, "SDIO_SMEM_TEST",
strnlen("SDIO_SMEM_TEST", TEST_CH_NAME_SIZE)))
return SDIO_SMEM;
else if (!strncmp(name, "SDIO_CIQ_TEST",
strnlen("SDIO_CIQ_TEST", TEST_CH_NAME_SIZE)))
return SDIO_CIQ;
else
return SDIO_MAX_CHANNELS;
return SDIO_MAX_CHANNELS;
}
/**
* Allocate and add SDIO_SMEM platform device
*/
#ifdef CONFIG_MSM_SDIO_SMEM
static int add_sdio_smem(void)
{
int ret = 0;
test_ctx->smem_pdev = platform_device_alloc("SDIO_SMEM", -1);
ret = platform_device_add(test_ctx->smem_pdev);
if (ret) {
pr_err(TEST_MODULE_NAME ": platform_device_add failed, "
"ret=%d\n", ret);
return ret;
}
return 0;
}
#endif
static int open_sdio_ch(struct test_channel *tch)
{
int ret = 0;
if (!tch) {
pr_err(TEST_MODULE_NAME ": %s NULL tch\n", __func__);
return -EINVAL;
}
if (!tch->ch_ready) {
TEST_DBG(TEST_MODULE_NAME ":openning channel %s\n",
tch->name);
if (tch->ch_id == SDIO_SMEM) {
#ifdef CONFIG_MSM_SDIO_SMEM
if (!test_ctx->smem_pdev)
ret = add_sdio_smem();
else
ret = sdio_smem_open(test_ctx->sdio_smem);
if (ret) {
pr_err(TEST_MODULE_NAME
":openning channel %s failed\n",
tch->name);
tch->ch_ready = false;
return -EINVAL;
}
#endif
} else {
tch->ch_ready = true;
ret = sdio_open(tch->name , &tch->ch, tch,
notify);
if (ret) {
pr_err(TEST_MODULE_NAME
":openning channel %s failed\n",
tch->name);
tch->ch_ready = false;
return -EINVAL;
}
}
}
return ret;
}
static int close_sdio_ch(struct test_channel *tch)
{
int ret = 0;
if (!tch) {
pr_err(TEST_MODULE_NAME ": %s NULL tch\n", __func__);
return -EINVAL;
}
if (tch->ch_id == SDIO_SMEM) {
#ifdef CONFIG_MSM_SDIO_SMEM
TEST_DBG(TEST_MODULE_NAME":%s closing channel %s",
__func__, tch->name);
ret = sdio_smem_unregister_client();
test_ctx->smem_counter = 0;
#endif
} else {
ret = sdio_close(tch->ch);
}
if (ret) {
pr_err(TEST_MODULE_NAME":%s close channel %s"
" failed\n", __func__, tch->name);
} else {
TEST_DBG(TEST_MODULE_NAME":%s close channel %s"
" success\n", __func__, tch->name);
tch->ch_ready = false;
}
return ret;
}
/**
* Config message
*/
static void send_config_msg(struct test_channel *test_ch)
{
int ret = 0 ;
u32 write_avail = 0;
int size = sizeof(test_ch->config_msg);
pr_debug(TEST_MODULE_NAME "%s\n", __func__);
memcpy(test_ch->buf, (void *)&test_ch->config_msg, size);
if (test_ctx->exit_flag) {
pr_info(TEST_MODULE_NAME ":Exit Test.\n");
return;
}
pr_info(TEST_MODULE_NAME ":Sending the config message.\n");
/* wait for data ready event */
write_avail = sdio_write_avail(test_ch->ch);
pr_debug(TEST_MODULE_NAME ":write_avail=%d\n", write_avail);
if (write_avail < size) {
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);
pr_debug(TEST_MODULE_NAME ":write_avail=%d\n", write_avail);
if (write_avail < size) {
pr_info(TEST_MODULE_NAME ":not enough write avail.\n");
return;
}
ret = sdio_write(test_ch->ch, test_ch->buf, size);
if (ret)
pr_err(TEST_MODULE_NAME ":%s sdio_write err=%d.\n",
__func__, -ret);
else
pr_info(TEST_MODULE_NAME ":%s sent config_msg successfully.\n",
__func__);
}
/**
* Loopback Test
*/
static void loopback_test(struct test_channel *test_ch)
{
int ret = 0 ;
u32 read_avail = 0;
u32 write_avail = 0;
while (1) {
if (test_ctx->exit_flag) {
pr_info(TEST_MODULE_NAME ":Exit Test.\n");
return;
}
TEST_DBG(TEST_MODULE_NAME "--LOOPBACK WAIT FOR EVENT--.\n");
/* wait for data ready event */
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);
if (read_avail == 0)
continue;
write_avail = sdio_write_avail(test_ch->ch);
if (write_avail < read_avail) {
pr_info(TEST_MODULE_NAME
":not enough write avail.\n");
continue;
}
ret = sdio_read(test_ch->ch, test_ch->buf, read_avail);
if (ret) {
pr_info(TEST_MODULE_NAME
":worker, sdio_read err=%d.\n", -ret);
continue;
}
test_ch->rx_bytes += read_avail;
TEST_DBG(TEST_MODULE_NAME ":worker total rx bytes = 0x%x.\n",
test_ch->rx_bytes);
ret = sdio_write(test_ch->ch,
test_ch->buf, read_avail);
if (ret) {
pr_info(TEST_MODULE_NAME
":loopback sdio_write err=%d.\n",
-ret);
continue;
}
test_ch->tx_bytes += read_avail;
TEST_DBG(TEST_MODULE_NAME
":loopback total tx bytes = 0x%x.\n",
test_ch->tx_bytes);
} /* end of while */
}
/**
* Check if all tests completed
*/
static void check_test_completion(void)
{
int i;
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))
continue;
if (!tch->test_completed) {
pr_info(TEST_MODULE_NAME ": %s - Channel %s test is "
"not completed", __func__, tch->name);
return;
}
}
pr_info(TEST_MODULE_NAME ": %s - Test is completed", __func__);
test_ctx->test_completed = 1;
wake_up(&test_ctx->wait_q);
}
static int pseudo_random_seed(unsigned int *seed_number)
{
if (!seed_number)
return 0;
*seed_number = (unsigned int)(((unsigned long)*seed_number *
(unsigned long)1103515367) + 35757);
return (int)((*seed_number / (64*1024)) % 500);
}
/* this function must be locked before accessing it */
static void lpm_test_update_entry(struct test_channel *tch,
enum lpm_test_msg_type msg_type,
char *msg_name,
int counter)
{
u32 index = 0;
static int print_full = 1;
struct sdio_test_device *test_device;
if (!tch) {
pr_err(TEST_MODULE_NAME ": %s - NULL test channel\n", __func__);
return;
}
test_device = tch->test_device;
if (!test_device) {
pr_err(TEST_MODULE_NAME ": %s - NULL test device\n", __func__);
return;
}
if (!test_device->lpm_arr) {
pr_err(TEST_MODULE_NAME ": %s - NULL lpm_arr\n", __func__);
return;
}
if (test_device->next_avail_entry_in_array >=
test_device->array_size) {
pr_err(TEST_MODULE_NAME ": %s - lpm array is full",
__func__);
if (print_full) {
print_hex_dump(KERN_INFO, TEST_MODULE_NAME ": lpm_arr:",
0, 32, 2,
(void *)test_device->lpm_arr,
sizeof(test_device->lpm_arr), false);
print_full = 0;
}
return;
}
index = test_device->next_avail_entry_in_array;
if ((msg_type == LPM_MSG_SEND) || (msg_type == LPM_MSG_REC))
test_device->lpm_arr[index].counter = counter;
else
test_device->lpm_arr[index].counter = 0;
test_device->lpm_arr[index].msg_type = msg_type;
memcpy(test_device->lpm_arr[index].msg_name, msg_name,
LPM_MSG_NAME_SIZE);
test_device->lpm_arr[index].current_ms =
jiffies_to_msecs(get_jiffies_64());
test_device->lpm_arr[index].read_avail_mask =
test_device->read_avail_mask;
if ((msg_type == LPM_SLEEP) || (msg_type == LPM_WAKEUP))
memcpy(test_device->lpm_arr[index].chan_name, "DEVICE ",
CHANNEL_NAME_SIZE);
else
memcpy(test_device->lpm_arr[index].chan_name, tch->name,
CHANNEL_NAME_SIZE);
test_device->next_avail_entry_in_array++;
}
static int wait_for_result_msg(struct test_channel *test_ch)
{
u32 read_avail = 0;
int ret = 0;
pr_info(TEST_MODULE_NAME ": %s - START, channel %s\n",
__func__, test_ch->name);
while (1) {
read_avail = sdio_read_avail(test_ch->ch);
if (read_avail == 0) {
pr_info(TEST_MODULE_NAME
": read_avail is 0 for chan %s\n",
test_ch->name);
wait_event(test_ch->wait_q,
atomic_read(&test_ch->rx_notify_count));
atomic_dec(&test_ch->rx_notify_count);
continue;
}
memset(test_ch->buf, 0x00, test_ch->buf_size);
ret = sdio_read(test_ch->ch, test_ch->buf, read_avail);
if (ret) {
pr_info(TEST_MODULE_NAME ": sdio_read for chan"
"%s failed, err=%d.\n",
test_ch->name, -ret);
goto exit_err;
}
if (test_ch->buf[0] != TEST_CONFIG_SIGNATURE) {
pr_info(TEST_MODULE_NAME ": Not a test_result "
"signature. expected 0x%x. received 0x%x "
"for chan %s\n",
TEST_CONFIG_SIGNATURE,
test_ch->buf[0],
test_ch->name);
continue;
} else {
pr_info(TEST_MODULE_NAME ": Signature is "
"TEST_CONFIG_SIGNATURE as expected for"
"channel %s\n", test_ch->name);
break;
}
}
return test_ch->buf[1];
exit_err:
return 0;
}
static void print_random_lpm_test_array(struct sdio_test_device *test_dev)
{
int i;
if (!test_dev) {
pr_err(TEST_MODULE_NAME ": %s - NULL test device\n", __func__);
return;
}
for (i = 0 ; i < test_dev->next_avail_entry_in_array ; ++i) {
if (i == 0)
pr_err(TEST_MODULE_NAME ": index %4d, chan=%2s, "
"code=%1d=%4s, msg#%1d, ms from before=-1, "
"read_mask=0x%d, ms=%2u",
i,
test_dev->lpm_arr[i].chan_name,
test_dev->lpm_arr[i].msg_type,
test_dev->lpm_arr[i].msg_name,
test_dev->lpm_arr[i].counter,
test_dev->lpm_arr[i].read_avail_mask,
test_dev->lpm_arr[i].current_ms);
else
pr_err(TEST_MODULE_NAME ": index "
"%4d, %2s, code=%1d=%4s, msg#%1d, ms from "
"before=%2u, read_mask=0x%d, ms=%2u",
i,
test_dev->lpm_arr[i].chan_name,
test_dev->lpm_arr[i].msg_type,
test_dev->lpm_arr[i].msg_name,
test_dev->lpm_arr[i].counter,
test_dev->lpm_arr[i].current_ms -
test_dev->lpm_arr[i-1].current_ms,
test_dev->lpm_arr[i].read_avail_mask,
test_dev->lpm_arr[i].current_ms);
udelay(1000);
}
}
static int check_random_lpm_test_array(struct sdio_test_device *test_dev)
{
int i = 0, j = 0;
unsigned int delta_ms = 0;
int arr_ind = 0;
int ret = 0;
int notify_counter = 0;
int sleep_counter = 0;
int wakeup_counter = 0;
int lpm_activity_counter = 0;
if (!test_dev) {
pr_err(TEST_MODULE_NAME ": %s - NULL test device\n", __func__);
return -ENODEV;
}
for (i = 0; i < test_dev->next_avail_entry_in_array; i++) {
notify_counter = 0;
sleep_counter = 0;
wakeup_counter = 0;
if ((test_dev->lpm_arr[i].msg_type == LPM_MSG_SEND) ||
(test_dev->lpm_arr[i].msg_type == LPM_MSG_REC)) {
/* find the next message in the array */
arr_ind = test_dev->next_avail_entry_in_array;
for (j = i+1; j < arr_ind; j++) {
if ((test_dev->lpm_arr[j].msg_type ==
LPM_MSG_SEND) ||
(test_dev->lpm_arr[j].msg_type ==
LPM_MSG_REC) ||
(test_dev->lpm_arr[j].msg_type ==
LPM_NOTIFY))
break;
if (test_dev->lpm_arr[j].msg_type ==
LPM_SLEEP)
sleep_counter++;
if (test_dev->lpm_arr[j].msg_type ==
LPM_WAKEUP)
wakeup_counter++;
}
if (j == arr_ind) {
ret = 0;
break;
}
delta_ms = test_dev->lpm_arr[j].current_ms -
test_dev->lpm_arr[i].current_ms;
if (delta_ms < 30) {
if ((sleep_counter == 0)
&& (wakeup_counter == 0)) {
continue;
} else {
pr_err(TEST_MODULE_NAME "%s: lpm "
"activity while delta is less "
"than 30, i=%d, j=%d, "
"sleep_counter=%d, "
"wakeup_counter=%d",
__func__, i, j,
sleep_counter, wakeup_counter);
ret = -ENODEV;
break;
}
} else {
if ((delta_ms > 90) &&
(test_dev->lpm_arr[i].
read_avail_mask == 0)) {
if (j != i+3) {
pr_err(TEST_MODULE_NAME
"%s: unexpected "
"lpm activity "
"while delta is "
"bigger than "
"90, i=%d, "
"j=%d, "
"notify_counter"
"=%d",
__func__, i, j,
notify_counter);
ret = -ENODEV;
break;
}
lpm_activity_counter++;
}
}
}
}
pr_info(TEST_MODULE_NAME ": %s - lpm_activity_counter=%d",
__func__, lpm_activity_counter);
return ret;
}
static int lpm_test_main_task(void *ptr)
{
u32 read_avail = 0;
int last_msg_index = 0;
struct test_channel *test_ch = (struct test_channel *)ptr;
struct sdio_test_device *test_dev;
struct lpm_msg lpm_msg;
int ret = 0;
int host_result = 0;
if (!test_ch) {
pr_err(TEST_MODULE_NAME ": %s - NULL channel\n", __func__);
return -ENODEV;
}
pr_err(TEST_MODULE_NAME ": %s - STARTED. channel %s\n",
__func__, test_ch->name);
test_dev = test_ch->test_device;
if (!test_dev) {
pr_err(TEST_MODULE_NAME ": %s - NULL Test Device\n", __func__);
return -ENODEV;
}
while (last_msg_index < test_ch->config_msg.num_packets - 1) {
TEST_DBG(TEST_MODULE_NAME ": %s - "
"IN LOOP last_msg_index=%d\n",
__func__, last_msg_index);
read_avail = sdio_read_avail(test_ch->ch);
if (read_avail == 0) {
TEST_DBG(TEST_MODULE_NAME
":read_avail 0 for chan %s, "
"wait for event\n",
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);
if (read_avail == 0) {
pr_err(TEST_MODULE_NAME
":read_avail size %d for chan %s not as"
" expected\n",
read_avail, test_ch->name);
continue;
}
}
memset(test_ch->buf, 0x00, sizeof(test_ch->buf));
ret = sdio_read(test_ch->ch, test_ch->buf, read_avail);
if (ret) {
pr_info(TEST_MODULE_NAME ":sdio_read for chan %s"
" err=%d.\n", test_ch->name, -ret);
goto exit_err;
}
memcpy((void *)&lpm_msg, test_ch->buf, sizeof(lpm_msg));
/*
* when reading from channel, we want to turn off the bit
* mask that implies that there is pending data on that channel
*/
if (test_ch->test_device != NULL) {
spin_lock_irqsave(&test_dev->lpm_array_lock,
test_dev->lpm_array_lock_flags);
test_ch->notify_counter_per_chan--;
/*
* if the channel has no pending data, turn off the
* pending data bit mask of the channel
*/
if (test_ch->notify_counter_per_chan == 0) {
test_ch->test_device->read_avail_mask =
test_ch->test_device->read_avail_mask &
~test_ch->channel_mask_id;
}
last_msg_index = lpm_msg.counter;
lpm_test_update_entry(test_ch,
LPM_MSG_REC,
"RECEIVE",
last_msg_index);
spin_unlock_irqrestore(&test_dev->lpm_array_lock,
test_dev->lpm_array_lock_flags);
}
}
pr_info(TEST_MODULE_NAME ":%s: Finished to recieve all (%d) "
"packets from the modem %s. Waiting for result_msg",
__func__, test_ch->config_msg.num_packets, test_ch->name);
/* Wait for the resault message from the modem */
test_ch->modem_result_per_chan = wait_for_result_msg(test_ch);
/*
* the DEVICE modem result is a failure if one of the channels on
* that device, got modem_result = 0. this is why we bitwise "AND" each
* time another channel completes its task
*/
test_dev->modem_result_per_dev &= test_ch->modem_result_per_chan;
/*
* when reading from channel, we want to turn off the bit
* mask that implies that there is pending data on that channel
*/
spin_lock_irqsave(&test_dev->lpm_array_lock,
test_dev->lpm_array_lock_flags);
test_dev->open_channels_counter_to_recv--;
/* turning off the read_avail bit of the channel */
test_ch->test_device->read_avail_mask =
test_ch->test_device->read_avail_mask &
~test_ch->channel_mask_id;
spin_unlock_irqrestore(&test_dev->lpm_array_lock,
test_dev->lpm_array_lock_flags);
/* Wait for all the packets to be sent to the modem */
while (1) {
spin_lock_irqsave(&test_dev->lpm_array_lock,
test_dev->lpm_array_lock_flags);
if (test_ch->next_index_in_sent_msg_per_chan >=
test_ch->config_msg.num_packets - 1) {
spin_unlock_irqrestore(&test_dev->lpm_array_lock,
test_dev->lpm_array_lock_flags);
break;
} else {
pr_info(TEST_MODULE_NAME ":%s: Didn't finished to send "
"all packets, "
"next_index_in_sent_msg_per_chan = %d ",
__func__,
test_ch->next_index_in_sent_msg_per_chan);
}
spin_unlock_irqrestore(&test_dev->lpm_array_lock,
test_dev->lpm_array_lock_flags);
msleep(60);
}
/*
* if device has still open channels to test, then the test on the
* device is still running but the test on current channel is completed
*/
if (test_dev->open_channels_counter_to_recv != 0 ||
test_dev->open_channels_counter_to_send != 0) {
test_ch->test_completed = 1;
test_ch->next_index_in_sent_msg_per_chan = 0;
return 0;
} else {
test_ctx->number_of_active_devices--;
sdio_al_unregister_lpm_cb(test_ch->sdio_al_device);
if (test_ch->test_type == SDIO_TEST_LPM_RANDOM)
host_result = check_random_lpm_test_array(test_dev);
if (host_result ||
!test_dev->modem_result_per_dev ||
test_ctx->runtime_debug)
print_random_lpm_test_array(test_dev);
pr_info(TEST_MODULE_NAME ": %s - host_result=%d.(0 for "
"SUCCESS) device_modem_result=%d (1 for SUCCESS)",
__func__, host_result, test_dev->modem_result_per_dev);
test_ch->test_completed = 1;
if (test_dev->modem_result_per_dev && !host_result) {
pr_info(TEST_MODULE_NAME ": %s - Random LPM "
"TEST_PASSED for device %d of %d\n",
__func__,
(test_ctx->max_number_of_devices-
test_ctx->number_of_active_devices),
test_ctx->max_number_of_devices);
test_dev->final_result_per_dev = 1; /* PASSED */
} else {
pr_info(TEST_MODULE_NAME ": %s - Random LPM "
"TEST_FAILED for device %d of %d\n",
__func__,
(test_ctx->max_number_of_devices-
test_ctx->number_of_active_devices),
test_ctx->max_number_of_devices);
test_dev->final_result_per_dev = 0; /* FAILED */
}
test_dev->next_avail_entry_in_array = 0;
test_ch->next_index_in_sent_msg_per_chan = 0;
check_test_completion();
kfree(test_ch->test_device->lpm_arr);
return 0;
}
exit_err:
pr_info(TEST_MODULE_NAME ": TEST FAIL for chan %s.\n",
test_ch->name);
test_ch->test_completed = 1;
test_dev->open_channels_counter_to_recv--;
test_dev->next_avail_entry_in_array = 0;
test_ch->next_index_in_sent_msg_per_chan = 0;
test_ch->test_result = TEST_FAILED;
check_test_completion();
return -ENODEV;
}
static int lpm_test_create_read_thread(struct test_channel *test_ch)
{
struct sdio_test_device *test_dev;
pr_info(TEST_MODULE_NAME ": %s - STARTED channel %s\n",
__func__, test_ch->name);
if (!test_ch) {
pr_err(TEST_MODULE_NAME ": %s - NULL test channel\n", __func__);
return -ENODEV;
}
test_dev = test_ch->test_device;
if (!test_dev) {
pr_err(TEST_MODULE_NAME ": %s - NULL test device\n", __func__);
return -ENODEV;
}
test_dev->lpm_test_task.task_name = SDIO_LPM_TEST;
test_dev->lpm_test_task.lpm_task =
kthread_create(lpm_test_main_task,
(void *)(test_ch),
test_dev->lpm_test_task.task_name);
if (IS_ERR(test_dev->lpm_test_task.lpm_task)) {
pr_err(TEST_MODULE_NAME ": %s - kthread_create() failed\n",
__func__);
return -ENOMEM;
}
wake_up_process(test_dev->lpm_test_task.lpm_task);
return 0;
}
static void lpm_continuous_rand_test(struct test_channel *test_ch)
{
unsigned int local_ms = 0;
int ret = 0;
unsigned int write_avail = 0;
struct sdio_test_device *test_dev;
pr_info(MODULE_NAME ": %s - STARTED\n", __func__);
if (!test_ch) {
pr_err(TEST_MODULE_NAME ": %s - NULL channel\n", __func__);
return;
}
test_dev = test_ch->test_device;
if (!test_dev) {
pr_err(TEST_MODULE_NAME ": %s - NULL Test Device\n", __func__);
return;
}
ret = lpm_test_create_read_thread(test_ch);
if (ret != 0) {
pr_err(TEST_MODULE_NAME ": %s - failed to create lpm reading "
"thread", __func__);
}
while (test_ch->next_index_in_sent_msg_per_chan <=
test_ch->config_msg.num_packets - 1) {
struct lpm_msg msg;
u32 ret = 0;
/* sleeping period is dependent on number of open channels */
test_ch->config_msg.test_param =
test_ctx->lpm_pseudo_random_seed;
local_ms = test_dev->open_channels_counter_to_send *
test_ctx->lpm_pseudo_random_seed;
TEST_DBG(TEST_MODULE_NAME ":%s: SLEEPING for %d ms",
__func__, local_ms);
msleep(local_ms);
msg.counter = test_ch->next_index_in_sent_msg_per_chan;
msg.signature = LPM_TEST_CONFIG_SIGNATURE;
msg.reserve1 = 0;
msg.reserve2 = 0;
/* wait for data ready event */
write_avail = sdio_write_avail(test_ch->ch);
pr_debug(TEST_MODULE_NAME ": %s: write_avail=%d\n",
__func__, write_avail);
if (write_avail < sizeof(msg)) {
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 < sizeof(msg)) {
pr_info(TEST_MODULE_NAME ": %s: not enough write "
"avail.\n", __func__);
break;
}
ret = sdio_write(test_ch->ch, (u32 *)&msg, sizeof(msg));
if (ret)
pr_err(TEST_MODULE_NAME ":%s: sdio_write err=%d.\n",
__func__, -ret);
TEST_DBG(TEST_MODULE_NAME ": %s: for chan %s, write, "
"msg # %d\n",
__func__,
test_ch->name,
test_ch->next_index_in_sent_msg_per_chan);
if (test_ch->test_type == SDIO_TEST_LPM_RANDOM) {
spin_lock_irqsave(&test_dev->lpm_array_lock,
test_dev->lpm_array_lock_flags);
lpm_test_update_entry(test_ch, LPM_MSG_SEND,
"SEND ",
test_ch->
next_index_in_sent_msg_per_chan);
test_ch->next_index_in_sent_msg_per_chan++;
spin_unlock_irqrestore(&test_dev->lpm_array_lock,
test_dev->lpm_array_lock_flags);
}
}
spin_lock_irqsave(&test_dev->lpm_array_lock,
test_dev->lpm_array_lock_flags);
test_dev->open_channels_counter_to_send--;
spin_unlock_irqrestore(&test_dev->lpm_array_lock,
test_dev->lpm_array_lock_flags);
pr_info(TEST_MODULE_NAME ": %s: - Finished to send all (%d) "
"packets to the modem on channel %s",
__func__, test_ch->config_msg.num_packets, test_ch->name);
return;
}
static void lpm_test(struct test_channel *test_ch)
{
pr_info(TEST_MODULE_NAME ": %s - START channel %s\n", __func__,
test_ch->name);
if (!test_ch) {
pr_err(TEST_MODULE_NAME ": %s - NULL test channel\n", __func__);
return;
}
test_ch->modem_result_per_chan = wait_for_result_msg(test_ch);
pr_debug(TEST_MODULE_NAME ": %s - delete the timeout timer\n",
__func__);
del_timer_sync(&test_ch->timeout_timer);
if (test_ch->modem_result_per_chan == 0) {
pr_err(TEST_MODULE_NAME ": LPM TEST - Client didn't sleep. "
"Result Msg - is_successful=%d\n", test_ch->buf[1]);
goto exit_err;
} else {
pr_info(TEST_MODULE_NAME ": %s -"
"LPM 9K WAS SLEEPING - PASS\n", __func__);
if (test_ch->test_result == TEST_PASSED) {
pr_info(TEST_MODULE_NAME ": LPM TEST_PASSED\n");
test_ch->test_completed = 1;
check_test_completion();
} else {
pr_err(TEST_MODULE_NAME ": LPM TEST - Host didn't "
"sleep. Client slept\n");
goto exit_err;
}
}
return;
exit_err:
pr_info(TEST_MODULE_NAME ": TEST FAIL for chan %s.\n",
test_ch->name);
test_ch->test_completed = 1;
test_ch->test_result = TEST_FAILED;
check_test_completion();
return;
}
/**
* LPM Test while the host wakes up the modem
*/
static void lpm_test_host_waker(struct test_channel *test_ch)
{
pr_info(TEST_MODULE_NAME ": %s - START\n", __func__);
wait_event(test_ch->wait_q, atomic_read(&test_ch->wakeup_client));
atomic_set(&test_ch->wakeup_client, 0);
pr_info(TEST_MODULE_NAME ": %s - Sending the config_msg to wakeup "
" the client\n", __func__);
send_config_msg(test_ch);
lpm_test(test_ch);
}
/**
* 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
* This function is applicable for packet mode channels only
*
* @test_ch: test channel
* @size: expected packet size
* @packet_counter: number to validate readed packet
*/
static int read_data_from_packet_ch(struct test_channel *test_ch,
unsigned int size,
int packet_counter)
{
u32 read_avail = 0;
int ret = 0;
if (!test_ch || !test_ch->ch) {
pr_err(TEST_MODULE_NAME
":%s: NULL channel\n", __func__);
return -EINVAL;
}
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;
}
/**
* Reads packet from test channel and checks that packet number
* encoded into the packet is equal to packet_counter
* This function is applicable for streaming mode channels only
*
* @test_ch: test channel
* @size: expected packet size
* @packet_counter: number to validate readed packet
*/
static int read_data_from_stream_ch(struct test_channel *test_ch,
unsigned int size,
int packet_counter)
{
u32 read_avail = 0;
int ret = 0;
if (!test_ch || !test_ch->ch) {
pr_err(TEST_MODULE_NAME
":%s: NULL channel\n", __func__);
return -EINVAL;
}
if (test_ch->ch->is_packet_mode) {
pr_err(TEST_MODULE_NAME
":%s:not streaming 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, size + A2_HEADER_OVERHEAD);
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[A2_HEADER_OVERHEAD/4] != packet_counter) &&
(size != 1)) {
pr_err(TEST_MODULE_NAME ":Read WRONG DATA"
" for chan %s, size=%d, packet_counter=%d\n",
test_ch->name, size, packet_counter);
print_hex_dump(KERN_INFO, TEST_MODULE_NAME ": rmnet:",
0, 32, 2,
(void *)test_ch->buf,
size + A2_HEADER_OVERHEAD, false);
return -EINVAL;
}
return 0;
}
/**
* Test close channel feature for SDIO_SMEM channel:
* close && re-open the SDIO_SMEM channel.
*/
#ifdef CONFIG_MSM_SDIO_SMEM
static void open_close_smem_test(struct test_channel *test_ch)
{
int i = 0;
int ret = 0;
pr_info(TEST_MODULE_NAME ":%s\n", __func__);
for (i = 0; i < 100 ; ++i) {
ret = close_sdio_ch(test_ch);
if (ret) {
pr_err(TEST_MODULE_NAME ":%s close_sdio_ch for ch %s"
" failed\n",
__func__, test_ch->name);
goto exit_err;
}
ret = open_sdio_ch(test_ch);
if (ret) {
pr_err(TEST_MODULE_NAME ":%s open_sdio_ch for ch %s "
" failed\n",
__func__, test_ch->name);
goto exit_err;
}
}
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;
}
#endif
/**
* 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 = 0;
u16 *buf16 = NULL;
int i;
int max_packet_count = 0;
unsigned int random_num = 0;
int curr_burst_size = 0;
if (!test_ch || !test_ch->ch) {
pr_err(TEST_MODULE_NAME ":%s NULL channel\n",
__func__);
return;
}
curr_burst_size = test_ch->max_burst_size;
size = test_ch->packet_length;
buf16 = (u16 *) test_ch->buf;
/* 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 */
if (test_ch->ch->is_packet_mode)
ret = read_data_from_packet_ch(test_ch,
size,
total_packet_count);
else
ret = read_data_from_stream_ch(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 = close_sdio_ch(test_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 = open_sdio_ch(test_ch);
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
*/
static void sender_test(struct test_channel *test_ch)
{
int ret = 0 ;
u32 read_avail = 0;
u32 write_avail = 0;
int packet_count = 0;
int size = 512;
u16 *buf16 = (u16 *) test_ch->buf;
int i;
int max_packet_count = 10000;
int random_num = 0;
max_packet_count = test_ch->config_msg.num_packets;
for (i = 0 ; i < size / 2 ; i++)
buf16[i] = (u16) (i & 0xFFFF);
pr_info(TEST_MODULE_NAME
":SENDER TEST START for chan %s\n", test_ch->name);
while (packet_count < max_packet_count) {
if (test_ctx->exit_flag) {
pr_info(TEST_MODULE_NAME ":Exit Test.\n");
return;
}
random_num = get_random_int();
size = (random_num % test_ch->packet_length) + 1;
TEST_DBG(TEST_MODULE_NAME "SENDER WAIT FOR EVENT for chan %s\n",
test_ch->name);
/* wait for data ready event */
write_avail = sdio_write_avail(test_ch->ch);
TEST_DBG(TEST_MODULE_NAME ":write_avail=%d\n", write_avail);
if (write_avail < size) {
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);
TEST_DBG(TEST_MODULE_NAME ":write_avail=%d\n", write_avail);
if (write_avail < size) {
pr_info(TEST_MODULE_NAME ":not enough write avail.\n");
continue;
}
test_ch->buf[0] = packet_count;
ret = sdio_write(test_ch->ch, test_ch->buf, size);
if (ret) {
pr_info(TEST_MODULE_NAME ":sender sdio_write err=%d.\n",
-ret);
goto exit_err;
}
/* wait for read data ready event */
TEST_DBG(TEST_MODULE_NAME ":sender wait for rx data for "
"chan %s\n",
test_ch->name);
read_avail = sdio_read_avail(test_ch->ch);
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);
if (read_avail != size) {
pr_info(TEST_MODULE_NAME
":read_avail size %d for chan %s not as "
"expected size %d.\n",
read_avail, test_ch->name, size);
goto exit_err;
}
memset(test_ch->buf, 0x00, size);
ret = sdio_read(test_ch->ch, test_ch->buf, size);
if (ret) {
pr_info(TEST_MODULE_NAME ":sender sdio_read for chan %s"
" err=%d.\n",
test_ch->name, -ret);
goto exit_err;
}
if ((test_ch->buf[0] != packet_count) && (size != 1)) {
pr_info(TEST_MODULE_NAME ":sender sdio_read WRONG DATA"
" for chan %s, size=%d\n",
test_ch->name, size);
goto exit_err;
}
test_ch->tx_bytes += size;
test_ch->rx_bytes += size;
packet_count++;
TEST_DBG(TEST_MODULE_NAME
":sender total rx bytes = 0x%x , packet#=%d, size=%d"
" for chan %s\n",
test_ch->rx_bytes, packet_count, size, test_ch->name);
TEST_DBG(TEST_MODULE_NAME
":sender total tx bytes = 0x%x , packet#=%d, size=%d"
" for chan %s\n",
test_ch->tx_bytes, packet_count, size, test_ch->name);
} /* end of while */
pr_info(TEST_MODULE_NAME
":SENDER TEST END: total rx bytes = 0x%x, "
" total tx bytes = 0x%x for chan %s\n",
test_ch->rx_bytes, test_ch->tx_bytes, test_ch->name);
pr_info(TEST_MODULE_NAME ": TEST PASS for chan %s.\n",
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 ": TEST FAIL for chan %s.\n",
test_ch->name);
test_ch->test_completed = 1;
test_ch->test_result = TEST_FAILED;
check_test_completion();
return;
}
/**
* A2 Perf Test
*/
static void a2_performance_test(struct test_channel *test_ch)
{
int ret = 0 ;
u32 read_avail = 0;
u32 write_avail = 0;
int tx_packet_count = 0;
int rx_packet_count = 0;
int size = 0;
u16 *buf16 = (u16 *) test_ch->buf;
int i;
int total_bytes = 0;
int max_packets = 10000;
u32 packet_size = test_ch->buf_size;
int rand_size = 0;
u64 start_jiffy, end_jiffy, delta_jiffies;
unsigned int time_msec = 0;
u32 throughput = 0;
max_packets = test_ch->config_msg.num_packets;
packet_size = test_ch->packet_length;
for (i = 0; i < packet_size / 2; i++)
buf16[i] = (u16) (i & 0xFFFF);
pr_info(TEST_MODULE_NAME ": A2 PERFORMANCE TEST START for chan %s\n",
test_ch->name);
start_jiffy = get_jiffies_64(); /* read the current time */
while (tx_packet_count < max_packets) {
if (test_ctx->exit_flag) {
pr_info(TEST_MODULE_NAME ":Exit Test.\n");
return;
}
if (test_ch->random_packet_size) {
rand_size = get_random_int();
packet_size = (rand_size % test_ch->packet_length) + 1;
if (packet_size < A2_MIN_PACKET_SIZE)
packet_size = A2_MIN_PACKET_SIZE;
}
/* wait for data ready event */
/* use a func to avoid compiler optimizations */
write_avail = sdio_write_avail(test_ch->ch);
read_avail = sdio_read_avail(test_ch->ch);
TEST_DBG(TEST_MODULE_NAME ":channel %s, write_avail=%d, "
"read_avail=%d for chan %s\n",
test_ch->name, write_avail, read_avail,
test_ch->name);
if ((write_avail == 0) && (read_avail == 0)) {
wait_event(test_ch->wait_q,
atomic_read(&test_ch->any_notify_count));
atomic_set(&test_ch->any_notify_count, 0);
}
write_avail = sdio_write_avail(test_ch->ch);
TEST_DBG(TEST_MODULE_NAME ":channel %s, write_avail=%d\n",
test_ch->name, write_avail);
if (write_avail > 0) {
size = min(packet_size, write_avail) ;
TEST_DBG(TEST_MODULE_NAME ":tx size = %d for chan %s\n",
size, test_ch->name);
test_ch->buf[0] = tx_packet_count;
test_ch->buf[(size/4)-1] = tx_packet_count;
ret = sdio_write(test_ch->ch, test_ch->buf, size);
if (ret) {
pr_info(TEST_MODULE_NAME ":sdio_write err=%d"
" for chan %s\n",
-ret, test_ch->name);
goto exit_err;
}
tx_packet_count++;
test_ch->tx_bytes += size;
}
read_avail = sdio_read_avail(test_ch->ch);
TEST_DBG(TEST_MODULE_NAME ":channel %s, read_avail=%d\n",
test_ch->name, read_avail);
if (read_avail > 0) {
size = min(packet_size, read_avail);
pr_debug(TEST_MODULE_NAME ":rx size = %d.\n", size);
ret = sdio_read(test_ch->ch, test_ch->buf, size);
if (ret) {
pr_info(TEST_MODULE_NAME ": sdio_read size %d "
" err=%d"
" for chan %s\n",
size, -ret, test_ch->name);
goto exit_err;
}
rx_packet_count++;
test_ch->rx_bytes += size;
}
TEST_DBG(TEST_MODULE_NAME
":total rx bytes = %d , rx_packet#=%d"
" for chan %s\n",
test_ch->rx_bytes, rx_packet_count, test_ch->name);
TEST_DBG(TEST_MODULE_NAME
":total tx bytes = %d , tx_packet#=%d"
" for chan %s\n",
test_ch->tx_bytes, tx_packet_count, test_ch->name);
} /* while (tx_packet_count < max_packets ) */
end_jiffy = get_jiffies_64(); /* read the current time */
delta_jiffies = end_jiffy - start_jiffy;
time_msec = jiffies_to_msecs(delta_jiffies);
pr_info(TEST_MODULE_NAME ":total rx bytes = 0x%x , rx_packet#=%d for"
" chan %s.\n",
test_ch->rx_bytes, rx_packet_count, test_ch->name);
pr_info(TEST_MODULE_NAME ":total tx bytes = 0x%x , tx_packet#=%d"
" for chan %s.\n",
test_ch->tx_bytes, tx_packet_count, test_ch->name);
total_bytes = (test_ch->tx_bytes + test_ch->rx_bytes);
pr_err(TEST_MODULE_NAME ":total bytes = %d, time msec = %d"
" for chan %s\n",
total_bytes , (int) time_msec, test_ch->name);
if (!test_ch->random_packet_size) {
throughput = (total_bytes / time_msec) * 8 / 1000;
pr_err(TEST_MODULE_NAME ":Performance = %d Mbit/sec for "
"chan %s\n",
throughput, test_ch->name);
}
#ifdef CONFIG_DEBUG_FS
switch (test_ch->ch_id) {
case SDIO_DUN:
test_ctx->debug.dun_throughput = throughput;
break;
case SDIO_RMNT:
test_ctx->debug.rmnt_throughput = throughput;
break;
default:
pr_err(TEST_MODULE_NAME "No debugfs for this channel "
"throughput");
}
#endif
pr_err(TEST_MODULE_NAME ": A2 PERFORMANCE TEST END for chan %s.\n",
test_ch->name);
pr_err(TEST_MODULE_NAME ": TEST PASS for chan %s\n", test_ch->name);
test_ch->test_completed = 1;
test_ch->test_result = TEST_PASSED;
check_test_completion();
return;
exit_err:
pr_err(TEST_MODULE_NAME ": TEST FAIL for chan %s\n", test_ch->name);
test_ch->test_completed = 1;
test_ch->test_result = TEST_FAILED;
check_test_completion();
return;
}
/**
* rx_cleanup
* This function reads all the messages sent by the modem until
* the read_avail is 0 after 1 second of sleep.
* The function returns the number of packets that was received.
*/
static void rx_cleanup(struct test_channel *test_ch, int *rx_packet_count)
{
int read_avail = 0;
int ret = 0;
int counter = 0;
if (!test_ch || !test_ch->ch) {
pr_err(TEST_MODULE_NAME ":%s NULL channel\n",
__func__);
return;
}
read_avail = sdio_read_avail(test_ch->ch);
TEST_DBG(TEST_MODULE_NAME ":channel %s, read_avail=%d\n",
test_ch->name, read_avail);
/* If no pending messages, wait to see if the modem sends data */
if (read_avail == 0) {
msleep(1000);
read_avail = sdio_read_avail(test_ch->ch);
}
while ((read_avail > 0) && (counter < 10)) {
TEST_DBG(TEST_MODULE_NAME ": read_avail=%d for ch %s\n",
read_avail, test_ch->name);
ret = sdio_read(test_ch->ch, test_ch->buf, read_avail);
if (ret) {
pr_info(TEST_MODULE_NAME ": sdio_read size %d "
" err=%d for chan %s\n",
read_avail, -ret, test_ch->name);
break;
}
(*rx_packet_count)++;
test_ch->rx_bytes += read_avail;
read_avail = sdio_read_avail(test_ch->ch);
if (read_avail == 0) {
msleep(1000);
counter++;
read_avail = sdio_read_avail(test_ch->ch);
}
}
pr_info(TEST_MODULE_NAME ": finished cleanup for ch %s, "
"rx_packet_count=%d, total rx bytes=%d\n",
test_ch->name, *rx_packet_count, test_ch->rx_bytes);
}
/**
* A2 RTT Test
* This function sends a packet and calculate the RTT time of
* this packet.
* The test also calculte Min, Max and Average RTT
*/
static void a2_rtt_test(struct test_channel *test_ch)
{
int ret = 0 ;
u32 read_avail = 0;
u32 write_avail = 0;
int tx_packet_count = 0;
int rx_packet_count = 0;
u16 *buf16 = (u16 *) test_ch->buf;
int i;
int max_packets = test_ch->config_msg.num_packets;
u32 packet_size = test_ch->packet_length;
s64 start_time, end_time;
int delta_usec = 0;
int time_average = 0;
int min_delta_usec = 0xFFFF;
int max_delta_usec = 0;
int total_time = 0;
int expected_read_size = 0;
for (i = 0; i < packet_size / 2; i++)
buf16[i] = (u16) (i & 0xFFFF);
pr_info(TEST_MODULE_NAME ": A2 RTT TEST START for chan %s\n",
test_ch->name);
/* Cleanup the pending RX data (such as loopback of te config msg) */
rx_cleanup(test_ch, &rx_packet_count);
rx_packet_count = 0;
while (tx_packet_count < max_packets) {
if (test_ctx->exit_flag) {
pr_info(TEST_MODULE_NAME ":Exit Test.\n");
return;
}
start_time = 0;
end_time = 0;
/* Allow sdio_al to go to sleep to change the read_threshold
* to 1
*/
msleep(100);
/* wait for data ready event */
write_avail = sdio_write_avail(test_ch->ch);
TEST_DBG(TEST_MODULE_NAME ":ch %s: write_avail=%d\n",
test_ch->name, write_avail);
if (write_avail == 0) {
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);
TEST_DBG(TEST_MODULE_NAME ":channel %s, write_avail=%d\n",
test_ch->name, write_avail);
if (write_avail > 0) {
TEST_DBG(TEST_MODULE_NAME ":tx size = %d for chan %s\n",
packet_size, test_ch->name);
test_ch->buf[0] = tx_packet_count;
start_time = ktime_to_us(ktime_get());
ret = sdio_write(test_ch->ch, test_ch->buf,
packet_size);
if (ret) {
pr_err(TEST_MODULE_NAME ":sdio_write err=%d"
" for chan %s\n",
-ret, test_ch->name);
goto exit_err;
}
tx_packet_count++;
test_ch->tx_bytes += packet_size;
} else {
pr_err(TEST_MODULE_NAME ": Invalid write_avail"
" %d for chan %s\n",
write_avail, test_ch->name);
goto exit_err;
}
expected_read_size = packet_size + A2_HEADER_OVERHEAD;
read_avail = sdio_read_avail(test_ch->ch);
TEST_DBG(TEST_MODULE_NAME ":channel %s, read_avail=%d\n",
test_ch->name, read_avail);
while (read_avail < expected_read_size) {
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);
}
if (read_avail >= expected_read_size) {
pr_debug(TEST_MODULE_NAME ":read_avail=%d for ch %s.\n",
read_avail, test_ch->name);
ret = sdio_read(test_ch->ch, test_ch->buf,
expected_read_size);
if (ret) {
pr_info(TEST_MODULE_NAME ": sdio_read size %d "
" err=%d for chan %s\n",
expected_read_size, -ret,
test_ch->name);
goto exit_err;
}
end_time = ktime_to_us(ktime_get());
rx_packet_count++;
test_ch->rx_bytes += expected_read_size;
} else {
pr_info(TEST_MODULE_NAME ": Invalid read_avail "
"%d for chan %s\n",
read_avail, test_ch->name);
goto exit_err;
}
delta_usec = (int)(end_time - start_time);
total_time += delta_usec;
if (delta_usec < min_delta_usec)
min_delta_usec = delta_usec;
if (delta_usec > max_delta_usec)
max_delta_usec = delta_usec;
TEST_DBG(TEST_MODULE_NAME
":RTT time=%d for packet #%d for chan %s\n",
delta_usec, tx_packet_count, test_ch->name);
} /* while (tx_packet_count < max_packets ) */
pr_info(TEST_MODULE_NAME ":total rx bytes = 0x%x , rx_packet#=%d for"
" chan %s.\n",
test_ch->rx_bytes, rx_packet_count, test_ch->name);
pr_info(TEST_MODULE_NAME ":total tx bytes = 0x%x , tx_packet#=%d"
" for chan %s.\n",
test_ch->tx_bytes, tx_packet_count, test_ch->name);
time_average = total_time / tx_packet_count;
pr_info(TEST_MODULE_NAME ":Average RTT time = %d for chan %s\n",
time_average, test_ch->name);
pr_info(TEST_MODULE_NAME ":MIN RTT time = %d for chan %s\n",
min_delta_usec, test_ch->name);
pr_info(TEST_MODULE_NAME ":MAX RTT time = %d for chan %s\n",
max_delta_usec, test_ch->name);
pr_info(TEST_MODULE_NAME ": A2 RTT TEST END for chan %s.\n",
test_ch->name);
pr_info(TEST_MODULE_NAME ": TEST PASS for chan %s\n", test_ch->name);
test_ch->test_completed = 1;
test_ch->test_result = TEST_PASSED;
check_test_completion();
return;
exit_err:
pr_err(TEST_MODULE_NAME ": TEST FAIL for chan %s\n", test_ch->name);
test_ch->test_completed = 1;
test_ch->test_result = TEST_FAILED;
check_test_completion();
return;
}
/**
* sender No loopback Test
*/
static void sender_no_loopback_test(struct test_channel *test_ch)
{
int ret = 0 ;
u32 write_avail = 0;
int packet_count = 0;
int size = 512;
u16 *buf16 = (u16 *) test_ch->buf;
int i;
int max_packet_count = 10000;
unsigned int random_num = 0;
max_packet_count = test_ch->config_msg.num_packets;
for (i = 0 ; i < size / 2 ; i++)
buf16[i] = (u16) (i & 0xFFFF);
pr_info(TEST_MODULE_NAME
":SENDER NO LP TEST START for chan %s\n", test_ch->name);
while (packet_count < max_packet_count) {
if (test_ctx->exit_flag) {
pr_info(TEST_MODULE_NAME ":Exit Test.\n");
return;
}
random_num = get_random_int();
size = (random_num % test_ch->packet_length) + 1;
TEST_DBG(TEST_MODULE_NAME ":SENDER WAIT FOR EVENT "
"for chan %s\n",
test_ch->name);
/* wait for data ready event */
write_avail = sdio_write_avail(test_ch->ch);
TEST_DBG(TEST_MODULE_NAME ":write_avail=%d\n", write_avail);
if (write_avail < size) {
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);
TEST_DBG(TEST_MODULE_NAME ":write_avail=%d\n", write_avail);
if (write_avail < size) {
pr_info(TEST_MODULE_NAME ":not enough write avail.\n");
continue;
}
test_ch->buf[0] = packet_count;
ret = sdio_write(test_ch->ch, test_ch->buf, size);
if (ret) {
pr_info(TEST_MODULE_NAME ":sender sdio_write err=%d.\n",
-ret);
goto exit_err;
}
test_ch->tx_bytes += size;
packet_count++;
TEST_DBG(TEST_MODULE_NAME
":sender total tx bytes = 0x%x , packet#=%d, size=%d"
" for chan %s\n",
test_ch->tx_bytes, packet_count, size, test_ch->name);
} /* end of while */
pr_info(TEST_MODULE_NAME
":SENDER TEST END: total tx bytes = 0x%x, "
" for chan %s\n",
test_ch->tx_bytes, test_ch->name);
test_ch->modem_result_per_chan = wait_for_result_msg(test_ch);
if (test_ch->modem_result_per_chan) {
pr_info(TEST_MODULE_NAME ": TEST PASS for chan %s.\n",
test_ch->name);
test_ch->test_result = TEST_PASSED;
} else {
pr_info(TEST_MODULE_NAME ": TEST FAILURE for chan %s.\n",
test_ch->name);
test_ch->test_result = TEST_FAILED;
}
test_ch->test_completed = 1;
check_test_completion();
return;
exit_err:
pr_info(TEST_MODULE_NAME ": TEST FAIL for chan %s.\n",
test_ch->name);
test_ch->test_completed = 1;
test_ch->test_result = TEST_FAILED;
check_test_completion();
return;
}
/**
* Modem reset Test
* The test verifies that it finished sending all the packets
* while there might be modem reset in the middle
*/
static void modem_reset_test(struct test_channel *test_ch)
{
int ret = 0 ;
u32 read_avail = 0;
u32 write_avail = 0;
int tx_packet_count = 0;
int rx_packet_count = 0;
int size = 0;
u16 *buf16 = (u16 *) test_ch->buf;
int i;
int max_packets = 10000;
u32 packet_size = test_ch->buf_size;
int is_err = 0;
max_packets = test_ch->config_msg.num_packets;
packet_size = test_ch->packet_length;
for (i = 0; i < packet_size / 2; i++)
buf16[i] = (u16) (i & 0xFFFF);
pr_info(TEST_MODULE_NAME ": Modem Reset TEST START for chan %s\n",
test_ch->name);
while (tx_packet_count < max_packets) {
if (test_ctx->exit_flag) {
pr_info(TEST_MODULE_NAME ":Exit Test.\n");
return;
}
if (test_ch->card_removed) {
pr_info(TEST_MODULE_NAME ": card removal was detected "
"for chan %s, tx_total=0x%x\n",
test_ch->name, test_ch->tx_bytes);
wait_event(test_ch->wait_q,
atomic_read(&test_ch->card_detected_event));
atomic_set(&test_ch->card_detected_event, 0);
pr_info(TEST_MODULE_NAME ": card_detected_event "
"for chan %s\n", test_ch->name);
if (test_ch->card_removed)
continue;
is_err = 0;
/* Need to wait for the modem to be ready */
msleep(5000);
pr_info(TEST_MODULE_NAME ": sending the config message "
"for chan %s\n", test_ch->name);
send_config_msg(test_ch);
}
/* wait for data ready event */
/* use a func to avoid compiler optimizations */
write_avail = sdio_write_avail(test_ch->ch);
read_avail = sdio_read_avail(test_ch->ch);
TEST_DBG(TEST_MODULE_NAME ":channel %s, write_avail=%d, "
"read_avail=%d for chan %s\n",
test_ch->name, write_avail, read_avail,
test_ch->name);
if ((write_avail == 0) && (read_avail == 0)) {
wait_event(test_ch->wait_q,
atomic_read(&test_ch->any_notify_count));
atomic_set(&test_ch->any_notify_count, 0);
}
if (atomic_read(&test_ch->card_detected_event)) {
atomic_set(&test_ch->card_detected_event, 0);
pr_info(TEST_MODULE_NAME ": card_detected_event "
"for chan %s, tx_total=0x%x\n",
test_ch->name, test_ch->tx_bytes);
if (test_ch->card_removed)
continue;
/* Need to wait for the modem to be ready */
msleep(5000);
is_err = 0;
pr_info(TEST_MODULE_NAME ": sending the config message "
"for chan %s\n", test_ch->name);
send_config_msg(test_ch);
}
write_avail = sdio_write_avail(test_ch->ch);
TEST_DBG(TEST_MODULE_NAME ":channel %s, write_avail=%d\n",
test_ch->name, write_avail);
if (write_avail > 0) {
size = min(packet_size, write_avail) ;
pr_debug(TEST_MODULE_NAME ":tx size = %d for chan %s\n",
size, test_ch->name);
test_ch->buf[0] = tx_packet_count;
test_ch->buf[(size/4)-1] = tx_packet_count;
TEST_DBG(TEST_MODULE_NAME ":channel %s, sdio_write, "
"size=%d\n", test_ch->name, size);
if (is_err) {
msleep(100);
continue;
}
ret = sdio_write(test_ch->ch, test_ch->buf, size);
if (ret) {
pr_info(TEST_MODULE_NAME ":sdio_write err=%d"
" for chan %s\n",
-ret, test_ch->name);
is_err = 1;
msleep(20);
continue;
}
tx_packet_count++;
test_ch->tx_bytes += size;
test_ch->config_msg.num_packets--;
}
read_avail = sdio_read_avail(test_ch->ch);
TEST_DBG(TEST_MODULE_NAME ":channel %s, read_avail=%d\n",
test_ch->name, read_avail);
if (read_avail > 0) {
size = min(packet_size, read_avail);
pr_debug(TEST_MODULE_NAME ":rx size = %d.\n", size);
TEST_DBG(TEST_MODULE_NAME ":channel %s, sdio_read, "
"size=%d\n", test_ch->name, size);
if (is_err) {
msleep(100);
continue;
}
ret = sdio_read(test_ch->ch, test_ch->buf, size);
if (ret) {
pr_info(TEST_MODULE_NAME ": sdio_read size %d "
" err=%d"
" for chan %s\n",
size, -ret, test_ch->name);
is_err = 1;
msleep(20);
continue;
}
rx_packet_count++;
test_ch->rx_bytes += size;
}
TEST_DBG(TEST_MODULE_NAME
":total rx bytes = %d , rx_packet#=%d"
" for chan %s\n",
test_ch->rx_bytes, rx_packet_count, test_ch->name);
TEST_DBG(TEST_MODULE_NAME
":total tx bytes = %d , tx_packet#=%d"
" for chan %s\n",
test_ch->tx_bytes, tx_packet_count, test_ch->name);
udelay(500);
} /* while (tx_packet_count < max_packets ) */
pr_info(TEST_MODULE_NAME ":total rx bytes = 0x%x , rx_packet#=%d for"
" chan %s.\n",
test_ch->rx_bytes, rx_packet_count, test_ch->name);
pr_info(TEST_MODULE_NAME ":total tx bytes = 0x%x , tx_packet#=%d"
" for chan %s.\n",
test_ch->tx_bytes, tx_packet_count, test_ch->name);
pr_err(TEST_MODULE_NAME ": Modem Reset TEST END for chan %s.\n",
test_ch->name);
pr_err(TEST_MODULE_NAME ": TEST PASS for chan %s\n", test_ch->name);
test_ch->test_completed = 1;
test_ch->test_result = TEST_PASSED;
check_test_completion();
return;
}
/**
* Worker thread to handle the tests types
*/
static void worker(struct work_struct *work)
{
struct test_channel *test_ch = NULL;
struct test_work *test_work = container_of(work,
struct test_work,
work);
int test_type = 0;
test_ch = test_work->test_ch;
if (test_ch == NULL) {
pr_err(TEST_MODULE_NAME ":NULL test_ch\n");
return;
}
test_type = test_ch->test_type;
switch (test_type) {
case SDIO_TEST_LOOPBACK_HOST:
loopback_test(test_ch);
break;
case SDIO_TEST_LOOPBACK_CLIENT:
sender_test(test_ch);
break;
case SDIO_TEST_PERF:
a2_performance_test(test_ch);
break;
case SDIO_TEST_LPM_CLIENT_WAKER:
lpm_test(test_ch);
break;
case SDIO_TEST_LPM_HOST_WAKER:
lpm_test_host_waker(test_ch);
break;
case SDIO_TEST_HOST_SENDER_NO_LP:
sender_no_loopback_test(test_ch);
break;
case SDIO_TEST_LPM_RANDOM:
lpm_continuous_rand_test(test_ch);
break;
case SDIO_TEST_RTT:
a2_rtt_test(test_ch);
break;
case SDIO_TEST_CLOSE_CHANNEL:
if (test_ch->ch_id != SDIO_SMEM)
open_close_test(test_ch);
break;
case SDIO_TEST_MODEM_RESET:
modem_reset_test(test_ch);
break;
default:
pr_err(TEST_MODULE_NAME ":Bad Test type = %d.\n",
(int) test_type);
}
}
/**
* Notification Callback
*
* Notify the worker
*
*/
static void notify(void *priv, unsigned channel_event)
{
struct test_channel *test_ch = (struct test_channel *) priv;
pr_debug(TEST_MODULE_NAME ": %s - notify event=%d.\n",
__func__, channel_event);
if (test_ch->ch == NULL) {
pr_info(TEST_MODULE_NAME ": %s - notify before ch ready.\n",
__func__);
return;
}
switch (channel_event) {
case SDIO_EVENT_DATA_READ_AVAIL:
atomic_inc(&test_ch->rx_notify_count);
atomic_set(&test_ch->any_notify_count, 1);
TEST_DBG(TEST_MODULE_NAME ": %s - SDIO_EVENT_DATA_READ_AVAIL, "
"any_notify_count=%d, rx_notify_count=%d\n",
__func__,
atomic_read(&test_ch->any_notify_count),
atomic_read(&test_ch->rx_notify_count));
/*
* when there is pending data on a channel we would like to
* turn on the bit mask that implies that there is pending
* data for that channel on that deivce
*/
if (test_ch->test_device != NULL &&
test_ch->test_type == SDIO_TEST_LPM_RANDOM) {
spin_lock_irqsave(&test_ch->test_device->lpm_array_lock,
test_ch->test_device->
lpm_array_lock_flags);
test_ch->test_device->read_avail_mask |=
test_ch->channel_mask_id;
test_ch->notify_counter_per_chan++;
lpm_test_update_entry(test_ch, LPM_NOTIFY, "NOTIFY", 0);
spin_unlock_irqrestore(&test_ch->test_device->
lpm_array_lock,
test_ch->test_device->
lpm_array_lock_flags);
}
break;
case SDIO_EVENT_DATA_WRITE_AVAIL:
atomic_inc(&test_ch->tx_notify_count);
atomic_set(&test_ch->any_notify_count, 1);
TEST_DBG(TEST_MODULE_NAME ": %s - SDIO_EVENT_DATA_WRITE_AVAIL, "
"any_notify_count=%d, tx_notify_count=%d\n",
__func__,
atomic_read(&test_ch->any_notify_count),
atomic_read(&test_ch->tx_notify_count));
break;
default:
BUG();
}
wake_up(&test_ch->wait_q);
}
#ifdef CONFIG_MSM_SDIO_SMEM
static int sdio_smem_test_cb(int event)
{
struct test_channel *tch = test_ctx->test_ch_arr[SDIO_SMEM];
int i;
int *smem_buf = (int *)test_ctx->smem_buf;
uint32_t val = 0;
int ret = 0;
pr_debug(TEST_MODULE_NAME ":%s: Received event %d\n", __func__, event);
if (!tch) {
pr_err(TEST_MODULE_NAME ": %s NULL tch\n", __func__);
return -EINVAL;
}
switch (event) {
case SDIO_SMEM_EVENT_READ_DONE:
tch->rx_bytes += SMEM_MAX_XFER_SIZE;
for (i = 0; i < SMEM_MAX_XFER_SIZE;) {
val = (int)*smem_buf;
if ((val != test_ctx->smem_counter) && tch->is_used) {
pr_err(TEST_MODULE_NAME ":%s: Invalid value %d "
"expected %d in smem arr",
__func__, val, test_ctx->smem_counter);
pr_err(TEST_MODULE_NAME ":SMEM test FAILED\n");
tch->test_completed = 1;
tch->test_result = TEST_FAILED;
check_test_completion();
ret = -EINVAL;
goto exit;
}
i += 4;
smem_buf++;
test_ctx->smem_counter++;
}
if (tch->rx_bytes >= 40000000) {
if ((!tch->test_completed) && tch->is_used) {
pr_info(TEST_MODULE_NAME ":SMEM test PASSED\n");
tch->test_completed = 1;
tch->test_result = TEST_PASSED;
check_test_completion();
}
}
break;
case SDIO_SMEM_EVENT_READ_ERR:
if (tch->is_used) {
pr_err(TEST_MODULE_NAME ":Read overflow, "
"SMEM test FAILED\n");
tch->test_completed = 1;
tch->test_result = TEST_FAILED;
ret = -EIO;
}
break;
default:
if (tch->is_used) {
pr_err(TEST_MODULE_NAME ":Unhandled event %d\n", event);
ret = -EINVAL;
}
break;
}
exit:
return ret;
}
static int sdio_smem_open(struct sdio_smem_client *sdio_smem)
{
int ret = 0;
if (!sdio_smem) {
pr_info(TEST_MODULE_NAME "%s: NULL sdio_smem_client\n",
__func__);
return -EINVAL;
}
if (test_ctx->test_ch_arr[SDIO_SMEM]->ch_ready) {
pr_info(TEST_MODULE_NAME "%s: SDIO_SMEM channel is already opened\n",
__func__);
return 0;
}
test_ctx->test_ch_arr[SDIO_SMEM]->ch_ready = 1;
sdio_smem->buf = test_ctx->smem_buf;
sdio_smem->size = SMEM_MAX_XFER_SIZE;
sdio_smem->cb_func = sdio_smem_test_cb;
ret = sdio_smem_register_client();
if (ret)
pr_info(TEST_MODULE_NAME "%s: Error (%d) registering sdio_smem "
"test client\n",
__func__, ret);
return ret;
}
static int sdio_smem_test_probe(struct platform_device *pdev)
{
test_ctx->sdio_smem = container_of(pdev, struct sdio_smem_client,
plat_dev);
return sdio_smem_open(test_ctx->sdio_smem);
}
static struct platform_driver sdio_smem_client_drv = {
.probe = sdio_smem_test_probe,
.driver = {
.name = "SDIO_SMEM_CLIENT",
.owner = THIS_MODULE,
},
};
#endif
static void sdio_test_lpm_timeout_handler(unsigned long data)
{
struct test_channel *tch = (struct test_channel *)data;
pr_info(TEST_MODULE_NAME ": %s - LPM TEST TIMEOUT Expired after "
"%d ms\n", __func__, tch->timeout_ms);
tch->test_completed = 1;
pr_info(TEST_MODULE_NAME ": %s - tch->test_result = TEST_FAILED\n",
__func__);
tch->test_completed = 1;
tch->test_result = TEST_FAILED;
check_test_completion();
return;
}
static void sdio_test_lpm_timer_handler(unsigned long data)
{
struct test_channel *tch = (struct test_channel *)data;
pr_info(TEST_MODULE_NAME ": %s - LPM TEST Timer Expired after "
"%d ms\n", __func__, tch->timer_interval_ms);
if (!tch) {
pr_err(TEST_MODULE_NAME ": %s - LPM TEST FAILED. "
"tch is NULL\n", __func__);
return;
}
if (!tch->ch) {
pr_err(TEST_MODULE_NAME ": %s - LPM TEST FAILED. tch->ch "
"is NULL\n", __func__);
tch->test_result = TEST_FAILED;
return;
}
/* Verfiy that we voted for sleep */
if (tch->is_ok_to_sleep) {
tch->test_result = TEST_PASSED;
pr_info(TEST_MODULE_NAME ": %s - 8K voted for sleep\n",
__func__);
} else {
tch->test_result = TEST_FAILED;
pr_info(TEST_MODULE_NAME ": %s - 8K voted against sleep\n",
__func__);
}
sdio_al_unregister_lpm_cb(tch->sdio_al_device);
if (tch->test_type == SDIO_TEST_LPM_HOST_WAKER) {
atomic_set(&tch->wakeup_client, 1);
wake_up(&tch->wait_q);
}
}
int sdio_test_wakeup_callback(void *device_handle, int is_vote_for_sleep)
{
int i = 0;
TEST_DBG(TEST_MODULE_NAME ": %s is_vote_for_sleep=%d!!!",
__func__, is_vote_for_sleep);
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))
continue;
if (tch->sdio_al_device == device_handle) {
tch->is_ok_to_sleep = is_vote_for_sleep;
if (tch->test_type == SDIO_TEST_LPM_RANDOM) {
spin_lock_irqsave(&tch->test_device->
lpm_array_lock,
tch->test_device->
lpm_array_lock_flags);
if (is_vote_for_sleep == 1)
lpm_test_update_entry(tch,
LPM_SLEEP,
"SLEEP ", 0);
else
lpm_test_update_entry(tch,
LPM_WAKEUP,
"WAKEUP", 0);
spin_unlock_irqrestore(&tch->test_device->
lpm_array_lock,
tch->test_device->
lpm_array_lock_flags);
break;
}
}
}
return 0;
}
static int sdio_test_find_dev(struct test_channel *tch)
{
int j;
int null_index = -1;
for (j = 0 ; j < MAX_NUM_OF_SDIO_DEVICES; ++j) {
struct sdio_test_device *test_dev =
&test_ctx->test_dev_arr[j];
if (test_dev->sdio_al_device == NULL) {
if (null_index == -1)
null_index = j;
continue;
}
if (test_dev->sdio_al_device ==
tch->ch->sdio_al_dev) {
test_dev->open_channels_counter_to_recv++;
test_dev->open_channels_counter_to_send++;
tch->test_device = test_dev;
/* setting mask id for pending data for
this channel */
tch->channel_mask_id = test_dev->next_mask_id;
test_dev->next_mask_id *= 2;
pr_info(TEST_MODULE_NAME ": %s - channel %s "
"got read_mask_id = 0x%x. device "
"next_mask_id=0x%x",
__func__, tch->name, tch->channel_mask_id,
test_dev->next_mask_id);
break;
}
}
/*
* happens ones a new device is "discovered" while testing. i.e
* if testing a few channels, a new deivce will be "discovered" once
* the first channel of a device is being tested
*/
if (j == MAX_NUM_OF_SDIO_DEVICES) {
struct sdio_test_device *test_dev =
&test_ctx->
test_dev_arr[null_index];
test_dev->sdio_al_device =
tch->ch->sdio_al_dev;
test_ctx->number_of_active_devices++;
test_ctx->max_number_of_devices++;
test_dev->open_channels_counter_to_recv++;
test_dev->open_channels_counter_to_send++;
test_dev->next_avail_entry_in_array = 0;
tch->test_device = test_dev;
tch->test_device->array_size =
LPM_ARRAY_SIZE;
test_dev->modem_result_per_dev = 1;
tch->modem_result_per_chan = 0;
spin_lock_init(&test_dev->
lpm_array_lock);
if (tch->test_type == SDIO_TEST_LPM_RANDOM) {
pr_err(MODULE_NAME ": %s - "
"Allocating Msg Array for "
"Maximum open channels for device (%d) "
"Channels. Array has %d entries",
__func__,
LPM_MAX_OPEN_CHAN_PER_DEV,
test_dev->array_size);
test_dev->lpm_arr =
kzalloc(sizeof(
struct lpm_entry_type) *
tch->
test_device->array_size,
GFP_KERNEL);
if (!test_dev->lpm_arr) {
pr_err(MODULE_NAME ": %s - "
"lpm_arr is NULL",
__func__);
return -ENOMEM;
}
}
/*
* in new device, initialize next_mask_id, and setting
* mask_id to the channel
*/
test_dev->next_mask_id = 0x1;
tch->channel_mask_id = test_dev->next_mask_id;
test_dev->next_mask_id *= 2;
pr_info(TEST_MODULE_NAME ": %s - channel %s got "
"read_mask_id = 0x%x. device next_mask_id=0x%x",
__func__,
tch->name,
tch->channel_mask_id,
test_dev->next_mask_id);
}
return 0;
}
static void check_test_result(void)
{
int result = 1;
int i = 0;
test_ctx->max_number_of_devices = 0;
pr_info(TEST_MODULE_NAME ": %s - Woke Up\n", __func__);
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))
continue;
if (tch->test_type == SDIO_TEST_LPM_RANDOM)
result &= tch->test_device->final_result_per_dev;
else
if (tch->test_result == TEST_FAILED) {
pr_info(TEST_MODULE_NAME ": %s - "
"Test FAILED\n", __func__);
test_ctx->test_result = TEST_FAILED;
pr_err(TEST_MODULE_NAME ": %s - "
"test_result %d",
__func__, test_ctx->test_result);
return;
}
}
if (result == 0) {
pr_info(TEST_MODULE_NAME ": %s - Test FAILED\n", __func__);
test_ctx->test_result = TEST_FAILED;
pr_err(TEST_MODULE_NAME ": %s - "
"test_result %d",
__func__, test_ctx->test_result);
return;
}
pr_info(TEST_MODULE_NAME ": %s - Test PASSED", __func__);
test_ctx->test_result = TEST_PASSED;
pr_err(TEST_MODULE_NAME ": %s - "
"test_result %d",
__func__, test_ctx->test_result);
return;
}
/**
* Test Main
*/
static int test_start(void)
{
int ret = -ENOMEM;
int i;
pr_debug(TEST_MODULE_NAME ":Starting Test ....\n");
test_ctx->test_completed = 0;
test_ctx->test_result = TEST_NO_RESULT;
test_ctx->debug.dun_throughput = 0;
test_ctx->debug.rmnt_throughput = 0;
test_ctx->number_of_active_devices = 0;
pr_err(TEST_MODULE_NAME ": %s - test_result %d",
__func__, test_ctx->test_result);
memset(test_ctx->test_dev_arr, 0,
sizeof(struct sdio_test_device)*MAX_NUM_OF_SDIO_DEVICES);
/* Open The Channels */
for (i = 0; i < SDIO_MAX_CHANNELS; i++) {
struct test_channel *tch = test_ctx->test_ch_arr[i];
if ((!tch) || (!tch->is_used))
continue;
tch->rx_bytes = 0;
tch->tx_bytes = 0;
atomic_set(&tch->tx_notify_count, 0);
atomic_set(&tch->rx_notify_count, 0);
atomic_set(&tch->any_notify_count, 0);
atomic_set(&tch->wakeup_client, 0);
/* in case there are values left from previous tests */
tch->notify_counter_per_chan = 0;
memset(tch->buf, 0x00, tch->buf_size);
tch->test_result = TEST_NO_RESULT;
tch->test_completed = 0;
ret = open_sdio_ch(tch);
if (ret)
continue;
if (tch->ch_id != SDIO_SMEM) {
ret = sdio_test_find_dev(tch);
if (ret) {
pr_err(TEST_MODULE_NAME ": %s - "
"sdio_test_find_dev() returned with "
"error", __func__);
return -ENODEV;
}
tch->sdio_al_device = tch->ch->sdio_al_dev;
}
if ((tch->test_type == SDIO_TEST_LPM_HOST_WAKER) ||
(tch->test_type == SDIO_TEST_LPM_CLIENT_WAKER) ||
(tch->test_type == SDIO_TEST_LPM_RANDOM))
sdio_al_register_lpm_cb(tch->sdio_al_device,
sdio_test_wakeup_callback);
}
/*
* make some space between opening the channels and sending the
* config messages
*/
msleep(100);
/*
* try to delay send_config_msg of all channels to after the point
* when we open them all
*/
for (i = 0; i < SDIO_MAX_CHANNELS; i++) {
struct test_channel *tch = test_ctx->test_ch_arr[i];
if ((!tch) || (!tch->is_used))
continue;
if ((tch->ch_ready) && (tch->ch_id != SDIO_SMEM))
send_config_msg(tch);
if ((tch->test_type == SDIO_TEST_LPM_HOST_WAKER) ||
(tch->test_type == SDIO_TEST_LPM_CLIENT_WAKER) ||
(tch->test_type == SDIO_TEST_LPM_RANDOM)) {
if (tch->timer_interval_ms > 0) {
pr_info(TEST_MODULE_NAME ": %s - init timer, "
"ms=%d\n",
__func__, tch->timer_interval_ms);
init_timer(&tch->timer);
tch->timer.data = (unsigned long)tch;
tch->timer.function =
sdio_test_lpm_timer_handler;
tch->timer.expires = jiffies +
msecs_to_jiffies(tch->timer_interval_ms);
add_timer(&tch->timer);
}
}
}
pr_debug(TEST_MODULE_NAME ":queue_work..\n");
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))
continue;
if (tch->ch_id == SDIO_SMEM) {
#ifdef CONFIG_MSM_SDIO_SMEM
if (tch->test_type == SDIO_TEST_CLOSE_CHANNEL)
open_close_smem_test(tch);
#endif
} else {
queue_work(tch->workqueue, &tch->test_work.work);
}
}
pr_info(TEST_MODULE_NAME ": %s - Waiting for the test completion\n",
__func__);
wait_event(test_ctx->wait_q, test_ctx->test_completed);
check_test_result();
/*
* Close the channels and zero the is_used flag so that if the modem
* will be reset after the test completion we won't re-open
* the channels
*/
for (i = 0; i < SDIO_MAX_CHANNELS; i++) {
struct test_channel *tch = test_ctx->test_ch_arr[i];
if ((!tch) || (!tch->is_used))
continue;
if (!tch->ch_ready) {
tch->is_used = 0;
continue;
}
close_sdio_ch(tch);
tch->is_used = 0;
}
if (test_ctx->test_result == TEST_PASSED)
return 0;
else
return -EINVAL;
}
static int set_params_loopback_9k(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_LOOPBACK_CLIENT;
tch->config_msg.signature = TEST_CONFIG_SIGNATURE;
tch->config_msg.test_case = SDIO_TEST_LOOPBACK_CLIENT;
tch->config_msg.num_packets = 10000;
tch->config_msg.num_iterations = 1;
tch->packet_length = 512;
if (tch->ch_id == SDIO_RPC)
tch->packet_length = 128;
tch->timer_interval_ms = 0;
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) {
pr_err(TEST_MODULE_NAME ":NULL channel\n");
return -EINVAL;
}
tch->is_used = 1;
tch->test_type = SDIO_TEST_PERF;
tch->config_msg.signature = TEST_CONFIG_SIGNATURE;
tch->config_msg.test_case = SDIO_TEST_LOOPBACK_CLIENT;
tch->packet_length = MAX_XFER_SIZE;
if (tch->ch_id == SDIO_DIAG)
tch->packet_length = 512;
else if (tch->ch_id == SDIO_DUN)
tch->packet_length = DUN_PACKET_SIZE;
pr_info(TEST_MODULE_NAME ": %s: packet_length=%d", __func__,
tch->packet_length);
tch->config_msg.num_packets = 10000;
tch->config_msg.num_iterations = 1;
tch->random_packet_size = 0;
tch->timer_interval_ms = 0;
return 0;
}
static int set_params_rtt(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_RTT;
tch->config_msg.signature = TEST_CONFIG_SIGNATURE;
tch->config_msg.test_case = SDIO_TEST_LOOPBACK_CLIENT;
tch->packet_length = 32;
tch->config_msg.num_packets = 200;
tch->config_msg.num_iterations = 1;
tch->random_packet_size = 0;
tch->timer_interval_ms = 0;
return 0;
}
static int set_params_a2_small_pkts(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_PERF;
tch->config_msg.signature = TEST_CONFIG_SIGNATURE;
tch->config_msg.test_case = SDIO_TEST_LOOPBACK_CLIENT;
tch->packet_length = 128;
tch->config_msg.num_packets = 1000000;
tch->config_msg.num_iterations = 1;
tch->random_packet_size = 1;
tch->timer_interval_ms = 0;
return 0;
}
static int set_params_modem_reset(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_MODEM_RESET;
tch->config_msg.signature = TEST_CONFIG_SIGNATURE;
tch->config_msg.test_case = SDIO_TEST_LOOPBACK_CLIENT;
tch->packet_length = 512;
if (tch->ch_id == SDIO_RPC)
tch->packet_length = 128;
else if ((tch->ch_id == SDIO_RMNT) || (tch->ch_id == SDIO_DUN))
tch->packet_length = MAX_XFER_SIZE;
tch->config_msg.num_packets = 50000;
tch->config_msg.num_iterations = 1;
tch->timer_interval_ms = 0;
return 0;
}
static int set_params_smem_test(struct test_channel *tch)
{
if (!tch) {
pr_err(TEST_MODULE_NAME ":NULL channel\n");
return -EINVAL;
}
tch->is_used = 1;
tch->timer_interval_ms = 0;
return 0;
}
static int set_params_lpm_test(struct test_channel *tch,
enum sdio_test_case_type test,
int timer_interval_ms)
{
static int first_time = 1;
if (!tch) {
pr_err(TEST_MODULE_NAME ": %s - NULL channel\n", __func__);
return -EINVAL;
}
tch->is_used = 1;
tch->test_type = test;
tch->config_msg.signature = TEST_CONFIG_SIGNATURE;
tch->config_msg.test_case = test;
tch->config_msg.num_packets = LPM_TEST_NUM_OF_PACKETS;
tch->config_msg.num_iterations = 1;
tch->timer_interval_ms = timer_interval_ms;
tch->timeout_ms = 10000;
tch->packet_length = 0;
if (test != SDIO_TEST_LPM_RANDOM) {
init_timer(&tch->timeout_timer);
tch->timeout_timer.data = (unsigned long)tch;
tch->timeout_timer.function = sdio_test_lpm_timeout_handler;
tch->timeout_timer.expires = jiffies +
msecs_to_jiffies(tch->timeout_ms);
add_timer(&tch->timeout_timer);
pr_info(TEST_MODULE_NAME ": %s - Initiated LPM TIMEOUT TIMER."
"set to %d ms\n",
__func__, tch->timeout_ms);
}
if (first_time) {
pr_info(TEST_MODULE_NAME ": %s - wake_lock_init() called\n",
__func__);
wake_lock_init(&test_ctx->wake_lock,
WAKE_LOCK_SUSPEND, TEST_MODULE_NAME);
first_time = 0;
}
pr_info(TEST_MODULE_NAME ": %s - wake_lock() for the TEST is "
"called channel %s. to prevent real sleeping\n",
__func__, tch->name);
wake_lock(&test_ctx->wake_lock);
return 0;
}
static int set_params_8k_sender_no_lp(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_HOST_SENDER_NO_LP;
tch->config_msg.signature = TEST_CONFIG_SIGNATURE;
tch->config_msg.test_case = SDIO_TEST_HOST_SENDER_NO_LP;
tch->config_msg.num_packets = 1000;
tch->config_msg.num_iterations = 1;
tch->packet_length = 512;
if (tch->ch_id == SDIO_RPC)
tch->packet_length = 128;
tch->timer_interval_ms = 0;
return 0;
}
static void set_pseudo_random_seed(void)
{
/* Set the seed accoring to the kernel command parameters if any or
get a random value */
if (seed != 0) {
test_ctx->lpm_pseudo_random_seed = seed;
} else {
test_ctx->lpm_pseudo_random_seed =
(unsigned int)(get_jiffies_64() & 0xFFFF);
test_ctx->lpm_pseudo_random_seed =
pseudo_random_seed(&test_ctx->lpm_pseudo_random_seed);
}
pr_info(TEST_MODULE_NAME ":%s: seed is %u",
__func__, test_ctx->lpm_pseudo_random_seed);
}
/*
for each channel
1. open channel
2. close channel
*/
static int close_channel_lpm_test(int channel_num)
{
int ret = 0;
struct test_channel *tch = NULL;
tch = test_ctx->test_ch_arr[channel_num];
ret = open_sdio_ch(tch);
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 = close_sdio_ch(tch);
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->is_used = 0;
return ret;
}
/**
* Write File.
*
* @note Trigger the test from user space by:
* echo 1 > /dev/sdio_al_test
*
*/
ssize_t test_write(struct file *filp, const char __user *buf, size_t size,
loff_t *f_pos)
{
sdio_al_test_initial_dev_and_chan(test_ctx);
if (strict_strtol(buf, 10, &test_ctx->testcase))
return -EINVAL;
switch (test_ctx->testcase) {
case 98:
pr_info(TEST_MODULE_NAME " set runtime debug on");
test_ctx->runtime_debug = 1;
return size;
case 99:
pr_info(TEST_MODULE_NAME " set runtime debug off");
test_ctx->runtime_debug = 0;
return size;
default:
pr_info(TEST_MODULE_NAME ":Bad Test number = %d.\n",
(int)test_ctx->testcase);
return size;
}
return size;
}
/**
* Test Channel Init.
*/
int test_channel_init(char *name)
{
struct test_channel *test_ch;
int ch_id = 0;
int ret;
pr_debug(TEST_MODULE_NAME ":%s.\n", __func__);
pr_info(TEST_MODULE_NAME ": init test cahnnel %s.\n", name);
ch_id = channel_name_to_id(name);
pr_debug(TEST_MODULE_NAME ":id = %d.\n", ch_id);
if (test_ctx->test_ch_arr[ch_id] == NULL) {
test_ch = kzalloc(sizeof(*test_ch), GFP_KERNEL);
if (test_ch == NULL) {
pr_err(TEST_MODULE_NAME ":kzalloc err for allocating "
"test_ch %s.\n",
name);
return -ENOMEM;
}
test_ctx->test_ch_arr[ch_id] = test_ch;
test_ch->ch_id = ch_id;
strncpy(test_ch->name, name,
strnlen(name, TEST_CH_NAME_SIZE)-SDIO_TEST_POSTFIX_SIZE);
test_ch->buf_size = MAX_XFER_SIZE;
test_ch->buf = kzalloc(test_ch->buf_size, GFP_KERNEL);
if (test_ch->buf == NULL) {
kfree(test_ch);
test_ctx->test_ch = NULL;
return -ENOMEM;
}
if (test_ch->ch_id == SDIO_SMEM) {
test_ctx->smem_buf = kzalloc(SMEM_MAX_XFER_SIZE,
GFP_KERNEL);
if (test_ctx->smem_buf == NULL) {
pr_err(TEST_MODULE_NAME ":%s: Unable to "
"allocate smem buf\n",
__func__);
kfree(test_ch);
test_ctx->test_ch = NULL;
return -ENOMEM;
}
#ifdef CONFIG_MSM_SDIO_SMEM
ret = platform_driver_register(&sdio_smem_client_drv);
if (ret) {
pr_err(TEST_MODULE_NAME ":%s: Unable to "
"register sdio smem "
"test client\n",
__func__);
return ret;
}
#endif
} else {
test_ch->workqueue =
create_singlethread_workqueue(test_ch->name);
test_ch->test_work.test_ch = test_ch;
INIT_WORK(&test_ch->test_work.work, worker);
init_waitqueue_head(&test_ch->wait_q);
}
} else {
test_ch = test_ctx->test_ch_arr[ch_id];
pr_info(TEST_MODULE_NAME ":%s: ch %s was detected again\n",
__func__, test_ch->name);
test_ch->card_removed = 0;
if ((test_ch->is_used) &&
(test_ch->test_type == SDIO_TEST_MODEM_RESET)) {
if (test_ch->ch_id == SDIO_SMEM) {
#ifdef CONFIG_MSM_SDIO_SMEM
ret = add_sdio_smem();
if (ret) {
test_ch->ch_ready = false;
return 0;
}
#endif
} else {
ret = open_sdio_ch(test_ch);
if (ret) {
pr_info(TEST_MODULE_NAME
":%s: open channel %s failed\n",
__func__, test_ch->name);
return 0;
}
ret = sdio_test_find_dev(test_ch);
if (ret) {
pr_err(TEST_MODULE_NAME ": %s - "
"sdio_test_find_dev() returned "
"with error", __func__);
return -ENODEV;
}
test_ch->sdio_al_device =
test_ch->ch->sdio_al_dev;
}
atomic_set(&test_ch->card_detected_event, 1);
wake_up(&test_ch->wait_q);
}
}
return 0;
}
static int sdio_test_channel_probe(struct platform_device *pdev)
{
if (!pdev)
return -EIO;
return test_channel_init((char *)pdev->name);
}
static int sdio_test_channel_remove(struct platform_device *pdev)
{
int ch_id;
if (!pdev)
return -EIO;
ch_id = channel_name_to_id((char *)pdev->name);
if (test_ctx->test_ch_arr[ch_id] == NULL)
return 0;
pr_info(TEST_MODULE_NAME "%s: remove ch %s\n",
__func__, test_ctx->test_ch_arr[ch_id]->name);
if ((ch_id == SDIO_SMEM) && (test_ctx->smem_pdev)) {
platform_device_unregister(test_ctx->smem_pdev);
test_ctx->smem_pdev = NULL;
}
test_ctx->test_ch_arr[ch_id]->ch_ready = 0;
test_ctx->test_ch_arr[ch_id]->card_removed = 1;
return 0;
}
static int sdio_test_channel_ciq_probe(struct platform_device *pdev)
{
int ret = 0;
if (!pdev)
return -ENODEV;
test_ctx->ciq_app_pdev = platform_device_alloc("SDIO_CIQ_TEST_APP", -1);
ret = platform_device_add(test_ctx->ciq_app_pdev);
if (ret) {
pr_err(MODULE_NAME ":platform_device_add failed, "
"ret=%d\n", ret);
return ret;
}
return sdio_test_channel_probe(pdev);
}
static int sdio_test_channel_ciq_remove(struct platform_device *pdev)
{
if (!pdev)
return -ENODEV;
platform_device_unregister(test_ctx->ciq_app_pdev);
return sdio_test_channel_remove(pdev);
}
static struct platform_driver sdio_rpc_drv = {
.probe = sdio_test_channel_probe,
.remove = sdio_test_channel_remove,
.driver = {
.name = "SDIO_RPC_TEST",
.owner = THIS_MODULE,
},
};
static struct platform_driver sdio_qmi_drv = {
.probe = sdio_test_channel_probe,
.remove = sdio_test_channel_remove,
.driver = {
.name = "SDIO_QMI_TEST",
.owner = THIS_MODULE,
},
};
static struct platform_driver sdio_diag_drv = {
.probe = sdio_test_channel_probe,
.remove = sdio_test_channel_remove,
.driver = {
.name = "SDIO_DIAG_TEST",
.owner = THIS_MODULE,
},
};
static struct platform_driver sdio_smem_drv = {
.probe = sdio_test_channel_probe,
.remove = sdio_test_channel_remove,
.driver = {
.name = "SDIO_SMEM_TEST",
.owner = THIS_MODULE,
},
};
static struct platform_driver sdio_rmnt_drv = {
.probe = sdio_test_channel_probe,
.remove = sdio_test_channel_remove,
.driver = {
.name = "SDIO_RMNT_TEST",
.owner = THIS_MODULE,
},
};
static struct platform_driver sdio_dun_drv = {
.probe = sdio_test_channel_probe,
.remove = sdio_test_channel_remove,
.driver = {
.name = "SDIO_DUN_TEST",
.owner = THIS_MODULE,
},
};
static struct platform_driver sdio_ciq_drv = {
.probe = sdio_test_channel_ciq_probe,
.remove = sdio_test_channel_ciq_remove,
.driver = {
.name = "SDIO_CIQ_TEST",
.owner = THIS_MODULE,
},
};
static struct class *test_class;
const struct file_operations test_fops = {
.owner = THIS_MODULE,
.write = test_write,
};
/**
* Module Init.
*/
static int __init test_init(void)
{
int ret;
pr_debug(TEST_MODULE_NAME ":test_init.\n");
test_ctx = kzalloc(sizeof(struct test_context), GFP_KERNEL);
if (test_ctx == NULL) {
pr_err(TEST_MODULE_NAME ":kzalloc err.\n");
return -ENOMEM;
}
test_ctx->test_ch = NULL;
test_ctx->signature = TEST_SIGNATURE;
test_ctx->name = "UNKNOWN";
init_waitqueue_head(&test_ctx->wait_q);
#ifdef CONFIG_DEBUG_FS
sdio_al_test_debugfs_init();
#endif
test_class = class_create(THIS_MODULE, TEST_MODULE_NAME);
ret = alloc_chrdev_region(&test_ctx->dev_num, 0, 1, TEST_MODULE_NAME);
if (ret) {
pr_err(TEST_MODULE_NAME "alloc_chrdev_region err.\n");
return -ENODEV;
}
test_ctx->dev = device_create(test_class, NULL, test_ctx->dev_num,
test_ctx, TEST_MODULE_NAME);
if (IS_ERR(test_ctx->dev)) {
pr_err(TEST_MODULE_NAME ":device_create err.\n");
return -ENODEV;
}
test_ctx->cdev = cdev_alloc();
if (test_ctx->cdev == NULL) {
pr_err(TEST_MODULE_NAME ":cdev_alloc err.\n");
return -ENODEV;
}
cdev_init(test_ctx->cdev, &test_fops);
test_ctx->cdev->owner = THIS_MODULE;
ret = cdev_add(test_ctx->cdev, test_ctx->dev_num, 1);
if (ret)
pr_err(TEST_MODULE_NAME ":cdev_add err=%d\n", -ret);
else
pr_debug(TEST_MODULE_NAME ":SDIO-AL-Test init OK..\n");
platform_driver_register(&sdio_rpc_drv);
platform_driver_register(&sdio_qmi_drv);
platform_driver_register(&sdio_diag_drv);
platform_driver_register(&sdio_smem_drv);
platform_driver_register(&sdio_rmnt_drv);
platform_driver_register(&sdio_dun_drv);
platform_driver_register(&sdio_ciq_drv);
return ret;
}
/**
* Module Exit.
*/
static void __exit test_exit(void)
{
int i;
pr_debug(TEST_MODULE_NAME ":test_exit.\n");
test_ctx->exit_flag = true;
msleep(100); /* allow gracefully exit of the worker thread */
cdev_del(test_ctx->cdev);
device_destroy(test_class, test_ctx->dev_num);
unregister_chrdev_region(test_ctx->dev_num, 1);
platform_driver_unregister(&sdio_rpc_drv);
platform_driver_unregister(&sdio_qmi_drv);
platform_driver_unregister(&sdio_diag_drv);
platform_driver_unregister(&sdio_smem_drv);
platform_driver_unregister(&sdio_rmnt_drv);
platform_driver_unregister(&sdio_dun_drv);
platform_driver_unregister(&sdio_ciq_drv);
for (i = 0; i < SDIO_MAX_CHANNELS; i++) {
struct test_channel *tch = test_ctx->test_ch_arr[i];
if (!tch)
continue;
kfree(tch->buf);
kfree(tch);
}
#ifdef CONFIG_DEBUG_FS
sdio_al_test_debugfs_cleanup();
#endif
kfree(test_ctx);
pr_debug(TEST_MODULE_NAME ":test_exit complete.\n");
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("SDIO_AL Test");
MODULE_AUTHOR("Amir Samuelov <amirs@codeaurora.org>");