18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci * Copyright (C) 2017 Linaro Ltd. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci#include <linux/slab.h> 78c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/errno.h> 118c2ecf20Sopenharmony_ci#include <linux/string.h> 128c2ecf20Sopenharmony_ci#include <linux/soc/qcom/qmi.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define QMI_ENCDEC_ENCODE_TLV(type, length, p_dst) do { \ 158c2ecf20Sopenharmony_ci *p_dst++ = type; \ 168c2ecf20Sopenharmony_ci *p_dst++ = ((u8)((length) & 0xFF)); \ 178c2ecf20Sopenharmony_ci *p_dst++ = ((u8)(((length) >> 8) & 0xFF)); \ 188c2ecf20Sopenharmony_ci} while (0) 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define QMI_ENCDEC_DECODE_TLV(p_type, p_length, p_src) do { \ 218c2ecf20Sopenharmony_ci *p_type = (u8)*p_src++; \ 228c2ecf20Sopenharmony_ci *p_length = (u8)*p_src++; \ 238c2ecf20Sopenharmony_ci *p_length |= ((u8)*p_src) << 8; \ 248c2ecf20Sopenharmony_ci} while (0) 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define QMI_ENCDEC_ENCODE_N_BYTES(p_dst, p_src, size) \ 278c2ecf20Sopenharmony_cido { \ 288c2ecf20Sopenharmony_ci memcpy(p_dst, p_src, size); \ 298c2ecf20Sopenharmony_ci p_dst = (u8 *)p_dst + size; \ 308c2ecf20Sopenharmony_ci p_src = (u8 *)p_src + size; \ 318c2ecf20Sopenharmony_ci} while (0) 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define QMI_ENCDEC_DECODE_N_BYTES(p_dst, p_src, size) \ 348c2ecf20Sopenharmony_cido { \ 358c2ecf20Sopenharmony_ci memcpy(p_dst, p_src, size); \ 368c2ecf20Sopenharmony_ci p_dst = (u8 *)p_dst + size; \ 378c2ecf20Sopenharmony_ci p_src = (u8 *)p_src + size; \ 388c2ecf20Sopenharmony_ci} while (0) 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#define UPDATE_ENCODE_VARIABLES(temp_si, buf_dst, \ 418c2ecf20Sopenharmony_ci encoded_bytes, tlv_len, encode_tlv, rc) \ 428c2ecf20Sopenharmony_cido { \ 438c2ecf20Sopenharmony_ci buf_dst = (u8 *)buf_dst + rc; \ 448c2ecf20Sopenharmony_ci encoded_bytes += rc; \ 458c2ecf20Sopenharmony_ci tlv_len += rc; \ 468c2ecf20Sopenharmony_ci temp_si = temp_si + 1; \ 478c2ecf20Sopenharmony_ci encode_tlv = 1; \ 488c2ecf20Sopenharmony_ci} while (0) 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci#define UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc) \ 518c2ecf20Sopenharmony_cido { \ 528c2ecf20Sopenharmony_ci buf_src = (u8 *)buf_src + rc; \ 538c2ecf20Sopenharmony_ci decoded_bytes += rc; \ 548c2ecf20Sopenharmony_ci} while (0) 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#define TLV_LEN_SIZE sizeof(u16) 578c2ecf20Sopenharmony_ci#define TLV_TYPE_SIZE sizeof(u8) 588c2ecf20Sopenharmony_ci#define OPTIONAL_TLV_TYPE_START 0x10 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic int qmi_encode(struct qmi_elem_info *ei_array, void *out_buf, 618c2ecf20Sopenharmony_ci const void *in_c_struct, u32 out_buf_len, 628c2ecf20Sopenharmony_ci int enc_level); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int qmi_decode(struct qmi_elem_info *ei_array, void *out_c_struct, 658c2ecf20Sopenharmony_ci const void *in_buf, u32 in_buf_len, int dec_level); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/** 688c2ecf20Sopenharmony_ci * skip_to_next_elem() - Skip to next element in the structure to be encoded 698c2ecf20Sopenharmony_ci * @ei_array: Struct info describing the element to be skipped. 708c2ecf20Sopenharmony_ci * @level: Depth level of encoding/decoding to identify nested structures. 718c2ecf20Sopenharmony_ci * 728c2ecf20Sopenharmony_ci * This function is used while encoding optional elements. If the flag 738c2ecf20Sopenharmony_ci * corresponding to an optional element is not set, then encoding the 748c2ecf20Sopenharmony_ci * optional element can be skipped. This function can be used to perform 758c2ecf20Sopenharmony_ci * that operation. 768c2ecf20Sopenharmony_ci * 778c2ecf20Sopenharmony_ci * Return: struct info of the next element that can be encoded. 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_cistatic struct qmi_elem_info *skip_to_next_elem(struct qmi_elem_info *ei_array, 808c2ecf20Sopenharmony_ci int level) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci struct qmi_elem_info *temp_ei = ei_array; 838c2ecf20Sopenharmony_ci u8 tlv_type; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci if (level > 1) { 868c2ecf20Sopenharmony_ci temp_ei = temp_ei + 1; 878c2ecf20Sopenharmony_ci } else { 888c2ecf20Sopenharmony_ci do { 898c2ecf20Sopenharmony_ci tlv_type = temp_ei->tlv_type; 908c2ecf20Sopenharmony_ci temp_ei = temp_ei + 1; 918c2ecf20Sopenharmony_ci } while (tlv_type == temp_ei->tlv_type); 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci return temp_ei; 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci/** 988c2ecf20Sopenharmony_ci * qmi_calc_min_msg_len() - Calculate the minimum length of a QMI message 998c2ecf20Sopenharmony_ci * @ei_array: Struct info array describing the structure. 1008c2ecf20Sopenharmony_ci * @level: Level to identify the depth of the nested structures. 1018c2ecf20Sopenharmony_ci * 1028c2ecf20Sopenharmony_ci * Return: Expected minimum length of the QMI message or 0 on error. 1038c2ecf20Sopenharmony_ci */ 1048c2ecf20Sopenharmony_cistatic int qmi_calc_min_msg_len(struct qmi_elem_info *ei_array, 1058c2ecf20Sopenharmony_ci int level) 1068c2ecf20Sopenharmony_ci{ 1078c2ecf20Sopenharmony_ci int min_msg_len = 0; 1088c2ecf20Sopenharmony_ci struct qmi_elem_info *temp_ei = ei_array; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (!ei_array) 1118c2ecf20Sopenharmony_ci return min_msg_len; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci while (temp_ei->data_type != QMI_EOTI) { 1148c2ecf20Sopenharmony_ci /* Optional elements do not count in minimum length */ 1158c2ecf20Sopenharmony_ci if (temp_ei->data_type == QMI_OPT_FLAG) { 1168c2ecf20Sopenharmony_ci temp_ei = skip_to_next_elem(temp_ei, level); 1178c2ecf20Sopenharmony_ci continue; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (temp_ei->data_type == QMI_DATA_LEN) { 1218c2ecf20Sopenharmony_ci min_msg_len += (temp_ei->elem_size == sizeof(u8) ? 1228c2ecf20Sopenharmony_ci sizeof(u8) : sizeof(u16)); 1238c2ecf20Sopenharmony_ci temp_ei++; 1248c2ecf20Sopenharmony_ci continue; 1258c2ecf20Sopenharmony_ci } else if (temp_ei->data_type == QMI_STRUCT) { 1268c2ecf20Sopenharmony_ci min_msg_len += qmi_calc_min_msg_len(temp_ei->ei_array, 1278c2ecf20Sopenharmony_ci (level + 1)); 1288c2ecf20Sopenharmony_ci temp_ei++; 1298c2ecf20Sopenharmony_ci } else if (temp_ei->data_type == QMI_STRING) { 1308c2ecf20Sopenharmony_ci if (level > 1) 1318c2ecf20Sopenharmony_ci min_msg_len += temp_ei->elem_len <= U8_MAX ? 1328c2ecf20Sopenharmony_ci sizeof(u8) : sizeof(u16); 1338c2ecf20Sopenharmony_ci min_msg_len += temp_ei->elem_len * temp_ei->elem_size; 1348c2ecf20Sopenharmony_ci temp_ei++; 1358c2ecf20Sopenharmony_ci } else { 1368c2ecf20Sopenharmony_ci min_msg_len += (temp_ei->elem_len * temp_ei->elem_size); 1378c2ecf20Sopenharmony_ci temp_ei++; 1388c2ecf20Sopenharmony_ci } 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* 1418c2ecf20Sopenharmony_ci * Type & Length info. not prepended for elements in the 1428c2ecf20Sopenharmony_ci * nested structure. 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_ci if (level == 1) 1458c2ecf20Sopenharmony_ci min_msg_len += (TLV_TYPE_SIZE + TLV_LEN_SIZE); 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci return min_msg_len; 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci/** 1528c2ecf20Sopenharmony_ci * qmi_encode_basic_elem() - Encodes elements of basic/primary data type 1538c2ecf20Sopenharmony_ci * @buf_dst: Buffer to store the encoded information. 1548c2ecf20Sopenharmony_ci * @buf_src: Buffer containing the elements to be encoded. 1558c2ecf20Sopenharmony_ci * @elem_len: Number of elements, in the buf_src, to be encoded. 1568c2ecf20Sopenharmony_ci * @elem_size: Size of a single instance of the element to be encoded. 1578c2ecf20Sopenharmony_ci * 1588c2ecf20Sopenharmony_ci * This function encodes the "elem_len" number of data elements, each of 1598c2ecf20Sopenharmony_ci * size "elem_size" bytes from the source buffer "buf_src" and stores the 1608c2ecf20Sopenharmony_ci * encoded information in the destination buffer "buf_dst". The elements are 1618c2ecf20Sopenharmony_ci * of primary data type which include u8 - u64 or similar. This 1628c2ecf20Sopenharmony_ci * function returns the number of bytes of encoded information. 1638c2ecf20Sopenharmony_ci * 1648c2ecf20Sopenharmony_ci * Return: The number of bytes of encoded information. 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_cistatic int qmi_encode_basic_elem(void *buf_dst, const void *buf_src, 1678c2ecf20Sopenharmony_ci u32 elem_len, u32 elem_size) 1688c2ecf20Sopenharmony_ci{ 1698c2ecf20Sopenharmony_ci u32 i, rc = 0; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci for (i = 0; i < elem_len; i++) { 1728c2ecf20Sopenharmony_ci QMI_ENCDEC_ENCODE_N_BYTES(buf_dst, buf_src, elem_size); 1738c2ecf20Sopenharmony_ci rc += elem_size; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci return rc; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci/** 1808c2ecf20Sopenharmony_ci * qmi_encode_struct_elem() - Encodes elements of struct data type 1818c2ecf20Sopenharmony_ci * @ei_array: Struct info array descibing the struct element. 1828c2ecf20Sopenharmony_ci * @buf_dst: Buffer to store the encoded information. 1838c2ecf20Sopenharmony_ci * @buf_src: Buffer containing the elements to be encoded. 1848c2ecf20Sopenharmony_ci * @elem_len: Number of elements, in the buf_src, to be encoded. 1858c2ecf20Sopenharmony_ci * @out_buf_len: Available space in the encode buffer. 1868c2ecf20Sopenharmony_ci * @enc_level: Depth of the nested structure from the main structure. 1878c2ecf20Sopenharmony_ci * 1888c2ecf20Sopenharmony_ci * This function encodes the "elem_len" number of struct elements, each of 1898c2ecf20Sopenharmony_ci * size "ei_array->elem_size" bytes from the source buffer "buf_src" and 1908c2ecf20Sopenharmony_ci * stores the encoded information in the destination buffer "buf_dst". The 1918c2ecf20Sopenharmony_ci * elements are of struct data type which includes any C structure. This 1928c2ecf20Sopenharmony_ci * function returns the number of bytes of encoded information. 1938c2ecf20Sopenharmony_ci * 1948c2ecf20Sopenharmony_ci * Return: The number of bytes of encoded information on success or negative 1958c2ecf20Sopenharmony_ci * errno on error. 1968c2ecf20Sopenharmony_ci */ 1978c2ecf20Sopenharmony_cistatic int qmi_encode_struct_elem(struct qmi_elem_info *ei_array, 1988c2ecf20Sopenharmony_ci void *buf_dst, const void *buf_src, 1998c2ecf20Sopenharmony_ci u32 elem_len, u32 out_buf_len, 2008c2ecf20Sopenharmony_ci int enc_level) 2018c2ecf20Sopenharmony_ci{ 2028c2ecf20Sopenharmony_ci int i, rc, encoded_bytes = 0; 2038c2ecf20Sopenharmony_ci struct qmi_elem_info *temp_ei = ei_array; 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci for (i = 0; i < elem_len; i++) { 2068c2ecf20Sopenharmony_ci rc = qmi_encode(temp_ei->ei_array, buf_dst, buf_src, 2078c2ecf20Sopenharmony_ci out_buf_len - encoded_bytes, enc_level); 2088c2ecf20Sopenharmony_ci if (rc < 0) { 2098c2ecf20Sopenharmony_ci pr_err("%s: STRUCT Encode failure\n", __func__); 2108c2ecf20Sopenharmony_ci return rc; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci buf_dst = buf_dst + rc; 2138c2ecf20Sopenharmony_ci buf_src = buf_src + temp_ei->elem_size; 2148c2ecf20Sopenharmony_ci encoded_bytes += rc; 2158c2ecf20Sopenharmony_ci } 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci return encoded_bytes; 2188c2ecf20Sopenharmony_ci} 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci/** 2218c2ecf20Sopenharmony_ci * qmi_encode_string_elem() - Encodes elements of string data type 2228c2ecf20Sopenharmony_ci * @ei_array: Struct info array descibing the string element. 2238c2ecf20Sopenharmony_ci * @buf_dst: Buffer to store the encoded information. 2248c2ecf20Sopenharmony_ci * @buf_src: Buffer containing the elements to be encoded. 2258c2ecf20Sopenharmony_ci * @out_buf_len: Available space in the encode buffer. 2268c2ecf20Sopenharmony_ci * @enc_level: Depth of the string element from the main structure. 2278c2ecf20Sopenharmony_ci * 2288c2ecf20Sopenharmony_ci * This function encodes a string element of maximum length "ei_array->elem_len" 2298c2ecf20Sopenharmony_ci * bytes from the source buffer "buf_src" and stores the encoded information in 2308c2ecf20Sopenharmony_ci * the destination buffer "buf_dst". This function returns the number of bytes 2318c2ecf20Sopenharmony_ci * of encoded information. 2328c2ecf20Sopenharmony_ci * 2338c2ecf20Sopenharmony_ci * Return: The number of bytes of encoded information on success or negative 2348c2ecf20Sopenharmony_ci * errno on error. 2358c2ecf20Sopenharmony_ci */ 2368c2ecf20Sopenharmony_cistatic int qmi_encode_string_elem(struct qmi_elem_info *ei_array, 2378c2ecf20Sopenharmony_ci void *buf_dst, const void *buf_src, 2388c2ecf20Sopenharmony_ci u32 out_buf_len, int enc_level) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci int rc; 2418c2ecf20Sopenharmony_ci int encoded_bytes = 0; 2428c2ecf20Sopenharmony_ci struct qmi_elem_info *temp_ei = ei_array; 2438c2ecf20Sopenharmony_ci u32 string_len = 0; 2448c2ecf20Sopenharmony_ci u32 string_len_sz = 0; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci string_len = strlen(buf_src); 2478c2ecf20Sopenharmony_ci string_len_sz = temp_ei->elem_len <= U8_MAX ? 2488c2ecf20Sopenharmony_ci sizeof(u8) : sizeof(u16); 2498c2ecf20Sopenharmony_ci if (string_len > temp_ei->elem_len) { 2508c2ecf20Sopenharmony_ci pr_err("%s: String to be encoded is longer - %d > %d\n", 2518c2ecf20Sopenharmony_ci __func__, string_len, temp_ei->elem_len); 2528c2ecf20Sopenharmony_ci return -EINVAL; 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci if (enc_level == 1) { 2568c2ecf20Sopenharmony_ci if (string_len + TLV_LEN_SIZE + TLV_TYPE_SIZE > 2578c2ecf20Sopenharmony_ci out_buf_len) { 2588c2ecf20Sopenharmony_ci pr_err("%s: Output len %d > Out Buf len %d\n", 2598c2ecf20Sopenharmony_ci __func__, string_len, out_buf_len); 2608c2ecf20Sopenharmony_ci return -ETOOSMALL; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci } else { 2638c2ecf20Sopenharmony_ci if (string_len + string_len_sz > out_buf_len) { 2648c2ecf20Sopenharmony_ci pr_err("%s: Output len %d > Out Buf len %d\n", 2658c2ecf20Sopenharmony_ci __func__, string_len, out_buf_len); 2668c2ecf20Sopenharmony_ci return -ETOOSMALL; 2678c2ecf20Sopenharmony_ci } 2688c2ecf20Sopenharmony_ci rc = qmi_encode_basic_elem(buf_dst, &string_len, 2698c2ecf20Sopenharmony_ci 1, string_len_sz); 2708c2ecf20Sopenharmony_ci encoded_bytes += rc; 2718c2ecf20Sopenharmony_ci } 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci rc = qmi_encode_basic_elem(buf_dst + encoded_bytes, buf_src, 2748c2ecf20Sopenharmony_ci string_len, temp_ei->elem_size); 2758c2ecf20Sopenharmony_ci encoded_bytes += rc; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci return encoded_bytes; 2788c2ecf20Sopenharmony_ci} 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci/** 2818c2ecf20Sopenharmony_ci * qmi_encode() - Core Encode Function 2828c2ecf20Sopenharmony_ci * @ei_array: Struct info array describing the structure to be encoded. 2838c2ecf20Sopenharmony_ci * @out_buf: Buffer to hold the encoded QMI message. 2848c2ecf20Sopenharmony_ci * @in_c_struct: Pointer to the C structure to be encoded. 2858c2ecf20Sopenharmony_ci * @out_buf_len: Available space in the encode buffer. 2868c2ecf20Sopenharmony_ci * @enc_level: Encode level to indicate the depth of the nested structure, 2878c2ecf20Sopenharmony_ci * within the main structure, being encoded. 2888c2ecf20Sopenharmony_ci * 2898c2ecf20Sopenharmony_ci * Return: The number of bytes of encoded information on success or negative 2908c2ecf20Sopenharmony_ci * errno on error. 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_cistatic int qmi_encode(struct qmi_elem_info *ei_array, void *out_buf, 2938c2ecf20Sopenharmony_ci const void *in_c_struct, u32 out_buf_len, 2948c2ecf20Sopenharmony_ci int enc_level) 2958c2ecf20Sopenharmony_ci{ 2968c2ecf20Sopenharmony_ci struct qmi_elem_info *temp_ei = ei_array; 2978c2ecf20Sopenharmony_ci u8 opt_flag_value = 0; 2988c2ecf20Sopenharmony_ci u32 data_len_value = 0, data_len_sz; 2998c2ecf20Sopenharmony_ci u8 *buf_dst = (u8 *)out_buf; 3008c2ecf20Sopenharmony_ci u8 *tlv_pointer; 3018c2ecf20Sopenharmony_ci u32 tlv_len; 3028c2ecf20Sopenharmony_ci u8 tlv_type; 3038c2ecf20Sopenharmony_ci u32 encoded_bytes = 0; 3048c2ecf20Sopenharmony_ci const void *buf_src; 3058c2ecf20Sopenharmony_ci int encode_tlv = 0; 3068c2ecf20Sopenharmony_ci int rc; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci if (!ei_array) 3098c2ecf20Sopenharmony_ci return 0; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci tlv_pointer = buf_dst; 3128c2ecf20Sopenharmony_ci tlv_len = 0; 3138c2ecf20Sopenharmony_ci if (enc_level == 1) 3148c2ecf20Sopenharmony_ci buf_dst = buf_dst + (TLV_LEN_SIZE + TLV_TYPE_SIZE); 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci while (temp_ei->data_type != QMI_EOTI) { 3178c2ecf20Sopenharmony_ci buf_src = in_c_struct + temp_ei->offset; 3188c2ecf20Sopenharmony_ci tlv_type = temp_ei->tlv_type; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (temp_ei->array_type == NO_ARRAY) { 3218c2ecf20Sopenharmony_ci data_len_value = 1; 3228c2ecf20Sopenharmony_ci } else if (temp_ei->array_type == STATIC_ARRAY) { 3238c2ecf20Sopenharmony_ci data_len_value = temp_ei->elem_len; 3248c2ecf20Sopenharmony_ci } else if (data_len_value <= 0 || 3258c2ecf20Sopenharmony_ci temp_ei->elem_len < data_len_value) { 3268c2ecf20Sopenharmony_ci pr_err("%s: Invalid data length\n", __func__); 3278c2ecf20Sopenharmony_ci return -EINVAL; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci switch (temp_ei->data_type) { 3318c2ecf20Sopenharmony_ci case QMI_OPT_FLAG: 3328c2ecf20Sopenharmony_ci rc = qmi_encode_basic_elem(&opt_flag_value, buf_src, 3338c2ecf20Sopenharmony_ci 1, sizeof(u8)); 3348c2ecf20Sopenharmony_ci if (opt_flag_value) 3358c2ecf20Sopenharmony_ci temp_ei = temp_ei + 1; 3368c2ecf20Sopenharmony_ci else 3378c2ecf20Sopenharmony_ci temp_ei = skip_to_next_elem(temp_ei, enc_level); 3388c2ecf20Sopenharmony_ci break; 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci case QMI_DATA_LEN: 3418c2ecf20Sopenharmony_ci memcpy(&data_len_value, buf_src, temp_ei->elem_size); 3428c2ecf20Sopenharmony_ci data_len_sz = temp_ei->elem_size == sizeof(u8) ? 3438c2ecf20Sopenharmony_ci sizeof(u8) : sizeof(u16); 3448c2ecf20Sopenharmony_ci /* Check to avoid out of range buffer access */ 3458c2ecf20Sopenharmony_ci if ((data_len_sz + encoded_bytes + TLV_LEN_SIZE + 3468c2ecf20Sopenharmony_ci TLV_TYPE_SIZE) > out_buf_len) { 3478c2ecf20Sopenharmony_ci pr_err("%s: Too Small Buffer @DATA_LEN\n", 3488c2ecf20Sopenharmony_ci __func__); 3498c2ecf20Sopenharmony_ci return -ETOOSMALL; 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci rc = qmi_encode_basic_elem(buf_dst, &data_len_value, 3528c2ecf20Sopenharmony_ci 1, data_len_sz); 3538c2ecf20Sopenharmony_ci UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst, 3548c2ecf20Sopenharmony_ci encoded_bytes, tlv_len, 3558c2ecf20Sopenharmony_ci encode_tlv, rc); 3568c2ecf20Sopenharmony_ci if (!data_len_value) 3578c2ecf20Sopenharmony_ci temp_ei = skip_to_next_elem(temp_ei, enc_level); 3588c2ecf20Sopenharmony_ci else 3598c2ecf20Sopenharmony_ci encode_tlv = 0; 3608c2ecf20Sopenharmony_ci break; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci case QMI_UNSIGNED_1_BYTE: 3638c2ecf20Sopenharmony_ci case QMI_UNSIGNED_2_BYTE: 3648c2ecf20Sopenharmony_ci case QMI_UNSIGNED_4_BYTE: 3658c2ecf20Sopenharmony_ci case QMI_UNSIGNED_8_BYTE: 3668c2ecf20Sopenharmony_ci case QMI_SIGNED_2_BYTE_ENUM: 3678c2ecf20Sopenharmony_ci case QMI_SIGNED_4_BYTE_ENUM: 3688c2ecf20Sopenharmony_ci /* Check to avoid out of range buffer access */ 3698c2ecf20Sopenharmony_ci if (((data_len_value * temp_ei->elem_size) + 3708c2ecf20Sopenharmony_ci encoded_bytes + TLV_LEN_SIZE + TLV_TYPE_SIZE) > 3718c2ecf20Sopenharmony_ci out_buf_len) { 3728c2ecf20Sopenharmony_ci pr_err("%s: Too Small Buffer @data_type:%d\n", 3738c2ecf20Sopenharmony_ci __func__, temp_ei->data_type); 3748c2ecf20Sopenharmony_ci return -ETOOSMALL; 3758c2ecf20Sopenharmony_ci } 3768c2ecf20Sopenharmony_ci rc = qmi_encode_basic_elem(buf_dst, buf_src, 3778c2ecf20Sopenharmony_ci data_len_value, 3788c2ecf20Sopenharmony_ci temp_ei->elem_size); 3798c2ecf20Sopenharmony_ci UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst, 3808c2ecf20Sopenharmony_ci encoded_bytes, tlv_len, 3818c2ecf20Sopenharmony_ci encode_tlv, rc); 3828c2ecf20Sopenharmony_ci break; 3838c2ecf20Sopenharmony_ci 3848c2ecf20Sopenharmony_ci case QMI_STRUCT: 3858c2ecf20Sopenharmony_ci rc = qmi_encode_struct_elem(temp_ei, buf_dst, buf_src, 3868c2ecf20Sopenharmony_ci data_len_value, 3878c2ecf20Sopenharmony_ci out_buf_len - encoded_bytes, 3888c2ecf20Sopenharmony_ci enc_level + 1); 3898c2ecf20Sopenharmony_ci if (rc < 0) 3908c2ecf20Sopenharmony_ci return rc; 3918c2ecf20Sopenharmony_ci UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst, 3928c2ecf20Sopenharmony_ci encoded_bytes, tlv_len, 3938c2ecf20Sopenharmony_ci encode_tlv, rc); 3948c2ecf20Sopenharmony_ci break; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci case QMI_STRING: 3978c2ecf20Sopenharmony_ci rc = qmi_encode_string_elem(temp_ei, buf_dst, buf_src, 3988c2ecf20Sopenharmony_ci out_buf_len - encoded_bytes, 3998c2ecf20Sopenharmony_ci enc_level); 4008c2ecf20Sopenharmony_ci if (rc < 0) 4018c2ecf20Sopenharmony_ci return rc; 4028c2ecf20Sopenharmony_ci UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst, 4038c2ecf20Sopenharmony_ci encoded_bytes, tlv_len, 4048c2ecf20Sopenharmony_ci encode_tlv, rc); 4058c2ecf20Sopenharmony_ci break; 4068c2ecf20Sopenharmony_ci default: 4078c2ecf20Sopenharmony_ci pr_err("%s: Unrecognized data type\n", __func__); 4088c2ecf20Sopenharmony_ci return -EINVAL; 4098c2ecf20Sopenharmony_ci } 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci if (encode_tlv && enc_level == 1) { 4128c2ecf20Sopenharmony_ci QMI_ENCDEC_ENCODE_TLV(tlv_type, tlv_len, tlv_pointer); 4138c2ecf20Sopenharmony_ci encoded_bytes += (TLV_TYPE_SIZE + TLV_LEN_SIZE); 4148c2ecf20Sopenharmony_ci tlv_pointer = buf_dst; 4158c2ecf20Sopenharmony_ci tlv_len = 0; 4168c2ecf20Sopenharmony_ci buf_dst = buf_dst + TLV_LEN_SIZE + TLV_TYPE_SIZE; 4178c2ecf20Sopenharmony_ci encode_tlv = 0; 4188c2ecf20Sopenharmony_ci } 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci return encoded_bytes; 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci/** 4258c2ecf20Sopenharmony_ci * qmi_decode_basic_elem() - Decodes elements of basic/primary data type 4268c2ecf20Sopenharmony_ci * @buf_dst: Buffer to store the decoded element. 4278c2ecf20Sopenharmony_ci * @buf_src: Buffer containing the elements in QMI wire format. 4288c2ecf20Sopenharmony_ci * @elem_len: Number of elements to be decoded. 4298c2ecf20Sopenharmony_ci * @elem_size: Size of a single instance of the element to be decoded. 4308c2ecf20Sopenharmony_ci * 4318c2ecf20Sopenharmony_ci * This function decodes the "elem_len" number of elements in QMI wire format, 4328c2ecf20Sopenharmony_ci * each of size "elem_size" bytes from the source buffer "buf_src" and stores 4338c2ecf20Sopenharmony_ci * the decoded elements in the destination buffer "buf_dst". The elements are 4348c2ecf20Sopenharmony_ci * of primary data type which include u8 - u64 or similar. This 4358c2ecf20Sopenharmony_ci * function returns the number of bytes of decoded information. 4368c2ecf20Sopenharmony_ci * 4378c2ecf20Sopenharmony_ci * Return: The total size of the decoded data elements, in bytes. 4388c2ecf20Sopenharmony_ci */ 4398c2ecf20Sopenharmony_cistatic int qmi_decode_basic_elem(void *buf_dst, const void *buf_src, 4408c2ecf20Sopenharmony_ci u32 elem_len, u32 elem_size) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci u32 i, rc = 0; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci for (i = 0; i < elem_len; i++) { 4458c2ecf20Sopenharmony_ci QMI_ENCDEC_DECODE_N_BYTES(buf_dst, buf_src, elem_size); 4468c2ecf20Sopenharmony_ci rc += elem_size; 4478c2ecf20Sopenharmony_ci } 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci return rc; 4508c2ecf20Sopenharmony_ci} 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci/** 4538c2ecf20Sopenharmony_ci * qmi_decode_struct_elem() - Decodes elements of struct data type 4548c2ecf20Sopenharmony_ci * @ei_array: Struct info array descibing the struct element. 4558c2ecf20Sopenharmony_ci * @buf_dst: Buffer to store the decoded element. 4568c2ecf20Sopenharmony_ci * @buf_src: Buffer containing the elements in QMI wire format. 4578c2ecf20Sopenharmony_ci * @elem_len: Number of elements to be decoded. 4588c2ecf20Sopenharmony_ci * @tlv_len: Total size of the encoded inforation corresponding to 4598c2ecf20Sopenharmony_ci * this struct element. 4608c2ecf20Sopenharmony_ci * @dec_level: Depth of the nested structure from the main structure. 4618c2ecf20Sopenharmony_ci * 4628c2ecf20Sopenharmony_ci * This function decodes the "elem_len" number of elements in QMI wire format, 4638c2ecf20Sopenharmony_ci * each of size "(tlv_len/elem_len)" bytes from the source buffer "buf_src" 4648c2ecf20Sopenharmony_ci * and stores the decoded elements in the destination buffer "buf_dst". The 4658c2ecf20Sopenharmony_ci * elements are of struct data type which includes any C structure. This 4668c2ecf20Sopenharmony_ci * function returns the number of bytes of decoded information. 4678c2ecf20Sopenharmony_ci * 4688c2ecf20Sopenharmony_ci * Return: The total size of the decoded data elements on success, negative 4698c2ecf20Sopenharmony_ci * errno on error. 4708c2ecf20Sopenharmony_ci */ 4718c2ecf20Sopenharmony_cistatic int qmi_decode_struct_elem(struct qmi_elem_info *ei_array, 4728c2ecf20Sopenharmony_ci void *buf_dst, const void *buf_src, 4738c2ecf20Sopenharmony_ci u32 elem_len, u32 tlv_len, 4748c2ecf20Sopenharmony_ci int dec_level) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci int i, rc, decoded_bytes = 0; 4778c2ecf20Sopenharmony_ci struct qmi_elem_info *temp_ei = ei_array; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci for (i = 0; i < elem_len && decoded_bytes < tlv_len; i++) { 4808c2ecf20Sopenharmony_ci rc = qmi_decode(temp_ei->ei_array, buf_dst, buf_src, 4818c2ecf20Sopenharmony_ci tlv_len - decoded_bytes, dec_level); 4828c2ecf20Sopenharmony_ci if (rc < 0) 4838c2ecf20Sopenharmony_ci return rc; 4848c2ecf20Sopenharmony_ci buf_src = buf_src + rc; 4858c2ecf20Sopenharmony_ci buf_dst = buf_dst + temp_ei->elem_size; 4868c2ecf20Sopenharmony_ci decoded_bytes += rc; 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci if ((dec_level <= 2 && decoded_bytes != tlv_len) || 4908c2ecf20Sopenharmony_ci (dec_level > 2 && (i < elem_len || decoded_bytes > tlv_len))) { 4918c2ecf20Sopenharmony_ci pr_err("%s: Fault in decoding: dl(%d), db(%d), tl(%d), i(%d), el(%d)\n", 4928c2ecf20Sopenharmony_ci __func__, dec_level, decoded_bytes, tlv_len, 4938c2ecf20Sopenharmony_ci i, elem_len); 4948c2ecf20Sopenharmony_ci return -EFAULT; 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci return decoded_bytes; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci/** 5018c2ecf20Sopenharmony_ci * qmi_decode_string_elem() - Decodes elements of string data type 5028c2ecf20Sopenharmony_ci * @ei_array: Struct info array descibing the string element. 5038c2ecf20Sopenharmony_ci * @buf_dst: Buffer to store the decoded element. 5048c2ecf20Sopenharmony_ci * @buf_src: Buffer containing the elements in QMI wire format. 5058c2ecf20Sopenharmony_ci * @tlv_len: Total size of the encoded inforation corresponding to 5068c2ecf20Sopenharmony_ci * this string element. 5078c2ecf20Sopenharmony_ci * @dec_level: Depth of the string element from the main structure. 5088c2ecf20Sopenharmony_ci * 5098c2ecf20Sopenharmony_ci * This function decodes the string element of maximum length 5108c2ecf20Sopenharmony_ci * "ei_array->elem_len" from the source buffer "buf_src" and puts it into 5118c2ecf20Sopenharmony_ci * the destination buffer "buf_dst". This function returns number of bytes 5128c2ecf20Sopenharmony_ci * decoded from the input buffer. 5138c2ecf20Sopenharmony_ci * 5148c2ecf20Sopenharmony_ci * Return: The total size of the decoded data elements on success, negative 5158c2ecf20Sopenharmony_ci * errno on error. 5168c2ecf20Sopenharmony_ci */ 5178c2ecf20Sopenharmony_cistatic int qmi_decode_string_elem(struct qmi_elem_info *ei_array, 5188c2ecf20Sopenharmony_ci void *buf_dst, const void *buf_src, 5198c2ecf20Sopenharmony_ci u32 tlv_len, int dec_level) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci int rc; 5228c2ecf20Sopenharmony_ci int decoded_bytes = 0; 5238c2ecf20Sopenharmony_ci u32 string_len = 0; 5248c2ecf20Sopenharmony_ci u32 string_len_sz = 0; 5258c2ecf20Sopenharmony_ci struct qmi_elem_info *temp_ei = ei_array; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci if (dec_level == 1) { 5288c2ecf20Sopenharmony_ci string_len = tlv_len; 5298c2ecf20Sopenharmony_ci } else { 5308c2ecf20Sopenharmony_ci string_len_sz = temp_ei->elem_len <= U8_MAX ? 5318c2ecf20Sopenharmony_ci sizeof(u8) : sizeof(u16); 5328c2ecf20Sopenharmony_ci rc = qmi_decode_basic_elem(&string_len, buf_src, 5338c2ecf20Sopenharmony_ci 1, string_len_sz); 5348c2ecf20Sopenharmony_ci decoded_bytes += rc; 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci if (string_len >= temp_ei->elem_len) { 5388c2ecf20Sopenharmony_ci pr_err("%s: String len %d >= Max Len %d\n", 5398c2ecf20Sopenharmony_ci __func__, string_len, temp_ei->elem_len); 5408c2ecf20Sopenharmony_ci return -ETOOSMALL; 5418c2ecf20Sopenharmony_ci } else if (string_len > tlv_len) { 5428c2ecf20Sopenharmony_ci pr_err("%s: String len %d > Input Buffer Len %d\n", 5438c2ecf20Sopenharmony_ci __func__, string_len, tlv_len); 5448c2ecf20Sopenharmony_ci return -EFAULT; 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci rc = qmi_decode_basic_elem(buf_dst, buf_src + decoded_bytes, 5488c2ecf20Sopenharmony_ci string_len, temp_ei->elem_size); 5498c2ecf20Sopenharmony_ci *((char *)buf_dst + string_len) = '\0'; 5508c2ecf20Sopenharmony_ci decoded_bytes += rc; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci return decoded_bytes; 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_ci/** 5568c2ecf20Sopenharmony_ci * find_ei() - Find element info corresponding to TLV Type 5578c2ecf20Sopenharmony_ci * @ei_array: Struct info array of the message being decoded. 5588c2ecf20Sopenharmony_ci * @type: TLV Type of the element being searched. 5598c2ecf20Sopenharmony_ci * 5608c2ecf20Sopenharmony_ci * Every element that got encoded in the QMI message will have a type 5618c2ecf20Sopenharmony_ci * information associated with it. While decoding the QMI message, 5628c2ecf20Sopenharmony_ci * this function is used to find the struct info regarding the element 5638c2ecf20Sopenharmony_ci * that corresponds to the type being decoded. 5648c2ecf20Sopenharmony_ci * 5658c2ecf20Sopenharmony_ci * Return: Pointer to struct info, if found 5668c2ecf20Sopenharmony_ci */ 5678c2ecf20Sopenharmony_cistatic struct qmi_elem_info *find_ei(struct qmi_elem_info *ei_array, 5688c2ecf20Sopenharmony_ci u32 type) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci struct qmi_elem_info *temp_ei = ei_array; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci while (temp_ei->data_type != QMI_EOTI) { 5738c2ecf20Sopenharmony_ci if (temp_ei->tlv_type == (u8)type) 5748c2ecf20Sopenharmony_ci return temp_ei; 5758c2ecf20Sopenharmony_ci temp_ei = temp_ei + 1; 5768c2ecf20Sopenharmony_ci } 5778c2ecf20Sopenharmony_ci 5788c2ecf20Sopenharmony_ci return NULL; 5798c2ecf20Sopenharmony_ci} 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_ci/** 5828c2ecf20Sopenharmony_ci * qmi_decode() - Core Decode Function 5838c2ecf20Sopenharmony_ci * @ei_array: Struct info array describing the structure to be decoded. 5848c2ecf20Sopenharmony_ci * @out_c_struct: Buffer to hold the decoded C struct 5858c2ecf20Sopenharmony_ci * @in_buf: Buffer containing the QMI message to be decoded 5868c2ecf20Sopenharmony_ci * @in_buf_len: Length of the QMI message to be decoded 5878c2ecf20Sopenharmony_ci * @dec_level: Decode level to indicate the depth of the nested structure, 5888c2ecf20Sopenharmony_ci * within the main structure, being decoded 5898c2ecf20Sopenharmony_ci * 5908c2ecf20Sopenharmony_ci * Return: The number of bytes of decoded information on success, negative 5918c2ecf20Sopenharmony_ci * errno on error. 5928c2ecf20Sopenharmony_ci */ 5938c2ecf20Sopenharmony_cistatic int qmi_decode(struct qmi_elem_info *ei_array, void *out_c_struct, 5948c2ecf20Sopenharmony_ci const void *in_buf, u32 in_buf_len, 5958c2ecf20Sopenharmony_ci int dec_level) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci struct qmi_elem_info *temp_ei = ei_array; 5988c2ecf20Sopenharmony_ci u8 opt_flag_value = 1; 5998c2ecf20Sopenharmony_ci u32 data_len_value = 0, data_len_sz = 0; 6008c2ecf20Sopenharmony_ci u8 *buf_dst = out_c_struct; 6018c2ecf20Sopenharmony_ci const u8 *tlv_pointer; 6028c2ecf20Sopenharmony_ci u32 tlv_len = 0; 6038c2ecf20Sopenharmony_ci u32 tlv_type; 6048c2ecf20Sopenharmony_ci u32 decoded_bytes = 0; 6058c2ecf20Sopenharmony_ci const void *buf_src = in_buf; 6068c2ecf20Sopenharmony_ci int rc; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci while (decoded_bytes < in_buf_len) { 6098c2ecf20Sopenharmony_ci if (dec_level >= 2 && temp_ei->data_type == QMI_EOTI) 6108c2ecf20Sopenharmony_ci return decoded_bytes; 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci if (dec_level == 1) { 6138c2ecf20Sopenharmony_ci tlv_pointer = buf_src; 6148c2ecf20Sopenharmony_ci QMI_ENCDEC_DECODE_TLV(&tlv_type, 6158c2ecf20Sopenharmony_ci &tlv_len, tlv_pointer); 6168c2ecf20Sopenharmony_ci buf_src += (TLV_TYPE_SIZE + TLV_LEN_SIZE); 6178c2ecf20Sopenharmony_ci decoded_bytes += (TLV_TYPE_SIZE + TLV_LEN_SIZE); 6188c2ecf20Sopenharmony_ci temp_ei = find_ei(ei_array, tlv_type); 6198c2ecf20Sopenharmony_ci if (!temp_ei && tlv_type < OPTIONAL_TLV_TYPE_START) { 6208c2ecf20Sopenharmony_ci pr_err("%s: Inval element info\n", __func__); 6218c2ecf20Sopenharmony_ci return -EINVAL; 6228c2ecf20Sopenharmony_ci } else if (!temp_ei) { 6238c2ecf20Sopenharmony_ci UPDATE_DECODE_VARIABLES(buf_src, 6248c2ecf20Sopenharmony_ci decoded_bytes, tlv_len); 6258c2ecf20Sopenharmony_ci continue; 6268c2ecf20Sopenharmony_ci } 6278c2ecf20Sopenharmony_ci } else { 6288c2ecf20Sopenharmony_ci /* 6298c2ecf20Sopenharmony_ci * No length information for elements in nested 6308c2ecf20Sopenharmony_ci * structures. So use remaining decodable buffer space. 6318c2ecf20Sopenharmony_ci */ 6328c2ecf20Sopenharmony_ci tlv_len = in_buf_len - decoded_bytes; 6338c2ecf20Sopenharmony_ci } 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci buf_dst = out_c_struct + temp_ei->offset; 6368c2ecf20Sopenharmony_ci if (temp_ei->data_type == QMI_OPT_FLAG) { 6378c2ecf20Sopenharmony_ci memcpy(buf_dst, &opt_flag_value, sizeof(u8)); 6388c2ecf20Sopenharmony_ci temp_ei = temp_ei + 1; 6398c2ecf20Sopenharmony_ci buf_dst = out_c_struct + temp_ei->offset; 6408c2ecf20Sopenharmony_ci } 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci if (temp_ei->data_type == QMI_DATA_LEN) { 6438c2ecf20Sopenharmony_ci data_len_sz = temp_ei->elem_size == sizeof(u8) ? 6448c2ecf20Sopenharmony_ci sizeof(u8) : sizeof(u16); 6458c2ecf20Sopenharmony_ci rc = qmi_decode_basic_elem(&data_len_value, buf_src, 6468c2ecf20Sopenharmony_ci 1, data_len_sz); 6478c2ecf20Sopenharmony_ci memcpy(buf_dst, &data_len_value, sizeof(u32)); 6488c2ecf20Sopenharmony_ci temp_ei = temp_ei + 1; 6498c2ecf20Sopenharmony_ci buf_dst = out_c_struct + temp_ei->offset; 6508c2ecf20Sopenharmony_ci tlv_len -= data_len_sz; 6518c2ecf20Sopenharmony_ci UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc); 6528c2ecf20Sopenharmony_ci } 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_ci if (temp_ei->array_type == NO_ARRAY) { 6558c2ecf20Sopenharmony_ci data_len_value = 1; 6568c2ecf20Sopenharmony_ci } else if (temp_ei->array_type == STATIC_ARRAY) { 6578c2ecf20Sopenharmony_ci data_len_value = temp_ei->elem_len; 6588c2ecf20Sopenharmony_ci } else if (data_len_value > temp_ei->elem_len) { 6598c2ecf20Sopenharmony_ci pr_err("%s: Data len %d > max spec %d\n", 6608c2ecf20Sopenharmony_ci __func__, data_len_value, temp_ei->elem_len); 6618c2ecf20Sopenharmony_ci return -ETOOSMALL; 6628c2ecf20Sopenharmony_ci } 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci switch (temp_ei->data_type) { 6658c2ecf20Sopenharmony_ci case QMI_UNSIGNED_1_BYTE: 6668c2ecf20Sopenharmony_ci case QMI_UNSIGNED_2_BYTE: 6678c2ecf20Sopenharmony_ci case QMI_UNSIGNED_4_BYTE: 6688c2ecf20Sopenharmony_ci case QMI_UNSIGNED_8_BYTE: 6698c2ecf20Sopenharmony_ci case QMI_SIGNED_2_BYTE_ENUM: 6708c2ecf20Sopenharmony_ci case QMI_SIGNED_4_BYTE_ENUM: 6718c2ecf20Sopenharmony_ci rc = qmi_decode_basic_elem(buf_dst, buf_src, 6728c2ecf20Sopenharmony_ci data_len_value, 6738c2ecf20Sopenharmony_ci temp_ei->elem_size); 6748c2ecf20Sopenharmony_ci UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc); 6758c2ecf20Sopenharmony_ci break; 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci case QMI_STRUCT: 6788c2ecf20Sopenharmony_ci rc = qmi_decode_struct_elem(temp_ei, buf_dst, buf_src, 6798c2ecf20Sopenharmony_ci data_len_value, tlv_len, 6808c2ecf20Sopenharmony_ci dec_level + 1); 6818c2ecf20Sopenharmony_ci if (rc < 0) 6828c2ecf20Sopenharmony_ci return rc; 6838c2ecf20Sopenharmony_ci UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc); 6848c2ecf20Sopenharmony_ci break; 6858c2ecf20Sopenharmony_ci 6868c2ecf20Sopenharmony_ci case QMI_STRING: 6878c2ecf20Sopenharmony_ci rc = qmi_decode_string_elem(temp_ei, buf_dst, buf_src, 6888c2ecf20Sopenharmony_ci tlv_len, dec_level); 6898c2ecf20Sopenharmony_ci if (rc < 0) 6908c2ecf20Sopenharmony_ci return rc; 6918c2ecf20Sopenharmony_ci UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc); 6928c2ecf20Sopenharmony_ci break; 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci default: 6958c2ecf20Sopenharmony_ci pr_err("%s: Unrecognized data type\n", __func__); 6968c2ecf20Sopenharmony_ci return -EINVAL; 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci temp_ei = temp_ei + 1; 6998c2ecf20Sopenharmony_ci } 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci return decoded_bytes; 7028c2ecf20Sopenharmony_ci} 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci/** 7058c2ecf20Sopenharmony_ci * qmi_encode_message() - Encode C structure as QMI encoded message 7068c2ecf20Sopenharmony_ci * @type: Type of QMI message 7078c2ecf20Sopenharmony_ci * @msg_id: Message ID of the message 7088c2ecf20Sopenharmony_ci * @len: Passed as max length of the message, updated to actual size 7098c2ecf20Sopenharmony_ci * @txn_id: Transaction ID 7108c2ecf20Sopenharmony_ci * @ei: QMI message descriptor 7118c2ecf20Sopenharmony_ci * @c_struct: Reference to structure to encode 7128c2ecf20Sopenharmony_ci * 7138c2ecf20Sopenharmony_ci * Return: Buffer with encoded message, or negative ERR_PTR() on error 7148c2ecf20Sopenharmony_ci */ 7158c2ecf20Sopenharmony_civoid *qmi_encode_message(int type, unsigned int msg_id, size_t *len, 7168c2ecf20Sopenharmony_ci unsigned int txn_id, struct qmi_elem_info *ei, 7178c2ecf20Sopenharmony_ci const void *c_struct) 7188c2ecf20Sopenharmony_ci{ 7198c2ecf20Sopenharmony_ci struct qmi_header *hdr; 7208c2ecf20Sopenharmony_ci ssize_t msglen = 0; 7218c2ecf20Sopenharmony_ci void *msg; 7228c2ecf20Sopenharmony_ci int ret; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci /* Check the possibility of a zero length QMI message */ 7258c2ecf20Sopenharmony_ci if (!c_struct) { 7268c2ecf20Sopenharmony_ci ret = qmi_calc_min_msg_len(ei, 1); 7278c2ecf20Sopenharmony_ci if (ret) { 7288c2ecf20Sopenharmony_ci pr_err("%s: Calc. len %d != 0, but NULL c_struct\n", 7298c2ecf20Sopenharmony_ci __func__, ret); 7308c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 7318c2ecf20Sopenharmony_ci } 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci msg = kzalloc(sizeof(*hdr) + *len, GFP_KERNEL); 7358c2ecf20Sopenharmony_ci if (!msg) 7368c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 7378c2ecf20Sopenharmony_ci 7388c2ecf20Sopenharmony_ci /* Encode message, if we have a message */ 7398c2ecf20Sopenharmony_ci if (c_struct) { 7408c2ecf20Sopenharmony_ci msglen = qmi_encode(ei, msg + sizeof(*hdr), c_struct, *len, 1); 7418c2ecf20Sopenharmony_ci if (msglen < 0) { 7428c2ecf20Sopenharmony_ci kfree(msg); 7438c2ecf20Sopenharmony_ci return ERR_PTR(msglen); 7448c2ecf20Sopenharmony_ci } 7458c2ecf20Sopenharmony_ci } 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci hdr = msg; 7488c2ecf20Sopenharmony_ci hdr->type = type; 7498c2ecf20Sopenharmony_ci hdr->txn_id = txn_id; 7508c2ecf20Sopenharmony_ci hdr->msg_id = msg_id; 7518c2ecf20Sopenharmony_ci hdr->msg_len = msglen; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci *len = sizeof(*hdr) + msglen; 7548c2ecf20Sopenharmony_ci 7558c2ecf20Sopenharmony_ci return msg; 7568c2ecf20Sopenharmony_ci} 7578c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qmi_encode_message); 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci/** 7608c2ecf20Sopenharmony_ci * qmi_decode_message() - Decode QMI encoded message to C structure 7618c2ecf20Sopenharmony_ci * @buf: Buffer with encoded message 7628c2ecf20Sopenharmony_ci * @len: Amount of data in @buf 7638c2ecf20Sopenharmony_ci * @ei: QMI message descriptor 7648c2ecf20Sopenharmony_ci * @c_struct: Reference to structure to decode into 7658c2ecf20Sopenharmony_ci * 7668c2ecf20Sopenharmony_ci * Return: The number of bytes of decoded information on success, negative 7678c2ecf20Sopenharmony_ci * errno on error. 7688c2ecf20Sopenharmony_ci */ 7698c2ecf20Sopenharmony_ciint qmi_decode_message(const void *buf, size_t len, 7708c2ecf20Sopenharmony_ci struct qmi_elem_info *ei, void *c_struct) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci if (!ei) 7738c2ecf20Sopenharmony_ci return -EINVAL; 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_ci if (!c_struct || !buf || !len) 7768c2ecf20Sopenharmony_ci return -EINVAL; 7778c2ecf20Sopenharmony_ci 7788c2ecf20Sopenharmony_ci return qmi_decode(ei, c_struct, buf + sizeof(struct qmi_header), 7798c2ecf20Sopenharmony_ci len - sizeof(struct qmi_header), 1); 7808c2ecf20Sopenharmony_ci} 7818c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qmi_decode_message); 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci/* Common header in all QMI responses */ 7848c2ecf20Sopenharmony_cistruct qmi_elem_info qmi_response_type_v01_ei[] = { 7858c2ecf20Sopenharmony_ci { 7868c2ecf20Sopenharmony_ci .data_type = QMI_SIGNED_2_BYTE_ENUM, 7878c2ecf20Sopenharmony_ci .elem_len = 1, 7888c2ecf20Sopenharmony_ci .elem_size = sizeof(u16), 7898c2ecf20Sopenharmony_ci .array_type = NO_ARRAY, 7908c2ecf20Sopenharmony_ci .tlv_type = QMI_COMMON_TLV_TYPE, 7918c2ecf20Sopenharmony_ci .offset = offsetof(struct qmi_response_type_v01, result), 7928c2ecf20Sopenharmony_ci .ei_array = NULL, 7938c2ecf20Sopenharmony_ci }, 7948c2ecf20Sopenharmony_ci { 7958c2ecf20Sopenharmony_ci .data_type = QMI_SIGNED_2_BYTE_ENUM, 7968c2ecf20Sopenharmony_ci .elem_len = 1, 7978c2ecf20Sopenharmony_ci .elem_size = sizeof(u16), 7988c2ecf20Sopenharmony_ci .array_type = NO_ARRAY, 7998c2ecf20Sopenharmony_ci .tlv_type = QMI_COMMON_TLV_TYPE, 8008c2ecf20Sopenharmony_ci .offset = offsetof(struct qmi_response_type_v01, error), 8018c2ecf20Sopenharmony_ci .ei_array = NULL, 8028c2ecf20Sopenharmony_ci }, 8038c2ecf20Sopenharmony_ci { 8048c2ecf20Sopenharmony_ci .data_type = QMI_EOTI, 8058c2ecf20Sopenharmony_ci .elem_len = 0, 8068c2ecf20Sopenharmony_ci .elem_size = 0, 8078c2ecf20Sopenharmony_ci .array_type = NO_ARRAY, 8088c2ecf20Sopenharmony_ci .tlv_type = QMI_COMMON_TLV_TYPE, 8098c2ecf20Sopenharmony_ci .offset = 0, 8108c2ecf20Sopenharmony_ci .ei_array = NULL, 8118c2ecf20Sopenharmony_ci }, 8128c2ecf20Sopenharmony_ci}; 8138c2ecf20Sopenharmony_ciEXPORT_SYMBOL(qmi_response_type_v01_ei); 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("QMI encoder/decoder helper"); 8168c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 817