162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved. 462306a36Sopenharmony_ci * Copyright (C) 2017 Linaro Ltd. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci#include <linux/slab.h> 762306a36Sopenharmony_ci#include <linux/uaccess.h> 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/errno.h> 1162306a36Sopenharmony_ci#include <linux/string.h> 1262306a36Sopenharmony_ci#include <linux/soc/qcom/qmi.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define QMI_ENCDEC_ENCODE_TLV(type, length, p_dst) do { \ 1562306a36Sopenharmony_ci *p_dst++ = type; \ 1662306a36Sopenharmony_ci *p_dst++ = ((u8)((length) & 0xFF)); \ 1762306a36Sopenharmony_ci *p_dst++ = ((u8)(((length) >> 8) & 0xFF)); \ 1862306a36Sopenharmony_ci} while (0) 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define QMI_ENCDEC_DECODE_TLV(p_type, p_length, p_src) do { \ 2162306a36Sopenharmony_ci *p_type = (u8)*p_src++; \ 2262306a36Sopenharmony_ci *p_length = (u8)*p_src++; \ 2362306a36Sopenharmony_ci *p_length |= ((u8)*p_src) << 8; \ 2462306a36Sopenharmony_ci} while (0) 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define QMI_ENCDEC_ENCODE_N_BYTES(p_dst, p_src, size) \ 2762306a36Sopenharmony_cido { \ 2862306a36Sopenharmony_ci memcpy(p_dst, p_src, size); \ 2962306a36Sopenharmony_ci p_dst = (u8 *)p_dst + size; \ 3062306a36Sopenharmony_ci p_src = (u8 *)p_src + size; \ 3162306a36Sopenharmony_ci} while (0) 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define QMI_ENCDEC_DECODE_N_BYTES(p_dst, p_src, size) \ 3462306a36Sopenharmony_cido { \ 3562306a36Sopenharmony_ci memcpy(p_dst, p_src, size); \ 3662306a36Sopenharmony_ci p_dst = (u8 *)p_dst + size; \ 3762306a36Sopenharmony_ci p_src = (u8 *)p_src + size; \ 3862306a36Sopenharmony_ci} while (0) 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci#define UPDATE_ENCODE_VARIABLES(temp_si, buf_dst, \ 4162306a36Sopenharmony_ci encoded_bytes, tlv_len, encode_tlv, rc) \ 4262306a36Sopenharmony_cido { \ 4362306a36Sopenharmony_ci buf_dst = (u8 *)buf_dst + rc; \ 4462306a36Sopenharmony_ci encoded_bytes += rc; \ 4562306a36Sopenharmony_ci tlv_len += rc; \ 4662306a36Sopenharmony_ci temp_si = temp_si + 1; \ 4762306a36Sopenharmony_ci encode_tlv = 1; \ 4862306a36Sopenharmony_ci} while (0) 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci#define UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc) \ 5162306a36Sopenharmony_cido { \ 5262306a36Sopenharmony_ci buf_src = (u8 *)buf_src + rc; \ 5362306a36Sopenharmony_ci decoded_bytes += rc; \ 5462306a36Sopenharmony_ci} while (0) 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci#define TLV_LEN_SIZE sizeof(u16) 5762306a36Sopenharmony_ci#define TLV_TYPE_SIZE sizeof(u8) 5862306a36Sopenharmony_ci#define OPTIONAL_TLV_TYPE_START 0x10 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf, 6162306a36Sopenharmony_ci const void *in_c_struct, u32 out_buf_len, 6262306a36Sopenharmony_ci int enc_level); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistatic int qmi_decode(const struct qmi_elem_info *ei_array, void *out_c_struct, 6562306a36Sopenharmony_ci const void *in_buf, u32 in_buf_len, int dec_level); 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/** 6862306a36Sopenharmony_ci * skip_to_next_elem() - Skip to next element in the structure to be encoded 6962306a36Sopenharmony_ci * @ei_array: Struct info describing the element to be skipped. 7062306a36Sopenharmony_ci * @level: Depth level of encoding/decoding to identify nested structures. 7162306a36Sopenharmony_ci * 7262306a36Sopenharmony_ci * This function is used while encoding optional elements. If the flag 7362306a36Sopenharmony_ci * corresponding to an optional element is not set, then encoding the 7462306a36Sopenharmony_ci * optional element can be skipped. This function can be used to perform 7562306a36Sopenharmony_ci * that operation. 7662306a36Sopenharmony_ci * 7762306a36Sopenharmony_ci * Return: struct info of the next element that can be encoded. 7862306a36Sopenharmony_ci */ 7962306a36Sopenharmony_cistatic const struct qmi_elem_info * 8062306a36Sopenharmony_ciskip_to_next_elem(const struct qmi_elem_info *ei_array, int level) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci const struct qmi_elem_info *temp_ei = ei_array; 8362306a36Sopenharmony_ci u8 tlv_type; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (level > 1) { 8662306a36Sopenharmony_ci temp_ei = temp_ei + 1; 8762306a36Sopenharmony_ci } else { 8862306a36Sopenharmony_ci do { 8962306a36Sopenharmony_ci tlv_type = temp_ei->tlv_type; 9062306a36Sopenharmony_ci temp_ei = temp_ei + 1; 9162306a36Sopenharmony_ci } while (tlv_type == temp_ei->tlv_type); 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return temp_ei; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci/** 9862306a36Sopenharmony_ci * qmi_calc_min_msg_len() - Calculate the minimum length of a QMI message 9962306a36Sopenharmony_ci * @ei_array: Struct info array describing the structure. 10062306a36Sopenharmony_ci * @level: Level to identify the depth of the nested structures. 10162306a36Sopenharmony_ci * 10262306a36Sopenharmony_ci * Return: Expected minimum length of the QMI message or 0 on error. 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_cistatic int qmi_calc_min_msg_len(const struct qmi_elem_info *ei_array, 10562306a36Sopenharmony_ci int level) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci int min_msg_len = 0; 10862306a36Sopenharmony_ci const struct qmi_elem_info *temp_ei = ei_array; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (!ei_array) 11162306a36Sopenharmony_ci return min_msg_len; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci while (temp_ei->data_type != QMI_EOTI) { 11462306a36Sopenharmony_ci /* Optional elements do not count in minimum length */ 11562306a36Sopenharmony_ci if (temp_ei->data_type == QMI_OPT_FLAG) { 11662306a36Sopenharmony_ci temp_ei = skip_to_next_elem(temp_ei, level); 11762306a36Sopenharmony_ci continue; 11862306a36Sopenharmony_ci } 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (temp_ei->data_type == QMI_DATA_LEN) { 12162306a36Sopenharmony_ci min_msg_len += (temp_ei->elem_size == sizeof(u8) ? 12262306a36Sopenharmony_ci sizeof(u8) : sizeof(u16)); 12362306a36Sopenharmony_ci temp_ei++; 12462306a36Sopenharmony_ci continue; 12562306a36Sopenharmony_ci } else if (temp_ei->data_type == QMI_STRUCT) { 12662306a36Sopenharmony_ci min_msg_len += qmi_calc_min_msg_len(temp_ei->ei_array, 12762306a36Sopenharmony_ci (level + 1)); 12862306a36Sopenharmony_ci temp_ei++; 12962306a36Sopenharmony_ci } else if (temp_ei->data_type == QMI_STRING) { 13062306a36Sopenharmony_ci if (level > 1) 13162306a36Sopenharmony_ci min_msg_len += temp_ei->elem_len <= U8_MAX ? 13262306a36Sopenharmony_ci sizeof(u8) : sizeof(u16); 13362306a36Sopenharmony_ci min_msg_len += temp_ei->elem_len * temp_ei->elem_size; 13462306a36Sopenharmony_ci temp_ei++; 13562306a36Sopenharmony_ci } else { 13662306a36Sopenharmony_ci min_msg_len += (temp_ei->elem_len * temp_ei->elem_size); 13762306a36Sopenharmony_ci temp_ei++; 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* 14162306a36Sopenharmony_ci * Type & Length info. not prepended for elements in the 14262306a36Sopenharmony_ci * nested structure. 14362306a36Sopenharmony_ci */ 14462306a36Sopenharmony_ci if (level == 1) 14562306a36Sopenharmony_ci min_msg_len += (TLV_TYPE_SIZE + TLV_LEN_SIZE); 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return min_msg_len; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/** 15262306a36Sopenharmony_ci * qmi_encode_basic_elem() - Encodes elements of basic/primary data type 15362306a36Sopenharmony_ci * @buf_dst: Buffer to store the encoded information. 15462306a36Sopenharmony_ci * @buf_src: Buffer containing the elements to be encoded. 15562306a36Sopenharmony_ci * @elem_len: Number of elements, in the buf_src, to be encoded. 15662306a36Sopenharmony_ci * @elem_size: Size of a single instance of the element to be encoded. 15762306a36Sopenharmony_ci * 15862306a36Sopenharmony_ci * This function encodes the "elem_len" number of data elements, each of 15962306a36Sopenharmony_ci * size "elem_size" bytes from the source buffer "buf_src" and stores the 16062306a36Sopenharmony_ci * encoded information in the destination buffer "buf_dst". The elements are 16162306a36Sopenharmony_ci * of primary data type which include u8 - u64 or similar. This 16262306a36Sopenharmony_ci * function returns the number of bytes of encoded information. 16362306a36Sopenharmony_ci * 16462306a36Sopenharmony_ci * Return: The number of bytes of encoded information. 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_cistatic int qmi_encode_basic_elem(void *buf_dst, const void *buf_src, 16762306a36Sopenharmony_ci u32 elem_len, u32 elem_size) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci u32 i, rc = 0; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci for (i = 0; i < elem_len; i++) { 17262306a36Sopenharmony_ci QMI_ENCDEC_ENCODE_N_BYTES(buf_dst, buf_src, elem_size); 17362306a36Sopenharmony_ci rc += elem_size; 17462306a36Sopenharmony_ci } 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci return rc; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci/** 18062306a36Sopenharmony_ci * qmi_encode_struct_elem() - Encodes elements of struct data type 18162306a36Sopenharmony_ci * @ei_array: Struct info array descibing the struct element. 18262306a36Sopenharmony_ci * @buf_dst: Buffer to store the encoded information. 18362306a36Sopenharmony_ci * @buf_src: Buffer containing the elements to be encoded. 18462306a36Sopenharmony_ci * @elem_len: Number of elements, in the buf_src, to be encoded. 18562306a36Sopenharmony_ci * @out_buf_len: Available space in the encode buffer. 18662306a36Sopenharmony_ci * @enc_level: Depth of the nested structure from the main structure. 18762306a36Sopenharmony_ci * 18862306a36Sopenharmony_ci * This function encodes the "elem_len" number of struct elements, each of 18962306a36Sopenharmony_ci * size "ei_array->elem_size" bytes from the source buffer "buf_src" and 19062306a36Sopenharmony_ci * stores the encoded information in the destination buffer "buf_dst". The 19162306a36Sopenharmony_ci * elements are of struct data type which includes any C structure. This 19262306a36Sopenharmony_ci * function returns the number of bytes of encoded information. 19362306a36Sopenharmony_ci * 19462306a36Sopenharmony_ci * Return: The number of bytes of encoded information on success or negative 19562306a36Sopenharmony_ci * errno on error. 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_cistatic int qmi_encode_struct_elem(const struct qmi_elem_info *ei_array, 19862306a36Sopenharmony_ci void *buf_dst, const void *buf_src, 19962306a36Sopenharmony_ci u32 elem_len, u32 out_buf_len, 20062306a36Sopenharmony_ci int enc_level) 20162306a36Sopenharmony_ci{ 20262306a36Sopenharmony_ci int i, rc, encoded_bytes = 0; 20362306a36Sopenharmony_ci const struct qmi_elem_info *temp_ei = ei_array; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci for (i = 0; i < elem_len; i++) { 20662306a36Sopenharmony_ci rc = qmi_encode(temp_ei->ei_array, buf_dst, buf_src, 20762306a36Sopenharmony_ci out_buf_len - encoded_bytes, enc_level); 20862306a36Sopenharmony_ci if (rc < 0) { 20962306a36Sopenharmony_ci pr_err("%s: STRUCT Encode failure\n", __func__); 21062306a36Sopenharmony_ci return rc; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci buf_dst = buf_dst + rc; 21362306a36Sopenharmony_ci buf_src = buf_src + temp_ei->elem_size; 21462306a36Sopenharmony_ci encoded_bytes += rc; 21562306a36Sopenharmony_ci } 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci return encoded_bytes; 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci/** 22162306a36Sopenharmony_ci * qmi_encode_string_elem() - Encodes elements of string data type 22262306a36Sopenharmony_ci * @ei_array: Struct info array descibing the string element. 22362306a36Sopenharmony_ci * @buf_dst: Buffer to store the encoded information. 22462306a36Sopenharmony_ci * @buf_src: Buffer containing the elements to be encoded. 22562306a36Sopenharmony_ci * @out_buf_len: Available space in the encode buffer. 22662306a36Sopenharmony_ci * @enc_level: Depth of the string element from the main structure. 22762306a36Sopenharmony_ci * 22862306a36Sopenharmony_ci * This function encodes a string element of maximum length "ei_array->elem_len" 22962306a36Sopenharmony_ci * bytes from the source buffer "buf_src" and stores the encoded information in 23062306a36Sopenharmony_ci * the destination buffer "buf_dst". This function returns the number of bytes 23162306a36Sopenharmony_ci * of encoded information. 23262306a36Sopenharmony_ci * 23362306a36Sopenharmony_ci * Return: The number of bytes of encoded information on success or negative 23462306a36Sopenharmony_ci * errno on error. 23562306a36Sopenharmony_ci */ 23662306a36Sopenharmony_cistatic int qmi_encode_string_elem(const struct qmi_elem_info *ei_array, 23762306a36Sopenharmony_ci void *buf_dst, const void *buf_src, 23862306a36Sopenharmony_ci u32 out_buf_len, int enc_level) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci int rc; 24162306a36Sopenharmony_ci int encoded_bytes = 0; 24262306a36Sopenharmony_ci const struct qmi_elem_info *temp_ei = ei_array; 24362306a36Sopenharmony_ci u32 string_len = 0; 24462306a36Sopenharmony_ci u32 string_len_sz = 0; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci string_len = strlen(buf_src); 24762306a36Sopenharmony_ci string_len_sz = temp_ei->elem_len <= U8_MAX ? 24862306a36Sopenharmony_ci sizeof(u8) : sizeof(u16); 24962306a36Sopenharmony_ci if (string_len > temp_ei->elem_len) { 25062306a36Sopenharmony_ci pr_err("%s: String to be encoded is longer - %d > %d\n", 25162306a36Sopenharmony_ci __func__, string_len, temp_ei->elem_len); 25262306a36Sopenharmony_ci return -EINVAL; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (enc_level == 1) { 25662306a36Sopenharmony_ci if (string_len + TLV_LEN_SIZE + TLV_TYPE_SIZE > 25762306a36Sopenharmony_ci out_buf_len) { 25862306a36Sopenharmony_ci pr_err("%s: Output len %d > Out Buf len %d\n", 25962306a36Sopenharmony_ci __func__, string_len, out_buf_len); 26062306a36Sopenharmony_ci return -ETOOSMALL; 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci } else { 26362306a36Sopenharmony_ci if (string_len + string_len_sz > out_buf_len) { 26462306a36Sopenharmony_ci pr_err("%s: Output len %d > Out Buf len %d\n", 26562306a36Sopenharmony_ci __func__, string_len, out_buf_len); 26662306a36Sopenharmony_ci return -ETOOSMALL; 26762306a36Sopenharmony_ci } 26862306a36Sopenharmony_ci rc = qmi_encode_basic_elem(buf_dst, &string_len, 26962306a36Sopenharmony_ci 1, string_len_sz); 27062306a36Sopenharmony_ci encoded_bytes += rc; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci rc = qmi_encode_basic_elem(buf_dst + encoded_bytes, buf_src, 27462306a36Sopenharmony_ci string_len, temp_ei->elem_size); 27562306a36Sopenharmony_ci encoded_bytes += rc; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return encoded_bytes; 27862306a36Sopenharmony_ci} 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci/** 28162306a36Sopenharmony_ci * qmi_encode() - Core Encode Function 28262306a36Sopenharmony_ci * @ei_array: Struct info array describing the structure to be encoded. 28362306a36Sopenharmony_ci * @out_buf: Buffer to hold the encoded QMI message. 28462306a36Sopenharmony_ci * @in_c_struct: Pointer to the C structure to be encoded. 28562306a36Sopenharmony_ci * @out_buf_len: Available space in the encode buffer. 28662306a36Sopenharmony_ci * @enc_level: Encode level to indicate the depth of the nested structure, 28762306a36Sopenharmony_ci * within the main structure, being encoded. 28862306a36Sopenharmony_ci * 28962306a36Sopenharmony_ci * Return: The number of bytes of encoded information on success or negative 29062306a36Sopenharmony_ci * errno on error. 29162306a36Sopenharmony_ci */ 29262306a36Sopenharmony_cistatic int qmi_encode(const struct qmi_elem_info *ei_array, void *out_buf, 29362306a36Sopenharmony_ci const void *in_c_struct, u32 out_buf_len, 29462306a36Sopenharmony_ci int enc_level) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci const struct qmi_elem_info *temp_ei = ei_array; 29762306a36Sopenharmony_ci u8 opt_flag_value = 0; 29862306a36Sopenharmony_ci u32 data_len_value = 0, data_len_sz; 29962306a36Sopenharmony_ci u8 *buf_dst = (u8 *)out_buf; 30062306a36Sopenharmony_ci u8 *tlv_pointer; 30162306a36Sopenharmony_ci u32 tlv_len; 30262306a36Sopenharmony_ci u8 tlv_type; 30362306a36Sopenharmony_ci u32 encoded_bytes = 0; 30462306a36Sopenharmony_ci const void *buf_src; 30562306a36Sopenharmony_ci int encode_tlv = 0; 30662306a36Sopenharmony_ci int rc; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci if (!ei_array) 30962306a36Sopenharmony_ci return 0; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci tlv_pointer = buf_dst; 31262306a36Sopenharmony_ci tlv_len = 0; 31362306a36Sopenharmony_ci if (enc_level == 1) 31462306a36Sopenharmony_ci buf_dst = buf_dst + (TLV_LEN_SIZE + TLV_TYPE_SIZE); 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci while (temp_ei->data_type != QMI_EOTI) { 31762306a36Sopenharmony_ci buf_src = in_c_struct + temp_ei->offset; 31862306a36Sopenharmony_ci tlv_type = temp_ei->tlv_type; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (temp_ei->array_type == NO_ARRAY) { 32162306a36Sopenharmony_ci data_len_value = 1; 32262306a36Sopenharmony_ci } else if (temp_ei->array_type == STATIC_ARRAY) { 32362306a36Sopenharmony_ci data_len_value = temp_ei->elem_len; 32462306a36Sopenharmony_ci } else if (data_len_value <= 0 || 32562306a36Sopenharmony_ci temp_ei->elem_len < data_len_value) { 32662306a36Sopenharmony_ci pr_err("%s: Invalid data length\n", __func__); 32762306a36Sopenharmony_ci return -EINVAL; 32862306a36Sopenharmony_ci } 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci switch (temp_ei->data_type) { 33162306a36Sopenharmony_ci case QMI_OPT_FLAG: 33262306a36Sopenharmony_ci rc = qmi_encode_basic_elem(&opt_flag_value, buf_src, 33362306a36Sopenharmony_ci 1, sizeof(u8)); 33462306a36Sopenharmony_ci if (opt_flag_value) 33562306a36Sopenharmony_ci temp_ei = temp_ei + 1; 33662306a36Sopenharmony_ci else 33762306a36Sopenharmony_ci temp_ei = skip_to_next_elem(temp_ei, enc_level); 33862306a36Sopenharmony_ci break; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci case QMI_DATA_LEN: 34162306a36Sopenharmony_ci memcpy(&data_len_value, buf_src, temp_ei->elem_size); 34262306a36Sopenharmony_ci data_len_sz = temp_ei->elem_size == sizeof(u8) ? 34362306a36Sopenharmony_ci sizeof(u8) : sizeof(u16); 34462306a36Sopenharmony_ci /* Check to avoid out of range buffer access */ 34562306a36Sopenharmony_ci if ((data_len_sz + encoded_bytes + TLV_LEN_SIZE + 34662306a36Sopenharmony_ci TLV_TYPE_SIZE) > out_buf_len) { 34762306a36Sopenharmony_ci pr_err("%s: Too Small Buffer @DATA_LEN\n", 34862306a36Sopenharmony_ci __func__); 34962306a36Sopenharmony_ci return -ETOOSMALL; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci rc = qmi_encode_basic_elem(buf_dst, &data_len_value, 35262306a36Sopenharmony_ci 1, data_len_sz); 35362306a36Sopenharmony_ci UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst, 35462306a36Sopenharmony_ci encoded_bytes, tlv_len, 35562306a36Sopenharmony_ci encode_tlv, rc); 35662306a36Sopenharmony_ci if (!data_len_value) 35762306a36Sopenharmony_ci temp_ei = skip_to_next_elem(temp_ei, enc_level); 35862306a36Sopenharmony_ci else 35962306a36Sopenharmony_ci encode_tlv = 0; 36062306a36Sopenharmony_ci break; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci case QMI_UNSIGNED_1_BYTE: 36362306a36Sopenharmony_ci case QMI_UNSIGNED_2_BYTE: 36462306a36Sopenharmony_ci case QMI_UNSIGNED_4_BYTE: 36562306a36Sopenharmony_ci case QMI_UNSIGNED_8_BYTE: 36662306a36Sopenharmony_ci case QMI_SIGNED_2_BYTE_ENUM: 36762306a36Sopenharmony_ci case QMI_SIGNED_4_BYTE_ENUM: 36862306a36Sopenharmony_ci /* Check to avoid out of range buffer access */ 36962306a36Sopenharmony_ci if (((data_len_value * temp_ei->elem_size) + 37062306a36Sopenharmony_ci encoded_bytes + TLV_LEN_SIZE + TLV_TYPE_SIZE) > 37162306a36Sopenharmony_ci out_buf_len) { 37262306a36Sopenharmony_ci pr_err("%s: Too Small Buffer @data_type:%d\n", 37362306a36Sopenharmony_ci __func__, temp_ei->data_type); 37462306a36Sopenharmony_ci return -ETOOSMALL; 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci rc = qmi_encode_basic_elem(buf_dst, buf_src, 37762306a36Sopenharmony_ci data_len_value, 37862306a36Sopenharmony_ci temp_ei->elem_size); 37962306a36Sopenharmony_ci UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst, 38062306a36Sopenharmony_ci encoded_bytes, tlv_len, 38162306a36Sopenharmony_ci encode_tlv, rc); 38262306a36Sopenharmony_ci break; 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci case QMI_STRUCT: 38562306a36Sopenharmony_ci rc = qmi_encode_struct_elem(temp_ei, buf_dst, buf_src, 38662306a36Sopenharmony_ci data_len_value, 38762306a36Sopenharmony_ci out_buf_len - encoded_bytes, 38862306a36Sopenharmony_ci enc_level + 1); 38962306a36Sopenharmony_ci if (rc < 0) 39062306a36Sopenharmony_ci return rc; 39162306a36Sopenharmony_ci UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst, 39262306a36Sopenharmony_ci encoded_bytes, tlv_len, 39362306a36Sopenharmony_ci encode_tlv, rc); 39462306a36Sopenharmony_ci break; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci case QMI_STRING: 39762306a36Sopenharmony_ci rc = qmi_encode_string_elem(temp_ei, buf_dst, buf_src, 39862306a36Sopenharmony_ci out_buf_len - encoded_bytes, 39962306a36Sopenharmony_ci enc_level); 40062306a36Sopenharmony_ci if (rc < 0) 40162306a36Sopenharmony_ci return rc; 40262306a36Sopenharmony_ci UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst, 40362306a36Sopenharmony_ci encoded_bytes, tlv_len, 40462306a36Sopenharmony_ci encode_tlv, rc); 40562306a36Sopenharmony_ci break; 40662306a36Sopenharmony_ci default: 40762306a36Sopenharmony_ci pr_err("%s: Unrecognized data type\n", __func__); 40862306a36Sopenharmony_ci return -EINVAL; 40962306a36Sopenharmony_ci } 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci if (encode_tlv && enc_level == 1) { 41262306a36Sopenharmony_ci QMI_ENCDEC_ENCODE_TLV(tlv_type, tlv_len, tlv_pointer); 41362306a36Sopenharmony_ci encoded_bytes += (TLV_TYPE_SIZE + TLV_LEN_SIZE); 41462306a36Sopenharmony_ci tlv_pointer = buf_dst; 41562306a36Sopenharmony_ci tlv_len = 0; 41662306a36Sopenharmony_ci buf_dst = buf_dst + TLV_LEN_SIZE + TLV_TYPE_SIZE; 41762306a36Sopenharmony_ci encode_tlv = 0; 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return encoded_bytes; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci/** 42562306a36Sopenharmony_ci * qmi_decode_basic_elem() - Decodes elements of basic/primary data type 42662306a36Sopenharmony_ci * @buf_dst: Buffer to store the decoded element. 42762306a36Sopenharmony_ci * @buf_src: Buffer containing the elements in QMI wire format. 42862306a36Sopenharmony_ci * @elem_len: Number of elements to be decoded. 42962306a36Sopenharmony_ci * @elem_size: Size of a single instance of the element to be decoded. 43062306a36Sopenharmony_ci * 43162306a36Sopenharmony_ci * This function decodes the "elem_len" number of elements in QMI wire format, 43262306a36Sopenharmony_ci * each of size "elem_size" bytes from the source buffer "buf_src" and stores 43362306a36Sopenharmony_ci * the decoded elements in the destination buffer "buf_dst". The elements are 43462306a36Sopenharmony_ci * of primary data type which include u8 - u64 or similar. This 43562306a36Sopenharmony_ci * function returns the number of bytes of decoded information. 43662306a36Sopenharmony_ci * 43762306a36Sopenharmony_ci * Return: The total size of the decoded data elements, in bytes. 43862306a36Sopenharmony_ci */ 43962306a36Sopenharmony_cistatic int qmi_decode_basic_elem(void *buf_dst, const void *buf_src, 44062306a36Sopenharmony_ci u32 elem_len, u32 elem_size) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci u32 i, rc = 0; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci for (i = 0; i < elem_len; i++) { 44562306a36Sopenharmony_ci QMI_ENCDEC_DECODE_N_BYTES(buf_dst, buf_src, elem_size); 44662306a36Sopenharmony_ci rc += elem_size; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci return rc; 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci/** 45362306a36Sopenharmony_ci * qmi_decode_struct_elem() - Decodes elements of struct data type 45462306a36Sopenharmony_ci * @ei_array: Struct info array describing the struct element. 45562306a36Sopenharmony_ci * @buf_dst: Buffer to store the decoded element. 45662306a36Sopenharmony_ci * @buf_src: Buffer containing the elements in QMI wire format. 45762306a36Sopenharmony_ci * @elem_len: Number of elements to be decoded. 45862306a36Sopenharmony_ci * @tlv_len: Total size of the encoded information corresponding to 45962306a36Sopenharmony_ci * this struct element. 46062306a36Sopenharmony_ci * @dec_level: Depth of the nested structure from the main structure. 46162306a36Sopenharmony_ci * 46262306a36Sopenharmony_ci * This function decodes the "elem_len" number of elements in QMI wire format, 46362306a36Sopenharmony_ci * each of size "(tlv_len/elem_len)" bytes from the source buffer "buf_src" 46462306a36Sopenharmony_ci * and stores the decoded elements in the destination buffer "buf_dst". The 46562306a36Sopenharmony_ci * elements are of struct data type which includes any C structure. This 46662306a36Sopenharmony_ci * function returns the number of bytes of decoded information. 46762306a36Sopenharmony_ci * 46862306a36Sopenharmony_ci * Return: The total size of the decoded data elements on success, negative 46962306a36Sopenharmony_ci * errno on error. 47062306a36Sopenharmony_ci */ 47162306a36Sopenharmony_cistatic int qmi_decode_struct_elem(const struct qmi_elem_info *ei_array, 47262306a36Sopenharmony_ci void *buf_dst, const void *buf_src, 47362306a36Sopenharmony_ci u32 elem_len, u32 tlv_len, 47462306a36Sopenharmony_ci int dec_level) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci int i, rc, decoded_bytes = 0; 47762306a36Sopenharmony_ci const struct qmi_elem_info *temp_ei = ei_array; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci for (i = 0; i < elem_len && decoded_bytes < tlv_len; i++) { 48062306a36Sopenharmony_ci rc = qmi_decode(temp_ei->ei_array, buf_dst, buf_src, 48162306a36Sopenharmony_ci tlv_len - decoded_bytes, dec_level); 48262306a36Sopenharmony_ci if (rc < 0) 48362306a36Sopenharmony_ci return rc; 48462306a36Sopenharmony_ci buf_src = buf_src + rc; 48562306a36Sopenharmony_ci buf_dst = buf_dst + temp_ei->elem_size; 48662306a36Sopenharmony_ci decoded_bytes += rc; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_ci if ((dec_level <= 2 && decoded_bytes != tlv_len) || 49062306a36Sopenharmony_ci (dec_level > 2 && (i < elem_len || decoded_bytes > tlv_len))) { 49162306a36Sopenharmony_ci pr_err("%s: Fault in decoding: dl(%d), db(%d), tl(%d), i(%d), el(%d)\n", 49262306a36Sopenharmony_ci __func__, dec_level, decoded_bytes, tlv_len, 49362306a36Sopenharmony_ci i, elem_len); 49462306a36Sopenharmony_ci return -EFAULT; 49562306a36Sopenharmony_ci } 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci return decoded_bytes; 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci/** 50162306a36Sopenharmony_ci * qmi_decode_string_elem() - Decodes elements of string data type 50262306a36Sopenharmony_ci * @ei_array: Struct info array describing the string element. 50362306a36Sopenharmony_ci * @buf_dst: Buffer to store the decoded element. 50462306a36Sopenharmony_ci * @buf_src: Buffer containing the elements in QMI wire format. 50562306a36Sopenharmony_ci * @tlv_len: Total size of the encoded information corresponding to 50662306a36Sopenharmony_ci * this string element. 50762306a36Sopenharmony_ci * @dec_level: Depth of the string element from the main structure. 50862306a36Sopenharmony_ci * 50962306a36Sopenharmony_ci * This function decodes the string element of maximum length 51062306a36Sopenharmony_ci * "ei_array->elem_len" from the source buffer "buf_src" and puts it into 51162306a36Sopenharmony_ci * the destination buffer "buf_dst". This function returns number of bytes 51262306a36Sopenharmony_ci * decoded from the input buffer. 51362306a36Sopenharmony_ci * 51462306a36Sopenharmony_ci * Return: The total size of the decoded data elements on success, negative 51562306a36Sopenharmony_ci * errno on error. 51662306a36Sopenharmony_ci */ 51762306a36Sopenharmony_cistatic int qmi_decode_string_elem(const struct qmi_elem_info *ei_array, 51862306a36Sopenharmony_ci void *buf_dst, const void *buf_src, 51962306a36Sopenharmony_ci u32 tlv_len, int dec_level) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci int rc; 52262306a36Sopenharmony_ci int decoded_bytes = 0; 52362306a36Sopenharmony_ci u32 string_len = 0; 52462306a36Sopenharmony_ci u32 string_len_sz = 0; 52562306a36Sopenharmony_ci const struct qmi_elem_info *temp_ei = ei_array; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci if (dec_level == 1) { 52862306a36Sopenharmony_ci string_len = tlv_len; 52962306a36Sopenharmony_ci } else { 53062306a36Sopenharmony_ci string_len_sz = temp_ei->elem_len <= U8_MAX ? 53162306a36Sopenharmony_ci sizeof(u8) : sizeof(u16); 53262306a36Sopenharmony_ci rc = qmi_decode_basic_elem(&string_len, buf_src, 53362306a36Sopenharmony_ci 1, string_len_sz); 53462306a36Sopenharmony_ci decoded_bytes += rc; 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci if (string_len >= temp_ei->elem_len) { 53862306a36Sopenharmony_ci pr_err("%s: String len %d >= Max Len %d\n", 53962306a36Sopenharmony_ci __func__, string_len, temp_ei->elem_len); 54062306a36Sopenharmony_ci return -ETOOSMALL; 54162306a36Sopenharmony_ci } else if (string_len > tlv_len) { 54262306a36Sopenharmony_ci pr_err("%s: String len %d > Input Buffer Len %d\n", 54362306a36Sopenharmony_ci __func__, string_len, tlv_len); 54462306a36Sopenharmony_ci return -EFAULT; 54562306a36Sopenharmony_ci } 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci rc = qmi_decode_basic_elem(buf_dst, buf_src + decoded_bytes, 54862306a36Sopenharmony_ci string_len, temp_ei->elem_size); 54962306a36Sopenharmony_ci *((char *)buf_dst + string_len) = '\0'; 55062306a36Sopenharmony_ci decoded_bytes += rc; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci return decoded_bytes; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci/** 55662306a36Sopenharmony_ci * find_ei() - Find element info corresponding to TLV Type 55762306a36Sopenharmony_ci * @ei_array: Struct info array of the message being decoded. 55862306a36Sopenharmony_ci * @type: TLV Type of the element being searched. 55962306a36Sopenharmony_ci * 56062306a36Sopenharmony_ci * Every element that got encoded in the QMI message will have a type 56162306a36Sopenharmony_ci * information associated with it. While decoding the QMI message, 56262306a36Sopenharmony_ci * this function is used to find the struct info regarding the element 56362306a36Sopenharmony_ci * that corresponds to the type being decoded. 56462306a36Sopenharmony_ci * 56562306a36Sopenharmony_ci * Return: Pointer to struct info, if found 56662306a36Sopenharmony_ci */ 56762306a36Sopenharmony_cistatic const struct qmi_elem_info *find_ei(const struct qmi_elem_info *ei_array, 56862306a36Sopenharmony_ci u32 type) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci const struct qmi_elem_info *temp_ei = ei_array; 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci while (temp_ei->data_type != QMI_EOTI) { 57362306a36Sopenharmony_ci if (temp_ei->tlv_type == (u8)type) 57462306a36Sopenharmony_ci return temp_ei; 57562306a36Sopenharmony_ci temp_ei = temp_ei + 1; 57662306a36Sopenharmony_ci } 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci return NULL; 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ci/** 58262306a36Sopenharmony_ci * qmi_decode() - Core Decode Function 58362306a36Sopenharmony_ci * @ei_array: Struct info array describing the structure to be decoded. 58462306a36Sopenharmony_ci * @out_c_struct: Buffer to hold the decoded C struct 58562306a36Sopenharmony_ci * @in_buf: Buffer containing the QMI message to be decoded 58662306a36Sopenharmony_ci * @in_buf_len: Length of the QMI message to be decoded 58762306a36Sopenharmony_ci * @dec_level: Decode level to indicate the depth of the nested structure, 58862306a36Sopenharmony_ci * within the main structure, being decoded 58962306a36Sopenharmony_ci * 59062306a36Sopenharmony_ci * Return: The number of bytes of decoded information on success, negative 59162306a36Sopenharmony_ci * errno on error. 59262306a36Sopenharmony_ci */ 59362306a36Sopenharmony_cistatic int qmi_decode(const struct qmi_elem_info *ei_array, void *out_c_struct, 59462306a36Sopenharmony_ci const void *in_buf, u32 in_buf_len, 59562306a36Sopenharmony_ci int dec_level) 59662306a36Sopenharmony_ci{ 59762306a36Sopenharmony_ci const struct qmi_elem_info *temp_ei = ei_array; 59862306a36Sopenharmony_ci u8 opt_flag_value = 1; 59962306a36Sopenharmony_ci u32 data_len_value = 0, data_len_sz = 0; 60062306a36Sopenharmony_ci u8 *buf_dst = out_c_struct; 60162306a36Sopenharmony_ci const u8 *tlv_pointer; 60262306a36Sopenharmony_ci u32 tlv_len = 0; 60362306a36Sopenharmony_ci u32 tlv_type; 60462306a36Sopenharmony_ci u32 decoded_bytes = 0; 60562306a36Sopenharmony_ci const void *buf_src = in_buf; 60662306a36Sopenharmony_ci int rc; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci while (decoded_bytes < in_buf_len) { 60962306a36Sopenharmony_ci if (dec_level >= 2 && temp_ei->data_type == QMI_EOTI) 61062306a36Sopenharmony_ci return decoded_bytes; 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci if (dec_level == 1) { 61362306a36Sopenharmony_ci tlv_pointer = buf_src; 61462306a36Sopenharmony_ci QMI_ENCDEC_DECODE_TLV(&tlv_type, 61562306a36Sopenharmony_ci &tlv_len, tlv_pointer); 61662306a36Sopenharmony_ci buf_src += (TLV_TYPE_SIZE + TLV_LEN_SIZE); 61762306a36Sopenharmony_ci decoded_bytes += (TLV_TYPE_SIZE + TLV_LEN_SIZE); 61862306a36Sopenharmony_ci temp_ei = find_ei(ei_array, tlv_type); 61962306a36Sopenharmony_ci if (!temp_ei && tlv_type < OPTIONAL_TLV_TYPE_START) { 62062306a36Sopenharmony_ci pr_err("%s: Inval element info\n", __func__); 62162306a36Sopenharmony_ci return -EINVAL; 62262306a36Sopenharmony_ci } else if (!temp_ei) { 62362306a36Sopenharmony_ci UPDATE_DECODE_VARIABLES(buf_src, 62462306a36Sopenharmony_ci decoded_bytes, tlv_len); 62562306a36Sopenharmony_ci continue; 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci } else { 62862306a36Sopenharmony_ci /* 62962306a36Sopenharmony_ci * No length information for elements in nested 63062306a36Sopenharmony_ci * structures. So use remaining decodable buffer space. 63162306a36Sopenharmony_ci */ 63262306a36Sopenharmony_ci tlv_len = in_buf_len - decoded_bytes; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci 63562306a36Sopenharmony_ci buf_dst = out_c_struct + temp_ei->offset; 63662306a36Sopenharmony_ci if (temp_ei->data_type == QMI_OPT_FLAG) { 63762306a36Sopenharmony_ci memcpy(buf_dst, &opt_flag_value, sizeof(u8)); 63862306a36Sopenharmony_ci temp_ei = temp_ei + 1; 63962306a36Sopenharmony_ci buf_dst = out_c_struct + temp_ei->offset; 64062306a36Sopenharmony_ci } 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci if (temp_ei->data_type == QMI_DATA_LEN) { 64362306a36Sopenharmony_ci data_len_sz = temp_ei->elem_size == sizeof(u8) ? 64462306a36Sopenharmony_ci sizeof(u8) : sizeof(u16); 64562306a36Sopenharmony_ci rc = qmi_decode_basic_elem(&data_len_value, buf_src, 64662306a36Sopenharmony_ci 1, data_len_sz); 64762306a36Sopenharmony_ci memcpy(buf_dst, &data_len_value, sizeof(u32)); 64862306a36Sopenharmony_ci temp_ei = temp_ei + 1; 64962306a36Sopenharmony_ci buf_dst = out_c_struct + temp_ei->offset; 65062306a36Sopenharmony_ci tlv_len -= data_len_sz; 65162306a36Sopenharmony_ci UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc); 65262306a36Sopenharmony_ci } 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (temp_ei->array_type == NO_ARRAY) { 65562306a36Sopenharmony_ci data_len_value = 1; 65662306a36Sopenharmony_ci } else if (temp_ei->array_type == STATIC_ARRAY) { 65762306a36Sopenharmony_ci data_len_value = temp_ei->elem_len; 65862306a36Sopenharmony_ci } else if (data_len_value > temp_ei->elem_len) { 65962306a36Sopenharmony_ci pr_err("%s: Data len %d > max spec %d\n", 66062306a36Sopenharmony_ci __func__, data_len_value, temp_ei->elem_len); 66162306a36Sopenharmony_ci return -ETOOSMALL; 66262306a36Sopenharmony_ci } 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci switch (temp_ei->data_type) { 66562306a36Sopenharmony_ci case QMI_UNSIGNED_1_BYTE: 66662306a36Sopenharmony_ci case QMI_UNSIGNED_2_BYTE: 66762306a36Sopenharmony_ci case QMI_UNSIGNED_4_BYTE: 66862306a36Sopenharmony_ci case QMI_UNSIGNED_8_BYTE: 66962306a36Sopenharmony_ci case QMI_SIGNED_2_BYTE_ENUM: 67062306a36Sopenharmony_ci case QMI_SIGNED_4_BYTE_ENUM: 67162306a36Sopenharmony_ci rc = qmi_decode_basic_elem(buf_dst, buf_src, 67262306a36Sopenharmony_ci data_len_value, 67362306a36Sopenharmony_ci temp_ei->elem_size); 67462306a36Sopenharmony_ci UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc); 67562306a36Sopenharmony_ci break; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci case QMI_STRUCT: 67862306a36Sopenharmony_ci rc = qmi_decode_struct_elem(temp_ei, buf_dst, buf_src, 67962306a36Sopenharmony_ci data_len_value, tlv_len, 68062306a36Sopenharmony_ci dec_level + 1); 68162306a36Sopenharmony_ci if (rc < 0) 68262306a36Sopenharmony_ci return rc; 68362306a36Sopenharmony_ci UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc); 68462306a36Sopenharmony_ci break; 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci case QMI_STRING: 68762306a36Sopenharmony_ci rc = qmi_decode_string_elem(temp_ei, buf_dst, buf_src, 68862306a36Sopenharmony_ci tlv_len, dec_level); 68962306a36Sopenharmony_ci if (rc < 0) 69062306a36Sopenharmony_ci return rc; 69162306a36Sopenharmony_ci UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc); 69262306a36Sopenharmony_ci break; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci default: 69562306a36Sopenharmony_ci pr_err("%s: Unrecognized data type\n", __func__); 69662306a36Sopenharmony_ci return -EINVAL; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci temp_ei = temp_ei + 1; 69962306a36Sopenharmony_ci } 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci return decoded_bytes; 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci/** 70562306a36Sopenharmony_ci * qmi_encode_message() - Encode C structure as QMI encoded message 70662306a36Sopenharmony_ci * @type: Type of QMI message 70762306a36Sopenharmony_ci * @msg_id: Message ID of the message 70862306a36Sopenharmony_ci * @len: Passed as max length of the message, updated to actual size 70962306a36Sopenharmony_ci * @txn_id: Transaction ID 71062306a36Sopenharmony_ci * @ei: QMI message descriptor 71162306a36Sopenharmony_ci * @c_struct: Reference to structure to encode 71262306a36Sopenharmony_ci * 71362306a36Sopenharmony_ci * Return: Buffer with encoded message, or negative ERR_PTR() on error 71462306a36Sopenharmony_ci */ 71562306a36Sopenharmony_civoid *qmi_encode_message(int type, unsigned int msg_id, size_t *len, 71662306a36Sopenharmony_ci unsigned int txn_id, const struct qmi_elem_info *ei, 71762306a36Sopenharmony_ci const void *c_struct) 71862306a36Sopenharmony_ci{ 71962306a36Sopenharmony_ci struct qmi_header *hdr; 72062306a36Sopenharmony_ci ssize_t msglen = 0; 72162306a36Sopenharmony_ci void *msg; 72262306a36Sopenharmony_ci int ret; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci /* Check the possibility of a zero length QMI message */ 72562306a36Sopenharmony_ci if (!c_struct) { 72662306a36Sopenharmony_ci ret = qmi_calc_min_msg_len(ei, 1); 72762306a36Sopenharmony_ci if (ret) { 72862306a36Sopenharmony_ci pr_err("%s: Calc. len %d != 0, but NULL c_struct\n", 72962306a36Sopenharmony_ci __func__, ret); 73062306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 73162306a36Sopenharmony_ci } 73262306a36Sopenharmony_ci } 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci msg = kzalloc(sizeof(*hdr) + *len, GFP_KERNEL); 73562306a36Sopenharmony_ci if (!msg) 73662306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci /* Encode message, if we have a message */ 73962306a36Sopenharmony_ci if (c_struct) { 74062306a36Sopenharmony_ci msglen = qmi_encode(ei, msg + sizeof(*hdr), c_struct, *len, 1); 74162306a36Sopenharmony_ci if (msglen < 0) { 74262306a36Sopenharmony_ci kfree(msg); 74362306a36Sopenharmony_ci return ERR_PTR(msglen); 74462306a36Sopenharmony_ci } 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci hdr = msg; 74862306a36Sopenharmony_ci hdr->type = type; 74962306a36Sopenharmony_ci hdr->txn_id = txn_id; 75062306a36Sopenharmony_ci hdr->msg_id = msg_id; 75162306a36Sopenharmony_ci hdr->msg_len = msglen; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci *len = sizeof(*hdr) + msglen; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci return msg; 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ciEXPORT_SYMBOL(qmi_encode_message); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci/** 76062306a36Sopenharmony_ci * qmi_decode_message() - Decode QMI encoded message to C structure 76162306a36Sopenharmony_ci * @buf: Buffer with encoded message 76262306a36Sopenharmony_ci * @len: Amount of data in @buf 76362306a36Sopenharmony_ci * @ei: QMI message descriptor 76462306a36Sopenharmony_ci * @c_struct: Reference to structure to decode into 76562306a36Sopenharmony_ci * 76662306a36Sopenharmony_ci * Return: The number of bytes of decoded information on success, negative 76762306a36Sopenharmony_ci * errno on error. 76862306a36Sopenharmony_ci */ 76962306a36Sopenharmony_ciint qmi_decode_message(const void *buf, size_t len, 77062306a36Sopenharmony_ci const struct qmi_elem_info *ei, void *c_struct) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci if (!ei) 77362306a36Sopenharmony_ci return -EINVAL; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci if (!c_struct || !buf || !len) 77662306a36Sopenharmony_ci return -EINVAL; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci return qmi_decode(ei, c_struct, buf + sizeof(struct qmi_header), 77962306a36Sopenharmony_ci len - sizeof(struct qmi_header), 1); 78062306a36Sopenharmony_ci} 78162306a36Sopenharmony_ciEXPORT_SYMBOL(qmi_decode_message); 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ci/* Common header in all QMI responses */ 78462306a36Sopenharmony_ciconst struct qmi_elem_info qmi_response_type_v01_ei[] = { 78562306a36Sopenharmony_ci { 78662306a36Sopenharmony_ci .data_type = QMI_SIGNED_2_BYTE_ENUM, 78762306a36Sopenharmony_ci .elem_len = 1, 78862306a36Sopenharmony_ci .elem_size = sizeof(u16), 78962306a36Sopenharmony_ci .array_type = NO_ARRAY, 79062306a36Sopenharmony_ci .tlv_type = QMI_COMMON_TLV_TYPE, 79162306a36Sopenharmony_ci .offset = offsetof(struct qmi_response_type_v01, result), 79262306a36Sopenharmony_ci .ei_array = NULL, 79362306a36Sopenharmony_ci }, 79462306a36Sopenharmony_ci { 79562306a36Sopenharmony_ci .data_type = QMI_SIGNED_2_BYTE_ENUM, 79662306a36Sopenharmony_ci .elem_len = 1, 79762306a36Sopenharmony_ci .elem_size = sizeof(u16), 79862306a36Sopenharmony_ci .array_type = NO_ARRAY, 79962306a36Sopenharmony_ci .tlv_type = QMI_COMMON_TLV_TYPE, 80062306a36Sopenharmony_ci .offset = offsetof(struct qmi_response_type_v01, error), 80162306a36Sopenharmony_ci .ei_array = NULL, 80262306a36Sopenharmony_ci }, 80362306a36Sopenharmony_ci { 80462306a36Sopenharmony_ci .data_type = QMI_EOTI, 80562306a36Sopenharmony_ci .elem_len = 0, 80662306a36Sopenharmony_ci .elem_size = 0, 80762306a36Sopenharmony_ci .array_type = NO_ARRAY, 80862306a36Sopenharmony_ci .tlv_type = QMI_COMMON_TLV_TYPE, 80962306a36Sopenharmony_ci .offset = 0, 81062306a36Sopenharmony_ci .ei_array = NULL, 81162306a36Sopenharmony_ci }, 81262306a36Sopenharmony_ci}; 81362306a36Sopenharmony_ciEXPORT_SYMBOL(qmi_response_type_v01_ei); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ciMODULE_DESCRIPTION("QMI encoder/decoder helper"); 81662306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 817