android_kernel_xiaomi_sm8350/drivers/soc/qcom/service-locator.c
Raghavendra Rao Ananta ad116db6e6 soc: qcom: service-locator: Add soft-dependency on QRTR
QRTR needs to be functional before service locator is up. Hence,
add a soft-dependency on the QRTR to ensure this happens when
these drivers are built as modules.

Change-Id: I1919ffc03f8eb215c21724a46c1250787ba2067c
Signed-off-by: Raghavendra Rao Ananta <rananta@codeaurora.org>
2020-04-30 13:24:10 -07:00

394 lines
9.9 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "servloc: %s: " fmt, __func__
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/string.h>
#include <linux/completion.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/soc/qcom/qmi.h>
#include <soc/qcom/service-locator.h>
#include "service-locator-private.h"
#define SERVREG_LOC_SERVICE_INSTANCE_ID 1
#define QMI_SERVREG_LOC_SERVER_INITIAL_TIMEOUT 2000
#define QMI_SERVREG_LOC_SERVER_TIMEOUT 2000
#define INITIAL_TIMEOUT 100000
#define LOCATOR_SERVICE_TIMEOUT 300000
#define LOCATOR_NOT_PRESENT 0
#define LOCATOR_PRESENT 1
static u32 locator_status = LOCATOR_PRESENT;
static bool service_inited;
module_param_named(enable, locator_status, uint, 0644);
static void pd_locator_work(struct work_struct *work);
struct pd_qmi_data {
struct notifier_block notifier;
struct completion service_available;
struct qmi_handle clnt_handle;
bool connected;
struct sockaddr_qrtr s_addr;
};
struct pd_qmi_work {
struct work_struct pd_loc_work;
struct pd_qmi_client_data *pdc;
struct notifier_block *notifier;
};
static DEFINE_MUTEX(service_init_mutex);
static struct pd_qmi_data service_locator;
/* Please refer soc/qcom/service-locator.h for use about APIs defined here */
static int service_locator_new_server(struct qmi_handle *qmi,
struct qmi_service *svc)
{
/* Create a Local client port for QMI communication */
service_locator.s_addr.sq_family = AF_QIPCRTR;
service_locator.s_addr.sq_node = svc->node;
service_locator.s_addr.sq_port = svc->port;
service_locator.connected = true;
if (!service_inited)
complete_all(&service_locator.service_available);
pr_info("Connection established with the Service locator\n");
return 0;
}
static void service_locator_del_server(struct qmi_handle *qmi,
struct qmi_service *svc)
{
service_locator.connected = false;
complete_all(&service_locator.service_available);
pr_info("Connection with service locator lost\n");
}
static struct qmi_ops server_ops = {
.new_server = service_locator_new_server,
.del_server = service_locator_del_server,
};
static void store_get_domain_list_response(struct pd_qmi_client_data *pd,
struct qmi_servreg_loc_get_domain_list_resp_msg_v01 *resp,
int offset)
{
int i;
for (i = offset; i < resp->domain_list_len; i++) {
pd->domain_list[i].instance_id =
resp->domain_list[i].instance_id;
strlcpy(pd->domain_list[i].name, resp->domain_list[i].name,
QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1);
pd->domain_list[i].service_data_valid =
resp->domain_list[i].service_data_valid;
pd->domain_list[i].service_data =
resp->domain_list[i].service_data;
}
}
static int servreg_loc_send_msg(
struct qmi_servreg_loc_get_domain_list_req_msg_v01 *req,
struct qmi_servreg_loc_get_domain_list_resp_msg_v01 *resp,
struct pd_qmi_client_data *pd)
{
int rc;
struct qmi_txn txn;
/*
* Send msg and get response. There is a chance that the service went
* away since the time we last checked for it to be available and
* actually made this call. In that case the call just fails.
*/
rc = qmi_txn_init(&service_locator.clnt_handle, &txn,
qmi_servreg_loc_get_domain_list_resp_msg_v01_ei, resp);
if (rc < 0) {
pr_err("QMI tx init failed for client %s, ret - %d\n",
pd->client_name, rc);
return rc;
}
rc = qmi_send_request(&service_locator.clnt_handle,
&service_locator.s_addr,
&txn, QMI_SERVREG_LOC_GET_DOMAIN_LIST_REQ_V01,
QMI_SERVREG_LOC_GET_DOMAIN_LIST_REQ_MSG_V01_MAX_MSG_LEN,
qmi_servreg_loc_get_domain_list_req_msg_v01_ei,
req);
if (rc < 0) {
pr_err("QMI send req failed for client %s, ret - %d\n",
pd->client_name, rc);
qmi_txn_cancel(&txn);
return rc;
}
rc = qmi_txn_wait(&txn,
msecs_to_jiffies(QMI_SERVREG_LOC_SERVER_TIMEOUT));
if (rc < 0) {
pr_err("QMI qmi txn wait failed for client %s, ret - %d\n",
pd->client_name, rc);
return rc;
}
/* Check the response */
if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
pr_err("QMI request for client %s failed 0x%x\n",
pd->client_name, resp->resp.error);
return -EREMOTEIO;
}
return rc;
}
static int service_locator_send_msg(struct pd_qmi_client_data *pd)
{
struct qmi_servreg_loc_get_domain_list_resp_msg_v01 *resp = NULL;
struct qmi_servreg_loc_get_domain_list_req_msg_v01 *req = NULL;
int rc;
int db_rev_count = 0, domains_read = 0;
if (!service_locator.connected) {
pr_err("Service locator not available!\n");
return -EAGAIN;
}
req = kzalloc(sizeof(
struct qmi_servreg_loc_get_domain_list_req_msg_v01),
GFP_KERNEL);
if (!req) {
pr_err("Unable to allocate memory for req message\n");
rc = -ENOMEM;
goto out;
}
resp = kzalloc(sizeof(
struct qmi_servreg_loc_get_domain_list_resp_msg_v01),
GFP_KERNEL);
if (!resp) {
pr_err("Unable to allocate memory for resp message\n");
rc = -ENOMEM;
goto out;
}
/* Prepare req and response message */
strlcpy(req->service_name, pd->service_name,
QMI_SERVREG_LOC_NAME_LENGTH_V01 + 1);
req->domain_offset_valid = true;
req->domain_offset = 0;
do {
req->domain_offset += domains_read;
rc = servreg_loc_send_msg(req, resp, pd);
if (rc < 0) {
pr_err("send msg failed rc:%d\n", rc);
goto out;
}
if (!domains_read) {
db_rev_count = pd->db_rev_count = resp->db_rev_count;
pd->total_domains = resp->total_domains;
if (!resp->total_domains) {
pr_err("No matching domains found\n");
goto out;
}
pd->domain_list = kmalloc(
sizeof(struct servreg_loc_entry_v01) *
resp->total_domains, GFP_KERNEL);
if (!pd->domain_list) {
pr_err("Cannot allocate domain list\n");
rc = -ENOMEM;
goto out;
}
}
if (db_rev_count != resp->db_rev_count) {
pr_err("Service Locator DB updated for client %s\n",
pd->client_name);
kfree(pd->domain_list);
pd->domain_list = NULL;
rc = -EAGAIN;
goto out;
}
if (resp->domain_list_len > resp->total_domains) {
/* Always read total_domains from the response msg */
resp->domain_list_len = resp->total_domains;
}
/* Copy the response*/
store_get_domain_list_response(pd, resp, domains_read);
domains_read += resp->domain_list_len;
} while (domains_read < resp->total_domains);
rc = 0;
out:
kfree(req);
kfree(resp);
return rc;
}
static int init_service_locator(void)
{
int rc = 0;
static bool service_timedout;
rc = mutex_lock_interruptible(&service_init_mutex);
if (rc)
return rc;
if (locator_status == LOCATOR_NOT_PRESENT) {
pr_err("Service Locator not enabled\n");
rc = -ENODEV;
goto inited;
}
if (service_timedout) {
rc = -ETIME;
goto inited;
}
if (service_inited)
goto inited;
init_completion(&service_locator.service_available);
service_locator.connected = false;
rc = qmi_handle_init(&service_locator.clnt_handle,
QMI_SERVREG_LOC_GET_DOMAIN_LIST_RESP_MSG_V01_MAX_MSG_LEN,
&server_ops, NULL);
if (rc < 0) {
pr_err("Service locator QMI handle init failed rc:%d\n", rc);
goto inited;
}
qmi_add_lookup(&service_locator.clnt_handle,
SERVREG_LOC_SERVICE_ID_V01,
SERVREG_LOC_SERVICE_VERS_V01,
SERVREG_LOC_SERVICE_INSTANCE_ID);
rc = wait_for_completion_interruptible_timeout(
&service_locator.service_available,
msecs_to_jiffies(LOCATOR_SERVICE_TIMEOUT));
if (rc < 0) {
pr_err("Wait for locator service interrupted by signal\n");
goto inited;
}
if (!rc) {
pr_err("%s: wait for locator service timed out\n", __func__);
service_timedout = true;
rc = -ETIME;
goto inited;
}
service_inited = true;
mutex_unlock(&service_init_mutex);
pr_info("Service locator initialized\n");
return 0;
inited:
mutex_unlock(&service_init_mutex);
return rc;
}
int get_service_location(const char *client_name, const char *service_name,
struct notifier_block *locator_nb)
{
struct pd_qmi_client_data *pqcd;
struct pd_qmi_work *pqw;
int rc = 0;
if (!locator_nb || !client_name || !service_name) {
rc = -EINVAL;
pr_err("Invalid input!\n");
goto err;
}
pqcd = kzalloc(sizeof(struct pd_qmi_client_data), GFP_KERNEL);
if (!pqcd) {
rc = -ENOMEM;
pr_err("Allocation failed\n");
goto err;
}
strlcpy(pqcd->client_name, client_name, ARRAY_SIZE(pqcd->client_name));
strlcpy(pqcd->service_name, service_name,
ARRAY_SIZE(pqcd->service_name));
pqw = kmalloc(sizeof(struct pd_qmi_work), GFP_KERNEL);
if (!pqw) {
rc = -ENOMEM;
pr_err("Allocation failed\n");
kfree(pqcd);
goto err;
}
pqw->notifier = locator_nb;
pqw->pdc = pqcd;
INIT_WORK(&pqw->pd_loc_work, pd_locator_work);
schedule_work(&pqw->pd_loc_work);
err:
return rc;
}
EXPORT_SYMBOL(get_service_location);
static void pd_locator_work(struct work_struct *work)
{
int rc = 0;
struct pd_qmi_client_data *data;
struct pd_qmi_work *pdqw = container_of(work, struct pd_qmi_work,
pd_loc_work);
data = pdqw->pdc;
rc = init_service_locator();
if (rc) {
pr_err("Unable to connect to service locator!, rc = %d\n", rc);
pdqw->notifier->notifier_call(pdqw->notifier,
LOCATOR_DOWN, NULL);
goto err_init_servloc;
}
rc = service_locator_send_msg(data);
if (rc) {
pr_err("Failed to get process domains for %s for client %s rc:%d\n",
data->service_name, data->client_name, rc);
pdqw->notifier->notifier_call(pdqw->notifier,
LOCATOR_DOWN, NULL);
goto err_servloc_send_msg;
}
pdqw->notifier->notifier_call(pdqw->notifier, LOCATOR_UP, data);
err_servloc_send_msg:
kfree(data->domain_list);
err_init_servloc:
kfree(data);
kfree(pdqw);
}
int find_subsys(const char *pd_path, char *subsys)
{
char *start, *end;
if (!subsys || !pd_path)
return -EINVAL;
start = strnstr(pd_path, "/", QMI_SERVREG_LOC_NAME_LENGTH_V01);
if (!start)
return -EINVAL;
start++;
end = strnstr(start, "/", QMI_SERVREG_LOC_NAME_LENGTH_V01);
if (!end || start == end)
return -EINVAL;
strlcpy(subsys, start, end - start + 1);
return 0;
}
EXPORT_SYMBOL(find_subsys);
MODULE_SOFTDEP("pre: qrtr");
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Qualcomm Technologies, Inc. Service Locator driver");