android_kernel_samsung_msm8976/lib/qmi_encdec.c
Kaushal Kumar 4a36e44c45 This is the 3.10.84 stable release
-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2
 
 iQIcBAABCAAGBQJVoAOcAAoJEDjbvchgkmk+UhcP/1EOwnsJDcZ/sZkkclNgRmrJ
 yLBCW65caLAI2E3SmIdKvHQwIx7lHzX5gmWRBrvx+fIl4KhaNKEQ0NCOf1ATaVuQ
 MkYMdkicXWpLiFNdKokezryevGS8T1RME+2QlPFv3++Rby1Gy90YD5tu7YlIrEn7
 sPRJQHEPCzVAQ7Lqhd66yHICM6/QvdefXj4pjh7vV8IMb2YwnY4vqYt7RxnJCUfP
 tqljxrT274kzpA2awzALNh+o3B3/Y4W9ROmlDWviw3JBc9gEqFXYwbDf8KDwA5c0
 sp9GPGed/dV5DFuqRcAHksJenFnE3E4gZjo/R5hluHQU27peBuRfXev2hZyBfZqG
 796eUOky8fb0OiyxHfT2vhfGeD7CHI/asvIAORjDBVUqzJy9nkkby3XJ0U4tW+pz
 VkcilD2oHw1uRIFH3JoBWTJ9W6CYSNFG1qxw+brgfKT5otJG/dBiI8kBABx+aTq7
 V+A2cvf11oVwDEb93dnVypMGsfCywqzJUwEIRli9fTFjK7Fg9CBSGX38nwVGUaRv
 M2/NeloTyWqUQE41Nd11gCu+hKQRtUU77nxpZcSeKn1XsbpO9/7dHTwcELRuKnTD
 9XDksqPznXmC9KXGj7XMcRkLyWyB//JHjay0FCS6b4S6v7R5nrEIRjcpdB+H1WLd
 zMOXRH4ZlcOAS/Yt2QMd
 =8AB3
 -----END PGP SIGNATURE-----

Merge upstream tag 'v3.10.84' into LA.BR.1.3.3

This merge brings us up-to-date as of upstream tag v3.10.84

* tag 'v3.10.84' (317 commits):
  Linux 3.10.84
  fs: Fix S_NOSEC handling
  KVM: x86: make vapics_in_nmi_mode atomic
  MIPS: Fix KVM guest fixmap address
  x86/PCI: Use host bridge _CRS info on Foxconn K8M890-8237A
  powerpc/perf: Fix book3s kernel to userspace backtraces
  arm: KVM: force execution of HCPTR access on VM exit
  Revert "crypto: talitos - convert to use be16_add_cpu()"
  crypto: talitos - avoid memleak in talitos_alg_alloc()
  sctp: Fix race between OOTB responce and route removal
  packet: avoid out of bounds read in round robin fanout
  packet: read num_members once in packet_rcv_fanout()
  bridge: fix br_stp_set_bridge_priority race conditions
  bridge: fix multicast router rlist endless loop
  sparc: Use GFP_ATOMIC in ldc_alloc_exp_dring() as it can be called in softirq context
  Linux 3.10.83
  bus: mvebu: pass the coherency availability information at init time
  KVM: nSVM: Check for NRIPS support before updating control field
  ARM: clk-imx6q: refine sata's parent
  d_walk() might skip too much
  ipv6: update ip6_rt_last_gc every time GC is run
  ipv6: prevent fib6_run_gc() contention
  xfrm: Increase the garbage collector threshold
  Btrfs: make xattr replace operations atomic
  x86/microcode/intel: Guard against stack overflow in the loader
  fs: take i_mutex during prepare_binprm for set[ug]id executables
  hpsa: add missing pci_set_master in kdump path
  hpsa: refine the pci enable/disable handling
  sb_edac: Fix erroneous bytes->gigabytes conversion
  ACPICA: Utilities: Cleanup to remove useless ACPI_PRINTF/FORMAT_xxx helpers.
  ACPICA: Utilities: Cleanup to convert physical address printing formats.
  __ptrace_may_access() should not deny sub-threads
  include/linux/sched.h: don't use task->pid/tgid in same_thread_group/has_group_leader_pid
  netfilter: Zero the tuple in nfnl_cthelper_parse_tuple()
  netfilter: nfnetlink_cthelper: Remove 'const' and '&' to avoid warnings
  config: Enable NEED_DMA_MAP_STATE by default when SWIOTLB is selected
  get rid of s_files and files_lock
  fput: turn "list_head delayed_fput_list" into llist_head
  Linux 3.10.82
  lpfc: Add iotag memory barrier
  pipe: iovec: Fix memory corruption when retrying atomic copy as non-atomic
  drm/mgag200: Reject non-character-cell-aligned mode widths
  tracing: Have filter check for balanced ops
  crypto: caam - fix RNG buffer cache alignment
  Linux 3.10.81
  btrfs: cleanup orphans while looking up default subvolume
  btrfs: incorrect handling for fiemap_fill_next_extent return
  cfg80211: wext: clear sinfo struct before calling driver
  mm/memory_hotplug.c: set zone->wait_table to null after freeing it
  drm/i915: Fix DDC probe for passive adapters
  pata_octeon_cf: fix broken build
  ozwpan: unchecked signed subtraction leads to DoS
  ozwpan: divide-by-zero leading to panic
  ozwpan: Use proper check to prevent heap overflow
  MIPS: Fix enabling of DEBUG_STACKOVERFLOW
  ring-buffer-benchmark: Fix the wrong sched_priority of producer
  USB: serial: ftdi_sio: Add support for a Motion Tracker Development Board
  USB: cp210x: add ID for HubZ dual ZigBee and Z-Wave dongle
  block: fix ext_dev_lock lockdep report
  Input: elantech - fix detection of touchpads where the revision matches a known rate
  ALSA: usb-audio: add MAYA44 USB+ mixer control names
  ALSA: usb-audio: Add mic volume fix quirk for Logitech Quickcam Fusion
  ALSA: hda/realtek - Add a fixup for another Acer Aspire 9420
  iio: adis16400: Compute the scan mask from channel indices
  iio: adis16400: Use != channel indices for the two voltage channels
  iio: adis16400: Report pressure channel scale
  xen: netback: read hotplug script once at start of day.
  udp: fix behavior of wrong checksums
  net_sched: invoke ->attach() after setting dev->qdisc
  unix/caif: sk_socket can disappear when state is unlocked
  net: dp83640: fix broken calibration routine.
  bridge: fix parsing of MLDv2 reports
  ipv4: Avoid crashing in ip_error
  net: phy: Allow EEE for all RGMII variants
  Linux 3.10.80
  fs/binfmt_elf.c:load_elf_binary(): return -EINVAL on zero-length mappings
  vfs: read file_handle only once in handle_to_path
  ACPI / init: Fix the ordering of acpi_reserve_resources()
  Input: elantech - fix semi-mt protocol for v3 HW
  rtlwifi: rtl8192cu: Fix kernel deadlock
  md/raid5: don't record new size if resize_stripes fails.
  svcrpc: fix potential GSSX_ACCEPT_SEC_CONTEXT decoding failures
  ARM: fix missing syscall trace exit
  ARM: dts: imx27: only map 4 Kbyte for fec registers
  crypto: s390/ghash - Fix incorrect ghash icv buffer handling.
  rt2x00: add new rt2800usb device DWA 130
  libata: Ignore spurious PHY event on LPM policy change
  libata: Add helper to determine when PHY events should be ignored
  ext4: check for zero length extent explicitly
  ext4: convert write_begin methods to stable_page_writes semantics
  mmc: atmel-mci: fix bad variable type for clkdiv
  powerpc: Align TOC to 256 bytes
  usb: gadget: configfs: Fix interfaces array NULL-termination
  usb-storage: Add NO_WP_DETECT quirk for Lacie 059f:0651 devices
  USB: cp210x: add ID for KCF Technologies PRN device
  USB: pl2303: Remove support for Samsung I330
  USB: visor: Match I330 phone more precisely
  xhci: gracefully handle xhci_irq dead device
  xhci: Solve full event ring by increasing TRBS_PER_SEGMENT to 256
  xhci: fix isoc endpoint dequeue from advancing too far on transaction error
  target/pscsi: Don't leak scsi_host if hba is VIRTUAL_HOST
  ASoC: wm8994: correct BCLK DIV 348 to 384
  ASoC: wm8960: fix "RINPUT3" audio route error
  ASoC: mc13783: Fix wrong mask value used in mc13xxx_reg_rmw() calls
  ALSA: hda - Add headphone quirk for Lifebook E752
  ALSA: hda - Add Conexant codecs CX20721, CX20722, CX20723 and CX20724
  d_walk() might skip too much
  lib: Fix strnlen_user() to not touch memory after specified maximum
  hwmon: (ntc_thermistor) Ensure iio channel is of type IIO_VOLTAGE
  libceph: request a new osdmap if lingering request maps to no osd
  lguest: fix out-by-one error in address checking.
  fs, omfs: add NULL terminator in the end up the token list
  KVM: MMU: fix CR4.SMEP=1, CR0.WP=0 with shadow pages
  net: socket: Fix the wrong returns for recvmsg and sendmsg
  kernel: use the gnu89 standard explicitly
  staging, rtl8192e, LLVMLinux: Remove unused inline prototype
  staging: rtl8712, rtl8712: avoid lots of build warnings
  staging, rtl8192e, LLVMLinux: Change extern inline to static inline
  drm/i915: Fix declaration of intel_gmbus_{is_forced_bit/is_port_falid}
  staging: wlags49_h2: fix extern inline functions
  Linux 3.10.79
  ACPICA: Utilities: Cleanup to enforce ACPI_PHYSADDR_TO_PTR()/ACPI_PTR_TO_PHYSADDR().
  ACPICA: Tables: Change acpi_find_root_pointer() to use acpi_physical_address.
  revert "softirq: Add support for triggering softirq work on softirqs"
  sound/oss: fix deadlock in sequencer_ioctl(SNDCTL_SEQ_OUTOFBAND)
  mmc: card: Don't access RPMB partitions for normal read/write
  pinctrl: Don't just pretend to protect pinctrl_maps, do it for real
  drm/i915: Add missing MacBook Pro models with dual channel LVDS
  ARM: mvebu: armada-xp-openblocks-ax3-4: Disable internal RTC
  ARM: dts: imx23-olinuxino: Fix dr_mode of usb0
  ARM: dts: imx28: Fix AUART4 TX-DMA interrupt name
  ARM: dts: imx25: Add #pwm-cells to pwm4
  gpio: sysfs: fix memory leaks and device hotplug
  gpio: unregister gpiochip device before removing it
  xen/console: Update console event channel on resume
  mm/memory-failure: call shake_page() when error hits thp tail page
  nilfs2: fix sanity check of btree level in nilfs_btree_root_broken()
  ocfs2: dlm: fix race between purge and get lock resource
  Linux 3.10.78
  ARC: signal handling robustify
  UBI: fix soft lockup in ubi_check_volume()
  Drivers: hv: vmbus: Don't wait after requesting offers
  ARM: dts: dove: Fix uart[23] reg property
  staging: panel: fix lcd type
  usb: gadget: printer: enqueue printer's response for setup request
  usb: host: oxu210hp: use new USB_RESUME_TIMEOUT
  3w-sas: fix command completion race
  3w-9xxx: fix command completion race
  3w-xxxx: fix command completion race
  ext4: fix data corruption caused by unwritten and delayed extents
  rbd: end I/O the entire obj_request on error
  serial: of-serial: Remove device_type = "serial" registration
  ALSA: hda - Fix mute-LED fixed mode
  ALSA: emu10k1: Emu10k2 32 bit DMA mode
  ALSA: emu10k1: Fix card shortname string buffer overflow
  ALSA: emux: Fix mutex deadlock in OSS emulation
  ALSA: emux: Fix mutex deadlock at unloading
  ipv4: Missing sk_nulls_node_init() in ping_unhash().
  Linux 3.10.77
  s390: Fix build error
  nosave: consolidate __nosave_{begin,end} in <asm/sections.h>
  memstick: mspro_block: add missing curly braces
  C6x: time: Ensure consistency in __init
  wl18xx: show rx_frames_per_rates as an array as it really is
  lib: memzero_explicit: use barrier instead of OPTIMIZER_HIDE_VAR
  e1000: add dummy allocator to fix race condition between mtu change and netpoll
  ksoftirqd: Enable IRQs and call cond_resched() before poking RCU
  RCU pathwalk breakage when running into a symlink overmounting something
  drm/i915: cope with large i2c transfers
  drm/radeon: fix doublescan modes (v2)
  i2c: core: Export bus recovery functions
  IB/mlx4: Fix WQE LSO segment calculation
  IB/core: don't disallow registering region starting at 0x0
  IB/core: disallow registering 0-sized memory region
  stk1160: Make sure current buffer is released
  mvsas: fix panic on expander attached SATA devices
  Drivers: hv: vmbus: Fix a bug in the error path in vmbus_open()
  xtensa: provide __NR_sync_file_range2 instead of __NR_sync_file_range
  xtensa: xtfpga: fix hardware lockup caused by LCD driver
  ACPICA: Utilities: split IO address types from data type models.
  drivers: parport: Kconfig: exclude arm64 for PARPORT_PC
  scsi: storvsc: Fix a bug in copy_from_bounce_buffer()
  UBI: fix check for "too many bytes"
  UBI: initialize LEB number variable
  UBI: fix out of bounds write
  UBI: account for bitflips in both the VID header and data
  tools/power turbostat: Use $(CURDIR) instead of $(PWD) and add support for O= option in Makefile
  powerpc/perf: Cap 64bit userspace backtraces to PERF_MAX_STACK_DEPTH
  ext4: make fsync to sync parent dir in no-journal for real this time
  arm64: kernel: compiling issue, need delete read_current_timer()
  video: vgacon: Don't build on arm64
  console: Disable VGA text console support on cris
  drivers: parport: Kconfig: exclude h8300 for PARPORT_PC
  parport: disable PC-style parallel port support on cris
  rtlwifi: rtl8192cu: Add new device ID
  rtlwifi: rtl8192cu: Add new USB ID
  ptrace: fix race between ptrace_resume() and wait_task_stopped()
  fs/binfmt_elf.c: fix bug in loading of PIE binaries
  Input: elantech - fix absolute mode setting on some ASUS laptops
  ALSA: emu10k1: don't deadlock in proc-functions
  usb: core: hub: use new USB_RESUME_TIMEOUT
  usb: host: sl811: use new USB_RESUME_TIMEOUT
  usb: host: xhci: use new USB_RESUME_TIMEOUT
  usb: host: isp116x: use new USB_RESUME_TIMEOUT
  usb: host: r8a66597: use new USB_RESUME_TIMEOUT
  usb: define a generic USB_RESUME_TIMEOUT macro
  usb: phy: Find the right match in devm_usb_phy_match
  ARM: S3C64XX: Use fixed IRQ bases to avoid conflicts on Cragganmore
  ARM: 8320/1: fix integer overflow in ELF_ET_DYN_BASE
  power_supply: lp8788-charger: Fix leaked power supply on probe fail
  ring-buffer: Replace this_cpu_*() with __this_cpu_*()
  spi: spidev: fix possible arithmetic overflow for multi-transfer message
  cdc-wdm: fix endianness bug in debug statements
  MIPS: Hibernate: flush TLB entries earlier
  KVM: use slowpath for cross page cached accesses
  s390/hibernate: fix save and restore of kernel text section
  KVM: s390: Zero out current VMDB of STSI before including level3 data.
  usb: gadget: composite: enable BESL support
  Btrfs: fix inode eviction infinite loop after cloning into it
  Btrfs: fix log tree corruption when fs mounted with -o discard
  tcp: avoid looping in tcp_send_fin()
  tcp: fix possible deadlock in tcp_send_fin()
  ip_forward: Drop frames with attached skb->sk
  Linux 3.10.76
  dcache: Fix locking bugs in backported "deal with deadlock in d_walk()"
  arc: mm: Fix build failure
  sb_edac: avoid INTERNAL ERROR message in EDAC with unspecified channel
  x86: mm: move mmap_sem unlock from mm_fault_error() to caller
  vm: make stack guard page errors return VM_FAULT_SIGSEGV rather than SIGBUS
  vm: add VM_FAULT_SIGSEGV handling support
  deal with deadlock in d_walk()
  move d_rcu from overlapping d_child to overlapping d_alias
  kconfig: Fix warning "‘jump’ may be used uninitialized"
  KVM: x86: SYSENTER emulation is broken
  netfilter: conntrack: disable generic tracking for known protocols
  Bluetooth: Ignore isochronous endpoints for Intel USB bootloader
  Bluetooth: Add support for Intel bootloader devices
  Bluetooth: btusb: Add IMC Networks (Broadcom based)
  Bluetooth: Add firmware update for Atheros 0cf3:311f
  Bluetooth: Enable Atheros 0cf3:311e for firmware upload
  mm: Fix NULL pointer dereference in madvise(MADV_WILLNEED) support
  splice: Apply generic position and size checks to each write
  jfs: fix readdir regression
  serial: 8250_dw: Fix deadlock in LCR workaround
  benet: Call dev_kfree_skby_any instead of kfree_skb.
  ixgb: Call dev_kfree_skby_any instead of dev_kfree_skb.
  tg3: Call dev_kfree_skby_any instead of dev_kfree_skb.
  bnx2: Call dev_kfree_skby_any instead of dev_kfree_skb.
  r8169: Call dev_kfree_skby_any instead of dev_kfree_skb.
  8139too: Call dev_kfree_skby_any instead of dev_kfree_skb.
  8139cp: Call dev_kfree_skby_any instead of kfree_skb.
  tcp: tcp_make_synack() should clear skb->tstamp
  tcp: fix FRTO undo on cumulative ACK of SACKed range
  ipv6: Don't reduce hop limit for an interface
  tcp: prevent fetching dst twice in early demux code
  remove extra definitions of U32_MAX
  conditionally define U32_MAX
  Linux 3.10.75
  pagemap: do not leak physical addresses to non-privileged userspace
  console: Fix console name size mismatch
  IB/mlx4: Saturate RoCE port PMA counters in case of overflow
  kernel.h: define u8, s8, u32, etc. limits
  net: llc: use correct size for sysctl timeout entries
  net: rds: use correct size for max unacked packets and bytes
  ipc: fix compat msgrcv with negative msgtyp
  core, nfqueue, openvswitch: fix compilation warning
  media: s5p-mfc: fix mmap support for 64bit arch
  iscsi target: fix oops when adding reject pdu
  ocfs2: _really_ sync the right range
  be2iscsi: Fix kernel panic when device initialization fails
  cifs: fix use-after-free bug in find_writable_file
  usb: xhci: apply XHCI_AVOID_BEI quirk to all Intel xHCI controllers
  cpuidle: ACPI: do not overwrite name and description of C0
  dmaengine: omap-dma: Fix memory leak when terminating running transfer
  iio: imu: Use iio_trigger_get for indio_dev->trig assignment
  iio: inv_mpu6050: Clear timestamps fifo while resetting hardware fifo
  Defer processing of REQ_PREEMPT requests for blocked devices
  USB: ftdi_sio: Use jtag quirk for SNAP Connect E10
  USB: ftdi_sio: Added custom PID for Synapse Wireless product
  radeon: Do not directly dereference pointers to BIOS area.
  writeback: fix possible underflow in write bandwidth calculation
  writeback: add missing INITIAL_JIFFIES init in global_update_bandwidth()
  mm/memory hotplug: postpone the reset of obsolete pgdat
  nbd: fix possible memory leak
  iwlwifi: dvm: run INIT firmware again upon .start()
  IB/uverbs: Prevent integer overflow in ib_umem_get address arithmetic
  IB/core: Avoid leakage from kernel to user space
  tcp: Fix crash in TCP Fast Open
  selinux: fix sel_write_enforce broken return value
  ALSA: hda - Fix headphone pin config for Lifebook T731
  ALSA: usb - Creative USB X-Fi Pro SB1095 volume knob support
  ALSA: hda - Add one more node in the EAPD supporting candidate list
  Linux 3.10.74
  net: ethernet: pcnet32: Setup the SRAM and NOUFLO on Am79C97{3, 5}
  powerpc/mpc85xx: Add ranges to etsec2 nodes
  hfsplus: fix B-tree corruption after insertion at position 0
  dm: hold suspend_lock while suspending device during device deletion
  vt6655: RFbSetPower fix missing rate RATE_12M
  perf: Fix irq_work 'tail' recursion
  Revert "iwlwifi: mvm: fix failure path when power_update fails in add_interface"
  mac80211: drop unencrypted frames in mesh fwding
  mac80211: disable u-APSD queues by default
  nl80211: ignore HT/VHT capabilities without QoS/WMM
  tcm_qla2xxx: Fix incorrect use of __transport_register_session
  tcm_fc: missing curly braces in ft_invl_hw_context()
  ASoC: wm8955: Fix wrong value references for boolean kctl
  ASoC: adav80x: Fix wrong value references for boolean kctl
  ASoC: ak4641: Fix wrong value references for boolean kctl
  ASoC: wm8904: Fix wrong value references for boolean kctl
  ASoC: wm8903: Fix wrong value references for boolean kctl
  ASoC: wm2000: Fix wrong value references for boolean kctl
  ASoC: wm8731: Fix wrong value references for boolean kctl
  ASoC: tas5086: Fix wrong value references for boolean kctl
  ASoC: wm8960: Fix wrong value references for boolean kctl
  ASoC: cs4271: Fix wrong value references for boolean kctl
  ASoC: sgtl5000: remove useless register write clearing CHRGPUMP_POWERUP

Change-Id: Ib7976ee2c7224e39074157e28db4158db40b00db
Signed-off-by: Kaushal Kumar <kaushalk@codeaurora.org>
2015-09-30 13:25:40 +05:30

881 lines
27 KiB
C

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