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