mirror of
https://github.com/team-infusion-developers/android_kernel_samsung_msm8976.git
synced 2024-10-31 18:09:19 +00:00
4a36e44c45
-----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>
880 lines
27 KiB
C
880 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");
|