soc: qcom: Service notification driver for remote services
Add a library for a kernel client to register and be notified of any state changes regarding a local or remote service which runs on a remote processor on the SoC. CRs-Fixed: 999530 Change-Id: Idd56140e11f4fdc48fd999a1e808f3263024f34d Signed-off-by: Pushkar Joshi <pushkarj@codeaurora.org> Signed-off-by: Deepak Katragadda <dkatraga@codeaurora.org> Signed-off-by: Puja Gupta <pujag@codeaurora.org>
This commit is contained in:
parent
dd45821bd9
commit
5576240bad
5 changed files with 1017 additions and 0 deletions
43
Documentation/arm/msm/service_notifier.txt
Normal file
43
Documentation/arm/msm/service_notifier.txt
Normal file
|
@ -0,0 +1,43 @@
|
|||
Introduction
|
||||
=============
|
||||
|
||||
The service notifier driver facilitates a mechanism for a client
|
||||
to register for state notifications regarding a particular remote service.
|
||||
A remote service here refers to a process providing certain services like audio,
|
||||
the identifier for which is provided by the service locator. The process
|
||||
domain will typically run on a remote processor within the same SoC.
|
||||
|
||||
Software Description
|
||||
=====================
|
||||
|
||||
The driver provides the following two APIs:
|
||||
* service_notif_register_notifier() - Register a notifier for a service
|
||||
On success, it returns back a handle. It takes the following arguments:
|
||||
service_path: Individual service identifier path for which a client
|
||||
registers for notifications.
|
||||
instance_id: Instance id specific to a subsystem.
|
||||
current_state: Current state of service returned by the registration
|
||||
process.
|
||||
notifier block: notifier callback for service events.
|
||||
|
||||
* service_notif_unregister_notifier() - Unregister a notifier for a service.
|
||||
This takes the handle returned during registration and the notifier block
|
||||
previously registered as the arguments.
|
||||
|
||||
Types of notifications:
|
||||
=======================
|
||||
|
||||
A client can get either a SERVICE_DOWN notification or a SERVICE_UP
|
||||
notification. A SERVICE_UP notification will be sent out when the SERVICE comes
|
||||
up and is functional while a SERVICE_DOWN notification is sent after a
|
||||
service ceases to exist. At the point a SERVICE_DOWN notification is sent out,
|
||||
all the clients should assume that the service is already dead.
|
||||
|
||||
Interaction with SSR
|
||||
=====================
|
||||
In general, it is recommended that clients register for either service
|
||||
notifications using the service notifier or SSR notifications, but not both.
|
||||
In case it is necessary to register for both, the client can expect to get
|
||||
the SERVICE_DOWN notification before the SUBSYS_AFTER_SHUTDOWN notification.
|
||||
However, the client may receive the SUBSYS_BEFORE_SHUTDOWN notification
|
||||
either before or after the SERVICE_DOWN notification.
|
|
@ -636,4 +636,14 @@ config QCOM_REMOTEQDSS
|
|||
enable/disable these events. Interface located in
|
||||
/sys/class/remoteqdss.
|
||||
|
||||
config MSM_SERVICE_NOTIFIER
|
||||
bool "Service Notifier"
|
||||
depends on MSM_SERVICE_LOCATOR && MSM_SUBSYSTEM_RESTART
|
||||
help
|
||||
The Service Notifier provides a library for a kernel client to
|
||||
register for state change notifications regarding a remote service.
|
||||
A remote service here refers to a process providing certain services
|
||||
like audio, the identifier for which is provided by the service
|
||||
locator.
|
||||
|
||||
source "drivers/soc/qcom/memshare/Kconfig"
|
||||
|
|
|
@ -68,6 +68,7 @@ obj-$(CONFIG_TRACER_PKT) += tracer_pkt.o
|
|||
obj-$(CONFIG_ICNSS) += icnss.o wlan_firmware_service_v01.o
|
||||
obj-$(CONFIG_SOC_BUS) += socinfo.o
|
||||
obj-$(CONFIG_QCOM_BUS_SCALING) += msm_bus/
|
||||
obj-$(CONFIG_MSM_SERVICE_NOTIFIER) += service-notifier.o
|
||||
obj-$(CONFIG_MSM_SECURE_BUFFER) += secure_buffer.o
|
||||
obj-$(CONFIG_MSM_MPM_OF) += mpm-of.o
|
||||
obj-$(CONFIG_MSM_EVENT_TIMER) += event_timer.o
|
||||
|
|
660
drivers/soc/qcom/service-notifier.c
Normal file
660
drivers/soc/qcom/service-notifier.c
Normal file
|
@ -0,0 +1,660 @@
|
|||
/*
|
||||
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) "service-notifier: %s: " fmt, __func__
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/err.h>
|
||||
|
||||
#include <soc/qcom/subsystem_restart.h>
|
||||
#include <soc/qcom/subsystem_notif.h>
|
||||
#include <soc/qcom/sysmon.h>
|
||||
#include <soc/qcom/service-locator.h>
|
||||
#include "service-notifier.h"
|
||||
|
||||
#define QMI_RESP_BIT_SHIFT(x) (x << 16)
|
||||
#define SERVREG_NOTIF_NAME_LENGTH QMI_SERVREG_NOTIF_NAME_LENGTH_V01
|
||||
#define SERVREG_NOTIF_SERVICE_ID SERVREG_NOTIF_SERVICE_ID_V01
|
||||
#define SERVREG_NOTIF_SERVICE_VERS SERVREG_NOTIF_SERVICE_VERS_V01
|
||||
|
||||
#define SERVREG_NOTIF_SET_ACK_REQ \
|
||||
QMI_SERVREG_NOTIF_STATE_UPDATED_IND_ACK_REQ_V01
|
||||
#define SERVREG_NOTIF_SET_ACK_REQ_MSG_LEN \
|
||||
QMI_SERVREG_NOTIF_SET_ACK_REQ_MSG_V01_MAX_MSG_LEN
|
||||
#define SERVREG_NOTIF_SET_ACK_RESP \
|
||||
QMI_SERVREG_NOTIF_STATE_UPDATED_IND_ACK_RESP_V01
|
||||
#define SERVREG_NOTIF_SET_ACK_RESP_MSG_LEN \
|
||||
QMI_SERVREG_NOTIF_SET_ACK_RESP_MSG_V01_MAX_MSG_LEN
|
||||
#define SERVREG_NOTIF_STATE_UPDATED_IND_MSG \
|
||||
QMI_SERVREG_NOTIF_STATE_UPDATED_IND_V01
|
||||
#define SERVREG_NOTIF_STATE_UPDATED_IND_MSG_LEN \
|
||||
QMI_SERVREG_NOTIF_STATE_UPDATED_IND_MSG_V01_MAX_MSG_LEN
|
||||
|
||||
#define SERVREG_NOTIF_REGISTER_LISTENER_REQ \
|
||||
QMI_SERVREG_NOTIF_REGISTER_LISTENER_REQ_V01
|
||||
#define SERVREG_NOTIF_REGISTER_LISTENER_REQ_MSG_LEN \
|
||||
QMI_SERVREG_NOTIF_REGISTER_LISTENER_REQ_MSG_V01_MAX_MSG_LEN
|
||||
#define SERVREG_NOTIF_REGISTER_LISTENER_RESP \
|
||||
QMI_SERVREG_NOTIF_REGISTER_LISTENER_RESP_V01
|
||||
#define SERVREG_NOTIF_REGISTER_LISTENER_RESP_MSG_LEN \
|
||||
QMI_SERVREG_NOTIF_REGISTER_LISTENER_RESP_MSG_V01_MAX_MSG_LEN
|
||||
|
||||
#define QMI_STATE_MIN_VAL QMI_SERVREG_NOTIF_SERVICE_STATE_ENUM_TYPE_MIN_VAL_V01
|
||||
#define QMI_STATE_MAX_VAL QMI_SERVREG_NOTIF_SERVICE_STATE_ENUM_TYPE_MAX_VAL_V01
|
||||
|
||||
#define SERVER_TIMEOUT 500
|
||||
|
||||
/*
|
||||
* Per user service data structure
|
||||
* struct service_notif_info - notifier struct for each unique service path
|
||||
* service_path - service provider path/location
|
||||
* instance_id - service instance id specific to a subsystem
|
||||
* service_notif_rcvr_list - list of clients interested in this service
|
||||
* providers notifications
|
||||
* curr_state: Current state of the service
|
||||
*/
|
||||
struct service_notif_info {
|
||||
char service_path[SERVREG_NOTIF_NAME_LENGTH];
|
||||
int instance_id;
|
||||
struct srcu_notifier_head service_notif_rcvr_list;
|
||||
struct list_head list;
|
||||
int curr_state;
|
||||
};
|
||||
static LIST_HEAD(service_list);
|
||||
static DEFINE_MUTEX(service_list_lock);
|
||||
|
||||
struct ind_req_resp {
|
||||
char service_path[SERVREG_NOTIF_NAME_LENGTH];
|
||||
int transaction_id;
|
||||
};
|
||||
|
||||
/*
|
||||
* Per Root Process Domain (Root service) data structure
|
||||
* struct qmi_client_info - QMI client info for each subsystem/instance id
|
||||
* instance_id - service instance id specific to a subsystem (Root PD)
|
||||
* clnt_handle - unique QMI client handle
|
||||
* service_connected - indicates if QMI service is up on the subsystem
|
||||
* ind_recv - completion variable to record receiving an indication
|
||||
* ssr_handle - The SSR handle provided by the SSR driver for the subsystem
|
||||
* on which the remote root PD runs.
|
||||
*/
|
||||
struct qmi_client_info {
|
||||
int instance_id;
|
||||
struct work_struct svc_arrive;
|
||||
struct work_struct svc_exit;
|
||||
struct work_struct svc_rcv_msg;
|
||||
struct work_struct ind_ack;
|
||||
struct workqueue_struct *svc_event_wq;
|
||||
struct qmi_handle *clnt_handle;
|
||||
struct notifier_block notifier;
|
||||
void *ssr_handle;
|
||||
struct notifier_block ssr_notifier;
|
||||
bool service_connected;
|
||||
struct completion ind_recv;
|
||||
struct list_head list;
|
||||
struct ind_req_resp ind_msg;
|
||||
};
|
||||
static LIST_HEAD(qmi_client_list);
|
||||
static DEFINE_MUTEX(qmi_list_lock);
|
||||
|
||||
static DEFINE_MUTEX(notif_add_lock);
|
||||
|
||||
static void root_service_clnt_recv_msg(struct work_struct *work);
|
||||
static void root_service_service_arrive(struct work_struct *work);
|
||||
static void root_service_exit_work(struct work_struct *work);
|
||||
|
||||
static struct service_notif_info *_find_service_info(const char *service_path)
|
||||
{
|
||||
struct service_notif_info *service_notif;
|
||||
|
||||
mutex_lock(&service_list_lock);
|
||||
list_for_each_entry(service_notif, &service_list, list)
|
||||
if (!strcmp(service_notif->service_path, service_path)) {
|
||||
mutex_unlock(&service_list_lock);
|
||||
return service_notif;
|
||||
}
|
||||
mutex_unlock(&service_list_lock);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int service_notif_queue_notification(struct service_notif_info
|
||||
*service_notif,
|
||||
enum qmi_servreg_notif_service_state_enum_type_v01 notif_type,
|
||||
void *info)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!service_notif)
|
||||
return -EINVAL;
|
||||
|
||||
if ((int) notif_type < QMI_STATE_MIN_VAL ||
|
||||
(int) notif_type > QMI_STATE_MAX_VAL)
|
||||
return -EINVAL;
|
||||
|
||||
if (service_notif->curr_state == notif_type)
|
||||
return 0;
|
||||
|
||||
if (!service_notif->service_notif_rcvr_list.head)
|
||||
return 0;
|
||||
|
||||
ret = srcu_notifier_call_chain(&service_notif->service_notif_rcvr_list,
|
||||
notif_type, info);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void root_service_clnt_recv_msg(struct work_struct *work)
|
||||
{
|
||||
int ret;
|
||||
struct qmi_client_info *data = container_of(work,
|
||||
struct qmi_client_info, svc_rcv_msg);
|
||||
|
||||
do {
|
||||
pr_debug("Notified about a Receive event (instance-id: %d)\n",
|
||||
data->instance_id);
|
||||
} while ((ret = qmi_recv_msg(data->clnt_handle)) == 0);
|
||||
|
||||
if (ret != -ENOMSG)
|
||||
pr_err("Error receiving message (instance-id: %d)\n",
|
||||
data->instance_id);
|
||||
}
|
||||
|
||||
static void root_service_clnt_notify(struct qmi_handle *handle,
|
||||
enum qmi_event_type event, void *notify_priv)
|
||||
{
|
||||
struct qmi_client_info *data = container_of(notify_priv,
|
||||
struct qmi_client_info, svc_arrive);
|
||||
|
||||
switch (event) {
|
||||
case QMI_RECV_MSG:
|
||||
schedule_work(&data->svc_rcv_msg);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void send_ind_ack(struct work_struct *work)
|
||||
{
|
||||
struct qmi_client_info *data = container_of(work,
|
||||
struct qmi_client_info, ind_ack);
|
||||
struct qmi_servreg_notif_set_ack_req_msg_v01 req;
|
||||
struct msg_desc req_desc, resp_desc;
|
||||
struct qmi_servreg_notif_set_ack_resp_msg_v01 resp = { { 0, 0 } };
|
||||
int rc;
|
||||
|
||||
req.transaction_id = data->ind_msg.transaction_id;
|
||||
snprintf(req.service_name, ARRAY_SIZE(req.service_name), "%s",
|
||||
data->ind_msg.service_path);
|
||||
|
||||
req_desc.msg_id = SERVREG_NOTIF_SET_ACK_REQ;
|
||||
req_desc.max_msg_len = SERVREG_NOTIF_SET_ACK_REQ_MSG_LEN;
|
||||
req_desc.ei_array = qmi_servreg_notif_set_ack_req_msg_v01_ei;
|
||||
|
||||
resp_desc.msg_id = SERVREG_NOTIF_SET_ACK_RESP;
|
||||
resp_desc.max_msg_len = SERVREG_NOTIF_SET_ACK_RESP_MSG_LEN;
|
||||
resp_desc.ei_array = qmi_servreg_notif_set_ack_resp_msg_v01_ei;
|
||||
|
||||
rc = qmi_send_req_wait(data->clnt_handle, &req_desc,
|
||||
&req, sizeof(req), &resp_desc, &resp,
|
||||
sizeof(resp), SERVER_TIMEOUT);
|
||||
if (rc < 0) {
|
||||
pr_err("%s: Sending Ack failed/server timeout, ret - %d\n",
|
||||
data->ind_msg.service_path, rc);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Check the response */
|
||||
if (QMI_RESP_BIT_SHIFT(resp.resp.result) != QMI_RESULT_SUCCESS_V01)
|
||||
pr_err("QMI request failed 0x%x\n",
|
||||
QMI_RESP_BIT_SHIFT(resp.resp.error));
|
||||
pr_debug("Indication ACKed for transid %d, service %s, instance %d!\n",
|
||||
data->ind_msg.transaction_id, data->ind_msg.service_path,
|
||||
data->instance_id);
|
||||
exit:
|
||||
complete(&data->ind_recv);
|
||||
}
|
||||
|
||||
static void root_service_service_ind_cb(struct qmi_handle *handle,
|
||||
unsigned int msg_id, void *msg,
|
||||
unsigned int msg_len, void *ind_cb_priv)
|
||||
{
|
||||
struct qmi_client_info *data = (struct qmi_client_info *)ind_cb_priv;
|
||||
struct service_notif_info *service_notif;
|
||||
struct msg_desc ind_desc;
|
||||
struct qmi_servreg_notif_state_updated_ind_msg_v01 ind_msg;
|
||||
int rc;
|
||||
|
||||
ind_desc.msg_id = SERVREG_NOTIF_STATE_UPDATED_IND_MSG;
|
||||
ind_desc.max_msg_len = SERVREG_NOTIF_STATE_UPDATED_IND_MSG_LEN;
|
||||
ind_desc.ei_array = qmi_servreg_notif_state_updated_ind_msg_v01_ei;
|
||||
rc = qmi_kernel_decode(&ind_desc, &ind_msg, msg, msg_len);
|
||||
if (rc < 0) {
|
||||
pr_err("Failed to decode message!\n");
|
||||
goto send_ind_resp;
|
||||
}
|
||||
|
||||
pr_debug("Indication received from %s, state: 0x%x, trans-id: %d\n",
|
||||
ind_msg.service_name, ind_msg.curr_state,
|
||||
ind_msg.transaction_id);
|
||||
|
||||
service_notif = _find_service_info(ind_msg.service_name);
|
||||
if (!service_notif)
|
||||
return;
|
||||
|
||||
if ((int)ind_msg.curr_state < QMI_STATE_MIN_VAL ||
|
||||
(int)ind_msg.curr_state > QMI_STATE_MAX_VAL)
|
||||
pr_err("Unexpected indication notification state %d\n",
|
||||
ind_msg.curr_state);
|
||||
else {
|
||||
mutex_lock(¬if_add_lock);
|
||||
mutex_lock(&service_list_lock);
|
||||
if (service_notif_queue_notification(service_notif,
|
||||
ind_msg.curr_state, NULL))
|
||||
pr_err("Nnotification failed for %s\n",
|
||||
ind_msg.service_name);
|
||||
service_notif->curr_state = ind_msg.curr_state;
|
||||
mutex_unlock(&service_list_lock);
|
||||
mutex_unlock(¬if_add_lock);
|
||||
}
|
||||
send_ind_resp:
|
||||
data->ind_msg.transaction_id = ind_msg.transaction_id;
|
||||
snprintf(data->ind_msg.service_path,
|
||||
ARRAY_SIZE(data->ind_msg.service_path), "%s",
|
||||
ind_msg.service_name);
|
||||
schedule_work(&data->ind_ack);
|
||||
rc = wait_for_completion_timeout(&data->ind_recv, SERVER_TIMEOUT);
|
||||
if (rc < 0) {
|
||||
pr_err("Timeout waiting for sending indication ACK!");
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static int send_notif_listener_msg_req(struct service_notif_info *service_notif,
|
||||
struct qmi_client_info *data,
|
||||
bool register_notif, int *curr_state)
|
||||
{
|
||||
struct qmi_servreg_notif_register_listener_req_msg_v01 req;
|
||||
struct qmi_servreg_notif_register_listener_resp_msg_v01
|
||||
resp = { { 0, 0 } };
|
||||
struct msg_desc req_desc, resp_desc;
|
||||
int rc;
|
||||
|
||||
snprintf(req.service_name, ARRAY_SIZE(req.service_name), "%s",
|
||||
service_notif->service_path);
|
||||
req.enable = register_notif;
|
||||
|
||||
req_desc.msg_id = SERVREG_NOTIF_REGISTER_LISTENER_REQ;
|
||||
req_desc.max_msg_len = SERVREG_NOTIF_REGISTER_LISTENER_REQ_MSG_LEN;
|
||||
req_desc.ei_array = qmi_servreg_notif_register_listener_req_msg_v01_ei;
|
||||
|
||||
resp_desc.msg_id = SERVREG_NOTIF_REGISTER_LISTENER_RESP;
|
||||
resp_desc.max_msg_len = SERVREG_NOTIF_REGISTER_LISTENER_RESP_MSG_LEN;
|
||||
resp_desc.ei_array =
|
||||
qmi_servreg_notif_register_listener_resp_msg_v01_ei;
|
||||
|
||||
rc = qmi_send_req_wait(data->clnt_handle, &req_desc, &req, sizeof(req),
|
||||
&resp_desc, &resp, sizeof(resp),
|
||||
SERVER_TIMEOUT);
|
||||
if (rc < 0) {
|
||||
pr_err("%s: Message sending failed/server timeout, ret - %d\n",
|
||||
service_notif->service_path, rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Check the response */
|
||||
if (QMI_RESP_BIT_SHIFT(resp.resp.result) != QMI_RESULT_SUCCESS_V01) {
|
||||
pr_err("QMI request failed 0x%x\n",
|
||||
QMI_RESP_BIT_SHIFT(resp.resp.error));
|
||||
return -EREMOTEIO;
|
||||
}
|
||||
|
||||
if ((int) resp.curr_state < QMI_STATE_MIN_VAL ||
|
||||
(int) resp.curr_state > QMI_STATE_MAX_VAL) {
|
||||
pr_err("Invalid notif info 0x%x\n", resp.curr_state);
|
||||
rc = -EINVAL;
|
||||
}
|
||||
service_notif->curr_state = resp.curr_state;
|
||||
*curr_state = resp.curr_state;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int register_notif_listener(struct service_notif_info *service_notif,
|
||||
struct qmi_client_info *data,
|
||||
int *curr_state)
|
||||
{
|
||||
return send_notif_listener_msg_req(service_notif, data, true,
|
||||
curr_state);
|
||||
}
|
||||
|
||||
static void root_service_service_arrive(struct work_struct *work)
|
||||
{
|
||||
struct service_notif_info *service_notif = NULL;
|
||||
struct qmi_client_info *data = container_of(work,
|
||||
struct qmi_client_info, svc_arrive);
|
||||
int rc;
|
||||
int curr_state;
|
||||
|
||||
/* Create a Local client port for QMI communication */
|
||||
data->clnt_handle = qmi_handle_create(root_service_clnt_notify, work);
|
||||
if (!data->clnt_handle) {
|
||||
pr_err("QMI client handle alloc failed (instance-id: %d)\n",
|
||||
data->instance_id);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Connect to the service on the root PD service */
|
||||
rc = qmi_connect_to_service(data->clnt_handle,
|
||||
SERVREG_NOTIF_SERVICE_ID, SERVREG_NOTIF_SERVICE_VERS,
|
||||
data->instance_id);
|
||||
if (rc < 0) {
|
||||
pr_err("Could not connect handle to service(instance-id: %d)\n",
|
||||
data->instance_id);
|
||||
qmi_handle_destroy(data->clnt_handle);
|
||||
data->clnt_handle = NULL;
|
||||
return;
|
||||
}
|
||||
data->service_connected = true;
|
||||
pr_info("Connection established between QMI handle and %d service\n",
|
||||
data->instance_id);
|
||||
/* Register for indication messages about service */
|
||||
rc = qmi_register_ind_cb(data->clnt_handle, root_service_service_ind_cb,
|
||||
(void *)data);
|
||||
if (rc < 0)
|
||||
pr_err("Indication callback register failed(instance-id: %d)\n",
|
||||
data->instance_id);
|
||||
|
||||
mutex_lock(¬if_add_lock);
|
||||
mutex_lock(&service_list_lock);
|
||||
list_for_each_entry(service_notif, &service_list, list) {
|
||||
if (service_notif->instance_id == data->instance_id) {
|
||||
rc = register_notif_listener(service_notif, data,
|
||||
&curr_state);
|
||||
if (rc) {
|
||||
pr_err("Notifier registration failed for %s\n",
|
||||
service_notif->service_path);
|
||||
} else {
|
||||
rc = service_notif_queue_notification(
|
||||
service_notif,
|
||||
curr_state, NULL);
|
||||
if (rc)
|
||||
pr_err("Notifier failed for %s\n",
|
||||
service_notif->service_path);
|
||||
service_notif->curr_state = curr_state;
|
||||
}
|
||||
}
|
||||
}
|
||||
mutex_unlock(&service_list_lock);
|
||||
mutex_unlock(¬if_add_lock);
|
||||
}
|
||||
|
||||
static void root_service_service_exit(struct qmi_client_info *data)
|
||||
{
|
||||
struct service_notif_info *service_notif = NULL;
|
||||
int rc;
|
||||
|
||||
/*
|
||||
* Send service down notifications to all clients
|
||||
* of registered for notifications for that service.
|
||||
*/
|
||||
mutex_lock(¬if_add_lock);
|
||||
mutex_lock(&service_list_lock);
|
||||
list_for_each_entry(service_notif, &service_list, list) {
|
||||
if (service_notif->instance_id == data->instance_id) {
|
||||
rc = service_notif_queue_notification(service_notif,
|
||||
SERVREG_NOTIF_SERVICE_STATE_DOWN_V01,
|
||||
NULL);
|
||||
if (rc)
|
||||
pr_err("Notification failed for %s\n",
|
||||
service_notif->service_path);
|
||||
service_notif->curr_state =
|
||||
SERVREG_NOTIF_SERVICE_STATE_DOWN_V01;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&service_list_lock);
|
||||
mutex_unlock(¬if_add_lock);
|
||||
|
||||
/*
|
||||
* Destroy client handle and try connecting when
|
||||
* service comes up again.
|
||||
*/
|
||||
data->service_connected = false;
|
||||
qmi_handle_destroy(data->clnt_handle);
|
||||
data->clnt_handle = NULL;
|
||||
}
|
||||
|
||||
static void root_service_exit_work(struct work_struct *work)
|
||||
{
|
||||
struct qmi_client_info *data = container_of(work,
|
||||
struct qmi_client_info, svc_exit);
|
||||
root_service_service_exit(data);
|
||||
}
|
||||
|
||||
static int service_event_notify(struct notifier_block *this,
|
||||
unsigned long code,
|
||||
void *_cmd)
|
||||
{
|
||||
struct qmi_client_info *data = container_of(this,
|
||||
struct qmi_client_info, notifier);
|
||||
|
||||
switch (code) {
|
||||
case QMI_SERVER_ARRIVE:
|
||||
pr_debug("Root PD service UP\n");
|
||||
queue_work(data->svc_event_wq, &data->svc_arrive);
|
||||
break;
|
||||
case QMI_SERVER_EXIT:
|
||||
pr_debug("Root PD service DOWN\n");
|
||||
queue_work(data->svc_event_wq, &data->svc_exit);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ssr_event_notify(struct notifier_block *this,
|
||||
unsigned long code,
|
||||
void *data)
|
||||
{
|
||||
struct qmi_client_info *info = container_of(this,
|
||||
struct qmi_client_info, ssr_notifier);
|
||||
switch (code) {
|
||||
case SUBSYS_BEFORE_SHUTDOWN:
|
||||
pr_debug("Root PD service Down (SSR notification)\n");
|
||||
root_service_service_exit(info);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static void *add_service_notif(const char *service_path, int instance_id,
|
||||
int *curr_state)
|
||||
{
|
||||
struct service_notif_info *service_notif;
|
||||
struct qmi_client_info *tmp, *qmi_data;
|
||||
long int rc;
|
||||
char subsys[SERVREG_NOTIF_NAME_LENGTH];
|
||||
|
||||
rc = find_subsys(service_path, subsys);
|
||||
if (rc < 0) {
|
||||
pr_err("Could not find subsys for %s\n", service_path);
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
|
||||
service_notif = kzalloc(sizeof(struct service_notif_info), GFP_KERNEL);
|
||||
if (!service_notif)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
strlcpy(service_notif->service_path, service_path,
|
||||
ARRAY_SIZE(service_notif->service_path));
|
||||
service_notif->instance_id = instance_id;
|
||||
|
||||
/* If we already have a connection to the root PD on which the remote
|
||||
* service we are interested in notifications about runs, then use
|
||||
* the existing QMI connection.
|
||||
*/
|
||||
mutex_lock(&qmi_list_lock);
|
||||
list_for_each_entry(tmp, &qmi_client_list, list) {
|
||||
if (tmp->instance_id == instance_id) {
|
||||
if (tmp->service_connected) {
|
||||
rc = register_notif_listener(service_notif, tmp,
|
||||
curr_state);
|
||||
if (rc) {
|
||||
mutex_unlock(&qmi_list_lock);
|
||||
pr_err("Register notifier failed: %s",
|
||||
service_path);
|
||||
kfree(service_notif);
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
}
|
||||
mutex_unlock(&qmi_list_lock);
|
||||
goto add_service_list;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&qmi_list_lock);
|
||||
|
||||
qmi_data = kzalloc(sizeof(struct qmi_client_info), GFP_KERNEL);
|
||||
if (!qmi_data) {
|
||||
kfree(service_notif);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
qmi_data->instance_id = instance_id;
|
||||
qmi_data->clnt_handle = NULL;
|
||||
qmi_data->notifier.notifier_call = service_event_notify;
|
||||
init_completion(&qmi_data->ind_recv);
|
||||
|
||||
qmi_data->svc_event_wq = create_singlethread_workqueue(subsys);
|
||||
if (!qmi_data->svc_event_wq) {
|
||||
rc = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
INIT_WORK(&qmi_data->svc_arrive, root_service_service_arrive);
|
||||
INIT_WORK(&qmi_data->svc_exit, root_service_exit_work);
|
||||
INIT_WORK(&qmi_data->svc_rcv_msg, root_service_clnt_recv_msg);
|
||||
INIT_WORK(&qmi_data->ind_ack, send_ind_ack);
|
||||
|
||||
*curr_state = service_notif->curr_state =
|
||||
SERVREG_NOTIF_SERVICE_STATE_UNINIT_V01;
|
||||
|
||||
rc = qmi_svc_event_notifier_register(SERVREG_NOTIF_SERVICE_ID,
|
||||
SERVREG_NOTIF_SERVICE_VERS, qmi_data->instance_id,
|
||||
&qmi_data->notifier);
|
||||
if (rc < 0) {
|
||||
pr_err("Notifier register failed (instance-id: %d)\n",
|
||||
qmi_data->instance_id);
|
||||
goto exit;
|
||||
}
|
||||
qmi_data->ssr_notifier.notifier_call = ssr_event_notify;
|
||||
qmi_data->ssr_handle = subsys_notif_register_notifier(subsys,
|
||||
&qmi_data->ssr_notifier);
|
||||
if (IS_ERR(qmi_data->ssr_handle)) {
|
||||
pr_err("SSR notif register for %s failed(instance-id: %d)\n",
|
||||
subsys, qmi_data->instance_id);
|
||||
rc = PTR_ERR(qmi_data->ssr_handle);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
mutex_lock(&qmi_list_lock);
|
||||
INIT_LIST_HEAD(&qmi_data->list);
|
||||
list_add_tail(&qmi_data->list, &qmi_client_list);
|
||||
mutex_unlock(&qmi_list_lock);
|
||||
|
||||
add_service_list:
|
||||
srcu_init_notifier_head(&service_notif->service_notif_rcvr_list);
|
||||
|
||||
mutex_lock(&service_list_lock);
|
||||
INIT_LIST_HEAD(&service_notif->list);
|
||||
list_add_tail(&service_notif->list, &service_list);
|
||||
mutex_unlock(&service_list_lock);
|
||||
|
||||
return service_notif;
|
||||
exit:
|
||||
if (qmi_data->svc_event_wq)
|
||||
destroy_workqueue(qmi_data->svc_event_wq);
|
||||
kfree(qmi_data);
|
||||
kfree(service_notif);
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
|
||||
/* service_notif_register_notifier() - Register a notifier for a service
|
||||
* On success, it returns back a handle. It takes the following arguments:
|
||||
* service_path: Individual service identifier path for which a client
|
||||
* registers for notifications.
|
||||
* instance_id: Instance id specific to a subsystem.
|
||||
* current_state: Current state of service returned by the registration
|
||||
* process.
|
||||
* notifier block: notifier callback for service events.
|
||||
*/
|
||||
void *service_notif_register_notifier(const char *service_path, int instance_id,
|
||||
struct notifier_block *nb, int *curr_state)
|
||||
{
|
||||
struct service_notif_info *service_notif;
|
||||
int ret = 0;
|
||||
|
||||
if (!service_path || !instance_id || !nb)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
service_notif = _find_service_info(service_path);
|
||||
mutex_lock(¬if_add_lock);
|
||||
if (!service_notif) {
|
||||
service_notif = (struct service_notif_info *)add_service_notif(
|
||||
service_path,
|
||||
instance_id,
|
||||
curr_state);
|
||||
if (IS_ERR(service_notif))
|
||||
goto exit;
|
||||
}
|
||||
|
||||
ret = srcu_notifier_chain_register(
|
||||
&service_notif->service_notif_rcvr_list, nb);
|
||||
*curr_state = service_notif->curr_state;
|
||||
if (ret < 0)
|
||||
service_notif = ERR_PTR(ret);
|
||||
exit:
|
||||
mutex_unlock(¬if_add_lock);
|
||||
return service_notif;
|
||||
}
|
||||
EXPORT_SYMBOL(service_notif_register_notifier);
|
||||
|
||||
/* service_notif_unregister_notifier() - Unregister a notifier for a service.
|
||||
* service_notif_handle - The notifier handler that was provided by the
|
||||
* service_notif_register_notifier function when the
|
||||
* client registered for notifications.
|
||||
* nb - The notifier block that was previously used during the registration.
|
||||
*/
|
||||
int service_notif_unregister_notifier(void *service_notif_handle,
|
||||
struct notifier_block *nb)
|
||||
{
|
||||
struct service_notif_info *service_notif;
|
||||
|
||||
if (!service_notif_handle || !nb)
|
||||
return -EINVAL;
|
||||
|
||||
service_notif = (struct service_notif_info *)service_notif_handle;
|
||||
if (service_notif < 0)
|
||||
return -EINVAL;
|
||||
|
||||
return srcu_notifier_chain_unregister(
|
||||
&service_notif->service_notif_rcvr_list, nb);
|
||||
}
|
||||
EXPORT_SYMBOL(service_notif_unregister_notifier);
|
303
drivers/soc/qcom/service-notifier.h
Normal file
303
drivers/soc/qcom/service-notifier.h
Normal file
|
@ -0,0 +1,303 @@
|
|||
/* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
*/
|
||||
#ifndef SERVICE_REGISTRY_NOTIFIER_H
|
||||
#define SERVICE_REGISTRY_NOTIFIER_H
|
||||
|
||||
#include <linux/qmi_encdec.h>
|
||||
|
||||
#include <soc/qcom/msm_qmi_interface.h>
|
||||
|
||||
#define SERVREG_NOTIF_SERVICE_ID_V01 0x42
|
||||
#define SERVREG_NOTIF_SERVICE_VERS_V01 0x01
|
||||
|
||||
#define QMI_SERVREG_NOTIF_REGISTER_LISTENER_REQ_V01 0x0020
|
||||
#define QMI_SERVREG_NOTIF_QUERY_STATE_REQ_V01 0x0021
|
||||
#define QMI_SERVREG_NOTIF_REGISTER_LISTENER_RESP_V01 0x0020
|
||||
#define QMI_SERVREG_NOTIF_QUERY_STATE_RESP_V01 0x0021
|
||||
#define QMI_SERVREG_NOTIF_STATE_UPDATED_IND_V01 0x0022
|
||||
#define QMI_SERVREG_NOTIF_STATE_UPDATED_IND_ACK_RESP_V01 0x0023
|
||||
#define QMI_SERVREG_NOTIF_STATE_UPDATED_IND_ACK_REQ_V01 0x0023
|
||||
|
||||
#define QMI_SERVREG_NOTIF_NAME_LENGTH_V01 64
|
||||
|
||||
enum qmi_servreg_notif_service_state_enum_type_v01 {
|
||||
QMI_SERVREG_NOTIF_SERVICE_STATE_ENUM_TYPE_MIN_VAL_V01 = INT_MIN,
|
||||
QMI_SERVREG_NOTIF_SERVICE_STATE_ENUM_TYPE_MAX_VAL_V01 = INT_MAX,
|
||||
SERVREG_NOTIF_SERVICE_STATE_DOWN_V01 = 0x0FFFFFFF,
|
||||
SERVREG_NOTIF_SERVICE_STATE_UP_V01 = 0x1FFFFFFF,
|
||||
SERVREG_NOTIF_SERVICE_STATE_UNINIT_V01 = 0x7FFFFFFF,
|
||||
};
|
||||
|
||||
struct qmi_servreg_notif_register_listener_req_msg_v01 {
|
||||
uint8_t enable;
|
||||
char service_name[QMI_SERVREG_NOTIF_NAME_LENGTH_V01 + 1];
|
||||
};
|
||||
#define QMI_SERVREG_NOTIF_REGISTER_LISTENER_REQ_MSG_V01_MAX_MSG_LEN 71
|
||||
struct elem_info qmi_servreg_notif_register_listener_req_msg_v01_ei[];
|
||||
|
||||
struct qmi_servreg_notif_register_listener_resp_msg_v01 {
|
||||
struct qmi_response_type_v01 resp;
|
||||
uint8_t curr_state_valid;
|
||||
enum qmi_servreg_notif_service_state_enum_type_v01 curr_state;
|
||||
};
|
||||
#define QMI_SERVREG_NOTIF_REGISTER_LISTENER_RESP_MSG_V01_MAX_MSG_LEN 14
|
||||
struct elem_info qmi_servreg_notif_register_listener_resp_msg_v01_ei[];
|
||||
|
||||
struct qmi_servreg_notif_query_state_req_msg_v01 {
|
||||
char service_name[QMI_SERVREG_NOTIF_NAME_LENGTH_V01 + 1];
|
||||
};
|
||||
#define QMI_SERVREG_NOTIF_QUERY_STATE_REQ_MSG_V01_MAX_MSG_LEN 67
|
||||
struct elem_info qmi_servreg_notif_query_state_req_msg_v01_ei[];
|
||||
|
||||
struct qmi_servreg_notif_query_state_resp_msg_v01 {
|
||||
struct qmi_response_type_v01 resp;
|
||||
uint8_t curr_state_valid;
|
||||
enum qmi_servreg_notif_service_state_enum_type_v01 curr_state;
|
||||
};
|
||||
#define QMI_SERVREG_NOTIF_QUERY_STATE_RESP_MSG_V01_MAX_MSG_LEN 14
|
||||
struct elem_info qmi_servreg_notif_query_state_resp_msg_v01_ei[];
|
||||
|
||||
struct qmi_servreg_notif_state_updated_ind_msg_v01 {
|
||||
enum qmi_servreg_notif_service_state_enum_type_v01 curr_state;
|
||||
char service_name[QMI_SERVREG_NOTIF_NAME_LENGTH_V01 + 1];
|
||||
uint16_t transaction_id;
|
||||
};
|
||||
#define QMI_SERVREG_NOTIF_STATE_UPDATED_IND_MSG_V01_MAX_MSG_LEN 79
|
||||
struct elem_info qmi_servreg_notif_state_updated_ind_msg_v01_ei[];
|
||||
|
||||
struct qmi_servreg_notif_set_ack_req_msg_v01 {
|
||||
char service_name[QMI_SERVREG_NOTIF_NAME_LENGTH_V01 + 1];
|
||||
uint16_t transaction_id;
|
||||
};
|
||||
#define QMI_SERVREG_NOTIF_SET_ACK_REQ_MSG_V01_MAX_MSG_LEN 72
|
||||
struct elem_info qmi_servreg_notif_set_ack_req_msg_v01_ei[];
|
||||
|
||||
struct qmi_servreg_notif_set_ack_resp_msg_v01 {
|
||||
struct qmi_response_type_v01 resp;
|
||||
};
|
||||
#define QMI_SERVREG_NOTIF_SET_ACK_RESP_MSG_V01_MAX_MSG_LEN 7
|
||||
struct elem_info qmi_servreg_notif_set_ack_resp_msg_v01_ei[];
|
||||
|
||||
struct elem_info qmi_servreg_notif_register_listener_req_msg_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_1_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = 0x01,
|
||||
.offset = offsetof(struct
|
||||
qmi_servreg_notif_register_listener_req_msg_v01,
|
||||
enable),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_STRING,
|
||||
.elem_len = QMI_SERVREG_NOTIF_NAME_LENGTH_V01 + 1,
|
||||
.elem_size = sizeof(char),
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(struct
|
||||
qmi_servreg_notif_register_listener_req_msg_v01,
|
||||
service_name),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.is_array = NO_ARRAY,
|
||||
.is_array = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct elem_info qmi_servreg_notif_register_listener_resp_msg_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct qmi_response_type_v01),
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(struct
|
||||
qmi_servreg_notif_register_listener_resp_msg_v01,
|
||||
resp),
|
||||
.ei_array = get_qmi_response_type_v01_ei(),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct
|
||||
qmi_servreg_notif_register_listener_resp_msg_v01,
|
||||
curr_state_valid),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_SIGNED_4_BYTE_ENUM,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(
|
||||
enum qmi_servreg_notif_service_state_enum_type_v01),
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct
|
||||
qmi_servreg_notif_register_listener_resp_msg_v01,
|
||||
curr_state),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.is_array = NO_ARRAY,
|
||||
.is_array = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct elem_info qmi_servreg_notif_query_state_req_msg_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRING,
|
||||
.elem_len = QMI_SERVREG_NOTIF_NAME_LENGTH_V01 + 1,
|
||||
.elem_size = sizeof(char),
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = 0x01,
|
||||
.offset = offsetof(struct
|
||||
qmi_servreg_notif_query_state_req_msg_v01,
|
||||
service_name),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.is_array = NO_ARRAY,
|
||||
.is_array = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct elem_info qmi_servreg_notif_query_state_resp_msg_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct qmi_response_type_v01),
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(struct
|
||||
qmi_servreg_notif_query_state_resp_msg_v01,
|
||||
resp),
|
||||
.ei_array = get_qmi_response_type_v01_ei(),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_OPT_FLAG,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint8_t),
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct
|
||||
qmi_servreg_notif_query_state_resp_msg_v01,
|
||||
curr_state_valid),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_SIGNED_4_BYTE_ENUM,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(enum
|
||||
qmi_servreg_notif_service_state_enum_type_v01),
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = 0x10,
|
||||
.offset = offsetof(struct
|
||||
qmi_servreg_notif_query_state_resp_msg_v01,
|
||||
curr_state),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.is_array = NO_ARRAY,
|
||||
.is_array = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct elem_info qmi_servreg_notif_state_updated_ind_msg_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_SIGNED_4_BYTE_ENUM,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(enum
|
||||
qmi_servreg_notif_service_state_enum_type_v01),
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = 0x01,
|
||||
.offset = offsetof(struct
|
||||
qmi_servreg_notif_state_updated_ind_msg_v01,
|
||||
curr_state),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_STRING,
|
||||
.elem_len = QMI_SERVREG_NOTIF_NAME_LENGTH_V01 + 1,
|
||||
.elem_size = sizeof(char),
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(struct
|
||||
qmi_servreg_notif_state_updated_ind_msg_v01,
|
||||
service_name),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_2_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint16_t),
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = 0x03,
|
||||
.offset = offsetof(struct
|
||||
qmi_servreg_notif_state_updated_ind_msg_v01,
|
||||
transaction_id),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.is_array = NO_ARRAY,
|
||||
.is_array = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct elem_info qmi_servreg_notif_set_ack_req_msg_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRING,
|
||||
.elem_len = QMI_SERVREG_NOTIF_NAME_LENGTH_V01 + 1,
|
||||
.elem_size = sizeof(char),
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = 0x01,
|
||||
.offset = offsetof(struct
|
||||
qmi_servreg_notif_set_ack_req_msg_v01,
|
||||
service_name),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_UNSIGNED_2_BYTE,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(uint16_t),
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(struct
|
||||
qmi_servreg_notif_set_ack_req_msg_v01,
|
||||
transaction_id),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.is_array = NO_ARRAY,
|
||||
.is_array = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
struct elem_info qmi_servreg_notif_set_ack_resp_msg_v01_ei[] = {
|
||||
{
|
||||
.data_type = QMI_STRUCT,
|
||||
.elem_len = 1,
|
||||
.elem_size = sizeof(struct qmi_response_type_v01),
|
||||
.is_array = NO_ARRAY,
|
||||
.tlv_type = 0x02,
|
||||
.offset = offsetof(struct
|
||||
qmi_servreg_notif_set_ack_resp_msg_v01,
|
||||
resp),
|
||||
.ei_array = get_qmi_response_type_v01_ei(),
|
||||
},
|
||||
{
|
||||
.data_type = QMI_EOTI,
|
||||
.is_array = NO_ARRAY,
|
||||
.is_array = QMI_COMMON_TLV_TYPE,
|
||||
},
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Add table
Reference in a new issue