blob: 3f618cb3f02ba0382cb472c5e6b0600ed95fc2f8 [file] [log] [blame]
Karthikeyan Ramasubramanian31e31d72013-01-03 15:59:54 -07001/* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
Karthikeyan Ramasubramaniand9522582012-07-18 19:25:37 -06002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 */
13
14#include <linux/slab.h>
15#include <linux/uaccess.h>
16#include <linux/module.h>
17#include <linux/kernel.h>
18#include <linux/errno.h>
19#include <linux/io.h>
20#include <linux/string.h>
21#include <linux/qmi_encdec.h>
22
23#include "qmi_encdec_priv.h"
24
25#define TLV_LEN_SIZE sizeof(uint16_t)
26#define TLV_TYPE_SIZE sizeof(uint8_t)
27
Karthikeyan Ramasubramaniandc4d5632012-11-07 19:42:25 -070028#ifdef CONFIG_QMI_ENCDEC_DEBUG
29
30#define qmi_encdec_dump(prefix_str, buf, buf_len) do { \
31 const u8 *ptr = buf; \
32 int i, linelen, remaining = buf_len; \
33 int rowsize = 16, groupsize = 1; \
34 unsigned char linebuf[256]; \
35 for (i = 0; i < buf_len; i += rowsize) { \
36 linelen = min(remaining, rowsize); \
37 remaining -= linelen; \
38 hex_dump_to_buffer(ptr + i, linelen, rowsize, groupsize, \
39 linebuf, sizeof(linebuf), false); \
40 pr_debug("%s: %s\n", prefix_str, linebuf); \
41 } \
42} while (0)
43
44#define QMI_ENCODE_LOG_MSG(buf, buf_len) do { \
45 qmi_encdec_dump("QMI_ENCODE_MSG", buf, buf_len); \
46} while (0)
47
48#define QMI_DECODE_LOG_MSG(buf, buf_len) do { \
49 qmi_encdec_dump("QMI_DECODE_MSG", buf, buf_len); \
50} while (0)
51
52#define QMI_ENCODE_LOG_ELEM(level, elem_len, elem_size, buf) do { \
53 pr_debug("QMI_ENCODE_ELEM lvl: %d, len: %d, size: %d\n", \
54 level, elem_len, elem_size); \
55 qmi_encdec_dump("QMI_ENCODE_ELEM", buf, (elem_len * elem_size)); \
56} while (0)
57
58#define QMI_DECODE_LOG_ELEM(level, elem_len, elem_size, buf) do { \
59 pr_debug("QMI_DECODE_ELEM lvl: %d, len: %d, size: %d\n", \
60 level, elem_len, elem_size); \
61 qmi_encdec_dump("QMI_DECODE_ELEM", buf, (elem_len * elem_size)); \
62} while (0)
63
64#define QMI_ENCODE_LOG_TLV(tlv_type, tlv_len) do { \
65 pr_debug("QMI_ENCODE_TLV type: %d, len: %d\n", tlv_type, tlv_len); \
66} while (0)
67
68#define QMI_DECODE_LOG_TLV(tlv_type, tlv_len) do { \
69 pr_debug("QMI_DECODE_TLV type: %d, len: %d\n", tlv_type, tlv_len); \
70} while (0)
71
72#else
73
74#define QMI_ENCODE_LOG_MSG(buf, buf_len) { }
75#define QMI_DECODE_LOG_MSG(buf, buf_len) { }
76#define QMI_ENCODE_LOG_ELEM(level, elem_len, elem_size, buf) { }
77#define QMI_DECODE_LOG_ELEM(level, elem_len, elem_size, buf) { }
78#define QMI_ENCODE_LOG_TLV(tlv_type, tlv_len) { }
79#define QMI_DECODE_LOG_TLV(tlv_type, tlv_len) { }
80
81#endif
82
Karthikeyan Ramasubramaniand9522582012-07-18 19:25:37 -060083static int _qmi_kernel_encode(struct elem_info *ei_array,
84 void *out_buf, void *in_c_struct,
Karthikeyan Ramasubramanian31e31d72013-01-03 15:59:54 -070085 uint32_t out_buf_len, int enc_level);
Karthikeyan Ramasubramaniand9522582012-07-18 19:25:37 -060086
87static int _qmi_kernel_decode(struct elem_info *ei_array,
88 void *out_c_struct,
89 void *in_buf, uint32_t in_buf_len,
90 int dec_level);
91
92/**
Karthikeyan Ramasubramanian0714f1b2013-01-08 17:21:53 -070093 * qmi_calc_max_msg_len() - Calculate the maximum length of a QMI message
94 * @ei_array: Struct info array describing the structure.
95 * @level: Level to identify the depth of the nested structures.
96 *
97 * @return: expected maximum length of the QMI message or 0 on failure.
98 */
99static int qmi_calc_max_msg_len(struct elem_info *ei_array,
100 int level)
101{
102 int max_msg_len = 0;
103 struct elem_info *temp_ei;
104
105 if (!ei_array)
106 return max_msg_len;
107
108 for (temp_ei = ei_array; temp_ei->data_type != QMI_EOTI; temp_ei++) {
109 /* Flag to identify the optional element is not encoded */
110 if (temp_ei->data_type == QMI_OPT_FLAG)
111 continue;
112
113 if (temp_ei->data_type == QMI_DATA_LEN) {
114 max_msg_len += (temp_ei->elem_size == sizeof(uint8_t) ?
115 sizeof(uint8_t) : sizeof(uint16_t));
116 continue;
117 } else if (temp_ei->data_type == QMI_STRUCT) {
118 max_msg_len += qmi_calc_max_msg_len(temp_ei->ei_array,
119 (level + 1));
120 } else {
121 max_msg_len += (temp_ei->elem_len * temp_ei->elem_size);
122 }
123
124 /*
125 * Type & Length info. not prepended for elements in the
126 * nested structure.
127 */
128 if (level == 1)
129 max_msg_len += (TLV_TYPE_SIZE + TLV_LEN_SIZE);
130 }
131 return max_msg_len;
132}
133
134/**
135 * qmi_verify_max_msg_len() - Verify the maximum length of a QMI message
136 * @desc: Pointer to structure descriptor.
137 *
138 * @return: true if the maximum message length embedded in structure
139 * descriptor matches the calculated value, else false.
140 */
141bool qmi_verify_max_msg_len(struct msg_desc *desc)
142{
143 int calc_max_msg_len;
144
145 if (!desc)
146 return false;
147
148 calc_max_msg_len = qmi_calc_max_msg_len(desc->ei_array, 1);
149 if (calc_max_msg_len != desc->max_msg_len) {
150 pr_err("%s: Calc. len %d != Passed len %d\n",
151 __func__, calc_max_msg_len, desc->max_msg_len);
152 return false;
153 }
154 return true;
155}
156
157/**
Karthikeyan Ramasubramaniand9522582012-07-18 19:25:37 -0600158 * qmi_kernel_encode() - Encode to QMI message wire format
159 * @desc: Pointer to structure descriptor.
160 * @out_buf: Buffer to hold the encoded QMI message.
161 * @out_buf_len: Length of the out buffer.
162 * @in_c_struct: C Structure to be encoded.
163 *
164 * @return: size of encoded message on success, < 0 for error.
165 */
166int qmi_kernel_encode(struct msg_desc *desc,
167 void *out_buf, uint32_t out_buf_len,
168 void *in_c_struct)
169{
170 int enc_level = 1;
Karthikeyan Ramasubramanian0714f1b2013-01-08 17:21:53 -0700171 int ret, calc_max_msg_len;
Karthikeyan Ramasubramaniand9522582012-07-18 19:25:37 -0600172
173 if (!desc || !desc->ei_array)
174 return -EINVAL;
175
176 if (!out_buf || !in_c_struct)
177 return -EINVAL;
178
179 if (desc->max_msg_len < out_buf_len)
180 return -ETOOSMALL;
181
Karthikeyan Ramasubramanian0714f1b2013-01-08 17:21:53 -0700182 ret = _qmi_kernel_encode(desc->ei_array, out_buf,
183 in_c_struct, out_buf_len, enc_level);
184 if (ret == -ETOOSMALL) {
185 calc_max_msg_len = qmi_calc_max_msg_len(desc->ei_array, 1);
186 pr_err("%s: Calc. len %d != Out buf len %d\n",
187 __func__, calc_max_msg_len, out_buf_len);
188 }
189 return ret;
Karthikeyan Ramasubramaniand9522582012-07-18 19:25:37 -0600190}
191EXPORT_SYMBOL(qmi_kernel_encode);
192
193/**
194 * qmi_encode_basic_elem() - Encodes elements of basic/primary data type
195 * @buf_dst: Buffer to store the encoded information.
196 * @buf_src: Buffer containing the elements to be encoded.
197 * @elem_len: Number of elements, in the buf_src, to be encoded.
198 * @elem_size: Size of a single instance of the element to be encoded.
199 *
200 * @return: number of bytes of encoded information.
201 *
202 * This function encodes the "elem_len" number of data elements, each of
203 * size "elem_size" bytes from the source buffer "buf_src" and stores the
204 * encoded information in the destination buffer "buf_dst". The elements are
205 * of primary data type which include uint8_t - uint64_t or similar. This
206 * function returns the number of bytes of encoded information.
207 */
208static int qmi_encode_basic_elem(void *buf_dst, void *buf_src,
209 uint32_t elem_len, uint32_t elem_size)
210{
211 uint32_t i, rc = 0;
212
213 for (i = 0; i < elem_len; i++) {
214 QMI_ENCDEC_ENCODE_N_BYTES(buf_dst, buf_src, elem_size);
215 rc += elem_size;
216 }
217
218 return rc;
219}
220
221/**
222 * qmi_encode_struct_elem() - Encodes elements of struct data type
223 * @ei_array: Struct info array descibing the struct element.
224 * @buf_dst: Buffer to store the encoded information.
225 * @buf_src: Buffer containing the elements to be encoded.
226 * @elem_len: Number of elements, in the buf_src, to be encoded.
Karthikeyan Ramasubramanian31e31d72013-01-03 15:59:54 -0700227 * @out_buf_len: Available space in the encode buffer.
Karthikeyan Ramasubramaniand9522582012-07-18 19:25:37 -0600228 * @enc_level: Depth of the nested structure from the main structure.
229 *
230 * @return: Mumber of bytes of encoded information, on success.
231 * < 0 on error.
232 *
233 * This function encodes the "elem_len" number of struct elements, each of
234 * size "ei_array->elem_size" bytes from the source buffer "buf_src" and
235 * stores the encoded information in the destination buffer "buf_dst". The
236 * elements are of struct data type which includes any C structure. This
237 * function returns the number of bytes of encoded information.
238 */
239static int qmi_encode_struct_elem(struct elem_info *ei_array,
240 void *buf_dst, void *buf_src,
Karthikeyan Ramasubramanian31e31d72013-01-03 15:59:54 -0700241 uint32_t elem_len, uint32_t out_buf_len,
242 int enc_level)
Karthikeyan Ramasubramaniand9522582012-07-18 19:25:37 -0600243{
244 int i, rc, encoded_bytes = 0;
245 struct elem_info *temp_ei = ei_array;
246
247 for (i = 0; i < elem_len; i++) {
Karthikeyan Ramasubramanian31e31d72013-01-03 15:59:54 -0700248 rc = _qmi_kernel_encode(temp_ei->ei_array, buf_dst, buf_src,
249 (out_buf_len - encoded_bytes),
250 enc_level);
Karthikeyan Ramasubramaniand9522582012-07-18 19:25:37 -0600251 if (rc < 0) {
252 pr_err("%s: STRUCT Encode failure\n", __func__);
253 return rc;
254 }
255 buf_dst = buf_dst + rc;
256 buf_src = buf_src + temp_ei->elem_size;
257 encoded_bytes += rc;
258 }
259
260 return encoded_bytes;
261}
262
263/**
264 * skip_to_next_elem() - Skip to next element in the structure to be encoded
265 * @ei_array: Struct info describing the element to be skipped.
266 *
267 * @return: Struct info of the next element that can be encoded.
268 *
269 * This function is used while encoding optional elements. If the flag
270 * corresponding to an optional element is not set, then encoding the
271 * optional element can be skipped. This function can be used to perform
272 * that operation.
273 */
274static struct elem_info *skip_to_next_elem(struct elem_info *ei_array)
275{
276 struct elem_info *temp_ei = ei_array;
277 uint8_t tlv_type;
278
279 do {
280 tlv_type = temp_ei->tlv_type;
281 temp_ei = temp_ei + 1;
282 } while (tlv_type == temp_ei->tlv_type);
283
284 return temp_ei;
285}
286
287/**
288 * _qmi_kernel_encode() - Core Encode Function
289 * @ei_array: Struct info array describing the structure to be encoded.
290 * @out_buf: Buffer to hold the encoded QMI message.
291 * @in_c_struct: Pointer to the C structure to be encoded.
Karthikeyan Ramasubramanian31e31d72013-01-03 15:59:54 -0700292 * @out_buf_len: Available space in the encode buffer.
Karthikeyan Ramasubramaniand9522582012-07-18 19:25:37 -0600293 * @enc_level: Encode level to indicate the depth of the nested structure,
294 * within the main structure, being encoded.
295 *
296 * @return: Number of bytes of encoded information, on success.
297 * < 0 on error.
298 */
299static int _qmi_kernel_encode(struct elem_info *ei_array,
300 void *out_buf, void *in_c_struct,
Karthikeyan Ramasubramanian31e31d72013-01-03 15:59:54 -0700301 uint32_t out_buf_len, int enc_level)
Karthikeyan Ramasubramaniand9522582012-07-18 19:25:37 -0600302{
303 struct elem_info *temp_ei = ei_array;
304 uint8_t opt_flag_value = 0;
305 uint32_t data_len_value = 0, data_len_sz;
306 uint8_t *buf_dst = (uint8_t *)out_buf;
307 uint8_t *tlv_pointer;
308 uint32_t tlv_len;
309 uint8_t tlv_type;
310 uint32_t encoded_bytes = 0;
311 void *buf_src;
312 int encode_tlv = 0;
313 int rc;
314
315 tlv_pointer = buf_dst;
316 tlv_len = 0;
317 buf_dst = buf_dst + (TLV_LEN_SIZE + TLV_TYPE_SIZE);
318
319 while (temp_ei->data_type != QMI_EOTI) {
320 buf_src = in_c_struct + temp_ei->offset;
321 tlv_type = temp_ei->tlv_type;
322
323 if (temp_ei->is_array == NO_ARRAY) {
324 data_len_value = 1;
325 } else if (temp_ei->is_array == STATIC_ARRAY) {
326 data_len_value = temp_ei->elem_len;
327 } else if (data_len_value <= 0 ||
328 temp_ei->elem_len < data_len_value) {
329 pr_err("%s: Invalid data length\n", __func__);
330 return -EINVAL;
331 }
332
333 switch (temp_ei->data_type) {
334 case QMI_OPT_FLAG:
335 rc = qmi_encode_basic_elem(&opt_flag_value, buf_src,
336 1, sizeof(uint8_t));
337 if (opt_flag_value)
338 temp_ei = temp_ei + 1;
339 else
340 temp_ei = skip_to_next_elem(temp_ei);
341 break;
342
343 case QMI_DATA_LEN:
344 memcpy(&data_len_value, buf_src, temp_ei->elem_size);
345 data_len_sz = temp_ei->elem_size == sizeof(uint8_t) ?
346 sizeof(uint8_t) : sizeof(uint16_t);
Karthikeyan Ramasubramanian31e31d72013-01-03 15:59:54 -0700347 /* Check to avoid out of range buffer access */
348 if ((data_len_sz + encoded_bytes + TLV_LEN_SIZE +
349 TLV_TYPE_SIZE) > out_buf_len) {
350 pr_err("%s: Too Small Buffer @DATA_LEN\n",
351 __func__);
352 return -ETOOSMALL;
353 }
Karthikeyan Ramasubramaniand9522582012-07-18 19:25:37 -0600354 rc = qmi_encode_basic_elem(buf_dst, &data_len_value,
355 1, data_len_sz);
356 if (data_len_value) {
357 UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst,
358 encoded_bytes, tlv_len, encode_tlv, rc);
359 encode_tlv = 0;
360 } else {
361 temp_ei = skip_to_next_elem(temp_ei);
362 }
363 break;
364
365 case QMI_UNSIGNED_1_BYTE:
366 case QMI_UNSIGNED_2_BYTE:
367 case QMI_UNSIGNED_4_BYTE:
368 case QMI_UNSIGNED_8_BYTE:
369 case QMI_SIGNED_2_BYTE_ENUM:
370 case QMI_SIGNED_4_BYTE_ENUM:
Karthikeyan Ramasubramanian31e31d72013-01-03 15:59:54 -0700371 /* Check to avoid out of range buffer access */
372 if (((data_len_value * temp_ei->elem_size) +
373 encoded_bytes + TLV_LEN_SIZE + TLV_TYPE_SIZE) >
374 out_buf_len) {
375 pr_err("%s: Too Small Buffer @data_type:%d\n",
376 __func__, temp_ei->data_type);
377 return -ETOOSMALL;
378 }
Karthikeyan Ramasubramaniand9522582012-07-18 19:25:37 -0600379 rc = qmi_encode_basic_elem(buf_dst, buf_src,
380 data_len_value, temp_ei->elem_size);
Karthikeyan Ramasubramaniandc4d5632012-11-07 19:42:25 -0700381 QMI_ENCODE_LOG_ELEM(enc_level, data_len_value,
382 temp_ei->elem_size, buf_src);
Karthikeyan Ramasubramaniand9522582012-07-18 19:25:37 -0600383 UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst,
384 encoded_bytes, tlv_len, encode_tlv, rc);
385 break;
386
387 case QMI_STRUCT:
388 rc = qmi_encode_struct_elem(temp_ei, buf_dst, buf_src,
Karthikeyan Ramasubramanian31e31d72013-01-03 15:59:54 -0700389 data_len_value, (out_buf_len - encoded_bytes),
390 (enc_level + 1));
Karthikeyan Ramasubramaniand9522582012-07-18 19:25:37 -0600391 if (rc < 0)
392 return rc;
393 UPDATE_ENCODE_VARIABLES(temp_ei, buf_dst,
394 encoded_bytes, tlv_len, encode_tlv, rc);
395 break;
396
397 default:
398 pr_err("%s: Unrecognized data type\n", __func__);
399 return -EINVAL;
400
401 }
402
403 if (encode_tlv && enc_level == 1) {
404 QMI_ENCDEC_ENCODE_TLV(tlv_type, tlv_len, tlv_pointer);
Karthikeyan Ramasubramaniandc4d5632012-11-07 19:42:25 -0700405 QMI_ENCODE_LOG_TLV(tlv_type, tlv_len);
Karthikeyan Ramasubramaniand9522582012-07-18 19:25:37 -0600406 encoded_bytes += (TLV_TYPE_SIZE + TLV_LEN_SIZE);
407 tlv_pointer = buf_dst;
408 tlv_len = 0;
409 buf_dst = buf_dst + TLV_LEN_SIZE + TLV_TYPE_SIZE;
410 encode_tlv = 0;
411 }
412 }
Karthikeyan Ramasubramaniandc4d5632012-11-07 19:42:25 -0700413 QMI_ENCODE_LOG_MSG(out_buf, encoded_bytes);
Karthikeyan Ramasubramaniand9522582012-07-18 19:25:37 -0600414 return encoded_bytes;
415}
416
417/**
418 * qmi_kernel_decode() - Decode to C Structure format
419 * @desc: Pointer to structure descriptor.
420 * @out_c_struct: Buffer to hold the decoded C structure.
421 * @in_buf: Buffer containg the QMI message to be decoded.
422 * @in_buf_len: Length of the incoming QMI message.
423 *
424 * @return: 0 on success, < 0 on error.
425 */
426int qmi_kernel_decode(struct msg_desc *desc, void *out_c_struct,
427 void *in_buf, uint32_t in_buf_len)
428{
429 int dec_level = 1;
430 int rc = 0;
431
432 if (!desc || !desc->ei_array)
433 return -EINVAL;
434
435 if (!out_c_struct || !in_buf || !in_buf_len)
436 return -EINVAL;
437
438 if (desc->max_msg_len < in_buf_len)
439 return -EINVAL;
440
441 rc = _qmi_kernel_decode(desc->ei_array, out_c_struct,
442 in_buf, in_buf_len, dec_level);
443 if (rc < 0)
444 return rc;
445 else
446 return 0;
447}
448EXPORT_SYMBOL(qmi_kernel_decode);
449
450/**
451 * qmi_decode_basic_elem() - Decodes elements of basic/primary data type
452 * @buf_dst: Buffer to store the decoded element.
453 * @buf_src: Buffer containing the elements in QMI wire format.
454 * @elem_len: Number of elements to be decoded.
455 * @elem_size: Size of a single instance of the element to be decoded.
456 *
457 * @return: Total size of the decoded data elements, in bytes.
458 *
459 * This function decodes the "elem_len" number of elements in QMI wire format,
460 * each of size "elem_size" bytes from the source buffer "buf_src" and stores
461 * the decoded elements in the destination buffer "buf_dst". The elements are
462 * of primary data type which include uint8_t - uint64_t or similar. This
463 * function returns the number of bytes of decoded information.
464 */
465static int qmi_decode_basic_elem(void *buf_dst, void *buf_src,
466 uint32_t elem_len, uint32_t elem_size)
467{
468 uint32_t i, rc = 0;
469
470 for (i = 0; i < elem_len; i++) {
471 QMI_ENCDEC_DECODE_N_BYTES(buf_dst, buf_src, elem_size);
472 rc += elem_size;
473 }
474
475 return rc;
476}
477
478/**
479 * qmi_decode_struct_elem() - Decodes elements of struct data type
480 * @ei_array: Struct info array descibing the struct element.
481 * @buf_dst: Buffer to store the decoded element.
482 * @buf_src: Buffer containing the elements in QMI wire format.
483 * @elem_len: Number of elements to be decoded.
484 * @tlv_len: Total size of the encoded inforation corresponding to
485 * this struct element.
486 * @dec_level: Depth of the nested structure from the main structure.
487 *
488 * @return: Total size of the decoded data elements, on success.
489 * < 0 on error.
490 *
491 * This function decodes the "elem_len" number of elements in QMI wire format,
492 * each of size "(tlv_len/elem_len)" bytes from the source buffer "buf_src"
493 * and stores the decoded elements in the destination buffer "buf_dst". The
494 * elements are of struct data type which includes any C structure. This
495 * function returns the number of bytes of decoded information.
496 */
497static int qmi_decode_struct_elem(struct elem_info *ei_array, void *buf_dst,
498 void *buf_src, uint32_t elem_len,
499 uint32_t tlv_len, int dec_level)
500{
501 int i, rc, decoded_bytes = 0;
502 struct elem_info *temp_ei = ei_array;
503
504 for (i = 0; i < elem_len; i++) {
505 rc = _qmi_kernel_decode(temp_ei->ei_array, buf_dst, buf_src,
506 (tlv_len/elem_len), dec_level);
507 if (rc < 0)
508 return rc;
509 if (rc != (tlv_len/elem_len)) {
510 pr_err("%s: Fault in decoding\n", __func__);
511 return -EFAULT;
512 }
513 buf_src = buf_src + rc;
514 buf_dst = buf_dst + temp_ei->elem_size;
515 decoded_bytes += rc;
516 }
517
518 return decoded_bytes;
519}
520
521/**
522 * find_ei() - Find element info corresponding to TLV Type
523 * @ei_array: Struct info array of the message being decoded.
524 * @type: TLV Type of the element being searched.
525 *
526 * @return: Pointer to struct info, if found
527 *
528 * Every element that got encoded in the QMI message will have a type
529 * information associated with it. While decoding the QMI message,
530 * this function is used to find the struct info regarding the element
531 * that corresponds to the type being decoded.
532 */
533static struct elem_info *find_ei(struct elem_info *ei_array,
534 uint32_t type)
535{
536 struct elem_info *temp_ei = ei_array;
537 while (temp_ei->data_type != QMI_EOTI) {
538 if (temp_ei->tlv_type == (uint8_t)type)
539 return temp_ei;
540 temp_ei = temp_ei + 1;
541 }
542 return NULL;
543}
544
545/**
546 * _qmi_kernel_decode() - Core Decode Function
547 * @ei_array: Struct info array describing the structure to be decoded.
548 * @out_c_struct: Buffer to hold the decoded C struct
549 * @in_buf: Buffer containing the QMI message to be decoded
550 * @in_buf_len: Length of the QMI message to be decoded
551 * @dec_level: Decode level to indicate the depth of the nested structure,
552 * within the main structure, being decoded
553 *
554 * @return: Number of bytes of decoded information, on success
555 * < 0 on error.
556 */
557static int _qmi_kernel_decode(struct elem_info *ei_array,
558 void *out_c_struct,
559 void *in_buf, uint32_t in_buf_len,
560 int dec_level)
561{
562 struct elem_info *temp_ei = ei_array;
563 uint8_t opt_flag_value = 1;
564 uint32_t data_len_value = 0, data_len_sz = 0;
565 uint8_t *buf_dst = out_c_struct;
566 uint8_t *tlv_pointer;
567 uint32_t tlv_len = 0;
568 uint32_t tlv_type;
569 uint32_t decoded_bytes = 0;
570 void *buf_src = in_buf;
571 int rc;
572
Karthikeyan Ramasubramaniandc4d5632012-11-07 19:42:25 -0700573 QMI_DECODE_LOG_MSG(in_buf, in_buf_len);
Karthikeyan Ramasubramaniand9522582012-07-18 19:25:37 -0600574 while (decoded_bytes < in_buf_len) {
575 if (dec_level == 1) {
576 tlv_pointer = buf_src;
577 QMI_ENCDEC_DECODE_TLV(&tlv_type,
578 &tlv_len, tlv_pointer);
Karthikeyan Ramasubramaniandc4d5632012-11-07 19:42:25 -0700579 QMI_DECODE_LOG_TLV(tlv_type, tlv_len);
Karthikeyan Ramasubramaniand9522582012-07-18 19:25:37 -0600580 buf_src += (TLV_TYPE_SIZE + TLV_LEN_SIZE);
581 decoded_bytes += (TLV_TYPE_SIZE + TLV_LEN_SIZE);
582 temp_ei = find_ei(ei_array, tlv_type);
583 if (!temp_ei) {
584 pr_err("%s: Inval element info\n", __func__);
585 return -EINVAL;
586 }
587 }
588
589 buf_dst = out_c_struct + temp_ei->offset;
590 if (temp_ei->data_type == QMI_OPT_FLAG) {
591 memcpy(buf_dst, &opt_flag_value, sizeof(uint8_t));
592 temp_ei = temp_ei + 1;
593 buf_dst = out_c_struct + temp_ei->offset;
594 }
595
596 if (temp_ei->data_type == QMI_DATA_LEN) {
597 data_len_sz = temp_ei->elem_size == sizeof(uint8_t) ?
598 sizeof(uint8_t) : sizeof(uint16_t);
599 rc = qmi_decode_basic_elem(&data_len_value, buf_src,
600 1, data_len_sz);
601 memcpy(buf_dst, &data_len_value, sizeof(uint32_t));
602 temp_ei = temp_ei + 1;
603 buf_dst = out_c_struct + temp_ei->offset;
604 UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc);
605 }
606
607 if (temp_ei->is_array == NO_ARRAY) {
608 data_len_value = 1;
609 } else if (temp_ei->is_array == STATIC_ARRAY) {
610 data_len_value = temp_ei->elem_len;
611 } else if (data_len_value > temp_ei->elem_len) {
612 pr_err("%s: Data len %d > max spec %d\n",
613 __func__, data_len_value, temp_ei->elem_len);
614 return -ETOOSMALL;
615 }
616
617 switch (temp_ei->data_type) {
618 case QMI_UNSIGNED_1_BYTE:
619 case QMI_UNSIGNED_2_BYTE:
620 case QMI_UNSIGNED_4_BYTE:
621 case QMI_UNSIGNED_8_BYTE:
622 case QMI_SIGNED_2_BYTE_ENUM:
623 case QMI_SIGNED_4_BYTE_ENUM:
624 rc = qmi_decode_basic_elem(buf_dst, buf_src,
625 data_len_value, temp_ei->elem_size);
Karthikeyan Ramasubramaniandc4d5632012-11-07 19:42:25 -0700626 QMI_DECODE_LOG_ELEM(dec_level, data_len_value,
627 temp_ei->elem_size, buf_dst);
Karthikeyan Ramasubramaniand9522582012-07-18 19:25:37 -0600628 UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc);
629 break;
630
631 case QMI_STRUCT:
632 rc = qmi_decode_struct_elem(temp_ei, buf_dst, buf_src,
633 data_len_value, tlv_len, (dec_level + 1));
634 if (rc < 0)
635 return rc;
636 UPDATE_DECODE_VARIABLES(buf_src, decoded_bytes, rc);
637 break;
638 default:
639 pr_err("%s: Unrecognized data type\n", __func__);
640 return -EINVAL;
641 }
642 temp_ei = temp_ei + 1;
643 }
644 return decoded_bytes;
645}
646MODULE_DESCRIPTION("QMI kernel enc/dec");
647MODULE_LICENSE("GPL v2");