soc: qcom: Add snapshot of QMI

This snapshot is taken as of msm-4.4 'commit <d2afad6a903b>
("Merge "ext4 crypto: enable HW based encryption with ICE"")'.

Fix the code style warnings and errors.

CRs-Fixed: 1079350
Change-Id: I7dfcaac6dd2f97b109793aee97db6e9951282194
Signed-off-by: Karthikeyan Ramasubramanian <kramasub@codeaurora.org>
This commit is contained in:
Karthikeyan Ramasubramanian
2016-09-16 17:15:13 -06:00
committed by Gerrit - the friendly Code Review server
parent 992e5a555e
commit fafd67f177
11 changed files with 4531 additions and 0 deletions

View File

@@ -0,0 +1,520 @@
Introduction
============
Qualcomm Technologies, Inc. MSM Interface(QMI) is a messaging format used
to communicate between software components in the modem and other
peripheral subsystems. This document proposes an architecture to introduce
the QMI messaging into the kernel. This document proposes introducing a
QMI encode/decode library to enable QMI message marshaling and an
interface library to enable sending and receiving QMI messages through
MSM IPC Router.
Hardware description
====================
QMI is a messaging format used to interface with the components in modem
and other subsystems. QMI does not drive or manage any hardware resources.
Software description
====================
QMI communication is based on a client-server model, where clients and
servers exchange messages in QMI wire format. A module can act as a client
of any number of QMI services and a QMI service can serve any number of
clients.
QMI communication is of request/response type or an unsolicited event type.
QMI client driver sends a request to a QMI service and receives a response.
QMI client driver registers with the QMI service to receive indications
regarding a system event and the QMI service sends the indications to the
client when the event occurs in the system.
The wire format of QMI message is as follows:
----------------------------------------------------
| QMI Header | TLV 0 | TLV 1 | ... | TLV N |
----------------------------------------------------
QMI Header:
-----------
--------------------------------------------------------
| Flags | Transaction ID | Message ID | Message Length |
--------------------------------------------------------
The flags field is used to indicate the kind of QMI message - request,
response or indication. The transaction ID is a client specific field
to uniquely match the QMI request and the response. The message ID is
also a client specific field to indicate the kind of information present
in the QMI payload. The message length field holds the size information
of the QMI payload.
Flags:
------
* 0 - QMI Request
* 2 - QMI Response
* 4 - QMI Indication
TLV:
----
QMI payload is represented using a series of Type, Length and Value fields.
Each information being passed is encoded into a type, length and value
combination. The type element identifies the type of information being
encoded. The length element specifies the length of the information/values
being encoded. The information can be of a primitive type or a structure
or an array.
-------------------------------------------
| Type | Length | Value 0 | ... | Value N |
-------------------------------------------
QMI Message Marshaling and Transport:
-------------------------------------
QMI encode/decode library is designed to encode the kernel C data
structures into QMI wire format and to decode the QMI messages into kernel
C data strcuture format. This library will provide a single interface to
transform any data structure into a QMI message and vice-versa.
QMI interface library is designed to send and receive QMI messages over
IPC Router.
----------------------------------
| Kernel Drivers |
----------------------------------
| |
| |
----------------- -----------------
| QMI Interface |___| QMI Enc/Dec |
| Library | | Library |
----------------- -----------------
|
|
-------------------
| IPC Message |
| Router |
-------------------
|
|
-------
| SMD |
-------
Design
======
The design goals of this proposed QMI messaging mechanism are:
* To enable QMI messaging from within the kernel
* To provide a common library to marshal QMI messages
* To provide a common interface library to send/receive QMI messages
* To support kernel QMI clients which have latency constraints
The reason behind this design decision is:
* To provide a simple QMI marshaling interface to the kernel users
* To hide the complexities of QMI message transports
* To minimize code redundancy
In order to provide a single encode/decode API, the library expects
the kernel drivers to pass the:
* starting address of the data structure to be encoded/decoded
* starting address of the QMI message buffer
* a table containing information regarding the data structure to
be encoded/decoded
The design is based on the idea that any complex data structure is a
collection of primary data elements. Hence the information about any
data structure can be constructed as an array of information about its
primary data elements. The following structure is defined to describe
information about a primary data element.
/**
* elem_info - Data structure to specify information about an element
* in a data structure. An array of this data structure
* can be used to specify info about a complex data
* structure to be encoded/decoded.
* @data_type: Data type of this element
* @elem_len: Array length of this element, if an array
* @elem_size: Size of a single instance of this data type
* @is_array: Array type of this element
* @tlv_type: QMI message specific type to identify which element
* is present in an incoming message
* @offset: To identify the address of the first instance of this
* element in the data structure
* @ei_array: Array to provide information about the nested structure
* within a data structure to be encoded/decoded.
*/
struct elem_info {
enum elem_type data_type;
uint32_t elem_len;
uint32_t elem_size;
enum array_type is_array;
uint8_t tlv_type;
uint32_t offset;
struct elem_info *ei_array;
};
The alternate design discussions include manual encoding/decoding of QMI
messages. From RPC experience, this approach has mostly been error prone.
This in turn lead to increased development and debugging effort. Another
approach included data-structure specific marshaling API -- i.e. every
data structure to be encoded/decoded should have a unique auto-generated
marshaling API. This approach comes with the cost of code redundancy and
was therefore rejected.
Power Management
================
N/A
SMP/multi-core
==============
The QMI encode/decode library does not access any global or shared data
structures. Hence it does not require any locking mechanisms to ensure
multi-core safety.
The QMI interface library uses mutexes while accessing shared resources.
Security
========
N/A
Performance
===========
This design proposal is to support kernel QMI clients which have latency
constraints. Hence the number and size of QMI messages are expected to be
kept short, in order to achieve latency of less than 1 ms consistently.
Interface
=========
Kernel-APIs:
------------
Encode/Decode Library APIs:
---------------------------
/**
* elem_type - Enum to identify the data type of elements in a data
* structure.
*/
enum elem_type {
QMI_OPT_FLAG = 1,
QMI_DATA_LEN,
QMI_UNSIGNED_1_BYTE,
QMI_UNSIGNED_2_BYTE,
QMI_UNSIGNED_4_BYTE,
QMI_UNSIGNED_8_BYTE,
QMI_SIGNED_2_BYTE_ENUM,
QMI_SIGNED_4_BYTE_ENUM,
QMI_STRUCT,
QMI_END_OF_TYPE_INFO,
};
/**
* array_type - Enum to identify if an element in a data structure is
* an array. If so, then is it a static length array or a
* variable length array.
*/
enum array_type {
NO_ARRAY = 0,
STATIC_ARRAY = 1,
VAR_LEN_ARRAY = 2,
};
/**
* msg_desc - Describe about the main/outer structure to be
* encoded/decoded.
* @msg_id: Message ID to identify the kind of QMI message.
* @max_msg_len: Maximum possible length of the QMI message.
* @ei_array: Array to provide information about a data structure.
*/
struct msg_desc {
uint16_t msg_id;
int max_msg_len;
struct elem_info *ei_array;
};
/**
* qmi_kernel_encode() - Encode to QMI message wire format
* @desc: Structure describing the data structure to be encoded.
* @out_buf: Buffer to hold the encoded QMI message.
* @out_buf_len: Length of the buffer to hold the QMI message.
* @in_c_struct: C Structure to be encoded.
*
* @return: size of encoded message on success,
* -ve value on failure.
*/
int qmi_kernel_encode(struct msg_desc *desc,
void *out_buf, uint32_t out_buf_len,
void *in_c_struct);
/**
* qmi_kernel_decode() - Decode to C Structure format
* @desc: Structure describing the data structure format.
* @out_c_struct: Buffer to hold the decoded C structure.
* @in_buf: Buffer containg the QMI message to be decoded.
* @in_buf_len: Length of the incoming QMI message.
*
* @return: 0 on success, -ve value on failure.
*/
int qmi_kernel_decode(struct msg_desc *desc, void *out_c_struct,
void *in_buf, uint32_t in_buf_len);
Interface Library APIs:
-----------------------
/**
* qmi_svc_event_notifier_register() - Register a notifier block to receive
* events regarding a QMI service
* @service_id: Service ID to identify the QMI service.
* @instance_id: Instance ID to identify the instance of the QMI service.
* @nb: Notifier block used to receive the event.
*
* @return: 0 if successfully registered, < 0 on error.
*/
int qmi_svc_event_notifier_register(uint32_t service_id,
uint32_t instance_id,
struct notifier_block *nb);
/**
* qmi_handle_create() - Create a QMI handle
* @notify: Callback to notify events on the handle created.
* @notify_priv: Private info to be passed along with the notification.
*
* @return: Valid QMI handle on success, NULL on error.
*/
struct qmi_handle *qmi_handle_create(
void (*notify)(struct qmi_handle *handle,
enum qmi_event_type event, void *notify_priv),
void *notify_priv);
/**
* qmi_connect_to_service() - Connect the QMI handle with a QMI service
* @handle: QMI handle to be connected with the QMI service.
* @service_id: Service id to identify the QMI service.
* @instance_id: Instance id to identify the instance of the QMI service.
*
* @return: 0 on success, < 0 on error.
*/
int qmi_connect_to_service(struct qmi_handle *handle,
uint32_t service_id, uint32_t instance_id);
/**
* qmi_register_ind_cb() - Register the indication callback function
* @handle: QMI handle with which the function is registered.
* @ind_cb: Callback function to be registered.
* @ind_cb_priv: Private data to be passed with the indication callback.
*
* @return: 0 on success, < 0 on error.
*/
int qmi_register_ind_cb(struct qmi_handle *handle,
void (*ind_cb)(struct qmi_handle *handle,
unsigned int msg_id, void *msg,
unsigned int msg_len, void *ind_cb_priv),
void *ind_cb_priv);
/**
* qmi_send_req_wait() - Send a synchronous QMI request
* @handle: QMI handle through which the QMI request is sent.
* @req_desc: Structure describing the request data structure.
* @req: Buffer containing the request data structure.
* @req_len: Length of the request data structure.
* @resp_desc: Structure describing the response data structure.
* @resp: Buffer to hold the response data structure.
* @resp_len: Length of the response data structure.
* @timeout_ms: Timeout before a response is received.
*
* @return: 0 on success, < 0 on error.
*/
int qmi_send_req_wait(struct qmi_handle *handle,
struct msg_desc *req_desc,
void *req, unsigned int req_len,
struct msg_desc *resp_desc,
void *resp, unsigned int resp_len,
unsigned long timeout_ms);
/**
* qmi_send_req_nowait() - Send an asynchronous QMI request
* @handle: QMI handle through which the QMI request is sent.
* @req_desc: Structure describing the request data structure.
* @req: Buffer containing the request data structure.
* @req_len: Length of the request data structure.
* @resp_desc: Structure describing the response data structure.
* @resp: Buffer to hold the response data structure.
* @resp_len: Length of the response data structure.
* @resp_cb: Callback function to be invoked when the response arrives.
* @resp_cb_data: Private information to be passed along with the callback.
*
* @return: 0 on success, < 0 on error.
*/
int qmi_send_req_nowait(struct qmi_handle *handle,
struct msg_desc *req_desc,
void *req, unsigned int req_len,
struct msg_desc *resp_desc,
void *resp, unsigned int resp_len,
void (*resp_cb)(struct qmi_handle *handle,
unsigned int msg_id, void *msg,
void *resp_cb_data),
void *resp_cb_data);
/**
* qmi_recv_msg() - Receive the QMI message
* @handle: Handle for which the QMI message has to be received.
*
* @return: 0 on success, < 0 on error.
*/
int qmi_recv_msg(struct qmi_handle *handle);
/**
* qmi_handle_destroy() - Destroy the QMI handle
* @handle: QMI handle to be destroyed.
*
* @return: 0 on success, < 0 on error.
*/
int qmi_handle_destroy(struct qmi_handle *handle);
/**
* qmi_svc_event_notifier_unregister() - Unregister service event notifier block
* @service_id: Service ID to identify the QMI service.
* @instance_id: Instance ID to identify the instance of the QMI service.
* @nb: Notifier block registered to receive the events.
*
* @return: 0 if successfully registered, < 0 on error.
*/
int qmi_svc_event_notifier_unregister(uint32_t service_id,
uint32_t instance_id,
struct notifier_block *nb);
/**
* qmi_svc_ops_options - Operations and options to be specified when
* a service registers.
* @version: Version field to identify the ops_options structure.
* @service_id: Service ID of the service being registered.
* @instance_id: Instance ID of the service being registered.
* @connect_cb: Callback when a new client connects with the service.
* @disconnect_cb: Callback when the client exits the connection.
* @req_desc_cb: Callback to get request structure and its descriptor
* for a message id.
* @req_cb: Callback to process the request.
*/
struct qmi_svc_ops_options {
unsigned version;
uint32_t service_id;
uint32_t instance_id;
int (*connect_cb)(struct qmi_handle *handle,
struct qmi_svc_clnt *clnt);
int (*disconnect_cb)(struct qmi_handle *handle,
struct qmi_svc_clnt *clnt);
struct msg_desc *(*req_desc_cb)(unsigned int msg_id,
void **req,
unsigned int req_len);
int (*req_cb)(struct qmi_handle *handle,
struct qmi_svc_clnt *clnt,
void *req_handle,
unsigned int msg_id,
void *req);
};
/**
* qmi_svc_register() - Register a QMI service with a QMI handle
* @handle: QMI handle on which the service has to be registered.
* @ops_options: Service specific operations and options.
*
* @return: 0 if successfully registered, < 0 on error.
*/
int qmi_svc_register(struct qmi_handle *handle,
void *ops_options);
/**
* qmi_send_resp() - Send response to a request
* @handle: QMI handle from which the response is sent.
* @clnt: Client to which the response is sent.
* @req_handle: Request for which the response is sent.
* @resp_desc: Descriptor explaining the response structure.
* @resp: Pointer to the response structure.
* @resp_len: Length of the response structure.
*
* @return: 0 on success, < 0 on error.
*/
int qmi_send_resp(struct qmi_handle *handle,
struct qmi_svc_clnt *clnt,
void *req_handle,
struct msg_desc *resp_desc,
void *resp,
unsigned int resp_len);
/**
* qmi_send_ind() - Send unsolicited event/indication to a client
* @handle: QMI handle from which the indication is sent.
* @clnt: Client to which the indication is sent.
* @ind_desc: Descriptor explaining the indication structure.
* @ind: Pointer to the indication structure.
* @ind_len: Length of the indication structure.
*
* @return: 0 on success, < 0 on error.
*/
int qmi_send_ind(struct qmi_handle *handle,
struct qmi_svc_clnt *clnt,
struct msg_desc *ind_desc,
void *ind,
unsigned int ind_len);
/**
* qmi_svc_unregister() - Unregister the service from a QMI handle
* @handle: QMI handle from which the service has to be unregistered.
*
* return: 0 on success, < 0 on error.
*/
int qmi_svc_unregister(struct qmi_handle *handle);
User-space APIs:
----------------
This proposal is meant only for kernel QMI clients/services and hence no
user-space interface is defined as part of this proposal.
Driver parameters
=================
N/A
Config options
==============
The QMI encode/decode library will be enabled by default in the kernel.
It can be disabled using CONFIG_QMI_ENCDEC kernel config option.
The QMI Interface library will be disabled by default in the kernel,
since it depends on other components which are disabled by default.
It can be enabled using CONFIG_MSM_QMI_INTERFACE kernel config option.
Dependencies
============
The QMI encode/decode library is a stand-alone module and is not
dependent on any other kernel modules.
The QMI Interface library depends on QMI Encode/Decode library and
IPC Message Router.
User space utilities
====================
N/A
Other
=====
N/A
Known issues
============
N/A
To do
=====
Look into the possibility of making QMI Interface Library transport
agnostic. This may involve the kernel drivers to register the transport,
with the QMI Interface Library, to be used for transporting QMI messages.

View File

@@ -354,3 +354,13 @@ config MSM_IPC_ROUTER_GLINK_XPRT
a System-on-Chip(SoC). When the GLINK channels become available,
this layer registers a transport with IPC Router and enable
message exchange.
config MSM_QMI_INTERFACE
depends on IPC_ROUTER
depends on QMI_ENCDEC
bool "MSM QMI Interface Library"
help
Library to send and receive QMI messages over IPC Router.
This library provides interface functions to the kernel drivers
to perform QMI message marshaling and transport them over IPC
Router.

View File

@@ -38,3 +38,4 @@ obj-$(CONFIG_MSM_IPC_ROUTER_SMD_XPRT) += ipc_router_smd_xprt.o
obj-$(CONFIG_MSM_IPC_ROUTER_HSIC_XPRT) += ipc_router_hsic_xprt.o
obj-$(CONFIG_MSM_IPC_ROUTER_MHI_XPRT) += ipc_router_mhi_xprt.o
obj-$(CONFIG_MSM_IPC_ROUTER_GLINK_XPRT) += ipc_router_glink_xprt.o
obj-$(CONFIG_MSM_QMI_INTERFACE) += qmi_interface.o

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,123 @@
/* Copyright (c) 2012-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 _QMI_INTERFACE_PRIV_H_
#define _QMI_INTERFACE_PRIV_H_
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/list.h>
#include <linux/socket.h>
#include <linux/gfp.h>
#include <linux/platform_device.h>
#include <linux/qmi_encdec.h>
#include <soc/qcom/msm_qmi_interface.h>
enum txn_type {
QMI_SYNC_TXN = 1,
QMI_ASYNC_TXN,
};
/**
* handle_type - Enum to identify QMI handle type
*/
enum handle_type {
QMI_CLIENT_HANDLE = 1,
QMI_SERVICE_HANDLE,
};
struct qmi_txn {
struct list_head list;
uint16_t txn_id;
enum txn_type type;
struct qmi_handle *handle;
void *enc_data;
unsigned int enc_data_len;
struct msg_desc *resp_desc;
void *resp;
unsigned int resp_len;
int resp_received;
int send_stat;
void (*resp_cb)(struct qmi_handle *handle, unsigned int msg_id,
void *msg, void *resp_cb_data, int stat);
void *resp_cb_data;
wait_queue_head_t wait_q;
};
/**
* svc_addr - Data structure to maintain a list of service addresses.
* @list_node: Service address list node used by "svc_addr_list"
* @port_addr: Service address in <node_id:port_id>.
*/
struct svc_addr {
struct list_head list_node;
struct msm_ipc_port_addr port_addr;
};
/**
* svc_event_nb - Service event notification structure.
* @nb_lock: Spinlock for the notifier block lists.
* @service_id: Service id for which list of notifier blocks are maintained.
* @instance_id: Instance id for which list of notifier blocks are maintained.
* @svc_event_rcvr_list: List of notifier blocks which clients have registered.
* @list: Used to chain this structure in a global list.
* @svc_addr_list_lock: Lock to protect @svc_addr_list.
* @svc_addr_list: List for mantaining all the address for a specific
* <service_id:instance_id>.
*/
struct svc_event_nb {
spinlock_t nb_lock;
uint32_t service_id;
uint32_t instance_id;
struct raw_notifier_head svc_event_rcvr_list;
struct list_head list;
struct mutex svc_addr_list_lock;
struct list_head svc_addr_list;
};
/**
* req_handle - Data structure to store request information
* @list: Points to req_handle_list maintained per connection.
* @conn_h: Connection handle on which the concerned request is received.
* @msg_id: Message ID of the request.
* @txn_id: Transaction ID of the request.
*/
struct req_handle {
struct list_head list;
struct qmi_svc_clnt_conn *conn_h;
uint16_t msg_id;
uint16_t txn_id;
};
/**
* qmi_svc_clnt_conn - Data structure to identify client service connection
* @list: List to chain up the client conncection to the connection list.
* @svc_handle: Service side information of the connection.
* @clnt_addr: Client side information of the connection.
* @clnt_addr_len: Length of the client address.
* @req_handle_list: Pending requests in this connection.
* @pending_tx_list: Pending response/indications awaiting flow control.
*/
struct qmi_svc_clnt_conn {
struct list_head list;
void *svc_handle;
void *clnt_addr;
size_t clnt_addr_len;
struct list_head req_handle_list;
struct delayed_work resume_tx_work;
struct list_head pending_txn_list;
struct mutex pending_txn_lock;
};
#endif

184
include/linux/qmi_encdec.h Normal file
View File

@@ -0,0 +1,184 @@
/* Copyright (c) 2012-2014, 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 _QMI_ENCDEC_H_
#define _QMI_ENCDEC_H_
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/list.h>
#include <linux/socket.h>
#include <linux/gfp.h>
#define QMI_REQUEST_CONTROL_FLAG 0x00
#define QMI_RESPONSE_CONTROL_FLAG 0x02
#define QMI_INDICATION_CONTROL_FLAG 0x04
#define QMI_HEADER_SIZE 7
/**
* elem_type - Enum to identify the data type of elements in a data
* structure.
*/
enum elem_type {
QMI_OPT_FLAG = 1,
QMI_DATA_LEN,
QMI_UNSIGNED_1_BYTE,
QMI_UNSIGNED_2_BYTE,
QMI_UNSIGNED_4_BYTE,
QMI_UNSIGNED_8_BYTE,
QMI_SIGNED_2_BYTE_ENUM,
QMI_SIGNED_4_BYTE_ENUM,
QMI_STRUCT,
QMI_STRING,
QMI_EOTI,
};
/**
* array_type - Enum to identify if an element in a data structure is
* an array. If so, then is it a static length array or a
* variable length array.
*/
enum array_type {
NO_ARRAY = 0,
STATIC_ARRAY = 1,
VAR_LEN_ARRAY = 2,
};
/**
* elem_info - Data structure to specify information about an element
* in a data structure. An array of this data structure
* can be used to specify info about a complex data
* structure to be encoded/decoded.
*
* @data_type: Data type of this element.
* @elem_len: Array length of this element, if an array.
* @elem_size: Size of a single instance of this data type.
* @is_array: Array type of this element.
* @tlv_type: QMI message specific type to identify which element
* is present in an incoming message.
* @offset: To identify the address of the first instance of this
* element in the data structure.
* @ei_array: Array to provide information about the nested structure
* within a data structure to be encoded/decoded.
*/
struct elem_info {
enum elem_type data_type;
uint32_t elem_len;
uint32_t elem_size;
enum array_type is_array;
uint8_t tlv_type;
uint32_t offset;
struct elem_info *ei_array;
};
/**
* @msg_desc - Describe about the main/outer structure to be
* encoded/decoded.
*
* @max_msg_len: Maximum possible length of the QMI message.
* @ei_array: Array to provide information about a data structure.
*/
struct msg_desc {
uint16_t msg_id;
int max_msg_len;
struct elem_info *ei_array;
};
struct qmi_header {
unsigned char cntl_flag;
uint16_t txn_id;
uint16_t msg_id;
uint16_t msg_len;
} __attribute__((__packed__));
static inline void encode_qmi_header(unsigned char *buf,
unsigned char cntl_flag, uint16_t txn_id,
uint16_t msg_id, uint16_t msg_len)
{
struct qmi_header *hdr = (struct qmi_header *)buf;
hdr->cntl_flag = cntl_flag;
hdr->txn_id = txn_id;
hdr->msg_id = msg_id;
hdr->msg_len = msg_len;
}
static inline void decode_qmi_header(unsigned char *buf,
unsigned char *cntl_flag, uint16_t *txn_id,
uint16_t *msg_id, uint16_t *msg_len)
{
struct qmi_header *hdr = (struct qmi_header *)buf;
*cntl_flag = hdr->cntl_flag;
*txn_id = hdr->txn_id;
*msg_id = hdr->msg_id;
*msg_len = hdr->msg_len;
}
#ifdef CONFIG_QMI_ENCDEC
/**
* qmi_kernel_encode() - Encode to QMI message wire format
* @desc: Pointer to structure descriptor.
* @out_buf: Buffer to hold the encoded QMI message.
* @out_buf_len: Length of the out buffer.
* @in_c_struct: C Structure to be encoded.
*
* @return: size of encoded message on success, < 0 on error.
*/
int qmi_kernel_encode(struct msg_desc *desc,
void *out_buf, uint32_t out_buf_len,
void *in_c_struct);
/**
* qmi_kernel_decode() - Decode to C Structure format
* @desc: Pointer to structure descriptor.
* @out_c_struct: Buffer to hold the decoded C structure.
* @in_buf: Buffer containg the QMI message to be decoded.
* @in_buf_len: Length of the incoming QMI message.
*
* @return: 0 on success, < 0 on error.
*/
int qmi_kernel_decode(struct msg_desc *desc, void *out_c_struct,
void *in_buf, uint32_t in_buf_len);
/**
* qmi_verify_max_msg_len() - Verify the maximum length of a QMI message
* @desc: Pointer to structure descriptor.
*
* @return: true if the maximum message length embedded in structure
* descriptor matches the calculated value, else false.
*/
bool qmi_verify_max_msg_len(struct msg_desc *desc);
#else
static inline int qmi_kernel_encode(struct msg_desc *desc,
void *out_buf, uint32_t out_buf_len,
void *in_c_struct)
{
return -EOPNOTSUPP;
}
static inline int qmi_kernel_decode(struct msg_desc *desc,
void *out_c_struct,
void *in_buf, uint32_t in_buf_len)
{
return -EOPNOTSUPP;
}
static inline bool qmi_verify_max_msg_len(struct msg_desc *desc)
{
return false;
}
#endif
#endif

View File

@@ -0,0 +1,501 @@
/* Copyright (c) 2012-2016, 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 _MSM_QMI_INTERFACE_H_
#define _MSM_QMI_INTERFACE_H_
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/list.h>
#include <linux/socket.h>
#include <linux/gfp.h>
#include <linux/qmi_encdec.h>
#include <linux/workqueue.h>
#define QMI_COMMON_TLV_TYPE 0
enum qmi_event_type {
QMI_RECV_MSG = 1,
QMI_SERVER_ARRIVE,
QMI_SERVER_EXIT,
};
/**
* struct qmi_handle - QMI Handle Data Structure
* @handle_hash: Hash Table Node in which this handle is present.
* @src_port: Pointer to port used for message exchange.
* @ctl_port: Pointer to port used for out-of-band event exchange.
* @handle_type: Type of handle(Service/Client).
* @next_txn_id: Transaction ID of the next outgoing request.
* @handle_wq: Workqueue to handle any handle-specific events.
* @handle_lock: Lock to protect access to elements in the handle.
* @notify_lock: Lock to protect and generate notification atomically.
* @notify: Function to notify the handle owner of an event.
* @notify_priv: Private info to be passed during the notifcation.
* @handle_reset: Flag to hold the reset state of the handle.
* @reset_waitq: Wait queue to wait for any reset events.
* @ctl_work: Work to handle the out-of-band events for this handle.
* @dest_info: Destination to which this handle is connected to.
* @dest_service_id: service id of the service that client connected to.
* @txn_list: List of transactions waiting for the response.
* @ind_cb: Function to notify the handle owner of an indication message.
* @ind_cb_priv: Private info to be passed during an indication notification.
* @resume_tx_work: Work to resume the tx when the transport is not busy.
* @pending_txn_list: List of requests pending tx due to busy transport.
* @conn_list: List of connections handled by the service.
* @svc_ops_options: Service specific operations and options.
*/
struct qmi_handle {
struct hlist_node handle_hash;
void *src_port;
void *ctl_port;
unsigned int handle_type;
uint16_t next_txn_id;
struct workqueue_struct *handle_wq;
struct mutex handle_lock;
spinlock_t notify_lock;
void (*notify)(struct qmi_handle *handle, enum qmi_event_type event,
void *notify_priv);
void *notify_priv;
int handle_reset;
wait_queue_head_t reset_waitq;
struct delayed_work ctl_work;
/* Client specific elements */
void *dest_info;
uint32_t dest_service_id;
struct list_head txn_list;
void (*ind_cb)(struct qmi_handle *handle,
unsigned int msg_id, void *msg,
unsigned int msg_len, void *ind_cb_priv);
void *ind_cb_priv;
struct delayed_work resume_tx_work;
struct list_head pending_txn_list;
/* Service specific elements */
struct list_head conn_list;
struct qmi_svc_ops_options *svc_ops_options;
};
enum qmi_result_type_v01 {
/* To force a 32 bit signed enum. Do not change or use*/
QMI_RESULT_TYPE_MIN_ENUM_VAL_V01 = INT_MIN,
QMI_RESULT_SUCCESS_V01 = 0,
QMI_RESULT_FAILURE_V01 = 1,
QMI_RESULT_TYPE_MAX_ENUM_VAL_V01 = INT_MAX,
};
enum qmi_error_type_v01 {
/* To force a 32 bit signed enum. Do not change or use*/
QMI_ERR_TYPE_MIN_ENUM_VAL_V01 = INT_MIN,
QMI_ERR_NONE_V01 = 0x0000,
QMI_ERR_MALFORMED_MSG_V01 = 0x0001,
QMI_ERR_NO_MEMORY_V01 = 0x0002,
QMI_ERR_INTERNAL_V01 = 0x0003,
QMI_ERR_CLIENT_IDS_EXHAUSTED_V01 = 0x0005,
QMI_ERR_INVALID_ID_V01 = 0x0029,
QMI_ERR_ENCODING_V01 = 0x003A,
QMI_ERR_INCOMPATIBLE_STATE_V01 = 0x005A,
QMI_ERR_NOT_SUPPORTED_V01 = 0x005E,
QMI_ERR_TYPE_MAX_ENUM_VAL_V01 = INT_MAX,
};
struct qmi_response_type_v01 {
enum qmi_result_type_v01 result;
enum qmi_error_type_v01 error;
};
/**
* qmi_svc_ops_options - Operations and options to be specified when
* a service registers.
* @version: Version field to identify the ops_options structure.
* @service_id: Service ID of the service.
* @service_vers: Version to identify the client-service compatibility.
* @service_ins: Instance ID registered by the service.
* @connect_cb: Callback when a new client connects with the service.
* @disconnect_cb: Callback when the client exits the connection.
* @req_desc_cb: Callback to get request structure and its descriptor
* for a message id.
* @req_cb: Callback to process the request.
*/
struct qmi_svc_ops_options {
unsigned int version;
uint32_t service_id;
uint32_t service_vers;
uint32_t service_ins;
int (*connect_cb)(struct qmi_handle *handle,
void *conn_handle);
int (*disconnect_cb)(struct qmi_handle *handle,
void *conn_handle);
int (*req_desc_cb)(unsigned int msg_id,
struct msg_desc **req_desc);
int (*req_cb)(struct qmi_handle *handle,
void *conn_handle,
void *req_handle,
unsigned int msg_id,
void *req);
};
#ifdef CONFIG_MSM_QMI_INTERFACE
/* Element info array describing common qmi response structure */
extern struct elem_info qmi_response_type_v01_ei[];
#define get_qmi_response_type_v01_ei() qmi_response_type_v01_ei
/**
* qmi_handle_create() - Create a QMI handle
* @notify: Callback to notify events on the handle created.
* @notify_priv: Private information to be passed along with the notification.
*
* @return: Valid QMI handle on success, NULL on error.
*/
struct qmi_handle *qmi_handle_create(
void (*notify)(struct qmi_handle *handle,
enum qmi_event_type event, void *notify_priv),
void *notify_priv);
/**
* qmi_handle_destroy() - Destroy the QMI handle
* @handle: QMI handle to be destroyed.
*
* @return: 0 on success, < 0 on error.
*/
int qmi_handle_destroy(struct qmi_handle *handle);
/**
* qmi_register_ind_cb() - Register the indication callback function
* @handle: QMI handle with which the function is registered.
* @ind_cb: Callback function to be registered.
* @ind_cb_priv: Private data to be passed with the indication callback.
*
* @return: 0 on success, < 0 on error.
*/
int qmi_register_ind_cb(struct qmi_handle *handle,
void (*ind_cb)(struct qmi_handle *handle,
unsigned int msg_id, void *msg,
unsigned int msg_len, void *ind_cb_priv),
void *ind_cb_priv);
/**
* qmi_send_req_wait() - Send a synchronous QMI request
* @handle: QMI handle through which the QMI request is sent.
* @request_desc: Structure describing the request data structure.
* @req: Buffer containing the request data structure.
* @req_len: Length of the request data structure.
* @resp_desc: Structure describing the response data structure.
* @resp: Buffer to hold the response data structure.
* @resp_len: Length of the response data structure.
* @timeout_ms: Timeout before a response is received.
*
* @return: 0 on success, < 0 on error.
*/
int qmi_send_req_wait(struct qmi_handle *handle,
struct msg_desc *req_desc,
void *req, unsigned int req_len,
struct msg_desc *resp_desc,
void *resp, unsigned int resp_len,
unsigned long timeout_ms);
/**
* qmi_send_req_nowait() - Send an asynchronous QMI request
* @handle: QMI handle through which the QMI request is sent.
* @request_desc: Structure describing the request data structure.
* @req: Buffer containing the request data structure.
* @req_len: Length of the request data structure.
* @resp_desc: Structure describing the response data structure.
* @resp: Buffer to hold the response data structure.
* @resp_len: Length of the response data structure.
* @resp_cb: Callback function to be invoked when the response arrives.
* @resp_cb_data: Private information to be passed along with the callback.
*
* @return: 0 on success, < 0 on error.
*/
int qmi_send_req_nowait(struct qmi_handle *handle,
struct msg_desc *req_desc,
void *req, unsigned int req_len,
struct msg_desc *resp_desc,
void *resp, unsigned int resp_len,
void (*resp_cb)(struct qmi_handle *handle,
unsigned int msg_id, void *msg,
void *resp_cb_data,
int stat),
void *resp_cb_data);
/**
* qmi_recv_msg() - Receive the QMI message
* @handle: Handle for which the QMI message has to be received.
*
* @return: 0 on success, < 0 on error.
*/
int qmi_recv_msg(struct qmi_handle *handle);
/**
* qmi_connect_to_service() - Connect the QMI handle with a QMI service
* @handle: QMI handle to be connected with the QMI service.
* @service_id: Service id to identify the QMI service.
* @service_vers: Version to identify the compatibility.
* @service_ins: Instance id to identify the instance of the QMI service.
*
* @return: 0 on success, < 0 on error.
*/
int qmi_connect_to_service(struct qmi_handle *handle,
uint32_t service_id,
uint32_t service_vers,
uint32_t service_ins);
/**
* qmi_svc_event_notifier_register() - Register a notifier block to receive
* events regarding a QMI service
* @service_id: Service ID to identify the QMI service.
* @service_vers: Version to identify the compatibility.
* @service_ins: Instance ID to identify the instance of the QMI service.
* @nb: Notifier block used to receive the event.
*
* @return: 0 if successfully registered, < 0 on error.
*/
int qmi_svc_event_notifier_register(uint32_t service_id,
uint32_t service_vers,
uint32_t service_ins,
struct notifier_block *nb);
/**
* qmi_svc_event_notifier_unregister() - Unregister service event
* notifier block
* @service_id: Service ID to identify the QMI service.
* @service_vers: Version to identify the compatibility.
* @service_ins: Instance ID to identify the instance of the QMI service.
* @nb: Notifier block registered to receive the events.
*
* @return: 0 if successfully registered, < 0 on error.
*/
int qmi_svc_event_notifier_unregister(uint32_t service_id,
uint32_t service_vers,
uint32_t service_ins,
struct notifier_block *nb);
/**
* qmi_svc_register() - Register a QMI service with a QMI handle
* @handle: QMI handle on which the service has to be registered.
* @ops_options: Service specific operations and options.
*
* @return: 0 if successfully registered, < 0 on error.
*/
int qmi_svc_register(struct qmi_handle *handle,
void *ops_options);
/**
* qmi_send_resp() - Send response to a request
* @handle: QMI handle from which the response is sent.
* @clnt: Client to which the response is sent.
* @req_handle: Request for which the response is sent.
* @resp_desc: Descriptor explaining the response structure.
* @resp: Pointer to the response structure.
* @resp_len: Length of the response structure.
*
* @return: 0 on success, < 0 on error.
*/
int qmi_send_resp(struct qmi_handle *handle,
void *conn_handle,
void *req_handle,
struct msg_desc *resp_desc,
void *resp,
unsigned int resp_len);
/**
* qmi_send_resp_from_cb() - Send response to a request from request_cb
* @handle: QMI handle from which the response is sent.
* @clnt: Client to which the response is sent.
* @req_handle: Request for which the response is sent.
* @resp_desc: Descriptor explaining the response structure.
* @resp: Pointer to the response structure.
* @resp_len: Length of the response structure.
*
* @return: 0 on success, < 0 on error.
*/
int qmi_send_resp_from_cb(struct qmi_handle *handle,
void *conn_handle,
void *req_handle,
struct msg_desc *resp_desc,
void *resp,
unsigned int resp_len);
/**
* qmi_send_ind() - Send unsolicited event/indication to a client
* @handle: QMI handle from which the indication is sent.
* @clnt: Client to which the indication is sent.
* @ind_desc: Descriptor explaining the indication structure.
* @ind: Pointer to the indication structure.
* @ind_len: Length of the indication structure.
*
* @return: 0 on success, < 0 on error.
*/
int qmi_send_ind(struct qmi_handle *handle,
void *conn_handle,
struct msg_desc *ind_desc,
void *ind,
unsigned int ind_len);
/**
* qmi_send_ind_from_cb() - Send indication to a client from registration_cb
* @handle: QMI handle from which the indication is sent.
* @clnt: Client to which the indication is sent.
* @ind_desc: Descriptor explaining the indication structure.
* @ind: Pointer to the indication structure.
* @ind_len: Length of the indication structure.
*
* @return: 0 on success, < 0 on error.
*/
int qmi_send_ind_from_cb(struct qmi_handle *handle,
void *conn_handle,
struct msg_desc *ind_desc,
void *ind,
unsigned int ind_len);
/**
* qmi_svc_unregister() - Unregister the service from a QMI handle
* @handle: QMI handle from which the service has to be unregistered.
*
* return: 0 on success, < 0 on error.
*/
int qmi_svc_unregister(struct qmi_handle *handle);
#else
#define get_qmi_response_type_v01_ei() NULL
static inline struct qmi_handle *qmi_handle_create(
void (*notify)(struct qmi_handle *handle,
enum qmi_event_type event, void *notify_priv),
void *notify_priv)
{
return NULL;
}
static inline int qmi_handle_destroy(struct qmi_handle *handle)
{
return -ENODEV;
}
static inline int qmi_register_ind_cb(struct qmi_handle *handle,
void (*ind_cb)(struct qmi_handle *handle,
unsigned int msg_id, void *msg,
unsigned int msg_len, void *ind_cb_priv),
void *ind_cb_priv)
{
return -ENODEV;
}
static inline int qmi_send_req_wait(struct qmi_handle *handle,
struct msg_desc *req_desc,
void *req, unsigned int req_len,
struct msg_desc *resp_desc,
void *resp, unsigned int resp_len,
unsigned long timeout_ms)
{
return -ENODEV;
}
static inline int qmi_send_req_nowait(struct qmi_handle *handle,
struct msg_desc *req_desc,
void *req, unsigned int req_len,
struct msg_desc *resp_desc,
void *resp, unsigned int resp_len,
void (*resp_cb)(struct qmi_handle *handle,
unsigned int msg_id, void *msg,
void *resp_cb_data),
void *resp_cb_data)
{
return -ENODEV;
}
static inline int qmi_recv_msg(struct qmi_handle *handle)
{
return -ENODEV;
}
static inline int qmi_connect_to_service(struct qmi_handle *handle,
uint32_t service_id,
uint32_t service_vers,
uint32_t service_ins)
{
return -ENODEV;
}
static inline int qmi_svc_event_notifier_register(uint32_t service_id,
uint32_t service_vers,
uint32_t service_ins,
struct notifier_block *nb)
{
return -ENODEV;
}
static inline int qmi_svc_event_notifier_unregister(uint32_t service_id,
uint32_t service_vers,
uint32_t service_ins,
struct notifier_block *nb)
{
return -ENODEV;
}
static inline int qmi_svc_register(struct qmi_handle *handle,
void *ops_options)
{
return -ENODEV;
}
static inline int qmi_send_resp(struct qmi_handle *handle,
void *conn_handle,
void *req_handle,
struct msg_desc *resp_desc,
void *resp,
unsigned int resp_len)
{
return -ENODEV;
}
static inline int qmi_send_resp_from_cb(struct qmi_handle *handle,
void *conn_handle,
void *req_handle,
struct msg_desc *resp_desc,
void *resp,
unsigned int resp_len)
{
return -ENODEV;
}
static inline int qmi_send_ind(struct qmi_handle *handle,
void *conn_handle,
struct msg_desc *ind_desc,
void *ind,
unsigned int ind_len)
{
return -ENODEV;
}
static inline int qmi_send_ind_from_cb(struct qmi_handle *handle,
void *conn_handle,
struct msg_desc *ind_desc,
void *ind,
unsigned int ind_len)
{
return -ENODEV;
}
static inline int qmi_svc_unregister(struct qmi_handle *handle)
{
return -ENODEV;
}
#endif
#endif

View File

@@ -550,4 +550,20 @@ config STACKDEPOT
config SBITMAP
bool
config QMI_ENCDEC
bool "QMI Encode/Decode Library"
help
Library to encode & decode QMI messages from within
the kernel. The kernel drivers encode the C structure into
QMI message wire format and then send it over a transport.
The kernel drivers receive the QMI message over a transport
and then decode it into a C structure.
config QMI_ENCDEC_DEBUG
bool "QMI Encode/Decode Library Debug"
help
Kernel config option to enable debugging QMI Encode/Decode
library. This will log the information regarding the element
and message being encoded & decoded.
endmenu

View File

@@ -230,3 +230,4 @@ obj-$(CONFIG_UBSAN) += ubsan.o
UBSAN_SANITIZE_ubsan.o := n
obj-$(CONFIG_SBITMAP) += sbitmap.o
obj-$(CONFIG_QMI_ENCDEC) += qmi_encdec.o

877
lib/qmi_encdec.c Normal file
View File

@@ -0,0 +1,877 @@
/* Copyright (c) 2012-2016, 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.
*
*/
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/string.h>
#include <linux/qmi_encdec.h>
#include "qmi_encdec_priv.h"
#define TLV_LEN_SIZE sizeof(uint16_t)
#define TLV_TYPE_SIZE sizeof(uint8_t)
#define OPTIONAL_TLV_TYPE_START 0x10
#ifdef CONFIG_QMI_ENCDEC_DEBUG
#define qmi_encdec_dump(prefix_str, buf, buf_len) do { \
const u8 *ptr = buf; \
int i, linelen, remaining = buf_len; \
int rowsize = 16, groupsize = 1; \
unsigned char linebuf[256]; \
for (i = 0; i < buf_len; i += rowsize) { \
linelen = min(remaining, rowsize); \
remaining -= linelen; \
hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize, \
linebuf, sizeof(linebuf), false); \
pr_debug("%s: %s\n", prefix_str, linebuf); \
} \
} while (0)
#define QMI_ENCODE_LOG_MSG(buf, buf_len) \
qmi_encdec_dump("QMI_ENCODE_MSG", buf, buf_len)
#define QMI_DECODE_LOG_MSG(buf, buf_len) \
qmi_encdec_dump("QMI_DECODE_MSG", buf, buf_len)
#define QMI_ENCODE_LOG_ELEM(level, elem_len, elem_size, buf) do { \
pr_debug("QMI_ENCODE_ELEM lvl: %d, len: %d, size: %d\n", \
level, elem_len, elem_size); \
qmi_encdec_dump("QMI_ENCODE_ELEM", buf, (elem_len * elem_size)); \
} while (0)
#define QMI_DECODE_LOG_ELEM(level, elem_len, elem_size, buf) do { \
pr_debug("QMI_DECODE_ELEM lvl: %d, len: %d, size: %d\n", \
level, elem_len, elem_size); \
qmi_encdec_dump("QMI_DECODE_ELEM", buf, (elem_len * elem_size)); \
} while (0)
#define QMI_ENCODE_LOG_TLV(tlv_type, tlv_len) \
pr_debug("QMI_ENCODE_TLV type: %d, len: %d\n", tlv_type, tlv_len)
#define QMI_DECODE_LOG_TLV(tlv_type, tlv_len) \
pr_debug("QMI_DECODE_TLV type: %d, len: %d\n", tlv_type, tlv_len)
#else
#define QMI_ENCODE_LOG_MSG(buf, buf_len) { }
#define QMI_DECODE_LOG_MSG(buf, buf_len) { }
#define QMI_ENCODE_LOG_ELEM(level, elem_len, elem_size, buf) { }
#define QMI_DECODE_LOG_ELEM(level, elem_len, elem_size, buf) { }
#define QMI_ENCODE_LOG_TLV(tlv_type, tlv_len) { }
#define QMI_DECODE_LOG_TLV(tlv_type, tlv_len) { }
#endif
static int _qmi_kernel_encode(struct elem_info *ei_array,
void *out_buf, void *in_c_struct,
uint32_t out_buf_len, int enc_level);
static int _qmi_kernel_decode(struct elem_info *ei_array,
void *out_c_struct,
void *in_buf, uint32_t in_buf_len,
int dec_level);
static struct elem_info *skip_to_next_elem(struct elem_info *ei_array,
int level);
/**
* qmi_calc_max_msg_len() - Calculate the maximum length of a QMI message
* @ei_array: Struct info array describing the structure.
* @level: Level to identify the depth of the nested structures.
*
* @return: expected maximum length of the QMI message or 0 on failure.
*/
static int qmi_calc_max_msg_len(struct elem_info *ei_array,
int level)
{
int max_msg_len = 0;
struct elem_info *temp_ei;
if (!ei_array)
return max_msg_len;
for (temp_ei = ei_array; temp_ei->data_type != QMI_EOTI; temp_ei++) {
/* Flag to identify the optional element is not encoded */
if (temp_ei->data_type == QMI_OPT_FLAG)
continue;
if (temp_ei->data_type == QMI_DATA_LEN) {
max_msg_len += (temp_ei->elem_size == sizeof(uint8_t) ?
sizeof(uint8_t) : sizeof(uint16_t));
continue;
} else if (temp_ei->data_type == QMI_STRUCT) {
max_msg_len += (temp_ei->elem_len *
qmi_calc_max_msg_len(temp_ei->ei_array,
(level + 1)));
} else if (temp_ei->data_type == QMI_STRING) {
if (level > 1)
max_msg_len += temp_ei->elem_len <= U8_MAX ?
sizeof(uint8_t) : sizeof(uint16_t);
max_msg_len += temp_ei->elem_len * temp_ei->elem_size;
} else {
max_msg_len += (temp_ei->elem_len * temp_ei->elem_size);
}
/*
* Type & Length info. not prepended for elements in the
* nested structure.
*/
if (level == 1)
max_msg_len += (TLV_TYPE_SIZE + TLV_LEN_SIZE);
}
return max_msg_len;
}
/**
* qmi_calc_min_msg_len() - Calculate the minimum length of a QMI message
* @ei_array: Struct info array describing the structure.
* @level: Level to identify the depth of the nested structures.
*
* @return: expected minimum length of the QMI message or 0 on failure.
*/
static int qmi_calc_min_msg_len(struct elem_info *ei_array,
int level)
{
int min_msg_len = 0;
struct elem_info *temp_ei = ei_array;
if (!ei_array)
return min_msg_len;
while (temp_ei->data_type != QMI_EOTI) {
/* Optional elements do not count in minimum length */
if (temp_ei->data_type == QMI_OPT_FLAG) {
temp_ei = skip_to_next_elem(temp_ei, level);
continue;
}
if (temp_ei->data_type == QMI_DATA_LEN) {
min_msg_len += (temp_ei->elem_size == sizeof(uint8_t) ?
sizeof(uint8_t) : sizeof(uint16_t));
temp_ei++;
continue;
} else if (temp_ei->data_type == QMI_STRUCT) {
min_msg_len += qmi_calc_min_msg_len(temp_ei->ei_array,
(level + 1));
temp_ei++;
} else if (temp_ei->data_type == QMI_STRING) {
if (level > 1)
min_msg_len += temp_ei->elem_len <= U8_MAX ?
sizeof(uint8_t) : sizeof(uint16_t);
min_msg_len += temp_ei->elem_len * temp_ei->elem_size;
temp_ei++;
} else {
min_msg_len += (temp_ei->elem_len * temp_ei->elem_size);
temp_ei++;
}
/*
* Type & Length info. not prepended for elements in the
* nested structure.
*/
if (level == 1)
min_msg_len += (TLV_TYPE_SIZE + TLV_LEN_SIZE);
}
return min_msg_len;
}
/**
* qmi_verify_max_msg_len() - Verify the maximum length of a QMI message
* @desc: Pointer to structure descriptor.
*
* @return: true if the maximum message length embedded in structure
* descriptor matches the calculated value, else false.
*/
bool qmi_verify_max_msg_len(struct msg_desc *desc)
{
int calc_max_msg_len;
if (!desc)
return false;
calc_max_msg_len = qmi_calc_max_msg_len(desc->ei_array, 1);
if (calc_max_msg_len != desc->max_msg_len) {
pr_err("%s: Calc. len %d != Passed len %d\n",
__func__, calc_max_msg_len, desc->max_msg_len);
return false;
}
return true;
}
/**
* qmi_kernel_encode() - Encode to QMI message wire format
* @desc: Pointer to structure descriptor.
* @out_buf: Buffer to hold the encoded QMI message.
* @out_buf_len: Length of the out buffer.
* @in_c_struct: C Structure to be encoded.
*
* @return: size of encoded message on success, < 0 for error.
*/
int qmi_kernel_encode(struct msg_desc *desc,
void *out_buf, uint32_t out_buf_len,
void *in_c_struct)
{
int enc_level = 1;
int ret, calc_max_msg_len, calc_min_msg_len;
if (!desc)
return -EINVAL;
/* Check the possibility of a zero length QMI message */
if (!in_c_struct) {
calc_min_msg_len = qmi_calc_min_msg_len(desc->ei_array, 1);
if (calc_min_msg_len) {
pr_err("%s: Calc. len %d != 0, but NULL in_c_struct\n",
__func__, calc_min_msg_len);
return -EINVAL;
} else {
return 0;
}
}
/*
* Not a zero-length message. Ensure the output buffer and
* element information array are not NULL.
*/
if (!out_buf || !desc->ei_array)
return -EINVAL;
if (desc->max_msg_len < out_buf_len)
return -ETOOSMALL;
ret = _qmi_kernel_encode(desc->ei_array, out_buf,
in_c_struct, out_buf_len, enc_level);
if (ret == -ETOOSMALL) {
calc_max_msg_len = qmi_calc_max_msg_len(desc->ei_array, 1);
pr_err("%s: Calc. len %d != Out buf len %d\n",
__func__, calc_max_msg_len, out_buf_len);
}
return ret;
}
EXPORT_SYMBOL(qmi_kernel_encode);
/**
* qmi_encode_basic_elem() - Encodes elements of basic/primary data type
* @buf_dst: Buffer to store the encoded information.
* @buf_src: Buffer containing the elements to be encoded.
* @elem_len: Number of elements, in the buf_src, to be encoded.
* @elem_size: Size of a single instance of the element to be encoded.
*
* @return: number of bytes of encoded information.
*
* This function encodes the "elem_len" number of data elements, each of
* size "elem_size" bytes from the source buffer "buf_src" and stores the
* encoded information in the destination buffer "buf_dst". The elements are
* of primary data type which include uint8_t - uint64_t or similar. This
* function returns the number of bytes of encoded information.
*/
static int qmi_encode_basic_elem(void *buf_dst, void *buf_src,
uint32_t elem_len, uint32_t elem_size)
{
uint32_t i, rc = 0;
for (i = 0; i < elem_len; i++) {
QMI_ENCDEC_ENCODE_N_BYTES(buf_dst, buf_src, elem_size);
rc += elem_size;
}
return rc;
}
/**
* qmi_encode_struct_elem() - Encodes elements of struct data type
* @ei_array: Struct info array descibing the struct element.
* @buf_dst: Buffer to store the encoded information.
* @buf_src: Buffer containing the elements to be encoded.
* @elem_len: Number of elements, in the buf_src, to be encoded.
* @out_buf_len: Available space in the encode buffer.
* @enc_level: Depth of the nested structure from the main structure.
*
* @return: Number of bytes of encoded information, on success.
* < 0 on error.
*
* This function encodes the "elem_len" number of struct elements, each of
* size "ei_array->elem_size" bytes from the source buffer "buf_src" and
* stores the encoded information in the destination buffer "buf_dst". The
* elements are of struct data type which includes any C structure. This
* function returns the number of bytes of encoded information.
*/
static int qmi_encode_struct_elem(struct elem_info *ei_array,
void *buf_dst, void *buf_src,
uint32_t elem_len, uint32_t out_buf_len,
int enc_level)
{
int i, rc, encoded_bytes = 0;
struct elem_info *temp_ei = ei_array;
for (i = 0; i < elem_len; i++) {
rc = _qmi_kernel_encode(temp_ei->ei_array, buf_dst, buf_src,
(out_buf_len - encoded_bytes),
enc_level);
if (rc < 0) {
pr_err("%s: STRUCT Encode failure\n", __func__);
return rc;
}
buf_dst = buf_dst + rc;
buf_src = buf_src + temp_ei->elem_size;
encoded_bytes += rc;
}
return encoded_bytes;
}
/**
* qmi_encode_string_elem() - Encodes elements of string data type
* @ei_array: Struct info array descibing the string element.
* @buf_dst: Buffer to store the encoded information.
* @buf_src: Buffer containing the elements to be encoded.
* @out_buf_len: Available space in the encode buffer.
* @enc_level: Depth of the string element from the main structure.
*
* @return: Number of bytes of encoded information, on success.
* < 0 on error.
*
* This function encodes a string element of maximum length "ei_array->elem_len"
* bytes from the source buffer "buf_src" and stores the encoded information in
* the destination buffer "buf_dst". This function returns the number of bytes
* of encoded information.
*/
static int qmi_encode_string_elem(struct elem_info *ei_array,
void *buf_dst, void *buf_src,
uint32_t out_buf_len, int enc_level)
{
int rc;
int encoded_bytes = 0;
struct elem_info *temp_ei = ei_array;
uint32_t string_len = 0;
uint32_t string_len_sz = 0;
string_len = strlen(buf_src);
string_len_sz = temp_ei->elem_len <= U8_MAX ?
sizeof(uint8_t) : sizeof(uint16_t);
if (string_len > temp_ei->elem_len) {
pr_err("%s: String to be encoded is longer - %d > %d\n",
__func__, string_len, temp_ei->elem_len);
return -EINVAL;
}
if (enc_level == 1) {
if (string_len + TLV_LEN_SIZE + TLV_TYPE_SIZE >
out_buf_len) {
pr_err("%s: Output len %d > Out Buf len %d\n",
__func__, string_len, out_buf_len);
return -ETOOSMALL;
}
} else {
if (string_len + string_len_sz > out_buf_len) {
pr_err("%s: Output len %d > Out Buf len %d\n",
__func__, string_len, out_buf_len);
return -ETOOSMALL;
}
rc = qmi_encode_basic_elem(buf_dst, &string_len,
1, string_len_sz);
encoded_bytes += rc;
}
rc = qmi_encode_basic_elem(buf_dst + encoded_bytes, buf_src,
string_len, temp_ei->elem_size);
encoded_bytes += rc;
QMI_ENCODE_LOG_ELEM(enc_level, string_len, temp_ei->elem_size, buf_src);
return encoded_bytes;
}
/**
* skip_to_next_elem() - Skip to next element in the structure to be encoded
* @ei_array: Struct info describing the element to be skipped.
* @level: Depth level of encoding/decoding to identify nested structures.
*
* @return: Struct info of the next element that can be encoded.
*
* This function is used while encoding optional elements. If the flag
* corresponding to an optional element is not set, then encoding the
* optional element can be skipped. This function can be used to perform
* that operation.
*/
static struct elem_info *skip_to_next_elem(struct elem_info *ei_array,
int level)
{
struct elem_info *temp_ei = ei_array;
uint8_t tlv_type;
if (level > 1) {
temp_ei = temp_ei + 1;
} else {
do {
tlv_type = temp_ei->tlv_type;
temp_ei = temp_ei + 1;
} while (tlv_type == temp_ei->tlv_type);
}
return temp_ei;
}
/**
* _qmi_kernel_encode() - Core Encode Function
* @ei_array: Struct info array describing the structure to be encoded.
* @out_buf: Buffer to hold the encoded QMI message.
* @in_c_struct: Pointer to the C structure to be encoded.
* @out_buf_len: Available space in the encode buffer.
* @enc_level: Encode level to indicate the depth of the nested structure,
* within the main structure, being encoded.
*
* @return: Number of bytes of encoded information, on success.
* < 0 on error.
*/
static int _qmi_kernel_encode(struct elem_info *ei_array,
void *out_buf, void *in_c_struct,
uint32_t out_buf_len, int enc_level)
{
struct elem_info *temp_ei = ei_array;
uint8_t opt_flag_value = 0;
uint32_t data_len_value = 0, data_len_sz;
uint8_t *buf_dst = (uint8_t *)out_buf;
uint8_t *tlv_pointer;
uint32_t tlv_len;
uint8_t tlv_type;
uint32_t encoded_bytes = 0;
void *buf_src;
int encode_tlv = 0;
int rc;
tlv_pointer = buf_dst;
tlv_len = 0;
if (enc_level == 1)
buf_dst = buf_dst + (TLV_LEN_SIZE + TLV_TYPE_SIZE);
while (temp_ei->data_type != QMI_EOTI) {
buf_src = in_c_struct + temp_ei->offset;
tlv_type = temp_ei->tlv_type;
if (temp_ei->is_array == NO_ARRAY) {
data_len_value = 1;
} else if (temp_ei->is_array == STATIC_ARRAY) {
data_len_value = temp_ei->elem_len;
} else if (data_len_value <= 0 ||
temp_ei->elem_len < data_len_value) {
pr_err("%s: Invalid data length\n", __func__);
return -EINVAL;
}
switch (temp_ei->data_type) {
case QMI_OPT_FLAG:
rc = qmi_encode_basic_elem(&opt_flag_value, buf_src,
1, sizeof(uint8_t));
if (opt_flag_value)
temp_ei = temp_ei + 1;
else
temp_ei = skip_to_next_elem(temp_ei, enc_level);
break;
case QMI_DATA_LEN:
memcpy(&data_len_value, buf_src, temp_ei->elem_size);
data_len_sz = temp_ei->elem_size == sizeof(uint8_t) ?
sizeof(uint8_t) : sizeof(uint16_t);
/* Check to avoid out of range buffer access */
if ((data_len_sz + encoded_bytes + TLV_LEN_SIZE +
TLV_TYPE_SIZE) > out_buf_len) {
pr_err("%s: Too Small Buffer @DATA_LEN\n",
__func__);
return -ETOOSMALL;
}
rc = qmi_encode_basic_elem(buf_dst, &data_len_value,
1, data_len_sz);
UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst,
encoded_bytes, tlv_len, encode_tlv, rc);
if (!data_len_value)
temp_ei = skip_to_next_elem(temp_ei, enc_level);
else
encode_tlv = 0;
break;
case QMI_UNSIGNED_1_BYTE:
case QMI_UNSIGNED_2_BYTE:
case QMI_UNSIGNED_4_BYTE:
case QMI_UNSIGNED_8_BYTE:
case QMI_SIGNED_2_BYTE_ENUM:
case QMI_SIGNED_4_BYTE_ENUM:
/* Check to avoid out of range buffer access */
if (((data_len_value * temp_ei->elem_size) +
encoded_bytes + TLV_LEN_SIZE + TLV_TYPE_SIZE) >
out_buf_len) {
pr_err("%s: Too Small Buffer @data_type:%d\n",
__func__, temp_ei->data_type);
return -ETOOSMALL;
}
rc = qmi_encode_basic_elem(buf_dst, buf_src,
data_len_value, temp_ei->elem_size);
QMI_ENCODE_LOG_ELEM(enc_level, data_len_value,
temp_ei->elem_size, buf_src);
UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst,
encoded_bytes, tlv_len, encode_tlv, rc);
break;
case QMI_STRUCT:
rc = qmi_encode_struct_elem(temp_ei, buf_dst, buf_src,
data_len_value, (out_buf_len - encoded_bytes),
(enc_level + 1));
if (rc < 0)
return rc;
UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst,
encoded_bytes, tlv_len, encode_tlv, rc);
break;
case QMI_STRING:
rc = qmi_encode_string_elem(temp_ei, buf_dst, buf_src,
out_buf_len - encoded_bytes, enc_level);
if (rc < 0)
return rc;
UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst,
encoded_bytes, tlv_len, encode_tlv, rc);
break;
default:
pr_err("%s: Unrecognized data type\n", __func__);
return -EINVAL;
}
if (encode_tlv && enc_level == 1) {
QMI_ENCDEC_ENCODE_TLV(tlv_type, tlv_len, tlv_pointer);
QMI_ENCODE_LOG_TLV(tlv_type, tlv_len);
encoded_bytes += (TLV_TYPE_SIZE + TLV_LEN_SIZE);
tlv_pointer = buf_dst;
tlv_len = 0;
buf_dst = buf_dst + TLV_LEN_SIZE + TLV_TYPE_SIZE;
encode_tlv = 0;
}
}
QMI_ENCODE_LOG_MSG(out_buf, encoded_bytes);
return encoded_bytes;
}
/**
* qmi_kernel_decode() - Decode to C Structure format
* @desc: Pointer to structure descriptor.
* @out_c_struct: Buffer to hold the decoded C structure.
* @in_buf: Buffer containg the QMI message to be decoded.
* @in_buf_len: Length of the incoming QMI message.
*
* @return: 0 on success, < 0 on error.
*/
int qmi_kernel_decode(struct msg_desc *desc, void *out_c_struct,
void *in_buf, uint32_t in_buf_len)
{
int dec_level = 1;
int rc = 0;
if (!desc || !desc->ei_array)
return -EINVAL;
if (!out_c_struct || !in_buf || !in_buf_len)
return -EINVAL;
if (desc->max_msg_len < in_buf_len)
return -EINVAL;
rc = _qmi_kernel_decode(desc->ei_array, out_c_struct,
in_buf, in_buf_len, dec_level);
if (rc < 0)
return rc;
else
return 0;
}
EXPORT_SYMBOL(qmi_kernel_decode);
/**
* qmi_decode_basic_elem() - Decodes elements of basic/primary data type
* @buf_dst: Buffer to store the decoded element.
* @buf_src: Buffer containing the elements in QMI wire format.
* @elem_len: Number of elements to be decoded.
* @elem_size: Size of a single instance of the element to be decoded.
*
* @return: Total size of the decoded data elements, in bytes.
*
* This function decodes the "elem_len" number of elements in QMI wire format,
* each of size "elem_size" bytes from the source buffer "buf_src" and stores
* the decoded elements in the destination buffer "buf_dst". The elements are
* of primary data type which include uint8_t - uint64_t or similar. This
* function returns the number of bytes of decoded information.
*/
static int qmi_decode_basic_elem(void *buf_dst, void *buf_src,
uint32_t elem_len, uint32_t elem_size)
{
uint32_t i, rc = 0;
for (i = 0; i < elem_len; i++) {
QMI_ENCDEC_DECODE_N_BYTES(buf_dst, buf_src, elem_size);
rc += elem_size;
}
return rc;
}
/**
* qmi_decode_struct_elem() - Decodes elements of struct data type
* @ei_array: Struct info array descibing the struct element.
* @buf_dst: Buffer to store the decoded element.
* @buf_src: Buffer containing the elements in QMI wire format.
* @elem_len: Number of elements to be decoded.
* @tlv_len: Total size of the encoded inforation corresponding to
* this struct element.
* @dec_level: Depth of the nested structure from the main structure.
*
* @return: Total size of the decoded data elements, on success.
* < 0 on error.
*
* This function decodes the "elem_len" number of elements in QMI wire format,
* each of size "(tlv_len/elem_len)" bytes from the source buffer "buf_src"
* and stores the decoded elements in the destination buffer "buf_dst". The
* elements are of struct data type which includes any C structure. This
* function returns the number of bytes of decoded information.
*/
static int qmi_decode_struct_elem(struct elem_info *ei_array, void *buf_dst,
void *buf_src, uint32_t elem_len,
uint32_t tlv_len, int dec_level)
{
int i, rc, decoded_bytes = 0;
struct elem_info *temp_ei = ei_array;
for (i = 0; i < elem_len && decoded_bytes < tlv_len; i++) {
rc = _qmi_kernel_decode(temp_ei->ei_array, buf_dst, buf_src,
(tlv_len - decoded_bytes), dec_level);
if (rc < 0)
return rc;
buf_src = buf_src + rc;
buf_dst = buf_dst + temp_ei->elem_size;
decoded_bytes += rc;
}
if ((dec_level <= 2 && decoded_bytes != tlv_len) ||
(dec_level > 2 && (i < elem_len || decoded_bytes > tlv_len))) {
pr_err("%s: Fault in decoding: dl(%d), db(%d), tl(%d), i(%d), el(%d)\n",
__func__, dec_level, decoded_bytes, tlv_len,
i, elem_len);
return -EFAULT;
}
return decoded_bytes;
}
/**
* qmi_decode_string_elem() - Decodes elements of string data type
* @ei_array: Struct info array descibing the string element.
* @buf_dst: Buffer to store the decoded element.
* @buf_src: Buffer containing the elements in QMI wire format.
* @tlv_len: Total size of the encoded inforation corresponding to
* this string element.
* @dec_level: Depth of the string element from the main structure.
*
* @return: Total size of the decoded data elements, on success.
* < 0 on error.
*
* This function decodes the string element of maximum length
* "ei_array->elem_len" from the source buffer "buf_src" and puts it into
* the destination buffer "buf_dst". This function returns number of bytes
* decoded from the input buffer.
*/
static int qmi_decode_string_elem(struct elem_info *ei_array, void *buf_dst,
void *buf_src, uint32_t tlv_len,
int dec_level)
{
int rc;
int decoded_bytes = 0;
uint32_t string_len = 0;
uint32_t string_len_sz = 0;
struct elem_info *temp_ei = ei_array;
if (dec_level == 1) {
string_len = tlv_len;
} else {
string_len_sz = temp_ei->elem_len <= U8_MAX ?
sizeof(uint8_t) : sizeof(uint16_t);
rc = qmi_decode_basic_elem(&string_len, buf_src,
1, string_len_sz);
decoded_bytes += rc;
}
if (string_len > temp_ei->elem_len) {
pr_err("%s: String len %d > Max Len %d\n",
__func__, string_len, temp_ei->elem_len);
return -ETOOSMALL;
} else if (string_len > tlv_len) {
pr_err("%s: String len %d > Input Buffer Len %d\n",
__func__, string_len, tlv_len);
return -EFAULT;
}
rc = qmi_decode_basic_elem(buf_dst, buf_src + decoded_bytes,
string_len, temp_ei->elem_size);
*((char *)buf_dst + string_len) = '\0';
decoded_bytes += rc;
QMI_DECODE_LOG_ELEM(dec_level, string_len, temp_ei->elem_size, buf_dst);
return decoded_bytes;
}
/**
* find_ei() - Find element info corresponding to TLV Type
* @ei_array: Struct info array of the message being decoded.
* @type: TLV Type of the element being searched.
*
* @return: Pointer to struct info, if found
*
* Every element that got encoded in the QMI message will have a type
* information associated with it. While decoding the QMI message,
* this function is used to find the struct info regarding the element
* that corresponds to the type being decoded.
*/
static struct elem_info *find_ei(struct elem_info *ei_array,
uint32_t type)
{
struct elem_info *temp_ei = ei_array;
while (temp_ei->data_type != QMI_EOTI) {
if (temp_ei->tlv_type == (uint8_t)type)
return temp_ei;
temp_ei = temp_ei + 1;
}
return NULL;
}
/**
* _qmi_kernel_decode() - Core Decode Function
* @ei_array: Struct info array describing the structure to be decoded.
* @out_c_struct: Buffer to hold the decoded C struct
* @in_buf: Buffer containing the QMI message to be decoded
* @in_buf_len: Length of the QMI message to be decoded
* @dec_level: Decode level to indicate the depth of the nested structure,
* within the main structure, being decoded
*
* @return: Number of bytes of decoded information, on success
* < 0 on error.
*/
static int _qmi_kernel_decode(struct elem_info *ei_array,
void *out_c_struct,
void *in_buf, uint32_t in_buf_len,
int dec_level)
{
struct elem_info *temp_ei = ei_array;
uint8_t opt_flag_value = 1;
uint32_t data_len_value = 0, data_len_sz = 0;
uint8_t *buf_dst = out_c_struct;
uint8_t *tlv_pointer;
uint32_t tlv_len = 0;
uint32_t tlv_type;
uint32_t decoded_bytes = 0;
void *buf_src = in_buf;
int rc;
QMI_DECODE_LOG_MSG(in_buf, in_buf_len);
while (decoded_bytes < in_buf_len) {
if (dec_level >= 2 && temp_ei->data_type == QMI_EOTI)
return decoded_bytes;
if (dec_level == 1) {
tlv_pointer = buf_src;
QMI_ENCDEC_DECODE_TLV(&tlv_type,
&tlv_len, tlv_pointer);
QMI_DECODE_LOG_TLV(tlv_type, tlv_len);
buf_src += (TLV_TYPE_SIZE + TLV_LEN_SIZE);
decoded_bytes += (TLV_TYPE_SIZE + TLV_LEN_SIZE);
temp_ei = find_ei(ei_array, tlv_type);
if (!temp_ei && (tlv_type < OPTIONAL_TLV_TYPE_START)) {
pr_err("%s: Inval element info\n", __func__);
return -EINVAL;
} else if (!temp_ei) {
UPDATE_DECODE_VARIABLES(buf_src,
decoded_bytes, tlv_len);
continue;
}
} else {
/*
* No length information for elements in nested
* structures. So use remaining decodable buffer space.
*/
tlv_len = in_buf_len - decoded_bytes;
}
buf_dst = out_c_struct + temp_ei->offset;
if (temp_ei->data_type == QMI_OPT_FLAG) {
memcpy(buf_dst, &opt_flag_value, sizeof(uint8_t));
temp_ei = temp_ei + 1;
buf_dst = out_c_struct + temp_ei->offset;
}
if (temp_ei->data_type == QMI_DATA_LEN) {
data_len_sz = temp_ei->elem_size == sizeof(uint8_t) ?
sizeof(uint8_t) : sizeof(uint16_t);
rc = qmi_decode_basic_elem(&data_len_value, buf_src,
1, data_len_sz);
memcpy(buf_dst, &data_len_value, sizeof(uint32_t));
temp_ei = temp_ei + 1;
buf_dst = out_c_struct + temp_ei->offset;
tlv_len -= data_len_sz;
UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc);
}
if (temp_ei->is_array == NO_ARRAY) {
data_len_value = 1;
} else if (temp_ei->is_array == STATIC_ARRAY) {
data_len_value = temp_ei->elem_len;
} else if (data_len_value > temp_ei->elem_len) {
pr_err("%s: Data len %d > max spec %d\n",
__func__, data_len_value, temp_ei->elem_len);
return -ETOOSMALL;
}
switch (temp_ei->data_type) {
case QMI_UNSIGNED_1_BYTE:
case QMI_UNSIGNED_2_BYTE:
case QMI_UNSIGNED_4_BYTE:
case QMI_UNSIGNED_8_BYTE:
case QMI_SIGNED_2_BYTE_ENUM:
case QMI_SIGNED_4_BYTE_ENUM:
rc = qmi_decode_basic_elem(buf_dst, buf_src,
data_len_value, temp_ei->elem_size);
QMI_DECODE_LOG_ELEM(dec_level, data_len_value,
temp_ei->elem_size, buf_dst);
UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc);
break;
case QMI_STRUCT:
rc = qmi_decode_struct_elem(temp_ei, buf_dst, buf_src,
data_len_value, tlv_len, (dec_level + 1));
if (rc < 0)
return rc;
UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc);
break;
case QMI_STRING:
rc = qmi_decode_string_elem(temp_ei, buf_dst, buf_src,
tlv_len, dec_level);
if (rc < 0)
return rc;
UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc);
break;
default:
pr_err("%s: Unrecognized data type\n", __func__);
return -EINVAL;
}
temp_ei = temp_ei + 1;
}
return decoded_bytes;
}
MODULE_DESCRIPTION("QMI kernel enc/dec");
MODULE_LICENSE("GPL v2");

66
lib/qmi_encdec_priv.h Normal file
View File

@@ -0,0 +1,66 @@
/* Copyright (c) 2012, 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 _QMI_ENCDEC_PRIV_H_
#define _QMI_ENCDEC_PRIV_H_
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/list.h>
#include <linux/socket.h>
#include <linux/gfp.h>
#include <linux/qmi_encdec.h>
#define QMI_ENCDEC_ENCODE_TLV(type, length, p_dst) do { \
*p_dst++ = type; \
*p_dst++ = ((uint8_t)((length) & 0xFF)); \
*p_dst++ = ((uint8_t)(((length) >> 8) & 0xFF)); \
} while (0)
#define QMI_ENCDEC_DECODE_TLV(p_type, p_length, p_src) do { \
*p_type = (uint8_t)*p_src++; \
*p_length = (uint8_t)*p_src++; \
*p_length |= ((uint8_t)*p_src) << 8; \
} while (0)
#define QMI_ENCDEC_ENCODE_N_BYTES(p_dst, p_src, size) \
do { \
memcpy(p_dst, p_src, size); \
p_dst = (uint8_t *)p_dst + size; \
p_src = (uint8_t *)p_src + size; \
} while (0)
#define QMI_ENCDEC_DECODE_N_BYTES(p_dst, p_src, size) \
do { \
memcpy(p_dst, p_src, size); \
p_dst = (uint8_t *)p_dst + size; \
p_src = (uint8_t *)p_src + size; \
} while (0)
#define UPDATE_ENCODE_VARIABLES(temp_si, buf_dst, \
encoded_bytes, tlv_len, encode_tlv, rc) \
do { \
buf_dst = (uint8_t *)buf_dst + rc; \
encoded_bytes += rc; \
tlv_len += rc; \
temp_si = temp_si + 1; \
encode_tlv = 1; \
} while (0)
#define UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc) \
do { \
buf_src = (uint8_t *)buf_src + rc; \
decoded_bytes += rc; \
} while (0)
#endif