162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2020 The Linux Foundation. All rights reserved.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/kernel.h>
762306a36Sopenharmony_ci#include <linux/module.h>
862306a36Sopenharmony_ci#include <linux/slab.h>
962306a36Sopenharmony_ci#include <linux/string.h>
1062306a36Sopenharmony_ci#include <linux/workqueue.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include "pdr_internal.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_cistruct pdr_service {
1562306a36Sopenharmony_ci	char service_name[SERVREG_NAME_LENGTH + 1];
1662306a36Sopenharmony_ci	char service_path[SERVREG_NAME_LENGTH + 1];
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci	struct sockaddr_qrtr addr;
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci	unsigned int instance;
2162306a36Sopenharmony_ci	unsigned int service;
2262306a36Sopenharmony_ci	u8 service_data_valid;
2362306a36Sopenharmony_ci	u32 service_data;
2462306a36Sopenharmony_ci	int state;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	bool need_notifier_register;
2762306a36Sopenharmony_ci	bool need_notifier_remove;
2862306a36Sopenharmony_ci	bool need_locator_lookup;
2962306a36Sopenharmony_ci	bool service_connected;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	struct list_head node;
3262306a36Sopenharmony_ci};
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistruct pdr_handle {
3562306a36Sopenharmony_ci	struct qmi_handle locator_hdl;
3662306a36Sopenharmony_ci	struct qmi_handle notifier_hdl;
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	struct sockaddr_qrtr locator_addr;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci	struct list_head lookups;
4162306a36Sopenharmony_ci	struct list_head indack_list;
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_ci	/* control access to pdr lookup/indack lists */
4462306a36Sopenharmony_ci	struct mutex list_lock;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	/* serialize pd status invocation */
4762306a36Sopenharmony_ci	struct mutex status_lock;
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	/* control access to the locator state */
5062306a36Sopenharmony_ci	struct mutex lock;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	bool locator_init_complete;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	struct work_struct locator_work;
5562306a36Sopenharmony_ci	struct work_struct notifier_work;
5662306a36Sopenharmony_ci	struct work_struct indack_work;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	struct workqueue_struct *notifier_wq;
5962306a36Sopenharmony_ci	struct workqueue_struct *indack_wq;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	void (*status)(int state, char *service_path, void *priv);
6262306a36Sopenharmony_ci	void *priv;
6362306a36Sopenharmony_ci};
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistruct pdr_list_node {
6662306a36Sopenharmony_ci	enum servreg_service_state curr_state;
6762306a36Sopenharmony_ci	u16 transaction_id;
6862306a36Sopenharmony_ci	struct pdr_service *pds;
6962306a36Sopenharmony_ci	struct list_head node;
7062306a36Sopenharmony_ci};
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic int pdr_locator_new_server(struct qmi_handle *qmi,
7362306a36Sopenharmony_ci				  struct qmi_service *svc)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	struct pdr_handle *pdr = container_of(qmi, struct pdr_handle,
7662306a36Sopenharmony_ci					      locator_hdl);
7762306a36Sopenharmony_ci	struct pdr_service *pds;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	/* Create a local client port for QMI communication */
8062306a36Sopenharmony_ci	pdr->locator_addr.sq_family = AF_QIPCRTR;
8162306a36Sopenharmony_ci	pdr->locator_addr.sq_node = svc->node;
8262306a36Sopenharmony_ci	pdr->locator_addr.sq_port = svc->port;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	mutex_lock(&pdr->lock);
8562306a36Sopenharmony_ci	pdr->locator_init_complete = true;
8662306a36Sopenharmony_ci	mutex_unlock(&pdr->lock);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/* Service pending lookup requests */
8962306a36Sopenharmony_ci	mutex_lock(&pdr->list_lock);
9062306a36Sopenharmony_ci	list_for_each_entry(pds, &pdr->lookups, node) {
9162306a36Sopenharmony_ci		if (pds->need_locator_lookup)
9262306a36Sopenharmony_ci			schedule_work(&pdr->locator_work);
9362306a36Sopenharmony_ci	}
9462306a36Sopenharmony_ci	mutex_unlock(&pdr->list_lock);
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	return 0;
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic void pdr_locator_del_server(struct qmi_handle *qmi,
10062306a36Sopenharmony_ci				   struct qmi_service *svc)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	struct pdr_handle *pdr = container_of(qmi, struct pdr_handle,
10362306a36Sopenharmony_ci					      locator_hdl);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	mutex_lock(&pdr->lock);
10662306a36Sopenharmony_ci	pdr->locator_init_complete = false;
10762306a36Sopenharmony_ci	mutex_unlock(&pdr->lock);
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci	pdr->locator_addr.sq_node = 0;
11062306a36Sopenharmony_ci	pdr->locator_addr.sq_port = 0;
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic const struct qmi_ops pdr_locator_ops = {
11462306a36Sopenharmony_ci	.new_server = pdr_locator_new_server,
11562306a36Sopenharmony_ci	.del_server = pdr_locator_del_server,
11662306a36Sopenharmony_ci};
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic int pdr_register_listener(struct pdr_handle *pdr,
11962306a36Sopenharmony_ci				 struct pdr_service *pds,
12062306a36Sopenharmony_ci				 bool enable)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct servreg_register_listener_resp resp;
12362306a36Sopenharmony_ci	struct servreg_register_listener_req req;
12462306a36Sopenharmony_ci	struct qmi_txn txn;
12562306a36Sopenharmony_ci	int ret;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	ret = qmi_txn_init(&pdr->notifier_hdl, &txn,
12862306a36Sopenharmony_ci			   servreg_register_listener_resp_ei,
12962306a36Sopenharmony_ci			   &resp);
13062306a36Sopenharmony_ci	if (ret < 0)
13162306a36Sopenharmony_ci		return ret;
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	req.enable = enable;
13462306a36Sopenharmony_ci	strscpy(req.service_path, pds->service_path, sizeof(req.service_path));
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	ret = qmi_send_request(&pdr->notifier_hdl, &pds->addr,
13762306a36Sopenharmony_ci			       &txn, SERVREG_REGISTER_LISTENER_REQ,
13862306a36Sopenharmony_ci			       SERVREG_REGISTER_LISTENER_REQ_LEN,
13962306a36Sopenharmony_ci			       servreg_register_listener_req_ei,
14062306a36Sopenharmony_ci			       &req);
14162306a36Sopenharmony_ci	if (ret < 0) {
14262306a36Sopenharmony_ci		qmi_txn_cancel(&txn);
14362306a36Sopenharmony_ci		return ret;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	ret = qmi_txn_wait(&txn, 5 * HZ);
14762306a36Sopenharmony_ci	if (ret < 0) {
14862306a36Sopenharmony_ci		pr_err("PDR: %s register listener txn wait failed: %d\n",
14962306a36Sopenharmony_ci		       pds->service_path, ret);
15062306a36Sopenharmony_ci		return ret;
15162306a36Sopenharmony_ci	}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
15462306a36Sopenharmony_ci		pr_err("PDR: %s register listener failed: 0x%x\n",
15562306a36Sopenharmony_ci		       pds->service_path, resp.resp.error);
15662306a36Sopenharmony_ci		return -EREMOTEIO;
15762306a36Sopenharmony_ci	}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	pds->state = resp.curr_state;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return 0;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic void pdr_notifier_work(struct work_struct *work)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	struct pdr_handle *pdr = container_of(work, struct pdr_handle,
16762306a36Sopenharmony_ci					      notifier_work);
16862306a36Sopenharmony_ci	struct pdr_service *pds;
16962306a36Sopenharmony_ci	int ret;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	mutex_lock(&pdr->list_lock);
17262306a36Sopenharmony_ci	list_for_each_entry(pds, &pdr->lookups, node) {
17362306a36Sopenharmony_ci		if (pds->service_connected) {
17462306a36Sopenharmony_ci			if (!pds->need_notifier_register)
17562306a36Sopenharmony_ci				continue;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci			pds->need_notifier_register = false;
17862306a36Sopenharmony_ci			ret = pdr_register_listener(pdr, pds, true);
17962306a36Sopenharmony_ci			if (ret < 0)
18062306a36Sopenharmony_ci				pds->state = SERVREG_SERVICE_STATE_DOWN;
18162306a36Sopenharmony_ci		} else {
18262306a36Sopenharmony_ci			if (!pds->need_notifier_remove)
18362306a36Sopenharmony_ci				continue;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci			pds->need_notifier_remove = false;
18662306a36Sopenharmony_ci			pds->state = SERVREG_SERVICE_STATE_DOWN;
18762306a36Sopenharmony_ci		}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci		mutex_lock(&pdr->status_lock);
19062306a36Sopenharmony_ci		pdr->status(pds->state, pds->service_path, pdr->priv);
19162306a36Sopenharmony_ci		mutex_unlock(&pdr->status_lock);
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci	mutex_unlock(&pdr->list_lock);
19462306a36Sopenharmony_ci}
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_cistatic int pdr_notifier_new_server(struct qmi_handle *qmi,
19762306a36Sopenharmony_ci				   struct qmi_service *svc)
19862306a36Sopenharmony_ci{
19962306a36Sopenharmony_ci	struct pdr_handle *pdr = container_of(qmi, struct pdr_handle,
20062306a36Sopenharmony_ci					      notifier_hdl);
20162306a36Sopenharmony_ci	struct pdr_service *pds;
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	mutex_lock(&pdr->list_lock);
20462306a36Sopenharmony_ci	list_for_each_entry(pds, &pdr->lookups, node) {
20562306a36Sopenharmony_ci		if (pds->service == svc->service &&
20662306a36Sopenharmony_ci		    pds->instance == svc->instance) {
20762306a36Sopenharmony_ci			pds->service_connected = true;
20862306a36Sopenharmony_ci			pds->need_notifier_register = true;
20962306a36Sopenharmony_ci			pds->addr.sq_family = AF_QIPCRTR;
21062306a36Sopenharmony_ci			pds->addr.sq_node = svc->node;
21162306a36Sopenharmony_ci			pds->addr.sq_port = svc->port;
21262306a36Sopenharmony_ci			queue_work(pdr->notifier_wq, &pdr->notifier_work);
21362306a36Sopenharmony_ci		}
21462306a36Sopenharmony_ci	}
21562306a36Sopenharmony_ci	mutex_unlock(&pdr->list_lock);
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	return 0;
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_cistatic void pdr_notifier_del_server(struct qmi_handle *qmi,
22162306a36Sopenharmony_ci				    struct qmi_service *svc)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	struct pdr_handle *pdr = container_of(qmi, struct pdr_handle,
22462306a36Sopenharmony_ci					      notifier_hdl);
22562306a36Sopenharmony_ci	struct pdr_service *pds;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	mutex_lock(&pdr->list_lock);
22862306a36Sopenharmony_ci	list_for_each_entry(pds, &pdr->lookups, node) {
22962306a36Sopenharmony_ci		if (pds->service == svc->service &&
23062306a36Sopenharmony_ci		    pds->instance == svc->instance) {
23162306a36Sopenharmony_ci			pds->service_connected = false;
23262306a36Sopenharmony_ci			pds->need_notifier_remove = true;
23362306a36Sopenharmony_ci			pds->addr.sq_node = 0;
23462306a36Sopenharmony_ci			pds->addr.sq_port = 0;
23562306a36Sopenharmony_ci			queue_work(pdr->notifier_wq, &pdr->notifier_work);
23662306a36Sopenharmony_ci		}
23762306a36Sopenharmony_ci	}
23862306a36Sopenharmony_ci	mutex_unlock(&pdr->list_lock);
23962306a36Sopenharmony_ci}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_cistatic const struct qmi_ops pdr_notifier_ops = {
24262306a36Sopenharmony_ci	.new_server = pdr_notifier_new_server,
24362306a36Sopenharmony_ci	.del_server = pdr_notifier_del_server,
24462306a36Sopenharmony_ci};
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic int pdr_send_indack_msg(struct pdr_handle *pdr, struct pdr_service *pds,
24762306a36Sopenharmony_ci			       u16 tid)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	struct servreg_set_ack_resp resp;
25062306a36Sopenharmony_ci	struct servreg_set_ack_req req;
25162306a36Sopenharmony_ci	struct qmi_txn txn;
25262306a36Sopenharmony_ci	int ret;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	ret = qmi_txn_init(&pdr->notifier_hdl, &txn, servreg_set_ack_resp_ei,
25562306a36Sopenharmony_ci			   &resp);
25662306a36Sopenharmony_ci	if (ret < 0)
25762306a36Sopenharmony_ci		return ret;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	req.transaction_id = tid;
26062306a36Sopenharmony_ci	strscpy(req.service_path, pds->service_path, sizeof(req.service_path));
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	ret = qmi_send_request(&pdr->notifier_hdl, &pds->addr,
26362306a36Sopenharmony_ci			       &txn, SERVREG_SET_ACK_REQ,
26462306a36Sopenharmony_ci			       SERVREG_SET_ACK_REQ_LEN,
26562306a36Sopenharmony_ci			       servreg_set_ack_req_ei,
26662306a36Sopenharmony_ci			       &req);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci	/* Skip waiting for response */
26962306a36Sopenharmony_ci	qmi_txn_cancel(&txn);
27062306a36Sopenharmony_ci	return ret;
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic void pdr_indack_work(struct work_struct *work)
27462306a36Sopenharmony_ci{
27562306a36Sopenharmony_ci	struct pdr_handle *pdr = container_of(work, struct pdr_handle,
27662306a36Sopenharmony_ci					      indack_work);
27762306a36Sopenharmony_ci	struct pdr_list_node *ind, *tmp;
27862306a36Sopenharmony_ci	struct pdr_service *pds;
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	list_for_each_entry_safe(ind, tmp, &pdr->indack_list, node) {
28162306a36Sopenharmony_ci		pds = ind->pds;
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci		mutex_lock(&pdr->status_lock);
28462306a36Sopenharmony_ci		pds->state = ind->curr_state;
28562306a36Sopenharmony_ci		pdr->status(pds->state, pds->service_path, pdr->priv);
28662306a36Sopenharmony_ci		mutex_unlock(&pdr->status_lock);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci		/* Ack the indication after clients release the PD resources */
28962306a36Sopenharmony_ci		pdr_send_indack_msg(pdr, pds, ind->transaction_id);
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci		mutex_lock(&pdr->list_lock);
29262306a36Sopenharmony_ci		list_del(&ind->node);
29362306a36Sopenharmony_ci		mutex_unlock(&pdr->list_lock);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci		kfree(ind);
29662306a36Sopenharmony_ci	}
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic void pdr_indication_cb(struct qmi_handle *qmi,
30062306a36Sopenharmony_ci			      struct sockaddr_qrtr *sq,
30162306a36Sopenharmony_ci			      struct qmi_txn *txn, const void *data)
30262306a36Sopenharmony_ci{
30362306a36Sopenharmony_ci	struct pdr_handle *pdr = container_of(qmi, struct pdr_handle,
30462306a36Sopenharmony_ci					      notifier_hdl);
30562306a36Sopenharmony_ci	const struct servreg_state_updated_ind *ind_msg = data;
30662306a36Sopenharmony_ci	struct pdr_list_node *ind;
30762306a36Sopenharmony_ci	struct pdr_service *pds = NULL, *iter;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	if (!ind_msg || !ind_msg->service_path[0] ||
31062306a36Sopenharmony_ci	    strlen(ind_msg->service_path) > SERVREG_NAME_LENGTH)
31162306a36Sopenharmony_ci		return;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	mutex_lock(&pdr->list_lock);
31462306a36Sopenharmony_ci	list_for_each_entry(iter, &pdr->lookups, node) {
31562306a36Sopenharmony_ci		if (strcmp(iter->service_path, ind_msg->service_path))
31662306a36Sopenharmony_ci			continue;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci		pds = iter;
31962306a36Sopenharmony_ci		break;
32062306a36Sopenharmony_ci	}
32162306a36Sopenharmony_ci	mutex_unlock(&pdr->list_lock);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	if (!pds)
32462306a36Sopenharmony_ci		return;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	pr_info("PDR: Indication received from %s, state: 0x%x, trans-id: %d\n",
32762306a36Sopenharmony_ci		ind_msg->service_path, ind_msg->curr_state,
32862306a36Sopenharmony_ci		ind_msg->transaction_id);
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_ci	ind = kzalloc(sizeof(*ind), GFP_KERNEL);
33162306a36Sopenharmony_ci	if (!ind)
33262306a36Sopenharmony_ci		return;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	ind->transaction_id = ind_msg->transaction_id;
33562306a36Sopenharmony_ci	ind->curr_state = ind_msg->curr_state;
33662306a36Sopenharmony_ci	ind->pds = pds;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	mutex_lock(&pdr->list_lock);
33962306a36Sopenharmony_ci	list_add_tail(&ind->node, &pdr->indack_list);
34062306a36Sopenharmony_ci	mutex_unlock(&pdr->list_lock);
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	queue_work(pdr->indack_wq, &pdr->indack_work);
34362306a36Sopenharmony_ci}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_cistatic const struct qmi_msg_handler qmi_indication_handler[] = {
34662306a36Sopenharmony_ci	{
34762306a36Sopenharmony_ci		.type = QMI_INDICATION,
34862306a36Sopenharmony_ci		.msg_id = SERVREG_STATE_UPDATED_IND_ID,
34962306a36Sopenharmony_ci		.ei = servreg_state_updated_ind_ei,
35062306a36Sopenharmony_ci		.decoded_size = sizeof(struct servreg_state_updated_ind),
35162306a36Sopenharmony_ci		.fn = pdr_indication_cb,
35262306a36Sopenharmony_ci	},
35362306a36Sopenharmony_ci	{}
35462306a36Sopenharmony_ci};
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_cistatic int pdr_get_domain_list(struct servreg_get_domain_list_req *req,
35762306a36Sopenharmony_ci			       struct servreg_get_domain_list_resp *resp,
35862306a36Sopenharmony_ci			       struct pdr_handle *pdr)
35962306a36Sopenharmony_ci{
36062306a36Sopenharmony_ci	struct qmi_txn txn;
36162306a36Sopenharmony_ci	int ret;
36262306a36Sopenharmony_ci
36362306a36Sopenharmony_ci	ret = qmi_txn_init(&pdr->locator_hdl, &txn,
36462306a36Sopenharmony_ci			   servreg_get_domain_list_resp_ei, resp);
36562306a36Sopenharmony_ci	if (ret < 0)
36662306a36Sopenharmony_ci		return ret;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	ret = qmi_send_request(&pdr->locator_hdl,
36962306a36Sopenharmony_ci			       &pdr->locator_addr,
37062306a36Sopenharmony_ci			       &txn, SERVREG_GET_DOMAIN_LIST_REQ,
37162306a36Sopenharmony_ci			       SERVREG_GET_DOMAIN_LIST_REQ_MAX_LEN,
37262306a36Sopenharmony_ci			       servreg_get_domain_list_req_ei,
37362306a36Sopenharmony_ci			       req);
37462306a36Sopenharmony_ci	if (ret < 0) {
37562306a36Sopenharmony_ci		qmi_txn_cancel(&txn);
37662306a36Sopenharmony_ci		return ret;
37762306a36Sopenharmony_ci	}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	ret = qmi_txn_wait(&txn, 5 * HZ);
38062306a36Sopenharmony_ci	if (ret < 0) {
38162306a36Sopenharmony_ci		pr_err("PDR: %s get domain list txn wait failed: %d\n",
38262306a36Sopenharmony_ci		       req->service_name, ret);
38362306a36Sopenharmony_ci		return ret;
38462306a36Sopenharmony_ci	}
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
38762306a36Sopenharmony_ci		pr_err("PDR: %s get domain list failed: 0x%x\n",
38862306a36Sopenharmony_ci		       req->service_name, resp->resp.error);
38962306a36Sopenharmony_ci		return -EREMOTEIO;
39062306a36Sopenharmony_ci	}
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	return 0;
39362306a36Sopenharmony_ci}
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_cistatic int pdr_locate_service(struct pdr_handle *pdr, struct pdr_service *pds)
39662306a36Sopenharmony_ci{
39762306a36Sopenharmony_ci	struct servreg_get_domain_list_resp *resp;
39862306a36Sopenharmony_ci	struct servreg_get_domain_list_req req;
39962306a36Sopenharmony_ci	struct servreg_location_entry *entry;
40062306a36Sopenharmony_ci	int domains_read = 0;
40162306a36Sopenharmony_ci	int ret, i;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
40462306a36Sopenharmony_ci	if (!resp)
40562306a36Sopenharmony_ci		return -ENOMEM;
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	/* Prepare req message */
40862306a36Sopenharmony_ci	strscpy(req.service_name, pds->service_name, sizeof(req.service_name));
40962306a36Sopenharmony_ci	req.domain_offset_valid = true;
41062306a36Sopenharmony_ci	req.domain_offset = 0;
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	do {
41362306a36Sopenharmony_ci		req.domain_offset = domains_read;
41462306a36Sopenharmony_ci		ret = pdr_get_domain_list(&req, resp, pdr);
41562306a36Sopenharmony_ci		if (ret < 0)
41662306a36Sopenharmony_ci			goto out;
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci		for (i = domains_read; i < resp->domain_list_len; i++) {
41962306a36Sopenharmony_ci			entry = &resp->domain_list[i];
42062306a36Sopenharmony_ci
42162306a36Sopenharmony_ci			if (strnlen(entry->name, sizeof(entry->name)) == sizeof(entry->name))
42262306a36Sopenharmony_ci				continue;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci			if (!strcmp(entry->name, pds->service_path)) {
42562306a36Sopenharmony_ci				pds->service_data_valid = entry->service_data_valid;
42662306a36Sopenharmony_ci				pds->service_data = entry->service_data;
42762306a36Sopenharmony_ci				pds->instance = entry->instance;
42862306a36Sopenharmony_ci				goto out;
42962306a36Sopenharmony_ci			}
43062306a36Sopenharmony_ci		}
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci		/* Update ret to indicate that the service is not yet found */
43362306a36Sopenharmony_ci		ret = -ENXIO;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci		/* Always read total_domains from the response msg */
43662306a36Sopenharmony_ci		if (resp->domain_list_len > resp->total_domains)
43762306a36Sopenharmony_ci			resp->domain_list_len = resp->total_domains;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci		domains_read += resp->domain_list_len;
44062306a36Sopenharmony_ci	} while (domains_read < resp->total_domains);
44162306a36Sopenharmony_ciout:
44262306a36Sopenharmony_ci	kfree(resp);
44362306a36Sopenharmony_ci	return ret;
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cistatic void pdr_notify_lookup_failure(struct pdr_handle *pdr,
44762306a36Sopenharmony_ci				      struct pdr_service *pds,
44862306a36Sopenharmony_ci				      int err)
44962306a36Sopenharmony_ci{
45062306a36Sopenharmony_ci	pr_err("PDR: service lookup for %s failed: %d\n",
45162306a36Sopenharmony_ci	       pds->service_name, err);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	if (err == -ENXIO)
45462306a36Sopenharmony_ci		return;
45562306a36Sopenharmony_ci
45662306a36Sopenharmony_ci	list_del(&pds->node);
45762306a36Sopenharmony_ci	pds->state = SERVREG_LOCATOR_ERR;
45862306a36Sopenharmony_ci	mutex_lock(&pdr->status_lock);
45962306a36Sopenharmony_ci	pdr->status(pds->state, pds->service_path, pdr->priv);
46062306a36Sopenharmony_ci	mutex_unlock(&pdr->status_lock);
46162306a36Sopenharmony_ci	kfree(pds);
46262306a36Sopenharmony_ci}
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_cistatic void pdr_locator_work(struct work_struct *work)
46562306a36Sopenharmony_ci{
46662306a36Sopenharmony_ci	struct pdr_handle *pdr = container_of(work, struct pdr_handle,
46762306a36Sopenharmony_ci					      locator_work);
46862306a36Sopenharmony_ci	struct pdr_service *pds, *tmp;
46962306a36Sopenharmony_ci	int ret = 0;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	/* Bail out early if the SERVREG LOCATOR QMI service is not up */
47262306a36Sopenharmony_ci	mutex_lock(&pdr->lock);
47362306a36Sopenharmony_ci	if (!pdr->locator_init_complete) {
47462306a36Sopenharmony_ci		mutex_unlock(&pdr->lock);
47562306a36Sopenharmony_ci		pr_debug("PDR: SERVICE LOCATOR service not available\n");
47662306a36Sopenharmony_ci		return;
47762306a36Sopenharmony_ci	}
47862306a36Sopenharmony_ci	mutex_unlock(&pdr->lock);
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	mutex_lock(&pdr->list_lock);
48162306a36Sopenharmony_ci	list_for_each_entry_safe(pds, tmp, &pdr->lookups, node) {
48262306a36Sopenharmony_ci		if (!pds->need_locator_lookup)
48362306a36Sopenharmony_ci			continue;
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_ci		ret = pdr_locate_service(pdr, pds);
48662306a36Sopenharmony_ci		if (ret < 0) {
48762306a36Sopenharmony_ci			pdr_notify_lookup_failure(pdr, pds, ret);
48862306a36Sopenharmony_ci			continue;
48962306a36Sopenharmony_ci		}
49062306a36Sopenharmony_ci
49162306a36Sopenharmony_ci		ret = qmi_add_lookup(&pdr->notifier_hdl, pds->service, 1,
49262306a36Sopenharmony_ci				     pds->instance);
49362306a36Sopenharmony_ci		if (ret < 0) {
49462306a36Sopenharmony_ci			pdr_notify_lookup_failure(pdr, pds, ret);
49562306a36Sopenharmony_ci			continue;
49662306a36Sopenharmony_ci		}
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci		pds->need_locator_lookup = false;
49962306a36Sopenharmony_ci	}
50062306a36Sopenharmony_ci	mutex_unlock(&pdr->list_lock);
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci/**
50462306a36Sopenharmony_ci * pdr_add_lookup() - register a tracking request for a PD
50562306a36Sopenharmony_ci * @pdr:		PDR client handle
50662306a36Sopenharmony_ci * @service_name:	service name of the tracking request
50762306a36Sopenharmony_ci * @service_path:	service path of the tracking request
50862306a36Sopenharmony_ci *
50962306a36Sopenharmony_ci * Registering a pdr lookup allows for tracking the life cycle of the PD.
51062306a36Sopenharmony_ci *
51162306a36Sopenharmony_ci * Return: pdr_service object on success, ERR_PTR on failure. -EALREADY is
51262306a36Sopenharmony_ci * returned if a lookup is already in progress for the given service path.
51362306a36Sopenharmony_ci */
51462306a36Sopenharmony_cistruct pdr_service *pdr_add_lookup(struct pdr_handle *pdr,
51562306a36Sopenharmony_ci				   const char *service_name,
51662306a36Sopenharmony_ci				   const char *service_path)
51762306a36Sopenharmony_ci{
51862306a36Sopenharmony_ci	struct pdr_service *pds, *tmp;
51962306a36Sopenharmony_ci	int ret;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(pdr))
52262306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	if (!service_name || strlen(service_name) > SERVREG_NAME_LENGTH ||
52562306a36Sopenharmony_ci	    !service_path || strlen(service_path) > SERVREG_NAME_LENGTH)
52662306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	pds = kzalloc(sizeof(*pds), GFP_KERNEL);
52962306a36Sopenharmony_ci	if (!pds)
53062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
53162306a36Sopenharmony_ci
53262306a36Sopenharmony_ci	pds->service = SERVREG_NOTIFIER_SERVICE;
53362306a36Sopenharmony_ci	strscpy(pds->service_name, service_name, sizeof(pds->service_name));
53462306a36Sopenharmony_ci	strscpy(pds->service_path, service_path, sizeof(pds->service_path));
53562306a36Sopenharmony_ci	pds->need_locator_lookup = true;
53662306a36Sopenharmony_ci
53762306a36Sopenharmony_ci	mutex_lock(&pdr->list_lock);
53862306a36Sopenharmony_ci	list_for_each_entry(tmp, &pdr->lookups, node) {
53962306a36Sopenharmony_ci		if (strcmp(tmp->service_path, service_path))
54062306a36Sopenharmony_ci			continue;
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci		mutex_unlock(&pdr->list_lock);
54362306a36Sopenharmony_ci		ret = -EALREADY;
54462306a36Sopenharmony_ci		goto err;
54562306a36Sopenharmony_ci	}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	list_add(&pds->node, &pdr->lookups);
54862306a36Sopenharmony_ci	mutex_unlock(&pdr->list_lock);
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	schedule_work(&pdr->locator_work);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	return pds;
55362306a36Sopenharmony_cierr:
55462306a36Sopenharmony_ci	kfree(pds);
55562306a36Sopenharmony_ci	return ERR_PTR(ret);
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ciEXPORT_SYMBOL(pdr_add_lookup);
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci/**
56062306a36Sopenharmony_ci * pdr_restart_pd() - restart PD
56162306a36Sopenharmony_ci * @pdr:	PDR client handle
56262306a36Sopenharmony_ci * @pds:	PD service handle
56362306a36Sopenharmony_ci *
56462306a36Sopenharmony_ci * Restarts the PD tracked by the PDR client handle for a given service path.
56562306a36Sopenharmony_ci *
56662306a36Sopenharmony_ci * Return: 0 on success, negative errno on failure.
56762306a36Sopenharmony_ci */
56862306a36Sopenharmony_ciint pdr_restart_pd(struct pdr_handle *pdr, struct pdr_service *pds)
56962306a36Sopenharmony_ci{
57062306a36Sopenharmony_ci	struct servreg_restart_pd_resp resp;
57162306a36Sopenharmony_ci	struct servreg_restart_pd_req req = { 0 };
57262306a36Sopenharmony_ci	struct sockaddr_qrtr addr;
57362306a36Sopenharmony_ci	struct pdr_service *tmp;
57462306a36Sopenharmony_ci	struct qmi_txn txn;
57562306a36Sopenharmony_ci	int ret;
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(pdr) || IS_ERR_OR_NULL(pds))
57862306a36Sopenharmony_ci		return -EINVAL;
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	mutex_lock(&pdr->list_lock);
58162306a36Sopenharmony_ci	list_for_each_entry(tmp, &pdr->lookups, node) {
58262306a36Sopenharmony_ci		if (tmp != pds)
58362306a36Sopenharmony_ci			continue;
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci		if (!pds->service_connected)
58662306a36Sopenharmony_ci			break;
58762306a36Sopenharmony_ci
58862306a36Sopenharmony_ci		/* Prepare req message */
58962306a36Sopenharmony_ci		strscpy(req.service_path, pds->service_path, sizeof(req.service_path));
59062306a36Sopenharmony_ci		addr = pds->addr;
59162306a36Sopenharmony_ci		break;
59262306a36Sopenharmony_ci	}
59362306a36Sopenharmony_ci	mutex_unlock(&pdr->list_lock);
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	if (!req.service_path[0])
59662306a36Sopenharmony_ci		return -EINVAL;
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_ci	ret = qmi_txn_init(&pdr->notifier_hdl, &txn,
59962306a36Sopenharmony_ci			   servreg_restart_pd_resp_ei,
60062306a36Sopenharmony_ci			   &resp);
60162306a36Sopenharmony_ci	if (ret < 0)
60262306a36Sopenharmony_ci		return ret;
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	ret = qmi_send_request(&pdr->notifier_hdl, &addr,
60562306a36Sopenharmony_ci			       &txn, SERVREG_RESTART_PD_REQ,
60662306a36Sopenharmony_ci			       SERVREG_RESTART_PD_REQ_MAX_LEN,
60762306a36Sopenharmony_ci			       servreg_restart_pd_req_ei, &req);
60862306a36Sopenharmony_ci	if (ret < 0) {
60962306a36Sopenharmony_ci		qmi_txn_cancel(&txn);
61062306a36Sopenharmony_ci		return ret;
61162306a36Sopenharmony_ci	}
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	ret = qmi_txn_wait(&txn, 5 * HZ);
61462306a36Sopenharmony_ci	if (ret < 0) {
61562306a36Sopenharmony_ci		pr_err("PDR: %s PD restart txn wait failed: %d\n",
61662306a36Sopenharmony_ci		       req.service_path, ret);
61762306a36Sopenharmony_ci		return ret;
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci
62062306a36Sopenharmony_ci	/* Check response if PDR is disabled */
62162306a36Sopenharmony_ci	if (resp.resp.result == QMI_RESULT_FAILURE_V01 &&
62262306a36Sopenharmony_ci	    resp.resp.error == QMI_ERR_DISABLED_V01) {
62362306a36Sopenharmony_ci		pr_err("PDR: %s PD restart is disabled: 0x%x\n",
62462306a36Sopenharmony_ci		       req.service_path, resp.resp.error);
62562306a36Sopenharmony_ci		return -EOPNOTSUPP;
62662306a36Sopenharmony_ci	}
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	/* Check the response for other error case*/
62962306a36Sopenharmony_ci	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
63062306a36Sopenharmony_ci		pr_err("PDR: %s request for PD restart failed: 0x%x\n",
63162306a36Sopenharmony_ci		       req.service_path, resp.resp.error);
63262306a36Sopenharmony_ci		return -EREMOTEIO;
63362306a36Sopenharmony_ci	}
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	return 0;
63662306a36Sopenharmony_ci}
63762306a36Sopenharmony_ciEXPORT_SYMBOL(pdr_restart_pd);
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci/**
64062306a36Sopenharmony_ci * pdr_handle_alloc() - initialize the PDR client handle
64162306a36Sopenharmony_ci * @status:	function to be called on PD state change
64262306a36Sopenharmony_ci * @priv:	handle for client's use
64362306a36Sopenharmony_ci *
64462306a36Sopenharmony_ci * Initializes the PDR client handle to allow for tracking/restart of PDs.
64562306a36Sopenharmony_ci *
64662306a36Sopenharmony_ci * Return: pdr_handle object on success, ERR_PTR on failure.
64762306a36Sopenharmony_ci */
64862306a36Sopenharmony_cistruct pdr_handle *pdr_handle_alloc(void (*status)(int state,
64962306a36Sopenharmony_ci						   char *service_path,
65062306a36Sopenharmony_ci						   void *priv), void *priv)
65162306a36Sopenharmony_ci{
65262306a36Sopenharmony_ci	struct pdr_handle *pdr;
65362306a36Sopenharmony_ci	int ret;
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	if (!status)
65662306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	pdr = kzalloc(sizeof(*pdr), GFP_KERNEL);
65962306a36Sopenharmony_ci	if (!pdr)
66062306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	pdr->status = status;
66362306a36Sopenharmony_ci	pdr->priv = priv;
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci	mutex_init(&pdr->status_lock);
66662306a36Sopenharmony_ci	mutex_init(&pdr->list_lock);
66762306a36Sopenharmony_ci	mutex_init(&pdr->lock);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	INIT_LIST_HEAD(&pdr->lookups);
67062306a36Sopenharmony_ci	INIT_LIST_HEAD(&pdr->indack_list);
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	INIT_WORK(&pdr->locator_work, pdr_locator_work);
67362306a36Sopenharmony_ci	INIT_WORK(&pdr->notifier_work, pdr_notifier_work);
67462306a36Sopenharmony_ci	INIT_WORK(&pdr->indack_work, pdr_indack_work);
67562306a36Sopenharmony_ci
67662306a36Sopenharmony_ci	pdr->notifier_wq = create_singlethread_workqueue("pdr_notifier_wq");
67762306a36Sopenharmony_ci	if (!pdr->notifier_wq) {
67862306a36Sopenharmony_ci		ret = -ENOMEM;
67962306a36Sopenharmony_ci		goto free_pdr_handle;
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	pdr->indack_wq = alloc_ordered_workqueue("pdr_indack_wq", WQ_HIGHPRI);
68362306a36Sopenharmony_ci	if (!pdr->indack_wq) {
68462306a36Sopenharmony_ci		ret = -ENOMEM;
68562306a36Sopenharmony_ci		goto destroy_notifier;
68662306a36Sopenharmony_ci	}
68762306a36Sopenharmony_ci
68862306a36Sopenharmony_ci	ret = qmi_handle_init(&pdr->locator_hdl,
68962306a36Sopenharmony_ci			      SERVREG_GET_DOMAIN_LIST_RESP_MAX_LEN,
69062306a36Sopenharmony_ci			      &pdr_locator_ops, NULL);
69162306a36Sopenharmony_ci	if (ret < 0)
69262306a36Sopenharmony_ci		goto destroy_indack;
69362306a36Sopenharmony_ci
69462306a36Sopenharmony_ci	ret = qmi_add_lookup(&pdr->locator_hdl, SERVREG_LOCATOR_SERVICE, 1, 1);
69562306a36Sopenharmony_ci	if (ret < 0)
69662306a36Sopenharmony_ci		goto release_qmi_handle;
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	ret = qmi_handle_init(&pdr->notifier_hdl,
69962306a36Sopenharmony_ci			      SERVREG_STATE_UPDATED_IND_MAX_LEN,
70062306a36Sopenharmony_ci			      &pdr_notifier_ops,
70162306a36Sopenharmony_ci			      qmi_indication_handler);
70262306a36Sopenharmony_ci	if (ret < 0)
70362306a36Sopenharmony_ci		goto release_qmi_handle;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci	return pdr;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_cirelease_qmi_handle:
70862306a36Sopenharmony_ci	qmi_handle_release(&pdr->locator_hdl);
70962306a36Sopenharmony_cidestroy_indack:
71062306a36Sopenharmony_ci	destroy_workqueue(pdr->indack_wq);
71162306a36Sopenharmony_cidestroy_notifier:
71262306a36Sopenharmony_ci	destroy_workqueue(pdr->notifier_wq);
71362306a36Sopenharmony_cifree_pdr_handle:
71462306a36Sopenharmony_ci	kfree(pdr);
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	return ERR_PTR(ret);
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_ciEXPORT_SYMBOL(pdr_handle_alloc);
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci/**
72162306a36Sopenharmony_ci * pdr_handle_release() - release the PDR client handle
72262306a36Sopenharmony_ci * @pdr:	PDR client handle
72362306a36Sopenharmony_ci *
72462306a36Sopenharmony_ci * Cleans up pending tracking requests and releases the underlying qmi handles.
72562306a36Sopenharmony_ci */
72662306a36Sopenharmony_civoid pdr_handle_release(struct pdr_handle *pdr)
72762306a36Sopenharmony_ci{
72862306a36Sopenharmony_ci	struct pdr_service *pds, *tmp;
72962306a36Sopenharmony_ci
73062306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(pdr))
73162306a36Sopenharmony_ci		return;
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_ci	mutex_lock(&pdr->list_lock);
73462306a36Sopenharmony_ci	list_for_each_entry_safe(pds, tmp, &pdr->lookups, node) {
73562306a36Sopenharmony_ci		list_del(&pds->node);
73662306a36Sopenharmony_ci		kfree(pds);
73762306a36Sopenharmony_ci	}
73862306a36Sopenharmony_ci	mutex_unlock(&pdr->list_lock);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ci	cancel_work_sync(&pdr->locator_work);
74162306a36Sopenharmony_ci	cancel_work_sync(&pdr->notifier_work);
74262306a36Sopenharmony_ci	cancel_work_sync(&pdr->indack_work);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	destroy_workqueue(pdr->notifier_wq);
74562306a36Sopenharmony_ci	destroy_workqueue(pdr->indack_wq);
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	qmi_handle_release(&pdr->locator_hdl);
74862306a36Sopenharmony_ci	qmi_handle_release(&pdr->notifier_hdl);
74962306a36Sopenharmony_ci
75062306a36Sopenharmony_ci	kfree(pdr);
75162306a36Sopenharmony_ci}
75262306a36Sopenharmony_ciEXPORT_SYMBOL(pdr_handle_release);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
75562306a36Sopenharmony_ciMODULE_DESCRIPTION("Qualcomm Protection Domain Restart helpers");
756