lineage_kernel_xcoverpro/drivers/misc/samsung/scsc/client_test.c

435 lines
12 KiB
C
Executable File

/****************************************************************************
*
* Copyright (c) 2014 - 2016 Samsung Electronics Co., Ltd. All rights reserved
*
****************************************************************************/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/uaccess.h>
#include <scsc/scsc_logring.h>
#include <scsc/scsc_mx.h>
struct scsc_mx_test {
/* scsc_service_client has to be the first */
struct scsc_service_client test_service_client;
struct scsc_service *primary_service;
struct scsc_service *secondary_service;
struct scsc_mx *mx;
bool started;
};
static struct scsc_mx_test *test;
/* First service to start */
static int service_id = SCSC_SERVICE_ID_NULL;
module_param(service_id, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(service_id, "ID of service to start, Default 0:NULL, 1:WLAN, 2:BT, 3:ANT, 5:ECHO");
/* Second service to start if != -1 */
static int service_id_2 = -1;
module_param(service_id_2, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(service_id_2, "ID of optional second service to start: Default -1:None, 0:NULL, 1:WLAN, 2:BT, 3:ANT, 5:ECHO");
#ifdef CONFIG_SCSC_MX_ALWAYS_ON
static int auto_start = 2;
#else
static int auto_start;
#endif
module_param(auto_start, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(auto_start, "Start service automatically: 0: disabled, 1: Enabled, 2: Deferred");
/* Delay after probe before starting mx140 when auto_start=2 */
#define SCSC_MX_BOOT_DELAY_MS 30000
static DEFINE_MUTEX(ss_lock);
/* char device entry declarations */
static dev_t client_test_dev_t;
static struct class *client_test_class;
static struct cdev *client_test_cdev;
static u8 test_failure_notification(struct scsc_service_client *client, struct mx_syserr_decode *err)
{
(void) client;
SCSC_TAG_DEBUG(MXMAN_TEST, "OK\n");
return err->level;
}
static bool test_stop_on_failure(struct scsc_service_client *client, struct mx_syserr_decode *err)
{
(void) client;
(void) err;
SCSC_TAG_DEBUG(MXMAN_TEST, "OK\n");
return false;
}
static void test_failure_reset(struct scsc_service_client *client, u8 level, u16 scsc_syserr_code)
{
(void)client;
(void)level;
(void)scsc_syserr_code;
SCSC_TAG_ERR(MXMAN_TEST, "OK\n");
}
static void stop_close_services(void)
{
int r;
mutex_lock(&ss_lock);
if (!test->started) {
pr_info("mx140: already stopped\n");
goto done;
}
if (test->primary_service) {
r = scsc_mx_service_stop(test->primary_service);
if (r)
SCSC_TAG_ERR(MXMAN_TEST, "scsc_mx_service_stop(primary_service) failed err: %d\n", r);
else
SCSC_TAG_DEBUG(MXMAN_TEST, "scsc_mx_service_stop(primary_service) OK\n");
r = scsc_mx_service_close(test->primary_service);
if (r)
SCSC_TAG_ERR(MXMAN_TEST, "scsc_mx_service_close(%d) failed err: %d\n", service_id, r);
else
SCSC_TAG_DEBUG(MXMAN_TEST, "scsc_mx_service_close(%d) OK\n", service_id);
test->primary_service = NULL;
}
if (test->secondary_service) {
r = scsc_mx_service_stop(test->secondary_service);
if (r)
SCSC_TAG_ERR(MXMAN_TEST, "scsc_mx_service_stop(secondary_service) failed err: %d\n", r);
else
SCSC_TAG_DEBUG(MXMAN_TEST, "scsc_mx_service_stop(secondary_service) OK\n");
r = scsc_mx_service_close(test->secondary_service);
if (r)
SCSC_TAG_ERR(MXMAN_TEST, "scsc_mx_service_close(%d) failed err: %d\n", service_id_2, r);
else
SCSC_TAG_DEBUG(MXMAN_TEST, "scsc_mx_service_close(%d) OK\n", service_id_2);
test->secondary_service = NULL;
}
test->started = false;
done:
mutex_unlock(&ss_lock);
}
static bool open_start_services(struct scsc_mx *mx)
{
struct scsc_service *primary_service;
struct scsc_service *secondary_service;
int r;
bool ok;
mutex_lock(&ss_lock);
if (test->started) {
pr_info("mx140: already started\n");
ok = true;
goto done;
}
primary_service = scsc_mx_service_open(mx, service_id, &test->test_service_client, &r);
if (!primary_service) {
SCSC_TAG_ERR(MXMAN_TEST, "scsc_mx_service_open for primary_service failed %d\n", r);
ok = false;
goto done;
}
r = scsc_mx_service_start(primary_service, 0);
if (r) {
SCSC_TAG_ERR(MXMAN_TEST, "scsc_mx_service_start for primary_service failed\n");
r = scsc_mx_service_close(primary_service);
if (r)
SCSC_TAG_ERR(MXMAN_TEST, "scsc_mx_service_close for primary_service %d failed\n", r);
ok = false;
goto done;
}
test->primary_service = primary_service;
if (service_id_2 != -1) {
secondary_service = scsc_mx_service_open(mx, service_id_2, &test->test_service_client, &r);
if (!secondary_service) {
SCSC_TAG_ERR(MXMAN_TEST, "scsc_mx_service_open for secondary_service failed %d\n", r);
r = scsc_mx_service_stop(test->primary_service);
if (r)
SCSC_TAG_ERR(MXMAN_TEST, "scsc_mx_service_stop(%d) failed err: %d\n", service_id, r);
else
SCSC_TAG_DEBUG(MXMAN_TEST, "scsc_mx_service_stop(%d) OK\n", service_id);
r = scsc_mx_service_close(test->primary_service);
if (r)
SCSC_TAG_ERR(MXMAN_TEST, "scsc_mx_service_close(%d) failed err: %d\n", service_id, r);
else
SCSC_TAG_DEBUG(MXMAN_TEST, "scsc_mx_service_close(%d) OK\n", service_id);
ok = false;
goto done;
}
r = scsc_mx_service_start(secondary_service, 0);
if (r) {
SCSC_TAG_ERR(MXMAN_TEST, "scsc_mx_service_start for secondary_service failed\n");
r = scsc_mx_service_close(secondary_service);
if (r)
SCSC_TAG_ERR(MXMAN_TEST, "scsc_mx_service_close(%d) failed err: %d\n", service_id, r);
else
SCSC_TAG_DEBUG(MXMAN_TEST, "scsc_mx_service_close(%d) OK\n", service_id);
r = scsc_mx_service_stop(test->primary_service);
if (r)
SCSC_TAG_ERR(MXMAN_TEST, "scsc_mx_service_stop(%d) failed err: %d\n", service_id, r);
else
SCSC_TAG_DEBUG(MXMAN_TEST, "scsc_mx_service_stop(%d) OK\n", service_id);
r = scsc_mx_service_close(test->primary_service);
if (r)
SCSC_TAG_ERR(MXMAN_TEST, "scsc_mx_service_close(%d) failed err: %d\n", service_id, r);
else
SCSC_TAG_DEBUG(MXMAN_TEST, "scsc_mx_service_close(%d) OK\n", service_id);
ok = false;
goto done;
}
test->secondary_service = secondary_service;
}
test->started = true;
ok = true;
done:
mutex_unlock(&ss_lock);
return ok;
}
static void delay_start_func(struct work_struct *work)
{
(void)work;
pr_info("mx140: Start wlbt null service\n");
if (!test->mx)
return;
if (!open_start_services(test->mx))
pr_err("mx140: Error starting delayed service\n");
}
static DECLARE_DELAYED_WORK(delay_start, delay_start_func);
/* Start the null service after a delay */
static void delay_open_start_services(void)
{
schedule_delayed_work(&delay_start, msecs_to_jiffies(SCSC_MX_BOOT_DELAY_MS));
}
/* Start service(s) and leave running until module unload */
void client_module_probe(struct scsc_mx_module_client *module_client, struct scsc_mx *mx, enum scsc_module_client_reason reason)
{
/* Avoid unused error */
(void)module_client;
SCSC_TAG_ERR(MXMAN_TEST, "mx140:\n");
test = kzalloc(sizeof(*test), GFP_KERNEL);
if (!test)
return;
test->test_service_client.failure_notification = test_failure_notification;
test->test_service_client.stop_on_failure_v2 = test_stop_on_failure;
test->test_service_client.failure_reset_v2 = test_failure_reset;
test->mx = mx;
switch (auto_start) {
case 1:
if (!open_start_services(test->mx)) {
SCSC_TAG_ERR(MXMAN_TEST, "Error starting service/s\n");
kfree(test);
return;
}
break;
case 2:
pr_info("mx140: delayed auto-start\n");
delay_open_start_services();
break;
default:
break;
}
SCSC_TAG_ERR(MXMAN_TEST, "OK\n");
}
void client_module_remove(struct scsc_mx_module_client *module_client, struct scsc_mx *mx, enum scsc_module_client_reason reason)
{
/* Avoid unused error */
(void)module_client;
pr_info("mx140: %s\n", __func__);
if (!test)
return;
if (test->mx != mx) {
SCSC_TAG_ERR(MXMAN_TEST, "test->mx != mx\n");
return;
}
/* Cancel any delayed start attempt */
cancel_delayed_work_sync(&delay_start);
stop_close_services();
/* de-allocate test structure */
kfree(test);
SCSC_TAG_DEBUG(MXMAN_TEST, "OK\n");
}
/* Test client driver registration */
static struct scsc_mx_module_client client_test_driver = {
.name = "MX client test driver",
.probe = client_module_probe,
.remove = client_module_remove,
};
static int client_test_dev_open(struct inode *inode, struct file *file)
{
SCSC_TAG_ERR(MXMAN_TEST, "open client test\n");
return 0;
}
static ssize_t client_test_dev_write(struct file *file, const char *data, size_t len, loff_t *offset)
{
unsigned long count;
char str[2]; /* One value and carry return */
long int val = 0;
bool ok = true;
if (len > 2) {
SCSC_TAG_ERR(MXMAN_TEST, "Incorrect value len %zd\n", len);
goto error;
}
count = copy_from_user(str, data, len);
str[1] = 0;
if (kstrtol(str, 10, &val)) {
SCSC_TAG_ERR(MXMAN_TEST, "Invalid value\n");
goto error;
}
if (test) {
if (val) {
SCSC_TAG_INFO(MXMAN_TEST, "Start services\n");
ok = open_start_services(test->mx);
} else {
SCSC_TAG_INFO(MXMAN_TEST, "Stop services\n");
stop_close_services();
}
} else {
SCSC_TAG_ERR(MXMAN_TEST, "Test not created\n");
goto error;
}
error:
SCSC_TAG_ERR(MXMAN_TEST, "%s\n", ok ? "OK" : "FAIL");
return ok ? len : -EIO;
}
static ssize_t client_test_dev_read(struct file *filp, char *buffer, size_t length, loff_t *offset)
{
return length;
}
static int client_test_dev_release(struct inode *inode, struct file *file)
{
SCSC_TAG_DEBUG(MXMAN_TEST, "close client test\n");
return 0;
}
static const struct file_operations client_test_dev_fops = {
.owner = THIS_MODULE,
.open = client_test_dev_open,
.read = client_test_dev_read,
.write = client_test_dev_write,
.release = client_test_dev_release,
};
static int __init scsc_client_test_module_init(void)
{
int r;
SCSC_TAG_DEBUG(MXMAN_TEST, "mx140:\n");
r = scsc_mx_module_register_client_module(&client_test_driver);
if (r) {
SCSC_TAG_ERR(MXMAN_TEST, "scsc_mx_module_register_client_module failed: r=%d\n", r);
return r;
}
r = alloc_chrdev_region(&client_test_dev_t, 0, 1, "wlbt-null-service");
if (r < 0) {
SCSC_TAG_ERR(MXMAN_TEST, "failed to alloc chrdev region\n");
goto fail_alloc_chrdev_region;
}
client_test_cdev = cdev_alloc();
if (!client_test_cdev) {
r = -ENOMEM;
SCSC_TAG_ERR(MXMAN_TEST, "failed to alloc cdev\n");
goto fail_alloc_cdev;
}
cdev_init(client_test_cdev, &client_test_dev_fops);
r = cdev_add(client_test_cdev, client_test_dev_t, 1);
if (r < 0) {
SCSC_TAG_ERR(MXMAN_TEST, "failed to add cdev\n");
goto fail_add_cdev;
}
client_test_class = class_create(THIS_MODULE, "sample");
if (!client_test_class) {
r = -EEXIST;
SCSC_TAG_ERR(MXMAN_TEST, "failed to create class\n");
goto fail_create_class;
}
if (!device_create(client_test_class, NULL, client_test_dev_t, NULL, "mx_client_test_%d", MINOR(client_test_dev_t))) {
r = -EINVAL;
SCSC_TAG_ERR(MXMAN_TEST, "failed to create device\n");
goto fail_create_device;
}
return 0;
fail_create_device:
class_destroy(client_test_class);
fail_create_class:
cdev_del(client_test_cdev);
fail_add_cdev:
fail_alloc_cdev:
unregister_chrdev_region(client_test_dev_t, 1);
fail_alloc_chrdev_region:
return r;
}
static void __exit scsc_client_test_module_exit(void)
{
SCSC_TAG_DEBUG(MXMAN_TEST, "mx140:\n");
scsc_mx_module_unregister_client_module(&client_test_driver);
SCSC_TAG_DEBUG(MXMAN_TEST, "exit\n");
device_destroy(client_test_class, client_test_dev_t);
class_destroy(client_test_class);
cdev_del(client_test_cdev);
unregister_chrdev_region(client_test_dev_t, 1);
}
late_initcall(scsc_client_test_module_init);
module_exit(scsc_client_test_module_exit);
MODULE_DESCRIPTION("mx140 Client Test Driver");
MODULE_AUTHOR("SCSC");
MODULE_LICENSE("GPL");