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

814 lines
21 KiB
C
Executable File

/****************************************************************************
*
* Copyright (c) 2014 - 2019 Samsung Electronics Co., Ltd. All rights reserved
*
****************************************************************************/
#include <linux/mutex.h>
#include <linux/version.h>
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0))
#include <scsc/scsc_wakelock.h>
#else
#include <linux/wakelock.h>
#endif
#include <linux/string.h>
#include "scsc_wlbtd.h"
#define MAX_TIMEOUT 30000 /* in milisecounds */
#define WRITE_FILE_TIMEOUT 1000 /* in milisecounds */
#define MAX_RSP_STRING_SIZE 128
#define PROP_VALUE_MAX 92
/* completion to indicate when EVENT_* is done */
static DECLARE_COMPLETION(event_done);
static DECLARE_COMPLETION(fw_sable_done);
static DECLARE_COMPLETION(fw_panic_done);
static DECLARE_COMPLETION(write_file_done);
static DEFINE_MUTEX(write_file_lock);
static DEFINE_MUTEX(build_type_lock);
static char *build_type;
static DEFINE_MUTEX(sable_lock);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0))
static struct scsc_wake_lock wlbtd_wakelock;
#else
static struct wake_lock wlbtd_wakelock;
#endif
const char *response_code_to_str(enum scsc_wlbtd_response_codes response_code)
{
switch (response_code) {
case SCSC_WLBTD_ERR_PARSE_FAILED:
return "SCSC_WLBTD_ERR_PARSE_FAILED";
case SCSC_WLBTD_FW_PANIC_TAR_GENERATED:
return "SCSC_WLBTD_FW_PANIC_TAR_GENERATED";
case SCSC_WLBTD_FW_PANIC_ERR_SCRIPT_FILE_NOT_FOUND:
return "SCSC_WLBTD_FW_PANIC_ERR_SCRIPT_FILE_NOT_FOUND";
case SCSC_WLBTD_FW_PANIC_ERR_NO_DEV:
return "SCSC_WLBTD_FW_PANIC_ERR_NO_DEV";
case SCSC_WLBTD_FW_PANIC_ERR_MMAP:
return "SCSC_WLBTD_FW_PANIC_ERR_MMAP";
case SCSC_WLBTD_FW_PANIC_ERR_SABLE_FILE:
return "SCSC_WLBTD_FW_PANIC_ERR_SABLE_FILE";
case SCSC_WLBTD_FW_PANIC_ERR_TAR:
return "SCSC_WLBTD_FW_PANIC_ERR_TAR";
case SCSC_WLBTD_OTHER_SBL_GENERATED:
return "SCSC_WLBTD_OTHER_SBL_GENERATED";
case SCSC_WLBTD_OTHER_TAR_GENERATED:
return "SCSC_WLBTD_OTHER_TAR_GENERATED";
case SCSC_WLBTD_OTHER_ERR_SCRIPT_FILE_NOT_FOUND:
return "SCSC_WLBTD_OTHER_ERR_SCRIPT_FILE_NOT_FOUND";
case SCSC_WLBTD_OTHER_ERR_NO_DEV:
return "SCSC_WLBTD_OTHER_ERR_NO_DEV";
case SCSC_WLBTD_OTHER_ERR_MMAP:
return "SCSC_WLBTD_OTHER_ERR_MMAP";
case SCSC_WLBTD_OTHER_ERR_SABLE_FILE:
return "SCSC_WLBTD_OTHER_ERR_SABLE_FILE";
case SCSC_WLBTD_OTHER_ERR_TAR:
return "SCSC_WLBTD_OTHER_ERR_TAR";
case SCSC_WLBTD_OTHER_IGNORE_TRIGGER:
return "SCSC_WLBTD_OTHER_IGNORE_TRIGGER";
default:
SCSC_TAG_ERR(WLBTD, "UNKNOWN response_code %d", response_code);
return "UNKNOWN response_code";
}
}
/**
* This callback runs whenever the socket receives messages.
*/
static int msg_from_wlbtd_cb(struct sk_buff *skb, struct genl_info *info)
{
unsigned int status = 0;
int ret_code = 0;
if (!info || !info->attrs[1] || !info->attrs[2] ||
(nla_len(info->attrs[1]) > MAX_RSP_STRING_SIZE) ||
(nla_len(info->attrs[2]) < sizeof(status))) {
SCSC_TAG_ERR(WLBTD, "Error parsing arguments\n");
ret_code = -EINVAL;
goto error_complete;
}
SCSC_TAG_INFO(WLBTD, "ATTR_STR: %s\n", (char *)nla_data(info->attrs[1]));
status = nla_get_u32(info->attrs[2]);
if (status)
SCSC_TAG_INFO(WLBTD, "ATTR_INT: %u\n", status);
error_complete:
if (!completion_done(&event_done))
complete(&event_done);
return ret_code;
}
static int msg_from_wlbtd_sable_cb(struct sk_buff *skb, struct genl_info *info)
{
unsigned short status;
if (!info || !info->attrs[1] || !info->attrs[2] ||
(nla_len(info->attrs[1]) > MAX_RSP_STRING_SIZE) ||
(nla_len(info->attrs[2]) < sizeof(status))) {
SCSC_TAG_ERR(WLBTD, "Error parsing arguments\n");
goto error_complete;
}
SCSC_TAG_INFO(WLBTD, "%s\n", nla_data(info->attrs[1]));
status = nla_get_u16(info->attrs[2]);
if ((enum scsc_wlbtd_response_codes)status < SCSC_WLBTD_LAST_RESPONSE_CODE)
SCSC_TAG_ERR(WLBTD, "%s\n", response_code_to_str((enum scsc_wlbtd_response_codes)status));
else {
SCSC_TAG_INFO(WLBTD, "Received invalid status value");
goto error_complete;
}
/* completion cases :
* 1) FW_PANIC_TAR_GENERATED
* for trigger scsc_log_fw_panic only one response from wlbtd when
* tar done
* ---> complete fw_panic_done
* 2) for all other triggers, we get 2 responses
* a) OTHER_SBL_GENERATED
* Once .sbl is written
* ---> complete event_done
* ---> complete fw_sable_done for extra waiter
* b) OTHER_TAR_GENERATED
* 2nd time when sable tar is done
* IGNORE this response and Don't complete
* 3) OTHER_IGNORE_TRIGGER
* When we get rapid requests for SABLE generation,
* to serialise while processing current request,
* we ignore requests other than "fw_panic" in wlbtd and
* send a msg "ignoring" back to kernel.
* ---> complete event_done
* ---> complete fw_sable_done for extra waiter
* 4) FW_PANIC_ERR_* and OTHER_ERR_*
* when something failed, file not found, mmap failed, etc.
* ---> complete the completion with waiter(s) based on if it was
* a fw_panic trigger or other trigger
* 5) ERR_PARSE_FAILED
* When msg parsing fails, wlbtd doesn't know the trigger type
* ---> complete the completion with waiter(s)
*/
switch (status) {
case SCSC_WLBTD_ERR_PARSE_FAILED:
if (!completion_done(&fw_panic_done)) {
SCSC_TAG_INFO(WLBTD, "completing fw_panic_done\n");
complete(&fw_panic_done);
}
if (!completion_done(&fw_sable_done)) {
SCSC_TAG_INFO(WLBTD, "completing fw_sable_done\n");
complete(&fw_sable_done);
}
if (!completion_done(&event_done)) {
SCSC_TAG_INFO(WLBTD, "completing event_done\n");
complete(&event_done);
}
break;
case SCSC_WLBTD_FW_PANIC_TAR_GENERATED:
case SCSC_WLBTD_FW_PANIC_ERR_TAR:
case SCSC_WLBTD_FW_PANIC_ERR_SCRIPT_FILE_NOT_FOUND:
case SCSC_WLBTD_FW_PANIC_ERR_NO_DEV:
case SCSC_WLBTD_FW_PANIC_ERR_MMAP:
case SCSC_WLBTD_FW_PANIC_ERR_SABLE_FILE:
if (!completion_done(&fw_panic_done)) {
SCSC_TAG_INFO(WLBTD, "completing fw_panic_done\n");
complete(&fw_panic_done);
}
break;
case SCSC_WLBTD_OTHER_TAR_GENERATED:
/* ignore */
break;
case SCSC_WLBTD_OTHER_SBL_GENERATED:
case SCSC_WLBTD_OTHER_ERR_TAR:
case SCSC_WLBTD_OTHER_ERR_SCRIPT_FILE_NOT_FOUND:
case SCSC_WLBTD_OTHER_ERR_NO_DEV:
case SCSC_WLBTD_OTHER_ERR_MMAP:
case SCSC_WLBTD_OTHER_ERR_SABLE_FILE:
case SCSC_WLBTD_OTHER_IGNORE_TRIGGER:
if (!completion_done(&fw_sable_done)) {
SCSC_TAG_INFO(WLBTD, "completing fw_sable_done\n");
complete(&fw_sable_done);
}
if (!completion_done(&event_done)) {
SCSC_TAG_INFO(WLBTD, "completing event_done\n");
complete(&event_done);
}
break;
default:
SCSC_TAG_ERR(WLBTD, "UNKNOWN reponse from WLBTD\n");
}
return 0;
error_complete:
if (!completion_done(&fw_panic_done)) {
SCSC_TAG_INFO(WLBTD, "completing fw_panic_done\n");
complete(&fw_panic_done);
}
if (!completion_done(&event_done)) {
SCSC_TAG_INFO(WLBTD, "completing event_done\n");
complete(&event_done);
}
return -EINVAL;
}
static int msg_from_wlbtd_build_type_cb(struct sk_buff *skb, struct genl_info *info)
{
char *build_type_str = NULL;
mutex_lock(&build_type_lock);
if (build_type) {
SCSC_TAG_INFO(WLBTD, "ro.build.type = %s\n", build_type);
mutex_unlock(&build_type_lock);
return 0;
}
if (!info) {
SCSC_TAG_ERR(WLBTD, "info is NULL\n");
mutex_unlock(&build_type_lock);
return -EINVAL;
}
if (!info->attrs[1]) {
SCSC_TAG_ERR(WLBTD, "info->attrs[1] = NULL\n");
mutex_unlock(&build_type_lock);
return -EINVAL;
}
if (!nla_len(info->attrs[1])) {
SCSC_TAG_ERR(WLBTD, "nla_len = 0\n");
mutex_unlock(&build_type_lock);
return -EINVAL;
}
if (nla_len(info->attrs[1]) > PROP_VALUE_MAX) {
SCSC_TAG_ERR(WLBTD, "Received invalid length of data\n");
mutex_unlock(&build_type_lock);
return -EINVAL;
}
/* nla_len includes trailing zero. Tested.*/
build_type = kmalloc(PROP_VALUE_MAX + 1, GFP_KERNEL);
if (!build_type) {
SCSC_TAG_ERR(WLBTD, "kmalloc failed: build_type = NULL\n");
mutex_unlock(&build_type_lock);
return -ENOMEM;
}
build_type_str = nla_data(info->attrs[1]);
SCSC_TAG_INFO(WLBTD, "build_type_str = %s\n", build_type_str);
if (!build_type_str) {
SCSC_TAG_ERR(WLBTD, "Failed to retrieve build type attribute\n");
mutex_unlock(&build_type_lock);
return -EINVAL;
}
strncpy(build_type, (const char *)build_type_str, PROP_VALUE_MAX);
SCSC_TAG_INFO(WLBTD, "ro.build.type = %s\n", build_type);
mutex_unlock(&build_type_lock);
return 0;
}
static int msg_from_wlbtd_write_file_cb(struct sk_buff *skb, struct genl_info *info)
{
int ret_code = 0;
if (!info || !info->attrs[3] ||
(nla_len(info->attrs[3]) > MAX_RSP_STRING_SIZE)){
ret_code = -EINVAL;
goto error_complete;
}
SCSC_TAG_INFO(WLBTD, "%s\n", (char *)nla_data(info->attrs[3]));
error_complete:
complete(&write_file_done);
return ret_code;
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0))
/**
* Here you can define some constraints for the attributes so Linux will
* validate them for you.
*/
static struct nla_policy policies[] = {
[ATTR_STR] = { .type = NLA_STRING, },
[ATTR_INT] = { .type = NLA_U32, },
};
static struct nla_policy policy_sable[] = {
[ATTR_INT] = { .type = NLA_U16, },
[ATTR_INT8] = { .type = NLA_U8, },
};
static struct nla_policy policies_build_type[] = {
[ATTR_STR] = { .type = NLA_STRING, },
};
static struct nla_policy policy_write_file[] = {
[ATTR_PATH] = { .type = NLA_STRING, },
[ATTR_CONTENT] = { .type = NLA_STRING, },
};
#endif
/**
* Actual message type definition.
*/
const struct genl_ops scsc_ops[] = {
{
.cmd = EVENT_SCSC,
.flags = 0,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0))
.policy = policies,
#endif
.doit = msg_from_wlbtd_cb,
.dumpit = NULL,
},
{
.cmd = EVENT_SYSTEM_PROPERTY,
.flags = 0,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0))
.policy = policies_build_type,
#endif
.doit = msg_from_wlbtd_build_type_cb,
.dumpit = NULL,
},
{
.cmd = EVENT_SABLE,
.flags = 0,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0))
.policy = policy_sable,
#endif
.doit = msg_from_wlbtd_sable_cb,
.dumpit = NULL,
},
{
.cmd = EVENT_WRITE_FILE,
.flags = 0,
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0))
.policy = policy_write_file,
#endif
.doit = msg_from_wlbtd_write_file_cb,
.dumpit = NULL,
},
};
/* The netlink family */
static struct genl_family scsc_nlfamily = {
.id = 0, /* Don't bother with a hardcoded ID */
.name = "scsc_mdp_family", /* Have users key off the name instead */
.hdrsize = 0, /* No private header */
.version = 1,
.maxattr = __ATTR_MAX,
.module = THIS_MODULE,
.ops = scsc_ops,
.n_ops = ARRAY_SIZE(scsc_ops),
.mcgrps = scsc_mcgrp,
.n_mcgrps = ARRAY_SIZE(scsc_mcgrp),
};
int scsc_wlbtd_get_and_print_build_type(void)
{
struct sk_buff *skb;
void *msg;
int rc = 0;
SCSC_TAG_DEBUG(WLBTD, "start\n");
wake_lock(&wlbtd_wakelock);
/* check if the value wasn't cached yet */
mutex_lock(&build_type_lock);
if (build_type) {
SCSC_TAG_WARNING(WLBTD, "ro.build.type = %s\n", build_type);
SCSC_TAG_DEBUG(WLBTD, "sync end\n");
mutex_unlock(&build_type_lock);
goto done;
}
mutex_unlock(&build_type_lock);
skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
if (!skb) {
SCSC_TAG_ERR(WLBTD, "Failed to construct message\n");
goto error;
}
SCSC_TAG_INFO(WLBTD, "create message\n");
msg = genlmsg_put(skb,
0, // PID is whatever
0, // Sequence number (don't care)
&scsc_nlfamily, // Pointer to family struct
0, // Flags
EVENT_SYSTEM_PROPERTY // Generic netlink command
);
if (!msg) {
SCSC_TAG_ERR(WLBTD, "Failed to create message\n");
goto error;
}
rc = nla_put_string(skb, ATTR_STR, "ro.build.type");
if (rc) {
SCSC_TAG_ERR(WLBTD, "nla_put_string failed. rc = %d\n", rc);
genlmsg_cancel(skb, msg);
goto error;
}
genlmsg_end(skb, msg);
SCSC_TAG_INFO(WLBTD, "finalize & send msg\n");
rc = genlmsg_multicast_allns(&scsc_nlfamily, skb, 0, 0, GFP_KERNEL);
if (rc) {
SCSC_TAG_ERR(WLBTD, "failed to send message. rc = %d\n", rc);
goto error;
}
SCSC_TAG_DEBUG(WLBTD, "async end\n");
done:
wake_unlock(&wlbtd_wakelock);
return rc;
error:
if (rc == -ESRCH) {
/* If no one registered to scsc_mdp_mcgrp (e.g. in case wlbtd
* is not running) genlmsg_multicast_allns returns -ESRCH.
* Ignore and return.
*/
SCSC_TAG_WARNING(WLBTD, "WLBTD not running ?\n");
wake_unlock(&wlbtd_wakelock);
return rc;
}
/* free skb */
nlmsg_free(skb);
wake_unlock(&wlbtd_wakelock);
return -1;
}
int wlbtd_write_file(const char *file_path, const char *file_content)
{
struct sk_buff *skb;
void *msg;
int rc = 0;
unsigned long completion_jiffies = 0;
unsigned long max_timeout_jiffies = msecs_to_jiffies(WRITE_FILE_TIMEOUT);
SCSC_TAG_DEBUG(WLBTD, "start\n");
mutex_lock(&write_file_lock);
wake_lock(&wlbtd_wakelock);
skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
if (!skb) {
SCSC_TAG_ERR(WLBTD, "Failed to construct message\n");
goto error;
}
SCSC_TAG_INFO(WLBTD, "create message to write %s\n", file_path);
msg = genlmsg_put(skb,
0, // PID is whatever
0, // Sequence number (don't care)
&scsc_nlfamily, // Pointer to family struct
0, // Flags
EVENT_WRITE_FILE// Generic netlink command
);
if (!msg) {
SCSC_TAG_ERR(WLBTD, "Failed to create message\n");
goto error;
}
SCSC_TAG_DEBUG(WLBTD, "add values to msg\n");
rc = nla_put_string(skb, ATTR_PATH, file_path);
if (rc) {
SCSC_TAG_ERR(WLBTD, "nla_put_u32 failed. rc = %d\n", rc);
genlmsg_cancel(skb, msg);
goto error;
}
rc = nla_put_string(skb, ATTR_CONTENT, file_content);
if (rc) {
SCSC_TAG_ERR(WLBTD, "nla_put_string failed. rc = %d\n", rc);
genlmsg_cancel(skb, msg);
goto error;
}
genlmsg_end(skb, msg);
SCSC_TAG_INFO(WLBTD, "finalize & send msg\n");
/* genlmsg_multicast_allns() frees skb */
rc = genlmsg_multicast_allns(&scsc_nlfamily, skb, 0, 0, GFP_KERNEL);
if (rc) {
if (rc == -ESRCH) {
/* If no one registered to scsc_mcgrp (e.g. in case
* wlbtd is not running) genlmsg_multicast_allns
* returns -ESRCH. Ignore and return.
*/
SCSC_TAG_WARNING(WLBTD, "WLBTD not running ?\n");
goto done;
}
SCSC_TAG_ERR(WLBTD, "Failed to send message. rc = %d\n", rc);
goto done;
}
SCSC_TAG_INFO(WLBTD, "waiting for completion\n");
/* wait for script to finish */
completion_jiffies = wait_for_completion_timeout(&write_file_done,
max_timeout_jiffies);
if (completion_jiffies == 0)
SCSC_TAG_ERR(WLBTD, "wait for completion timed out !\n");
else {
completion_jiffies = jiffies_to_msecs(max_timeout_jiffies - completion_jiffies);
SCSC_TAG_INFO(WLBTD, "written %s in %dms\n", file_path,
completion_jiffies ? completion_jiffies : 1);
}
/* reinit so completion can be re-used */
reinit_completion(&write_file_done);
SCSC_TAG_DEBUG(WLBTD, "end\n");
done:
wake_unlock(&wlbtd_wakelock);
mutex_unlock(&write_file_lock);
return rc;
error:
/* free skb */
nlmsg_free(skb);
wake_unlock(&wlbtd_wakelock);
mutex_unlock(&write_file_lock);
return -1;
}
EXPORT_SYMBOL(wlbtd_write_file);
int call_wlbtd_sable(u8 trigger_code, u16 reason_code)
{
struct sk_buff *skb;
void *msg;
int rc = 0;
unsigned long completion_jiffies = 0;
unsigned long max_timeout_jiffies = msecs_to_jiffies(MAX_TIMEOUT);
mutex_lock(&sable_lock);
wake_lock(&wlbtd_wakelock);
SCSC_TAG_INFO(WLBTD, "start:trigger - %s\n",
scsc_get_trigger_str((int)trigger_code));
skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
if (!skb) {
SCSC_TAG_ERR(WLBTD, "Failed to construct message\n");
goto error;
}
SCSC_TAG_DEBUG(WLBTD, "create message\n");
msg = genlmsg_put(skb,
0, // PID is whatever
0, // Sequence number (don't care)
&scsc_nlfamily, // Pointer to family struct
0, // Flags
EVENT_SABLE // Generic netlink command
);
if (!msg) {
SCSC_TAG_ERR(WLBTD, "Failed to create message\n");
goto error;
}
SCSC_TAG_DEBUG(WLBTD, "add values to msg\n");
rc = nla_put_u16(skb, ATTR_INT, reason_code);
if (rc) {
SCSC_TAG_ERR(WLBTD, "nla_put_u16 failed. rc = %d\n", rc);
genlmsg_cancel(skb, msg);
goto error;
}
rc = nla_put_u8(skb, ATTR_INT8, trigger_code);
if (rc) {
SCSC_TAG_ERR(WLBTD, "nla_put_u8 failed. rc = %d\n", rc);
genlmsg_cancel(skb, msg);
goto error;
}
genlmsg_end(skb, msg);
SCSC_TAG_DEBUG(WLBTD, "finalize & send msg\n");
/* genlmsg_multicast_allns() frees skb */
rc = genlmsg_multicast_allns(&scsc_nlfamily, skb, 0, 0, GFP_KERNEL);
if (rc) {
if (rc == -ESRCH) {
/* If no one registered to scsc_mcgrp (e.g. in case
* wlbtd is not running) genlmsg_multicast_allns
* returns -ESRCH. Ignore and return.
*/
SCSC_TAG_WARNING(WLBTD, "WLBTD not running ?\n");
goto done;
}
SCSC_TAG_ERR(WLBTD, "Failed to send message. rc = %d\n", rc);
goto done;
}
SCSC_TAG_INFO(WLBTD, "waiting for completion\n");
/* wait for script to finish */
if (trigger_code == SCSC_LOG_FW_PANIC)
completion_jiffies = wait_for_completion_timeout(&fw_panic_done,
max_timeout_jiffies);
else
completion_jiffies = wait_for_completion_timeout(&event_done,
max_timeout_jiffies);
if (completion_jiffies) {
completion_jiffies = max_timeout_jiffies - completion_jiffies;
SCSC_TAG_INFO(WLBTD, "sable generated in %dms\n",
(int)jiffies_to_msecs(completion_jiffies) ? : 1);
} else
SCSC_TAG_ERR(WLBTD, "wait for completion timed out for %s\n",
scsc_get_trigger_str((int)trigger_code));
/* reinit so completion can be re-used */
if (trigger_code == SCSC_LOG_FW_PANIC)
reinit_completion(&fw_panic_done);
else
reinit_completion(&event_done);
SCSC_TAG_INFO(WLBTD, " end:trigger - %s\n",
scsc_get_trigger_str((int)trigger_code));
done:
wake_unlock(&wlbtd_wakelock);
mutex_unlock(&sable_lock);
return rc;
error:
/* free skb */
nlmsg_free(skb);
wake_unlock(&wlbtd_wakelock);
mutex_unlock(&sable_lock);
return -1;
}
EXPORT_SYMBOL(call_wlbtd_sable);
void scsc_wlbtd_wait_for_sable_logging(void)
{
unsigned long completion_jiffies = 0;
unsigned long max_timeout_jiffies = msecs_to_jiffies(MAX_TIMEOUT);
/* Just waits for the log collection not tarring */
completion_jiffies = wait_for_completion_timeout(&fw_sable_done,
max_timeout_jiffies);
if (!completion_jiffies)
SCSC_TAG_ERR(WLBTD, "wait for sable logging timed out !\n");
/* reinit so completion can be re-used */
reinit_completion(&fw_sable_done);
}
EXPORT_SYMBOL(scsc_wlbtd_wait_for_sable_logging);
int call_wlbtd(const char *script_path)
{
struct sk_buff *skb;
void *msg;
int rc = 0;
unsigned long completion_jiffies = 0;
unsigned long max_timeout_jiffies = msecs_to_jiffies(MAX_TIMEOUT);
SCSC_TAG_DEBUG(WLBTD, "start\n");
wake_lock(&wlbtd_wakelock);
skb = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
if (!skb) {
SCSC_TAG_ERR(WLBTD, "Failed to construct message\n");
goto error;
}
SCSC_TAG_INFO(WLBTD, "create message to run %s\n", script_path);
msg = genlmsg_put(skb,
0, // PID is whatever
0, // Sequence number (don't care)
&scsc_nlfamily, // Pointer to family struct
0, // Flags
EVENT_SCSC // Generic netlink command
);
if (!msg) {
SCSC_TAG_ERR(WLBTD, "Failed to create message\n");
goto error;
}
SCSC_TAG_DEBUG(WLBTD, "add values to msg\n");
rc = nla_put_u32(skb, ATTR_INT, 9);
if (rc) {
SCSC_TAG_ERR(WLBTD, "nla_put_u32 failed. rc = %d\n", rc);
genlmsg_cancel(skb, msg);
goto error;
}
rc = nla_put_string(skb, ATTR_STR, script_path);
if (rc) {
SCSC_TAG_ERR(WLBTD, "nla_put_string failed. rc = %d\n", rc);
genlmsg_cancel(skb, msg);
goto error;
}
genlmsg_end(skb, msg);
SCSC_TAG_INFO(WLBTD, "finalize & send msg\n");
/* genlmsg_multicast_allns() frees skb */
rc = genlmsg_multicast_allns(&scsc_nlfamily, skb, 0, 0, GFP_KERNEL);
if (rc) {
if (rc == -ESRCH) {
/* If no one registered to scsc_mcgrp (e.g. in case
* wlbtd is not running) genlmsg_multicast_allns
* returns -ESRCH. Ignore and return.
*/
SCSC_TAG_WARNING(WLBTD, "WLBTD not running ?\n");
goto done;
}
SCSC_TAG_ERR(WLBTD, "Failed to send message. rc = %d\n", rc);
goto done;
}
SCSC_TAG_INFO(WLBTD, "waiting for completion\n");
/* wait for script to finish */
completion_jiffies = wait_for_completion_timeout(&event_done,
max_timeout_jiffies);
if (completion_jiffies) {
completion_jiffies = max_timeout_jiffies - completion_jiffies;
SCSC_TAG_INFO(WLBTD, "done in %dms\n",
(int)jiffies_to_msecs(completion_jiffies) ? : 1);
} else
SCSC_TAG_ERR(WLBTD, "wait for completion timed out !\n");
/* reinit so completion can be re-used */
reinit_completion(&event_done);
SCSC_TAG_DEBUG(WLBTD, "end\n");
done:
wake_unlock(&wlbtd_wakelock);
return rc;
error:
/* free skb */
nlmsg_free(skb);
wake_unlock(&wlbtd_wakelock);
return -1;
}
EXPORT_SYMBOL(call_wlbtd);
int scsc_wlbtd_init(void)
{
int r = 0;
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 4, 0))
wake_lock_init(NULL, &(wlbtd_wakelock.ws), "wlbtd_wl");
#else
wake_lock_init(&wlbtd_wakelock, WAKE_LOCK_SUSPEND, "wlbtd_wl");
#endif
init_completion(&event_done);
init_completion(&fw_sable_done);
init_completion(&fw_panic_done);
init_completion(&write_file_done);
/* register the family so that wlbtd can bind */
r = genl_register_family(&scsc_nlfamily);
if (r) {
SCSC_TAG_ERR(WLBTD, "Failed to register family. (%d)\n", r);
return -1;
}
return r;
}
int scsc_wlbtd_deinit(void)
{
int ret = 0;
/* unregister family */
ret = genl_unregister_family(&scsc_nlfamily);
if (ret) {
SCSC_TAG_ERR(WLBTD, "genl_unregister_family failed (%d)\n",
ret);
return -1;
}
kfree(build_type);
build_type = NULL;
wake_lock_destroy(&wlbtd_wakelock);
return ret;
}