xref: /OK3568_Linux_fs/kernel/drivers/soc/qcom/pdr_interface.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Copyright (C) 2020 The Linux Foundation. All rights reserved.
4*4882a593Smuzhiyun  */
5*4882a593Smuzhiyun 
6*4882a593Smuzhiyun #include <linux/kernel.h>
7*4882a593Smuzhiyun #include <linux/module.h>
8*4882a593Smuzhiyun #include <linux/slab.h>
9*4882a593Smuzhiyun #include <linux/string.h>
10*4882a593Smuzhiyun #include <linux/workqueue.h>
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include "pdr_internal.h"
13*4882a593Smuzhiyun 
14*4882a593Smuzhiyun struct pdr_service {
15*4882a593Smuzhiyun 	char service_name[SERVREG_NAME_LENGTH + 1];
16*4882a593Smuzhiyun 	char service_path[SERVREG_NAME_LENGTH + 1];
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun 	struct sockaddr_qrtr addr;
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun 	unsigned int instance;
21*4882a593Smuzhiyun 	unsigned int service;
22*4882a593Smuzhiyun 	u8 service_data_valid;
23*4882a593Smuzhiyun 	u32 service_data;
24*4882a593Smuzhiyun 	int state;
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun 	bool need_notifier_register;
27*4882a593Smuzhiyun 	bool need_notifier_remove;
28*4882a593Smuzhiyun 	bool need_locator_lookup;
29*4882a593Smuzhiyun 	bool service_connected;
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun 	struct list_head node;
32*4882a593Smuzhiyun };
33*4882a593Smuzhiyun 
34*4882a593Smuzhiyun struct pdr_handle {
35*4882a593Smuzhiyun 	struct qmi_handle locator_hdl;
36*4882a593Smuzhiyun 	struct qmi_handle notifier_hdl;
37*4882a593Smuzhiyun 
38*4882a593Smuzhiyun 	struct sockaddr_qrtr locator_addr;
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun 	struct list_head lookups;
41*4882a593Smuzhiyun 	struct list_head indack_list;
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun 	/* control access to pdr lookup/indack lists */
44*4882a593Smuzhiyun 	struct mutex list_lock;
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun 	/* serialize pd status invocation */
47*4882a593Smuzhiyun 	struct mutex status_lock;
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun 	/* control access to the locator state */
50*4882a593Smuzhiyun 	struct mutex lock;
51*4882a593Smuzhiyun 
52*4882a593Smuzhiyun 	bool locator_init_complete;
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun 	struct work_struct locator_work;
55*4882a593Smuzhiyun 	struct work_struct notifier_work;
56*4882a593Smuzhiyun 	struct work_struct indack_work;
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun 	struct workqueue_struct *notifier_wq;
59*4882a593Smuzhiyun 	struct workqueue_struct *indack_wq;
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun 	void (*status)(int state, char *service_path, void *priv);
62*4882a593Smuzhiyun 	void *priv;
63*4882a593Smuzhiyun };
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun struct pdr_list_node {
66*4882a593Smuzhiyun 	enum servreg_service_state curr_state;
67*4882a593Smuzhiyun 	u16 transaction_id;
68*4882a593Smuzhiyun 	struct pdr_service *pds;
69*4882a593Smuzhiyun 	struct list_head node;
70*4882a593Smuzhiyun };
71*4882a593Smuzhiyun 
pdr_locator_new_server(struct qmi_handle * qmi,struct qmi_service * svc)72*4882a593Smuzhiyun static int pdr_locator_new_server(struct qmi_handle *qmi,
73*4882a593Smuzhiyun 				  struct qmi_service *svc)
74*4882a593Smuzhiyun {
75*4882a593Smuzhiyun 	struct pdr_handle *pdr = container_of(qmi, struct pdr_handle,
76*4882a593Smuzhiyun 					      locator_hdl);
77*4882a593Smuzhiyun 	struct pdr_service *pds;
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun 	/* Create a local client port for QMI communication */
80*4882a593Smuzhiyun 	pdr->locator_addr.sq_family = AF_QIPCRTR;
81*4882a593Smuzhiyun 	pdr->locator_addr.sq_node = svc->node;
82*4882a593Smuzhiyun 	pdr->locator_addr.sq_port = svc->port;
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	mutex_lock(&pdr->lock);
85*4882a593Smuzhiyun 	pdr->locator_init_complete = true;
86*4882a593Smuzhiyun 	mutex_unlock(&pdr->lock);
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	/* Service pending lookup requests */
89*4882a593Smuzhiyun 	mutex_lock(&pdr->list_lock);
90*4882a593Smuzhiyun 	list_for_each_entry(pds, &pdr->lookups, node) {
91*4882a593Smuzhiyun 		if (pds->need_locator_lookup)
92*4882a593Smuzhiyun 			schedule_work(&pdr->locator_work);
93*4882a593Smuzhiyun 	}
94*4882a593Smuzhiyun 	mutex_unlock(&pdr->list_lock);
95*4882a593Smuzhiyun 
96*4882a593Smuzhiyun 	return 0;
97*4882a593Smuzhiyun }
98*4882a593Smuzhiyun 
pdr_locator_del_server(struct qmi_handle * qmi,struct qmi_service * svc)99*4882a593Smuzhiyun static void pdr_locator_del_server(struct qmi_handle *qmi,
100*4882a593Smuzhiyun 				   struct qmi_service *svc)
101*4882a593Smuzhiyun {
102*4882a593Smuzhiyun 	struct pdr_handle *pdr = container_of(qmi, struct pdr_handle,
103*4882a593Smuzhiyun 					      locator_hdl);
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun 	mutex_lock(&pdr->lock);
106*4882a593Smuzhiyun 	pdr->locator_init_complete = false;
107*4882a593Smuzhiyun 	mutex_unlock(&pdr->lock);
108*4882a593Smuzhiyun 
109*4882a593Smuzhiyun 	pdr->locator_addr.sq_node = 0;
110*4882a593Smuzhiyun 	pdr->locator_addr.sq_port = 0;
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun static struct qmi_ops pdr_locator_ops = {
114*4882a593Smuzhiyun 	.new_server = pdr_locator_new_server,
115*4882a593Smuzhiyun 	.del_server = pdr_locator_del_server,
116*4882a593Smuzhiyun };
117*4882a593Smuzhiyun 
pdr_register_listener(struct pdr_handle * pdr,struct pdr_service * pds,bool enable)118*4882a593Smuzhiyun static int pdr_register_listener(struct pdr_handle *pdr,
119*4882a593Smuzhiyun 				 struct pdr_service *pds,
120*4882a593Smuzhiyun 				 bool enable)
121*4882a593Smuzhiyun {
122*4882a593Smuzhiyun 	struct servreg_register_listener_resp resp;
123*4882a593Smuzhiyun 	struct servreg_register_listener_req req;
124*4882a593Smuzhiyun 	struct qmi_txn txn;
125*4882a593Smuzhiyun 	int ret;
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun 	ret = qmi_txn_init(&pdr->notifier_hdl, &txn,
128*4882a593Smuzhiyun 			   servreg_register_listener_resp_ei,
129*4882a593Smuzhiyun 			   &resp);
130*4882a593Smuzhiyun 	if (ret < 0)
131*4882a593Smuzhiyun 		return ret;
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 	req.enable = enable;
134*4882a593Smuzhiyun 	strcpy(req.service_path, pds->service_path);
135*4882a593Smuzhiyun 
136*4882a593Smuzhiyun 	ret = qmi_send_request(&pdr->notifier_hdl, &pds->addr,
137*4882a593Smuzhiyun 			       &txn, SERVREG_REGISTER_LISTENER_REQ,
138*4882a593Smuzhiyun 			       SERVREG_REGISTER_LISTENER_REQ_LEN,
139*4882a593Smuzhiyun 			       servreg_register_listener_req_ei,
140*4882a593Smuzhiyun 			       &req);
141*4882a593Smuzhiyun 	if (ret < 0) {
142*4882a593Smuzhiyun 		qmi_txn_cancel(&txn);
143*4882a593Smuzhiyun 		return ret;
144*4882a593Smuzhiyun 	}
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun 	ret = qmi_txn_wait(&txn, 5 * HZ);
147*4882a593Smuzhiyun 	if (ret < 0) {
148*4882a593Smuzhiyun 		pr_err("PDR: %s register listener txn wait failed: %d\n",
149*4882a593Smuzhiyun 		       pds->service_path, ret);
150*4882a593Smuzhiyun 		return ret;
151*4882a593Smuzhiyun 	}
152*4882a593Smuzhiyun 
153*4882a593Smuzhiyun 	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
154*4882a593Smuzhiyun 		pr_err("PDR: %s register listener failed: 0x%x\n",
155*4882a593Smuzhiyun 		       pds->service_path, resp.resp.error);
156*4882a593Smuzhiyun 		return -EREMOTEIO;
157*4882a593Smuzhiyun 	}
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	pds->state = resp.curr_state;
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun 	return 0;
162*4882a593Smuzhiyun }
163*4882a593Smuzhiyun 
pdr_notifier_work(struct work_struct * work)164*4882a593Smuzhiyun static void pdr_notifier_work(struct work_struct *work)
165*4882a593Smuzhiyun {
166*4882a593Smuzhiyun 	struct pdr_handle *pdr = container_of(work, struct pdr_handle,
167*4882a593Smuzhiyun 					      notifier_work);
168*4882a593Smuzhiyun 	struct pdr_service *pds;
169*4882a593Smuzhiyun 	int ret;
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	mutex_lock(&pdr->list_lock);
172*4882a593Smuzhiyun 	list_for_each_entry(pds, &pdr->lookups, node) {
173*4882a593Smuzhiyun 		if (pds->service_connected) {
174*4882a593Smuzhiyun 			if (!pds->need_notifier_register)
175*4882a593Smuzhiyun 				continue;
176*4882a593Smuzhiyun 
177*4882a593Smuzhiyun 			pds->need_notifier_register = false;
178*4882a593Smuzhiyun 			ret = pdr_register_listener(pdr, pds, true);
179*4882a593Smuzhiyun 			if (ret < 0)
180*4882a593Smuzhiyun 				pds->state = SERVREG_SERVICE_STATE_DOWN;
181*4882a593Smuzhiyun 		} else {
182*4882a593Smuzhiyun 			if (!pds->need_notifier_remove)
183*4882a593Smuzhiyun 				continue;
184*4882a593Smuzhiyun 
185*4882a593Smuzhiyun 			pds->need_notifier_remove = false;
186*4882a593Smuzhiyun 			pds->state = SERVREG_SERVICE_STATE_DOWN;
187*4882a593Smuzhiyun 		}
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 		mutex_lock(&pdr->status_lock);
190*4882a593Smuzhiyun 		pdr->status(pds->state, pds->service_path, pdr->priv);
191*4882a593Smuzhiyun 		mutex_unlock(&pdr->status_lock);
192*4882a593Smuzhiyun 	}
193*4882a593Smuzhiyun 	mutex_unlock(&pdr->list_lock);
194*4882a593Smuzhiyun }
195*4882a593Smuzhiyun 
pdr_notifier_new_server(struct qmi_handle * qmi,struct qmi_service * svc)196*4882a593Smuzhiyun static int pdr_notifier_new_server(struct qmi_handle *qmi,
197*4882a593Smuzhiyun 				   struct qmi_service *svc)
198*4882a593Smuzhiyun {
199*4882a593Smuzhiyun 	struct pdr_handle *pdr = container_of(qmi, struct pdr_handle,
200*4882a593Smuzhiyun 					      notifier_hdl);
201*4882a593Smuzhiyun 	struct pdr_service *pds;
202*4882a593Smuzhiyun 
203*4882a593Smuzhiyun 	mutex_lock(&pdr->list_lock);
204*4882a593Smuzhiyun 	list_for_each_entry(pds, &pdr->lookups, node) {
205*4882a593Smuzhiyun 		if (pds->service == svc->service &&
206*4882a593Smuzhiyun 		    pds->instance == svc->instance) {
207*4882a593Smuzhiyun 			pds->service_connected = true;
208*4882a593Smuzhiyun 			pds->need_notifier_register = true;
209*4882a593Smuzhiyun 			pds->addr.sq_family = AF_QIPCRTR;
210*4882a593Smuzhiyun 			pds->addr.sq_node = svc->node;
211*4882a593Smuzhiyun 			pds->addr.sq_port = svc->port;
212*4882a593Smuzhiyun 			queue_work(pdr->notifier_wq, &pdr->notifier_work);
213*4882a593Smuzhiyun 		}
214*4882a593Smuzhiyun 	}
215*4882a593Smuzhiyun 	mutex_unlock(&pdr->list_lock);
216*4882a593Smuzhiyun 
217*4882a593Smuzhiyun 	return 0;
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun 
pdr_notifier_del_server(struct qmi_handle * qmi,struct qmi_service * svc)220*4882a593Smuzhiyun static void pdr_notifier_del_server(struct qmi_handle *qmi,
221*4882a593Smuzhiyun 				    struct qmi_service *svc)
222*4882a593Smuzhiyun {
223*4882a593Smuzhiyun 	struct pdr_handle *pdr = container_of(qmi, struct pdr_handle,
224*4882a593Smuzhiyun 					      notifier_hdl);
225*4882a593Smuzhiyun 	struct pdr_service *pds;
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun 	mutex_lock(&pdr->list_lock);
228*4882a593Smuzhiyun 	list_for_each_entry(pds, &pdr->lookups, node) {
229*4882a593Smuzhiyun 		if (pds->service == svc->service &&
230*4882a593Smuzhiyun 		    pds->instance == svc->instance) {
231*4882a593Smuzhiyun 			pds->service_connected = false;
232*4882a593Smuzhiyun 			pds->need_notifier_remove = true;
233*4882a593Smuzhiyun 			pds->addr.sq_node = 0;
234*4882a593Smuzhiyun 			pds->addr.sq_port = 0;
235*4882a593Smuzhiyun 			queue_work(pdr->notifier_wq, &pdr->notifier_work);
236*4882a593Smuzhiyun 		}
237*4882a593Smuzhiyun 	}
238*4882a593Smuzhiyun 	mutex_unlock(&pdr->list_lock);
239*4882a593Smuzhiyun }
240*4882a593Smuzhiyun 
241*4882a593Smuzhiyun static struct qmi_ops pdr_notifier_ops = {
242*4882a593Smuzhiyun 	.new_server = pdr_notifier_new_server,
243*4882a593Smuzhiyun 	.del_server = pdr_notifier_del_server,
244*4882a593Smuzhiyun };
245*4882a593Smuzhiyun 
pdr_send_indack_msg(struct pdr_handle * pdr,struct pdr_service * pds,u16 tid)246*4882a593Smuzhiyun static int pdr_send_indack_msg(struct pdr_handle *pdr, struct pdr_service *pds,
247*4882a593Smuzhiyun 			       u16 tid)
248*4882a593Smuzhiyun {
249*4882a593Smuzhiyun 	struct servreg_set_ack_resp resp;
250*4882a593Smuzhiyun 	struct servreg_set_ack_req req;
251*4882a593Smuzhiyun 	struct qmi_txn txn;
252*4882a593Smuzhiyun 	int ret;
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun 	ret = qmi_txn_init(&pdr->notifier_hdl, &txn, servreg_set_ack_resp_ei,
255*4882a593Smuzhiyun 			   &resp);
256*4882a593Smuzhiyun 	if (ret < 0)
257*4882a593Smuzhiyun 		return ret;
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun 	req.transaction_id = tid;
260*4882a593Smuzhiyun 	strcpy(req.service_path, pds->service_path);
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 	ret = qmi_send_request(&pdr->notifier_hdl, &pds->addr,
263*4882a593Smuzhiyun 			       &txn, SERVREG_SET_ACK_REQ,
264*4882a593Smuzhiyun 			       SERVREG_SET_ACK_REQ_LEN,
265*4882a593Smuzhiyun 			       servreg_set_ack_req_ei,
266*4882a593Smuzhiyun 			       &req);
267*4882a593Smuzhiyun 
268*4882a593Smuzhiyun 	/* Skip waiting for response */
269*4882a593Smuzhiyun 	qmi_txn_cancel(&txn);
270*4882a593Smuzhiyun 	return ret;
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun 
pdr_indack_work(struct work_struct * work)273*4882a593Smuzhiyun static void pdr_indack_work(struct work_struct *work)
274*4882a593Smuzhiyun {
275*4882a593Smuzhiyun 	struct pdr_handle *pdr = container_of(work, struct pdr_handle,
276*4882a593Smuzhiyun 					      indack_work);
277*4882a593Smuzhiyun 	struct pdr_list_node *ind, *tmp;
278*4882a593Smuzhiyun 	struct pdr_service *pds;
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun 	list_for_each_entry_safe(ind, tmp, &pdr->indack_list, node) {
281*4882a593Smuzhiyun 		pds = ind->pds;
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 		mutex_lock(&pdr->status_lock);
284*4882a593Smuzhiyun 		pds->state = ind->curr_state;
285*4882a593Smuzhiyun 		pdr->status(pds->state, pds->service_path, pdr->priv);
286*4882a593Smuzhiyun 		mutex_unlock(&pdr->status_lock);
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun 		/* Ack the indication after clients release the PD resources */
289*4882a593Smuzhiyun 		pdr_send_indack_msg(pdr, pds, ind->transaction_id);
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 		mutex_lock(&pdr->list_lock);
292*4882a593Smuzhiyun 		list_del(&ind->node);
293*4882a593Smuzhiyun 		mutex_unlock(&pdr->list_lock);
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun 		kfree(ind);
296*4882a593Smuzhiyun 	}
297*4882a593Smuzhiyun }
298*4882a593Smuzhiyun 
pdr_indication_cb(struct qmi_handle * qmi,struct sockaddr_qrtr * sq,struct qmi_txn * txn,const void * data)299*4882a593Smuzhiyun static void pdr_indication_cb(struct qmi_handle *qmi,
300*4882a593Smuzhiyun 			      struct sockaddr_qrtr *sq,
301*4882a593Smuzhiyun 			      struct qmi_txn *txn, const void *data)
302*4882a593Smuzhiyun {
303*4882a593Smuzhiyun 	struct pdr_handle *pdr = container_of(qmi, struct pdr_handle,
304*4882a593Smuzhiyun 					      notifier_hdl);
305*4882a593Smuzhiyun 	const struct servreg_state_updated_ind *ind_msg = data;
306*4882a593Smuzhiyun 	struct pdr_list_node *ind;
307*4882a593Smuzhiyun 	struct pdr_service *pds;
308*4882a593Smuzhiyun 	bool found = false;
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 	if (!ind_msg || !ind_msg->service_path[0] ||
311*4882a593Smuzhiyun 	    strlen(ind_msg->service_path) > SERVREG_NAME_LENGTH)
312*4882a593Smuzhiyun 		return;
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun 	mutex_lock(&pdr->list_lock);
315*4882a593Smuzhiyun 	list_for_each_entry(pds, &pdr->lookups, node) {
316*4882a593Smuzhiyun 		if (strcmp(pds->service_path, ind_msg->service_path))
317*4882a593Smuzhiyun 			continue;
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun 		found = true;
320*4882a593Smuzhiyun 		break;
321*4882a593Smuzhiyun 	}
322*4882a593Smuzhiyun 	mutex_unlock(&pdr->list_lock);
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun 	if (!found)
325*4882a593Smuzhiyun 		return;
326*4882a593Smuzhiyun 
327*4882a593Smuzhiyun 	pr_info("PDR: Indication received from %s, state: 0x%x, trans-id: %d\n",
328*4882a593Smuzhiyun 		ind_msg->service_path, ind_msg->curr_state,
329*4882a593Smuzhiyun 		ind_msg->transaction_id);
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun 	ind = kzalloc(sizeof(*ind), GFP_KERNEL);
332*4882a593Smuzhiyun 	if (!ind)
333*4882a593Smuzhiyun 		return;
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 	ind->transaction_id = ind_msg->transaction_id;
336*4882a593Smuzhiyun 	ind->curr_state = ind_msg->curr_state;
337*4882a593Smuzhiyun 	ind->pds = pds;
338*4882a593Smuzhiyun 
339*4882a593Smuzhiyun 	mutex_lock(&pdr->list_lock);
340*4882a593Smuzhiyun 	list_add_tail(&ind->node, &pdr->indack_list);
341*4882a593Smuzhiyun 	mutex_unlock(&pdr->list_lock);
342*4882a593Smuzhiyun 
343*4882a593Smuzhiyun 	queue_work(pdr->indack_wq, &pdr->indack_work);
344*4882a593Smuzhiyun }
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun static struct qmi_msg_handler qmi_indication_handler[] = {
347*4882a593Smuzhiyun 	{
348*4882a593Smuzhiyun 		.type = QMI_INDICATION,
349*4882a593Smuzhiyun 		.msg_id = SERVREG_STATE_UPDATED_IND_ID,
350*4882a593Smuzhiyun 		.ei = servreg_state_updated_ind_ei,
351*4882a593Smuzhiyun 		.decoded_size = sizeof(struct servreg_state_updated_ind),
352*4882a593Smuzhiyun 		.fn = pdr_indication_cb,
353*4882a593Smuzhiyun 	},
354*4882a593Smuzhiyun 	{}
355*4882a593Smuzhiyun };
356*4882a593Smuzhiyun 
pdr_get_domain_list(struct servreg_get_domain_list_req * req,struct servreg_get_domain_list_resp * resp,struct pdr_handle * pdr)357*4882a593Smuzhiyun static int pdr_get_domain_list(struct servreg_get_domain_list_req *req,
358*4882a593Smuzhiyun 			       struct servreg_get_domain_list_resp *resp,
359*4882a593Smuzhiyun 			       struct pdr_handle *pdr)
360*4882a593Smuzhiyun {
361*4882a593Smuzhiyun 	struct qmi_txn txn;
362*4882a593Smuzhiyun 	int ret;
363*4882a593Smuzhiyun 
364*4882a593Smuzhiyun 	ret = qmi_txn_init(&pdr->locator_hdl, &txn,
365*4882a593Smuzhiyun 			   servreg_get_domain_list_resp_ei, resp);
366*4882a593Smuzhiyun 	if (ret < 0)
367*4882a593Smuzhiyun 		return ret;
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 	ret = qmi_send_request(&pdr->locator_hdl,
370*4882a593Smuzhiyun 			       &pdr->locator_addr,
371*4882a593Smuzhiyun 			       &txn, SERVREG_GET_DOMAIN_LIST_REQ,
372*4882a593Smuzhiyun 			       SERVREG_GET_DOMAIN_LIST_REQ_MAX_LEN,
373*4882a593Smuzhiyun 			       servreg_get_domain_list_req_ei,
374*4882a593Smuzhiyun 			       req);
375*4882a593Smuzhiyun 	if (ret < 0) {
376*4882a593Smuzhiyun 		qmi_txn_cancel(&txn);
377*4882a593Smuzhiyun 		return ret;
378*4882a593Smuzhiyun 	}
379*4882a593Smuzhiyun 
380*4882a593Smuzhiyun 	ret = qmi_txn_wait(&txn, 5 * HZ);
381*4882a593Smuzhiyun 	if (ret < 0) {
382*4882a593Smuzhiyun 		pr_err("PDR: %s get domain list txn wait failed: %d\n",
383*4882a593Smuzhiyun 		       req->service_name, ret);
384*4882a593Smuzhiyun 		return ret;
385*4882a593Smuzhiyun 	}
386*4882a593Smuzhiyun 
387*4882a593Smuzhiyun 	if (resp->resp.result != QMI_RESULT_SUCCESS_V01) {
388*4882a593Smuzhiyun 		pr_err("PDR: %s get domain list failed: 0x%x\n",
389*4882a593Smuzhiyun 		       req->service_name, resp->resp.error);
390*4882a593Smuzhiyun 		return -EREMOTEIO;
391*4882a593Smuzhiyun 	}
392*4882a593Smuzhiyun 
393*4882a593Smuzhiyun 	return 0;
394*4882a593Smuzhiyun }
395*4882a593Smuzhiyun 
pdr_locate_service(struct pdr_handle * pdr,struct pdr_service * pds)396*4882a593Smuzhiyun static int pdr_locate_service(struct pdr_handle *pdr, struct pdr_service *pds)
397*4882a593Smuzhiyun {
398*4882a593Smuzhiyun 	struct servreg_get_domain_list_resp *resp;
399*4882a593Smuzhiyun 	struct servreg_get_domain_list_req req;
400*4882a593Smuzhiyun 	struct servreg_location_entry *entry;
401*4882a593Smuzhiyun 	int domains_read = 0;
402*4882a593Smuzhiyun 	int ret, i;
403*4882a593Smuzhiyun 
404*4882a593Smuzhiyun 	resp = kzalloc(sizeof(*resp), GFP_KERNEL);
405*4882a593Smuzhiyun 	if (!resp)
406*4882a593Smuzhiyun 		return -ENOMEM;
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun 	/* Prepare req message */
409*4882a593Smuzhiyun 	strcpy(req.service_name, pds->service_name);
410*4882a593Smuzhiyun 	req.domain_offset_valid = true;
411*4882a593Smuzhiyun 	req.domain_offset = 0;
412*4882a593Smuzhiyun 
413*4882a593Smuzhiyun 	do {
414*4882a593Smuzhiyun 		req.domain_offset = domains_read;
415*4882a593Smuzhiyun 		ret = pdr_get_domain_list(&req, resp, pdr);
416*4882a593Smuzhiyun 		if (ret < 0)
417*4882a593Smuzhiyun 			goto out;
418*4882a593Smuzhiyun 
419*4882a593Smuzhiyun 		for (i = domains_read; i < resp->domain_list_len; i++) {
420*4882a593Smuzhiyun 			entry = &resp->domain_list[i];
421*4882a593Smuzhiyun 
422*4882a593Smuzhiyun 			if (strnlen(entry->name, sizeof(entry->name)) == sizeof(entry->name))
423*4882a593Smuzhiyun 				continue;
424*4882a593Smuzhiyun 
425*4882a593Smuzhiyun 			if (!strcmp(entry->name, pds->service_path)) {
426*4882a593Smuzhiyun 				pds->service_data_valid = entry->service_data_valid;
427*4882a593Smuzhiyun 				pds->service_data = entry->service_data;
428*4882a593Smuzhiyun 				pds->instance = entry->instance;
429*4882a593Smuzhiyun 				goto out;
430*4882a593Smuzhiyun 			}
431*4882a593Smuzhiyun 		}
432*4882a593Smuzhiyun 
433*4882a593Smuzhiyun 		/* Update ret to indicate that the service is not yet found */
434*4882a593Smuzhiyun 		ret = -ENXIO;
435*4882a593Smuzhiyun 
436*4882a593Smuzhiyun 		/* Always read total_domains from the response msg */
437*4882a593Smuzhiyun 		if (resp->domain_list_len > resp->total_domains)
438*4882a593Smuzhiyun 			resp->domain_list_len = resp->total_domains;
439*4882a593Smuzhiyun 
440*4882a593Smuzhiyun 		domains_read += resp->domain_list_len;
441*4882a593Smuzhiyun 	} while (domains_read < resp->total_domains);
442*4882a593Smuzhiyun out:
443*4882a593Smuzhiyun 	kfree(resp);
444*4882a593Smuzhiyun 	return ret;
445*4882a593Smuzhiyun }
446*4882a593Smuzhiyun 
pdr_notify_lookup_failure(struct pdr_handle * pdr,struct pdr_service * pds,int err)447*4882a593Smuzhiyun static void pdr_notify_lookup_failure(struct pdr_handle *pdr,
448*4882a593Smuzhiyun 				      struct pdr_service *pds,
449*4882a593Smuzhiyun 				      int err)
450*4882a593Smuzhiyun {
451*4882a593Smuzhiyun 	pr_err("PDR: service lookup for %s failed: %d\n",
452*4882a593Smuzhiyun 	       pds->service_name, err);
453*4882a593Smuzhiyun 
454*4882a593Smuzhiyun 	if (err == -ENXIO)
455*4882a593Smuzhiyun 		return;
456*4882a593Smuzhiyun 
457*4882a593Smuzhiyun 	list_del(&pds->node);
458*4882a593Smuzhiyun 	pds->state = SERVREG_LOCATOR_ERR;
459*4882a593Smuzhiyun 	mutex_lock(&pdr->status_lock);
460*4882a593Smuzhiyun 	pdr->status(pds->state, pds->service_path, pdr->priv);
461*4882a593Smuzhiyun 	mutex_unlock(&pdr->status_lock);
462*4882a593Smuzhiyun 	kfree(pds);
463*4882a593Smuzhiyun }
464*4882a593Smuzhiyun 
pdr_locator_work(struct work_struct * work)465*4882a593Smuzhiyun static void pdr_locator_work(struct work_struct *work)
466*4882a593Smuzhiyun {
467*4882a593Smuzhiyun 	struct pdr_handle *pdr = container_of(work, struct pdr_handle,
468*4882a593Smuzhiyun 					      locator_work);
469*4882a593Smuzhiyun 	struct pdr_service *pds, *tmp;
470*4882a593Smuzhiyun 	int ret = 0;
471*4882a593Smuzhiyun 
472*4882a593Smuzhiyun 	/* Bail out early if the SERVREG LOCATOR QMI service is not up */
473*4882a593Smuzhiyun 	mutex_lock(&pdr->lock);
474*4882a593Smuzhiyun 	if (!pdr->locator_init_complete) {
475*4882a593Smuzhiyun 		mutex_unlock(&pdr->lock);
476*4882a593Smuzhiyun 		pr_debug("PDR: SERVICE LOCATOR service not available\n");
477*4882a593Smuzhiyun 		return;
478*4882a593Smuzhiyun 	}
479*4882a593Smuzhiyun 	mutex_unlock(&pdr->lock);
480*4882a593Smuzhiyun 
481*4882a593Smuzhiyun 	mutex_lock(&pdr->list_lock);
482*4882a593Smuzhiyun 	list_for_each_entry_safe(pds, tmp, &pdr->lookups, node) {
483*4882a593Smuzhiyun 		if (!pds->need_locator_lookup)
484*4882a593Smuzhiyun 			continue;
485*4882a593Smuzhiyun 
486*4882a593Smuzhiyun 		ret = pdr_locate_service(pdr, pds);
487*4882a593Smuzhiyun 		if (ret < 0) {
488*4882a593Smuzhiyun 			pdr_notify_lookup_failure(pdr, pds, ret);
489*4882a593Smuzhiyun 			continue;
490*4882a593Smuzhiyun 		}
491*4882a593Smuzhiyun 
492*4882a593Smuzhiyun 		ret = qmi_add_lookup(&pdr->notifier_hdl, pds->service, 1,
493*4882a593Smuzhiyun 				     pds->instance);
494*4882a593Smuzhiyun 		if (ret < 0) {
495*4882a593Smuzhiyun 			pdr_notify_lookup_failure(pdr, pds, ret);
496*4882a593Smuzhiyun 			continue;
497*4882a593Smuzhiyun 		}
498*4882a593Smuzhiyun 
499*4882a593Smuzhiyun 		pds->need_locator_lookup = false;
500*4882a593Smuzhiyun 	}
501*4882a593Smuzhiyun 	mutex_unlock(&pdr->list_lock);
502*4882a593Smuzhiyun }
503*4882a593Smuzhiyun 
504*4882a593Smuzhiyun /**
505*4882a593Smuzhiyun  * pdr_add_lookup() - register a tracking request for a PD
506*4882a593Smuzhiyun  * @pdr:		PDR client handle
507*4882a593Smuzhiyun  * @service_name:	service name of the tracking request
508*4882a593Smuzhiyun  * @service_path:	service path of the tracking request
509*4882a593Smuzhiyun  *
510*4882a593Smuzhiyun  * Registering a pdr lookup allows for tracking the life cycle of the PD.
511*4882a593Smuzhiyun  *
512*4882a593Smuzhiyun  * Return: pdr_service object on success, ERR_PTR on failure. -EALREADY is
513*4882a593Smuzhiyun  * returned if a lookup is already in progress for the given service path.
514*4882a593Smuzhiyun  */
pdr_add_lookup(struct pdr_handle * pdr,const char * service_name,const char * service_path)515*4882a593Smuzhiyun struct pdr_service *pdr_add_lookup(struct pdr_handle *pdr,
516*4882a593Smuzhiyun 				   const char *service_name,
517*4882a593Smuzhiyun 				   const char *service_path)
518*4882a593Smuzhiyun {
519*4882a593Smuzhiyun 	struct pdr_service *pds, *tmp;
520*4882a593Smuzhiyun 	int ret;
521*4882a593Smuzhiyun 
522*4882a593Smuzhiyun 	if (IS_ERR_OR_NULL(pdr))
523*4882a593Smuzhiyun 		return ERR_PTR(-EINVAL);
524*4882a593Smuzhiyun 
525*4882a593Smuzhiyun 	if (!service_name || strlen(service_name) > SERVREG_NAME_LENGTH ||
526*4882a593Smuzhiyun 	    !service_path || strlen(service_path) > SERVREG_NAME_LENGTH)
527*4882a593Smuzhiyun 		return ERR_PTR(-EINVAL);
528*4882a593Smuzhiyun 
529*4882a593Smuzhiyun 	pds = kzalloc(sizeof(*pds), GFP_KERNEL);
530*4882a593Smuzhiyun 	if (!pds)
531*4882a593Smuzhiyun 		return ERR_PTR(-ENOMEM);
532*4882a593Smuzhiyun 
533*4882a593Smuzhiyun 	pds->service = SERVREG_NOTIFIER_SERVICE;
534*4882a593Smuzhiyun 	strcpy(pds->service_name, service_name);
535*4882a593Smuzhiyun 	strcpy(pds->service_path, service_path);
536*4882a593Smuzhiyun 	pds->need_locator_lookup = true;
537*4882a593Smuzhiyun 
538*4882a593Smuzhiyun 	mutex_lock(&pdr->list_lock);
539*4882a593Smuzhiyun 	list_for_each_entry(tmp, &pdr->lookups, node) {
540*4882a593Smuzhiyun 		if (strcmp(tmp->service_path, service_path))
541*4882a593Smuzhiyun 			continue;
542*4882a593Smuzhiyun 
543*4882a593Smuzhiyun 		mutex_unlock(&pdr->list_lock);
544*4882a593Smuzhiyun 		ret = -EALREADY;
545*4882a593Smuzhiyun 		goto err;
546*4882a593Smuzhiyun 	}
547*4882a593Smuzhiyun 
548*4882a593Smuzhiyun 	list_add(&pds->node, &pdr->lookups);
549*4882a593Smuzhiyun 	mutex_unlock(&pdr->list_lock);
550*4882a593Smuzhiyun 
551*4882a593Smuzhiyun 	schedule_work(&pdr->locator_work);
552*4882a593Smuzhiyun 
553*4882a593Smuzhiyun 	return pds;
554*4882a593Smuzhiyun err:
555*4882a593Smuzhiyun 	kfree(pds);
556*4882a593Smuzhiyun 	return ERR_PTR(ret);
557*4882a593Smuzhiyun }
558*4882a593Smuzhiyun EXPORT_SYMBOL(pdr_add_lookup);
559*4882a593Smuzhiyun 
560*4882a593Smuzhiyun /**
561*4882a593Smuzhiyun  * pdr_restart_pd() - restart PD
562*4882a593Smuzhiyun  * @pdr:	PDR client handle
563*4882a593Smuzhiyun  * @pds:	PD service handle
564*4882a593Smuzhiyun  *
565*4882a593Smuzhiyun  * Restarts the PD tracked by the PDR client handle for a given service path.
566*4882a593Smuzhiyun  *
567*4882a593Smuzhiyun  * Return: 0 on success, negative errno on failure.
568*4882a593Smuzhiyun  */
pdr_restart_pd(struct pdr_handle * pdr,struct pdr_service * pds)569*4882a593Smuzhiyun int pdr_restart_pd(struct pdr_handle *pdr, struct pdr_service *pds)
570*4882a593Smuzhiyun {
571*4882a593Smuzhiyun 	struct servreg_restart_pd_resp resp;
572*4882a593Smuzhiyun 	struct servreg_restart_pd_req req = { 0 };
573*4882a593Smuzhiyun 	struct sockaddr_qrtr addr;
574*4882a593Smuzhiyun 	struct pdr_service *tmp;
575*4882a593Smuzhiyun 	struct qmi_txn txn;
576*4882a593Smuzhiyun 	int ret;
577*4882a593Smuzhiyun 
578*4882a593Smuzhiyun 	if (IS_ERR_OR_NULL(pdr) || IS_ERR_OR_NULL(pds))
579*4882a593Smuzhiyun 		return -EINVAL;
580*4882a593Smuzhiyun 
581*4882a593Smuzhiyun 	mutex_lock(&pdr->list_lock);
582*4882a593Smuzhiyun 	list_for_each_entry(tmp, &pdr->lookups, node) {
583*4882a593Smuzhiyun 		if (tmp != pds)
584*4882a593Smuzhiyun 			continue;
585*4882a593Smuzhiyun 
586*4882a593Smuzhiyun 		if (!pds->service_connected)
587*4882a593Smuzhiyun 			break;
588*4882a593Smuzhiyun 
589*4882a593Smuzhiyun 		/* Prepare req message */
590*4882a593Smuzhiyun 		strcpy(req.service_path, pds->service_path);
591*4882a593Smuzhiyun 		addr = pds->addr;
592*4882a593Smuzhiyun 		break;
593*4882a593Smuzhiyun 	}
594*4882a593Smuzhiyun 	mutex_unlock(&pdr->list_lock);
595*4882a593Smuzhiyun 
596*4882a593Smuzhiyun 	if (!req.service_path[0])
597*4882a593Smuzhiyun 		return -EINVAL;
598*4882a593Smuzhiyun 
599*4882a593Smuzhiyun 	ret = qmi_txn_init(&pdr->notifier_hdl, &txn,
600*4882a593Smuzhiyun 			   servreg_restart_pd_resp_ei,
601*4882a593Smuzhiyun 			   &resp);
602*4882a593Smuzhiyun 	if (ret < 0)
603*4882a593Smuzhiyun 		return ret;
604*4882a593Smuzhiyun 
605*4882a593Smuzhiyun 	ret = qmi_send_request(&pdr->notifier_hdl, &addr,
606*4882a593Smuzhiyun 			       &txn, SERVREG_RESTART_PD_REQ,
607*4882a593Smuzhiyun 			       SERVREG_RESTART_PD_REQ_MAX_LEN,
608*4882a593Smuzhiyun 			       servreg_restart_pd_req_ei, &req);
609*4882a593Smuzhiyun 	if (ret < 0) {
610*4882a593Smuzhiyun 		qmi_txn_cancel(&txn);
611*4882a593Smuzhiyun 		return ret;
612*4882a593Smuzhiyun 	}
613*4882a593Smuzhiyun 
614*4882a593Smuzhiyun 	ret = qmi_txn_wait(&txn, 5 * HZ);
615*4882a593Smuzhiyun 	if (ret < 0) {
616*4882a593Smuzhiyun 		pr_err("PDR: %s PD restart txn wait failed: %d\n",
617*4882a593Smuzhiyun 		       req.service_path, ret);
618*4882a593Smuzhiyun 		return ret;
619*4882a593Smuzhiyun 	}
620*4882a593Smuzhiyun 
621*4882a593Smuzhiyun 	/* Check response if PDR is disabled */
622*4882a593Smuzhiyun 	if (resp.resp.result == QMI_RESULT_FAILURE_V01 &&
623*4882a593Smuzhiyun 	    resp.resp.error == QMI_ERR_DISABLED_V01) {
624*4882a593Smuzhiyun 		pr_err("PDR: %s PD restart is disabled: 0x%x\n",
625*4882a593Smuzhiyun 		       req.service_path, resp.resp.error);
626*4882a593Smuzhiyun 		return -EOPNOTSUPP;
627*4882a593Smuzhiyun 	}
628*4882a593Smuzhiyun 
629*4882a593Smuzhiyun 	/* Check the response for other error case*/
630*4882a593Smuzhiyun 	if (resp.resp.result != QMI_RESULT_SUCCESS_V01) {
631*4882a593Smuzhiyun 		pr_err("PDR: %s request for PD restart failed: 0x%x\n",
632*4882a593Smuzhiyun 		       req.service_path, resp.resp.error);
633*4882a593Smuzhiyun 		return -EREMOTEIO;
634*4882a593Smuzhiyun 	}
635*4882a593Smuzhiyun 
636*4882a593Smuzhiyun 	return 0;
637*4882a593Smuzhiyun }
638*4882a593Smuzhiyun EXPORT_SYMBOL(pdr_restart_pd);
639*4882a593Smuzhiyun 
640*4882a593Smuzhiyun /**
641*4882a593Smuzhiyun  * pdr_handle_alloc() - initialize the PDR client handle
642*4882a593Smuzhiyun  * @status:	function to be called on PD state change
643*4882a593Smuzhiyun  * @priv:	handle for client's use
644*4882a593Smuzhiyun  *
645*4882a593Smuzhiyun  * Initializes the PDR client handle to allow for tracking/restart of PDs.
646*4882a593Smuzhiyun  *
647*4882a593Smuzhiyun  * Return: pdr_handle object on success, ERR_PTR on failure.
648*4882a593Smuzhiyun  */
pdr_handle_alloc(void (* status)(int state,char * service_path,void * priv),void * priv)649*4882a593Smuzhiyun struct pdr_handle *pdr_handle_alloc(void (*status)(int state,
650*4882a593Smuzhiyun 						   char *service_path,
651*4882a593Smuzhiyun 						   void *priv), void *priv)
652*4882a593Smuzhiyun {
653*4882a593Smuzhiyun 	struct pdr_handle *pdr;
654*4882a593Smuzhiyun 	int ret;
655*4882a593Smuzhiyun 
656*4882a593Smuzhiyun 	if (!status)
657*4882a593Smuzhiyun 		return ERR_PTR(-EINVAL);
658*4882a593Smuzhiyun 
659*4882a593Smuzhiyun 	pdr = kzalloc(sizeof(*pdr), GFP_KERNEL);
660*4882a593Smuzhiyun 	if (!pdr)
661*4882a593Smuzhiyun 		return ERR_PTR(-ENOMEM);
662*4882a593Smuzhiyun 
663*4882a593Smuzhiyun 	pdr->status = status;
664*4882a593Smuzhiyun 	pdr->priv = priv;
665*4882a593Smuzhiyun 
666*4882a593Smuzhiyun 	mutex_init(&pdr->status_lock);
667*4882a593Smuzhiyun 	mutex_init(&pdr->list_lock);
668*4882a593Smuzhiyun 	mutex_init(&pdr->lock);
669*4882a593Smuzhiyun 
670*4882a593Smuzhiyun 	INIT_LIST_HEAD(&pdr->lookups);
671*4882a593Smuzhiyun 	INIT_LIST_HEAD(&pdr->indack_list);
672*4882a593Smuzhiyun 
673*4882a593Smuzhiyun 	INIT_WORK(&pdr->locator_work, pdr_locator_work);
674*4882a593Smuzhiyun 	INIT_WORK(&pdr->notifier_work, pdr_notifier_work);
675*4882a593Smuzhiyun 	INIT_WORK(&pdr->indack_work, pdr_indack_work);
676*4882a593Smuzhiyun 
677*4882a593Smuzhiyun 	pdr->notifier_wq = create_singlethread_workqueue("pdr_notifier_wq");
678*4882a593Smuzhiyun 	if (!pdr->notifier_wq) {
679*4882a593Smuzhiyun 		ret = -ENOMEM;
680*4882a593Smuzhiyun 		goto free_pdr_handle;
681*4882a593Smuzhiyun 	}
682*4882a593Smuzhiyun 
683*4882a593Smuzhiyun 	pdr->indack_wq = alloc_ordered_workqueue("pdr_indack_wq", WQ_HIGHPRI);
684*4882a593Smuzhiyun 	if (!pdr->indack_wq) {
685*4882a593Smuzhiyun 		ret = -ENOMEM;
686*4882a593Smuzhiyun 		goto destroy_notifier;
687*4882a593Smuzhiyun 	}
688*4882a593Smuzhiyun 
689*4882a593Smuzhiyun 	ret = qmi_handle_init(&pdr->locator_hdl,
690*4882a593Smuzhiyun 			      SERVREG_GET_DOMAIN_LIST_RESP_MAX_LEN,
691*4882a593Smuzhiyun 			      &pdr_locator_ops, NULL);
692*4882a593Smuzhiyun 	if (ret < 0)
693*4882a593Smuzhiyun 		goto destroy_indack;
694*4882a593Smuzhiyun 
695*4882a593Smuzhiyun 	ret = qmi_add_lookup(&pdr->locator_hdl, SERVREG_LOCATOR_SERVICE, 1, 1);
696*4882a593Smuzhiyun 	if (ret < 0)
697*4882a593Smuzhiyun 		goto release_qmi_handle;
698*4882a593Smuzhiyun 
699*4882a593Smuzhiyun 	ret = qmi_handle_init(&pdr->notifier_hdl,
700*4882a593Smuzhiyun 			      SERVREG_STATE_UPDATED_IND_MAX_LEN,
701*4882a593Smuzhiyun 			      &pdr_notifier_ops,
702*4882a593Smuzhiyun 			      qmi_indication_handler);
703*4882a593Smuzhiyun 	if (ret < 0)
704*4882a593Smuzhiyun 		goto release_qmi_handle;
705*4882a593Smuzhiyun 
706*4882a593Smuzhiyun 	return pdr;
707*4882a593Smuzhiyun 
708*4882a593Smuzhiyun release_qmi_handle:
709*4882a593Smuzhiyun 	qmi_handle_release(&pdr->locator_hdl);
710*4882a593Smuzhiyun destroy_indack:
711*4882a593Smuzhiyun 	destroy_workqueue(pdr->indack_wq);
712*4882a593Smuzhiyun destroy_notifier:
713*4882a593Smuzhiyun 	destroy_workqueue(pdr->notifier_wq);
714*4882a593Smuzhiyun free_pdr_handle:
715*4882a593Smuzhiyun 	kfree(pdr);
716*4882a593Smuzhiyun 
717*4882a593Smuzhiyun 	return ERR_PTR(ret);
718*4882a593Smuzhiyun }
719*4882a593Smuzhiyun EXPORT_SYMBOL(pdr_handle_alloc);
720*4882a593Smuzhiyun 
721*4882a593Smuzhiyun /**
722*4882a593Smuzhiyun  * pdr_handle_release() - release the PDR client handle
723*4882a593Smuzhiyun  * @pdr:	PDR client handle
724*4882a593Smuzhiyun  *
725*4882a593Smuzhiyun  * Cleans up pending tracking requests and releases the underlying qmi handles.
726*4882a593Smuzhiyun  */
pdr_handle_release(struct pdr_handle * pdr)727*4882a593Smuzhiyun void pdr_handle_release(struct pdr_handle *pdr)
728*4882a593Smuzhiyun {
729*4882a593Smuzhiyun 	struct pdr_service *pds, *tmp;
730*4882a593Smuzhiyun 
731*4882a593Smuzhiyun 	if (IS_ERR_OR_NULL(pdr))
732*4882a593Smuzhiyun 		return;
733*4882a593Smuzhiyun 
734*4882a593Smuzhiyun 	mutex_lock(&pdr->list_lock);
735*4882a593Smuzhiyun 	list_for_each_entry_safe(pds, tmp, &pdr->lookups, node) {
736*4882a593Smuzhiyun 		list_del(&pds->node);
737*4882a593Smuzhiyun 		kfree(pds);
738*4882a593Smuzhiyun 	}
739*4882a593Smuzhiyun 	mutex_unlock(&pdr->list_lock);
740*4882a593Smuzhiyun 
741*4882a593Smuzhiyun 	cancel_work_sync(&pdr->locator_work);
742*4882a593Smuzhiyun 	cancel_work_sync(&pdr->notifier_work);
743*4882a593Smuzhiyun 	cancel_work_sync(&pdr->indack_work);
744*4882a593Smuzhiyun 
745*4882a593Smuzhiyun 	destroy_workqueue(pdr->notifier_wq);
746*4882a593Smuzhiyun 	destroy_workqueue(pdr->indack_wq);
747*4882a593Smuzhiyun 
748*4882a593Smuzhiyun 	qmi_handle_release(&pdr->locator_hdl);
749*4882a593Smuzhiyun 	qmi_handle_release(&pdr->notifier_hdl);
750*4882a593Smuzhiyun 
751*4882a593Smuzhiyun 	kfree(pdr);
752*4882a593Smuzhiyun }
753*4882a593Smuzhiyun EXPORT_SYMBOL(pdr_handle_release);
754*4882a593Smuzhiyun 
755*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
756*4882a593Smuzhiyun MODULE_DESCRIPTION("Qualcomm Protection Domain Restart helpers");
757