summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjorn Andersson <bjorn.andersson@linaro.org>2016-05-10 22:22:50 -0700
committerBjorn Andersson <bjorn.andersson@linaro.org>2016-05-10 22:22:50 -0700
commit8dbff020d2881985c94f8e1de268bc0668f109b9 (patch)
tree4607bb6507d7fd0cfa9939a31f6a3dff18b3def7
parentb374fe4aabf77a7f2f59e1db5a08f21afb656c74 (diff)
qmi_loc2: Initial import from Codeaurora release
Direct import of: https://us.codeaurora.org/patches/quic/imm/PATCH_1580713_410c_GPS_userspace_proxy.tar.gz Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
-rw-r--r--Makefile20
-rw-r--r--gps_proxy.c441
-rw-r--r--gps_proxy.h54
-rw-r--r--qmi_gps.c464
-rw-r--r--qmi_gps.h181
-rw-r--r--qmi_gps.qmi69
-rw-r--r--qmi_tlv.c262
-rw-r--r--util.c77
-rw-r--r--util.h38
9 files changed, 1606 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..2b611a1
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,20 @@
+OUT := gps_proxy
+
+CFLAGS := -Wall -g -I../qrtr/lib
+LDFLAGS := -L../qrtr -lqrtr
+
+SRCS := qmi_gps.c qmi_tlv.c gps_proxy.c util.c
+OBJS := $(SRCS:.c=.o)
+
+$(OUT): $(OBJS)
+ $(CC) -o $@ $^ $(LDFLAGS)
+
+#%.c: %.qmi
+# qmic < $<
+
+test: $(OUT)
+ ./$(OUT)
+
+clean:
+ rm -f $(OUT) $(OBJS)
+
diff --git a/gps_proxy.c b/gps_proxy.c
new file mode 100644
index 0000000..821742b
--- /dev/null
+++ b/gps_proxy.c
@@ -0,0 +1,441 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libqrtr.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "gps_proxy.h"
+#include "qmi_gps.h"
+#include "util.h"
+
+#define QMI_LOC_SERVICE 0x10
+#define QMI_LOC_VERSION 0x02
+#define QMI_LOC_INSTANCE 0x00
+
+#define QMI_LOC_EVENT_MASK_NMEA_V02 ((uint64_t)0x00000004ull)
+#define MAX_BUF_SZ 512
+
+typedef enum {
+ NEED_SEND = 1,
+ NEED_RECV
+} op_mask_t;
+
+struct qmi_packet {
+ uint8_t flags;
+ uint16_t txn_id;
+ uint16_t msg_id;
+ uint16_t msg_len;
+ uint8_t data[];
+} __attribute__((__packed__));
+
+/* TODO: include from kernel once it lands */
+struct sockaddr_qrtr {
+ unsigned short sq_family;
+ uint32_t sq_node;
+ uint32_t sq_port;
+};
+
+static unsigned g_txn = 1;
+static uint8_t g_session_id = 0;
+static uint32_t g_loc_node = -1;
+static uint32_t g_loc_port = -1;
+static bool dbgprintf_enabled;
+
+static void dbgprintf(const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!dbgprintf_enabled)
+ return;
+
+ va_start(ap, fmt);
+ vprintf(fmt, ap);
+ va_end(ap);
+}
+
+int qrtr_send_recv(int sock, unsigned node, unsigned port, void *ptr, size_t len, char *buf,
+ size_t buf_len, unsigned *in_node, unsigned *in_port,
+ struct qmi_packet **qmi, int op_mask)
+{
+ int ret = 0;
+ if (NEED_SEND & op_mask) {
+ ret = qrtr_sendto(sock, node, port, ptr, len);
+ if (ret < 0) {
+ fprintf(stderr, "[GPS] failed to send request: %s\n", strerror(-ret));
+ ret = -EINVAL;
+ }
+ }
+
+ if (NEED_RECV & op_mask) {
+ /* wait for response */
+ ret = qrtr_recvfrom(sock, buf, buf_len, in_node, in_port);
+ if (ret < 0) {
+ fprintf(stderr, "[GPS] failed to receive response: %s\n", strerror(-ret));
+ ret = -EINVAL;
+ }
+
+ *qmi = (struct qmi_packet*)buf;
+ if ((*qmi)->msg_len != ret - sizeof(struct qmi_packet)) {
+ fprintf(stderr, "[GPS] Invalid length of incoming qmi request\n");
+ ret = -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int check_result(struct gps_qmi_result *result)
+{
+ if (!result) {
+ fprintf(stderr, "[GPS] Can't get response result from message\n");
+ return -EINVAL;
+ }
+ if (result->result != QMI_GPS_RESULT_SUCCESS) {
+ fprintf(stderr, "[GPS] Stop Response error: %d, %d\n",
+ (int)result->result,(int)result->error);
+ return -EINVAL;
+ }
+ ++g_txn;
+
+ return 0;
+}
+
+int gps_send_start(int sock, unsigned node, unsigned port)
+{
+ int ret = 0;
+ struct gps_qmi_result *result;
+ struct gps_loc_start_req *req;
+ struct gps_loc_start_resp *resp;
+ size_t len;
+ void *ptr;
+ unsigned in_node,in_port;
+ char buf[MAX_BUF_SZ];
+ struct qmi_packet *qmi;
+ unsigned txn;
+
+
+ dbgprintf("[GPS] send LOC_START to (%d:%d)\n", node, port);
+
+ req = gps_loc_start_req_alloc(g_txn);
+ gps_loc_start_req_set_session_id(req,g_session_id);
+ ptr = gps_loc_start_req_encode(req, &len);
+ if (!ptr)
+ goto free_resp;
+
+ ret = qrtr_send_recv(sock, node, port, ptr, len, buf, sizeof(buf),
+ &in_node, &in_port, &qmi, NEED_SEND | NEED_RECV);
+ if (!ret)
+ goto free_resp;
+
+ /* check response status */
+ switch (qmi->msg_id) {
+ case QMI_GPS_LOC_START:
+ resp = gps_loc_start_resp_parse(qmi, qmi->msg_len, &txn);
+ if ((!resp) || (txn != g_txn)) {
+ fprintf(stderr, "[GPS] Invalid response message\n");
+ ret = -EINVAL;
+ goto free_resp;
+ }
+ result = gps_loc_start_resp_get_result(resp);
+ ret = check_result(result);
+ if (ret)
+ goto free_resp;
+
+ break;
+
+ default:
+ fprintf(stderr, "[GPS] Unknown message: id=%d size=%d\n", qmi->msg_id, qmi->msg_len);
+ ret = -EINVAL;
+ break;
+ }
+
+
+free_resp:
+ gps_loc_start_req_free(req);
+
+ return ret;
+}
+
+int gps_send_stop(int sock, unsigned node, unsigned port)
+{
+ int ret = 0;
+ struct gps_qmi_result *result;
+ struct gps_loc_stop_req *req;
+ struct gps_loc_stop_resp *resp;
+ void *ptr;
+ unsigned in_node,in_port;
+ char buf[MAX_BUF_SZ];
+ struct qmi_packet *qmi;
+ unsigned txn;
+ int stop = 0;
+ size_t len;
+
+ dbgprintf("[GPS] send LOC_STOP to (%d:%d)\n", node, port);
+
+ req = gps_loc_stop_req_alloc(g_txn);
+ gps_loc_stop_req_set_session_id(req,g_session_id);
+ ptr = gps_loc_stop_req_encode(req, &len);
+ if (!ptr)
+ goto free_resp;
+
+ ret = qrtr_send_recv(sock, node, port, ptr, len, buf, sizeof(buf),
+ &in_node, &in_port, &qmi, NEED_SEND);
+ if (!ret)
+ goto free_resp;
+
+ while (!stop) {
+ ret = qrtr_send_recv(sock, node, port, ptr, len, buf, sizeof(buf),
+ &in_node, &in_port, &qmi, NEED_RECV);
+ if (!ret)
+ goto free_resp;
+
+ /* check response status */
+ switch (qmi->msg_id) {
+ case QMI_GPS_LOC_STOP:
+ resp = gps_loc_stop_resp_parse(qmi, qmi->msg_len, &txn);
+ if ((!resp) || (txn != g_txn)) {
+ fprintf(stderr, "[GPS] Invalid response message\n");
+ ret = -EINVAL;
+ goto free_resp;
+ }
+ result = gps_loc_stop_resp_get_result(resp);
+ ret = check_result(result);
+ if (ret)
+ goto free_resp;
+ stop = 1;
+
+ break;
+ case QMI_GPS_LOC_NMEA_IND:
+ break;
+
+ default:
+ fprintf(stderr, "[GPS] Unknown message: id=%d size=%d\n", qmi->msg_id, qmi->msg_len);
+ ret = -EINVAL;
+ stop = 1;
+ break;
+ }
+ }
+
+free_resp:
+ gps_loc_stop_req_free(req);
+
+ return ret;
+}
+
+int gps_send_register_nmea_event(int sock, unsigned node, unsigned port)
+{
+ int ret = 0;
+ struct gps_qmi_result *result;
+ struct gps_loc_reg_events_req *req;
+ struct gps_loc_reg_events_resp *resp;
+ size_t len;
+ void *ptr;
+ unsigned in_node,in_port;
+ char buf[MAX_BUF_SZ];
+ struct qmi_packet *qmi;
+ unsigned txn;
+
+
+ dbgprintf("[GPS] send LOC_REG_EVENT to (%d:%d)\n", node, port);
+
+ req = gps_loc_reg_events_req_alloc(g_txn);
+ gps_loc_reg_events_req_set_event_reg_mask(req,QMI_LOC_EVENT_MASK_NMEA_V02);
+ ptr = gps_loc_reg_events_req_encode(req, &len);
+ if (!ptr)
+ goto free_resp;
+
+ ret = qrtr_send_recv(sock, node, port, ptr, len, buf, sizeof(buf),
+ &in_node, &in_port, &qmi, NEED_SEND | NEED_RECV);
+ if (!ret)
+ goto free_resp;
+
+ /* check response status */
+ switch (qmi->msg_id) {
+ case QMI_GPS_LOC_REG_EVENTS:
+ resp = gps_loc_reg_events_resp_parse(qmi, qmi->msg_len, &txn);
+ if ((!resp) || (txn != g_txn)) {
+ fprintf(stderr, "[GPS] Invalid response message\n");
+ ret = -EINVAL;
+ goto free_resp;
+ }
+ result = gps_loc_reg_events_resp_get_result(resp);
+ ret = check_result(result);
+ if (ret)
+ goto free_resp;
+
+ break;
+
+ default:
+ fprintf(stderr, "[GPS] Unknown message: id=%d size=%d\n", qmi->msg_id, qmi->msg_len);
+ ret = -EINVAL;
+ break;
+ }
+
+
+free_resp:
+ gps_loc_reg_events_req_free(req);
+
+ return ret;
+}
+
+void loc_service_cb(void *udata,uint32_t service,uint32_t instance,uint32_t node,uint32_t port)
+{
+ dbgprintf("[GPS] New LOC service found at (%u:%u)\n", node, port);
+ g_loc_node = node;
+ g_loc_port = port;
+}
+
+int handle_gps_ind(int sock,int dev)
+{
+ struct sockaddr_qrtr sq;
+ socklen_t sl;
+ char buf[MAX_BUF_SZ];
+ int ret,len;
+ unsigned txn;
+ struct gps_loc_event_nmea_ind *req;
+ struct gps_proxy_data nmea_data;
+
+ sl = sizeof(sq);
+ len = ret = recvfrom(sock, buf, sizeof(buf), 0, (void *)&sq, &sl);
+ if (ret < 0) {
+ fprintf(stderr, "[GPS] recvfrom failed: %d\n", ret);
+ return ret;
+ }
+ req = gps_loc_event_nmea_ind_parse(buf,len,&txn);
+ if (req) {
+ if (0 < gps_loc_event_nmea_ind_get_nmea(req,nmea_data.nmea_string,sizeof(nmea_data.nmea_string))) {
+ nmea_data.nmea_length = strnlen(nmea_data.nmea_string, sizeof(nmea_data.nmea_string)-1) +1;
+ ret = ioctl(dev, QGPS_SEND_NMEA, &nmea_data);
+ if ( ret < 0)
+ {
+ fprintf(stderr, "[GPS] can't post nmea string: %d\n", ret);
+ }
+
+ }
+ }
+
+ dbgprintf("[GPS] packet; from: %d:%d\n", sq.sq_node, sq.sq_port);
+ if (dbgprintf_enabled)
+ print_hex_dump("[GPS <-]", buf, ret);
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int gps_fd;
+ fd_set rfds;
+ int nfds;
+ int ret;
+ int ch_fd = 0;
+
+ if (argc == 2 && strcmp(argv[1], "-v") == 0)
+ dbgprintf_enabled = true;
+
+ ch_fd = open("/dev/gps_proxy_ch", O_RDONLY);
+ if (ch_fd < 0){
+ fprintf(stdout,"OPEN: %s\n", strerror(errno));
+ return -1;
+ }
+
+ gps_fd = qrtr_open(0);
+ if (gps_fd < 0) {
+ fprintf(stderr, "failed to create qrtr socket");
+ goto clean_file;
+ }
+
+ ret = qrtr_lookup(gps_fd, QMI_LOC_SERVICE, QMI_LOC_VERSION,
+ QMI_LOC_INSTANCE,0xFFFFFFFF,loc_service_cb,NULL);
+ if (ret < 0 || g_loc_node == -1 || g_loc_port == -1) {
+ fprintf(stderr, "failed to lookup LOC service");
+ goto clean;
+ }
+
+ for (;;) {
+ ret = ioctl(ch_fd, QGPS_REGISTER_HANDLE, 0);
+
+ ret = gps_send_register_nmea_event(gps_fd,g_loc_node,g_loc_port);
+ if (ret < 0) {
+ fprintf(stderr, "failed to send LOC_REG_EVENTS");
+ goto clean;
+ }
+
+ ret = gps_send_start(gps_fd,g_loc_node,g_loc_port);
+ if (ret < 0) {
+ fprintf(stderr, "failed to send LOC_START");
+ goto clean;
+ }
+
+ for (;;) {
+ FD_ZERO(&rfds);
+ FD_SET(gps_fd, &rfds);
+
+ nfds = gps_fd + 1;
+ ret = select(nfds, &rfds, NULL, NULL, NULL);
+ if (ret < 0) {
+ fprintf(stderr, "select failed: %d\n", ret);
+ break;
+ } else if (ret == 0) {
+ continue;
+ }
+
+ if (FD_ISSET(gps_fd, &rfds))
+ handle_gps_ind(gps_fd,ch_fd);
+
+ ret = ioctl(ch_fd, QGPS_IS_ACTIVE, 0);
+ if (0!=ret) {
+ dbgprintf("All clients have disconnected\n");
+ break;
+ }
+ }
+
+ ret = gps_send_stop(gps_fd,g_loc_node,g_loc_port);
+ if (ret < 0) {
+ fprintf(stderr, "failed to send LOC_STOP");
+ goto clean;
+ }
+ }
+clean:
+ qrtr_close(gps_fd);
+clean_file:
+ close(ch_fd);
+ return 0;
+}
diff --git a/gps_proxy.h b/gps_proxy.h
new file mode 100644
index 0000000..0a03aab
--- /dev/null
+++ b/gps_proxy.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __GPS_PROXY_H__
+#define __GPS_PROXY_H__
+
+#define QMI_LOC_NMEA_STRING_MAX_LENGTH_V02 201
+
+enum QGPS_IOC_TTY_IOCTL_CMDS {
+ QGPS_REGISTER_HANDLE_IOC = 0,
+ QGPS_SEND_NMEA_IOC,
+ QGPS_IS_ACTIVE_IOC,
+};
+/*#define QGPS_REGISTER_HANDLE 0
+#define QGPS_SEND_NMEA 1
+#define QGPS_IS_ACTIVE 2*/
+
+#define QGPS_IOC_MAGIC 'q'
+#define QGPS_REGISTER_HANDLE _IO(QGPS_IOC_MAGIC, QGPS_REGISTER_HANDLE_IOC)
+#define QGPS_SEND_NMEA _IO(QGPS_IOC_MAGIC, QGPS_SEND_NMEA_IOC)
+#define QGPS_IS_ACTIVE _IO(QGPS_IOC_MAGIC, QGPS_IS_ACTIVE_IOC)
+
+struct gps_proxy_data {
+ size_t nmea_length;
+ char nmea_string[QMI_LOC_NMEA_STRING_MAX_LENGTH_V02];
+};
+
+#endif /* __GPS_PROXY_H__ */
diff --git a/qmi_gps.c b/qmi_gps.c
new file mode 100644
index 0000000..7a7444e
--- /dev/null
+++ b/qmi_gps.c
@@ -0,0 +1,464 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include "qmi_gps.h"
+
+struct gps_loc_start_req *gps_loc_start_req_alloc(unsigned txn)
+{
+ return (struct gps_loc_start_req*)qmi_tlv_init(txn, 34, 0);
+}
+
+struct gps_loc_start_req *gps_loc_start_req_parse(void *buf, size_t len, unsigned *txn)
+{
+ return (struct gps_loc_start_req*)qmi_tlv_decode(buf, len, txn, 0);
+}
+
+void *gps_loc_start_req_encode(struct gps_loc_start_req *loc_start_req, size_t *len)
+{
+ return qmi_tlv_encode((struct qmi_tlv*)loc_start_req, len);
+}
+
+void gps_loc_start_req_free(struct gps_loc_start_req *loc_start_req)
+{
+ qmi_tlv_free((struct qmi_tlv*)loc_start_req);
+}
+
+int gps_loc_start_req_set_session_id(struct gps_loc_start_req *loc_start_req, uint8_t val)
+{
+ return qmi_tlv_set((struct qmi_tlv*)loc_start_req, 1, &val, sizeof(uint8_t));
+}
+
+int gps_loc_start_req_get_session_id(struct gps_loc_start_req *loc_start_req, uint8_t *val)
+{
+ uint8_t *ptr;
+ size_t len;
+
+ ptr = qmi_tlv_get((struct qmi_tlv*)loc_start_req, 1, &len);
+ if (!ptr)
+ return -ENOENT;
+
+ if (len != sizeof(uint8_t))
+ return -EINVAL;
+
+ *val = *(uint8_t*)ptr;
+ return 0;
+}
+
+int gps_loc_start_req_set_fix_recurrence(struct gps_loc_start_req *loc_start_req, uint32_t val)
+{
+ return qmi_tlv_set((struct qmi_tlv*)loc_start_req, 16, &val, sizeof(uint32_t));
+}
+
+int gps_loc_start_req_get_fix_recurrence(struct gps_loc_start_req *loc_start_req, uint32_t *val)
+{
+ uint32_t *ptr;
+ size_t len;
+
+ ptr = qmi_tlv_get((struct qmi_tlv*)loc_start_req, 16, &len);
+ if (!ptr)
+ return -ENOENT;
+
+ if (len != sizeof(uint32_t))
+ return -EINVAL;
+
+ *val = *(uint32_t*)ptr;
+ return 0;
+}
+
+int gps_loc_start_req_set_horizontal_accuracy_level(struct gps_loc_start_req *loc_start_req, uint32_t val)
+{
+ return qmi_tlv_set((struct qmi_tlv*)loc_start_req, 17, &val, sizeof(uint32_t));
+}
+
+int gps_loc_start_req_get_horizontal_accuracy_level(struct gps_loc_start_req *loc_start_req, uint32_t *val)
+{
+ uint32_t *ptr;
+ size_t len;
+
+ ptr = qmi_tlv_get((struct qmi_tlv*)loc_start_req, 17, &len);
+ if (!ptr)
+ return -ENOENT;
+
+ if (len != sizeof(uint32_t))
+ return -EINVAL;
+
+ *val = *(uint32_t*)ptr;
+ return 0;
+}
+
+int gps_loc_start_req_set_intermediate_report_state(struct gps_loc_start_req *loc_start_req, uint32_t val)
+{
+ return qmi_tlv_set((struct qmi_tlv*)loc_start_req, 18, &val, sizeof(uint32_t));
+}
+
+int gps_loc_start_req_get_intermediate_report_state(struct gps_loc_start_req *loc_start_req, uint32_t *val)
+{
+ uint32_t *ptr;
+ size_t len;
+
+ ptr = qmi_tlv_get((struct qmi_tlv*)loc_start_req, 18, &len);
+ if (!ptr)
+ return -ENOENT;
+
+ if (len != sizeof(uint32_t))
+ return -EINVAL;
+
+ *val = *(uint32_t*)ptr;
+ return 0;
+}
+
+int gps_loc_start_req_set_min_interval(struct gps_loc_start_req *loc_start_req, uint32_t val)
+{
+ return qmi_tlv_set((struct qmi_tlv*)loc_start_req, 19, &val, sizeof(uint32_t));
+}
+
+int gps_loc_start_req_get_min_interval(struct gps_loc_start_req *loc_start_req, uint32_t *val)
+{
+ uint32_t *ptr;
+ size_t len;
+
+ ptr = qmi_tlv_get((struct qmi_tlv*)loc_start_req, 19, &len);
+ if (!ptr)
+ return -ENOENT;
+
+ if (len != sizeof(uint32_t))
+ return -EINVAL;
+
+ *val = *(uint32_t*)ptr;
+ return 0;
+}
+
+int gps_loc_start_req_set_application_id(struct gps_loc_start_req *loc_start_req, struct gps_loc_application_id *val)
+{
+ return qmi_tlv_set((struct qmi_tlv*)loc_start_req, 20, val, sizeof(struct gps_loc_application_id));
+}
+
+struct gps_loc_application_id *gps_loc_start_req_get_application_id(struct gps_loc_start_req *loc_start_req)
+{
+ size_t len;
+ void *ptr;
+
+ ptr = qmi_tlv_get((struct qmi_tlv*)loc_start_req, 20, &len);
+ if (!ptr)
+ return NULL;
+
+ if (len != sizeof(struct gps_loc_application_id))
+ return NULL;
+
+ return ptr;
+}
+
+int gps_loc_start_req_set_config_altitude_assumed(struct gps_loc_start_req *loc_start_req, uint32_t val)
+{
+ return qmi_tlv_set((struct qmi_tlv*)loc_start_req, 21, &val, sizeof(uint32_t));
+}
+
+int gps_loc_start_req_get_config_altitude_assumed(struct gps_loc_start_req *loc_start_req, uint32_t *val)
+{
+ uint32_t *ptr;
+ size_t len;
+
+ ptr = qmi_tlv_get((struct qmi_tlv*)loc_start_req, 21, &len);
+ if (!ptr)
+ return -ENOENT;
+
+ if (len != sizeof(uint32_t))
+ return -EINVAL;
+
+ *val = *(uint32_t*)ptr;
+ return 0;
+}
+
+int gps_loc_start_req_set_min_intermediate_position_report_interval(struct gps_loc_start_req *loc_start_req, uint32_t val)
+{
+ return qmi_tlv_set((struct qmi_tlv*)loc_start_req, 22, &val, sizeof(uint32_t));
+}
+
+int gps_loc_start_req_get_min_intermediate_position_report_interval(struct gps_loc_start_req *loc_start_req, uint32_t *val)
+{
+ uint32_t *ptr;
+ size_t len;
+
+ ptr = qmi_tlv_get((struct qmi_tlv*)loc_start_req, 22, &len);
+ if (!ptr)
+ return -ENOENT;
+
+ if (len != sizeof(uint32_t))
+ return -EINVAL;
+
+ *val = *(uint32_t*)ptr;
+ return 0;
+}
+
+struct gps_loc_start_resp *gps_loc_start_resp_alloc(unsigned txn)
+{
+ return (struct gps_loc_start_resp*)qmi_tlv_init(txn, 34, 2);
+}
+
+struct gps_loc_start_resp *gps_loc_start_resp_parse(void *buf, size_t len, unsigned *txn)
+{
+ return (struct gps_loc_start_resp*)qmi_tlv_decode(buf, len, txn, 2);
+}
+
+void *gps_loc_start_resp_encode(struct gps_loc_start_resp *loc_start_resp, size_t *len)
+{
+ return qmi_tlv_encode((struct qmi_tlv*)loc_start_resp, len);
+}
+
+void gps_loc_start_resp_free(struct gps_loc_start_resp *loc_start_resp)
+{
+ qmi_tlv_free((struct qmi_tlv*)loc_start_resp);
+}
+
+int gps_loc_start_resp_set_result(struct gps_loc_start_resp *loc_start_resp, struct gps_qmi_result *val)
+{
+ return qmi_tlv_set((struct qmi_tlv*)loc_start_resp, 2, val, sizeof(struct gps_qmi_result));
+}
+
+struct gps_qmi_result *gps_loc_start_resp_get_result(struct gps_loc_start_resp *loc_start_resp)
+{
+ size_t len;
+ void *ptr;
+
+ ptr = qmi_tlv_get((struct qmi_tlv*)loc_start_resp, 2, &len);
+ if (!ptr)
+ return NULL;
+
+ if (len != sizeof(struct gps_qmi_result))
+ return NULL;
+
+ return ptr;
+}
+
+struct gps_loc_stop_req *gps_loc_stop_req_alloc(unsigned txn)
+{
+ return (struct gps_loc_stop_req*)qmi_tlv_init(txn, 35, 0);
+}
+
+struct gps_loc_stop_req *gps_loc_stop_req_parse(void *buf, size_t len, unsigned *txn)
+{
+ return (struct gps_loc_stop_req*)qmi_tlv_decode(buf, len, txn, 0);
+}
+
+void *gps_loc_stop_req_encode(struct gps_loc_stop_req *loc_stop_req, size_t *len)
+{
+ return qmi_tlv_encode((struct qmi_tlv*)loc_stop_req, len);
+}
+
+void gps_loc_stop_req_free(struct gps_loc_stop_req *loc_stop_req)
+{
+ qmi_tlv_free((struct qmi_tlv*)loc_stop_req);
+}
+
+int gps_loc_stop_req_set_session_id(struct gps_loc_stop_req *loc_stop_req, uint8_t val)
+{
+ return qmi_tlv_set((struct qmi_tlv*)loc_stop_req, 1, &val, sizeof(uint8_t));
+}
+
+int gps_loc_stop_req_get_session_id(struct gps_loc_stop_req *loc_stop_req, uint8_t *val)
+{
+ uint8_t *ptr;
+ size_t len;
+
+ ptr = qmi_tlv_get((struct qmi_tlv*)loc_stop_req, 1, &len);
+ if (!ptr)
+ return -ENOENT;
+
+ if (len != sizeof(uint8_t))
+ return -EINVAL;
+
+ *val = *(uint8_t*)ptr;
+ return 0;
+}
+
+struct gps_loc_stop_resp *gps_loc_stop_resp_alloc(unsigned txn)
+{
+ return (struct gps_loc_stop_resp*)qmi_tlv_init(txn, 35, 2);
+}
+
+struct gps_loc_stop_resp *gps_loc_stop_resp_parse(void *buf, size_t len, unsigned *txn)
+{
+ return (struct gps_loc_stop_resp*)qmi_tlv_decode(buf, len, txn, 2);
+}
+
+void *gps_loc_stop_resp_encode(struct gps_loc_stop_resp *loc_stop_resp, size_t *len)
+{
+ return qmi_tlv_encode((struct qmi_tlv*)loc_stop_resp, len);
+}
+
+void gps_loc_stop_resp_free(struct gps_loc_stop_resp *loc_stop_resp)
+{
+ qmi_tlv_free((struct qmi_tlv*)loc_stop_resp);
+}
+
+int gps_loc_stop_resp_set_result(struct gps_loc_stop_resp *loc_stop_resp, struct gps_qmi_result *val)
+{
+ return qmi_tlv_set((struct qmi_tlv*)loc_stop_resp, 2, val, sizeof(struct gps_qmi_result));
+}
+
+struct gps_qmi_result *gps_loc_stop_resp_get_result(struct gps_loc_stop_resp *loc_stop_resp)
+{
+ size_t len;
+ void *ptr;
+
+ ptr = qmi_tlv_get((struct qmi_tlv*)loc_stop_resp, 2, &len);
+ if (!ptr)
+ return NULL;
+
+ if (len != sizeof(struct gps_qmi_result))
+ return NULL;
+
+ return ptr;
+}
+
+struct gps_loc_reg_events_req *gps_loc_reg_events_req_alloc(unsigned txn)
+{
+ return (struct gps_loc_reg_events_req*)qmi_tlv_init(txn, 33, 0);
+}
+
+struct gps_loc_reg_events_req *gps_loc_reg_events_req_parse(void *buf, size_t len, unsigned *txn)
+{
+ return (struct gps_loc_reg_events_req*)qmi_tlv_decode(buf, len, txn, 0);
+}
+
+void *gps_loc_reg_events_req_encode(struct gps_loc_reg_events_req *loc_reg_events_req, size_t *len)
+{
+ return qmi_tlv_encode((struct qmi_tlv*)loc_reg_events_req, len);
+}
+
+void gps_loc_reg_events_req_free(struct gps_loc_reg_events_req *loc_reg_events_req)
+{
+ qmi_tlv_free((struct qmi_tlv*)loc_reg_events_req);
+}
+
+int gps_loc_reg_events_req_set_event_reg_mask(struct gps_loc_reg_events_req *loc_reg_events_req, uint64_t val)
+{
+ return qmi_tlv_set((struct qmi_tlv*)loc_reg_events_req, 1, &val, sizeof(uint64_t));
+}
+
+int gps_loc_reg_events_req_get_event_reg_mask(struct gps_loc_reg_events_req *loc_reg_events_req, uint64_t *val)
+{
+ uint64_t *ptr;
+ size_t len;
+
+ ptr = qmi_tlv_get((struct qmi_tlv*)loc_reg_events_req, 1, &len);
+ if (!ptr)
+ return -ENOENT;
+
+ if (len != sizeof(uint64_t))
+ return -EINVAL;
+
+ *val = *(uint64_t*)ptr;
+ return 0;
+}
+
+struct gps_loc_reg_events_resp *gps_loc_reg_events_resp_alloc(unsigned txn)
+{
+ return (struct gps_loc_reg_events_resp*)qmi_tlv_init(txn, 33, 2);
+}
+
+struct gps_loc_reg_events_resp *gps_loc_reg_events_resp_parse(void *buf, size_t len, unsigned *txn)
+{
+ return (struct gps_loc_reg_events_resp*)qmi_tlv_decode(buf, len, txn, 2);
+}
+
+void *gps_loc_reg_events_resp_encode(struct gps_loc_reg_events_resp *loc_reg_events_resp, size_t *len)
+{
+ return qmi_tlv_encode((struct qmi_tlv*)loc_reg_events_resp, len);
+}
+
+void gps_loc_reg_events_resp_free(struct gps_loc_reg_events_resp *loc_reg_events_resp)
+{
+ qmi_tlv_free((struct qmi_tlv*)loc_reg_events_resp);
+}
+
+int gps_loc_reg_events_resp_set_result(struct gps_loc_reg_events_resp *loc_reg_events_resp, struct gps_qmi_result *val)
+{
+ return qmi_tlv_set((struct qmi_tlv*)loc_reg_events_resp, 2, val, sizeof(struct gps_qmi_result));
+}
+
+struct gps_qmi_result *gps_loc_reg_events_resp_get_result(struct gps_loc_reg_events_resp *loc_reg_events_resp)
+{
+ size_t len;
+ void *ptr;
+
+ ptr = qmi_tlv_get((struct qmi_tlv*)loc_reg_events_resp, 2, &len);
+ if (!ptr)
+ return NULL;
+
+ if (len != sizeof(struct gps_qmi_result))
+ return NULL;
+
+ return ptr;
+}
+
+struct gps_loc_event_nmea_ind *gps_loc_event_nmea_ind_alloc(unsigned txn)
+{
+ return (struct gps_loc_event_nmea_ind*)qmi_tlv_init(txn, 38, 4);
+}
+
+struct gps_loc_event_nmea_ind *gps_loc_event_nmea_ind_parse(void *buf, size_t len, unsigned *txn)
+{
+ return (struct gps_loc_event_nmea_ind*)qmi_tlv_decode(buf, len, txn, 4);
+}
+
+void *gps_loc_event_nmea_ind_encode(struct gps_loc_event_nmea_ind *loc_event_nmea_ind, size_t *len)
+{
+ return qmi_tlv_encode((struct qmi_tlv*)loc_event_nmea_ind, len);
+}
+
+void gps_loc_event_nmea_ind_free(struct gps_loc_event_nmea_ind *loc_event_nmea_ind)
+{
+ qmi_tlv_free((struct qmi_tlv*)loc_event_nmea_ind);
+}
+
+int gps_loc_event_nmea_ind_set_nmea(struct gps_loc_event_nmea_ind *loc_event_nmea_ind, char *buf, size_t len)
+{
+ return qmi_tlv_set((struct qmi_tlv*)loc_event_nmea_ind, 1, buf, len);
+}
+
+int gps_loc_event_nmea_ind_get_nmea(struct gps_loc_event_nmea_ind *loc_event_nmea_ind, char *buf, size_t buflen)
+{
+ size_t len;
+ char *ptr;
+
+ ptr = qmi_tlv_get((struct qmi_tlv*)loc_event_nmea_ind, 1, &len);
+ if (!ptr)
+ return -ENOENT;
+
+ if (len >= buflen)
+ return -ENOMEM;
+
+ memcpy(buf, ptr, len);
+ buf[len] = '\0';
+ return len;
+}
+
diff --git a/qmi_gps.h b/qmi_gps.h
new file mode 100644
index 0000000..a402614
--- /dev/null
+++ b/qmi_gps.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __QMI_GPS_H__
+#define __QMI_GPS_H__
+
+#include <stdint.h>
+#include <stdlib.h>
+
+struct qmi_tlv;
+
+struct qmi_tlv *qmi_tlv_init(unsigned txn, unsigned msg_id, unsigned type);
+struct qmi_tlv *qmi_tlv_decode(void *buf, size_t len, unsigned *txn, unsigned type);
+void *qmi_tlv_encode(struct qmi_tlv *tlv, size_t *len);
+void qmi_tlv_free(struct qmi_tlv *tlv);
+
+void *qmi_tlv_get(struct qmi_tlv *tlv, unsigned id, size_t *len);
+void *qmi_tlv_get_array(struct qmi_tlv *tlv, unsigned id, unsigned len_size, size_t *len, size_t *size);
+int qmi_tlv_set(struct qmi_tlv *tlv, unsigned id, void *buf, size_t len);
+int qmi_tlv_set_array(struct qmi_tlv *tlv, unsigned id, unsigned len_size, void *buf, size_t len, size_t size);
+
+#define QMI_GPS_RESULT_SUCCESS 0
+#define QMI_GPS_RESULT_FAILURE 1
+#define QMI_GPS_ERR_NONE 0
+#define QMI_GPS_ERR_INTERNAL 3
+#define QMI_GPS_ERR_MALFORMED_MSG 1
+#define QMI_GPS_QMI_ERR_NO_MEMORY 2
+#define QMI_GPS_QMI_ERR_INVALID_HANDLE 9
+#define QMI_GPS_LOC_START 34
+#define QMI_GPS_LOC_STOP 35
+#define QMI_GPS_LOC_REG_EVENTS 33
+#define QMI_GPS_LOC_NMEA_IND 38
+#define QMI_LOC_MAX_APP_ID_NAME_LENGTH_V02 32
+#define QMI_LOC_MAX_APP_ID_PROVIDER_LENGTH_V02 24
+#define QMI_LOC_MAX_APP_ID_VERSION_LENGTH_V02 8
+
+struct gps_qmi_result {
+ uint16_t result;
+ uint16_t error;
+};
+
+struct gps_loc_application_id {
+ char application_provider[QMI_LOC_MAX_APP_ID_PROVIDER_LENGTH_V02+1];
+ char application_name[QMI_LOC_MAX_APP_ID_NAME_LENGTH_V02];
+ uint8_t application_version_valid;
+ char application_version[QMI_LOC_MAX_APP_ID_VERSION_LENGTH_V02];
+};
+
+struct gps_loc_start_req;
+struct gps_loc_start_resp;
+struct gps_loc_stop_req;
+struct gps_loc_stop_resp;
+struct gps_loc_reg_events_req;
+struct gps_loc_reg_events_resp;
+struct gps_loc_event_nmea_ind;
+
+/*
+ * gps_loc_start_req message
+ */
+struct gps_loc_start_req *gps_loc_start_req_alloc(unsigned txn);
+struct gps_loc_start_req *gps_loc_start_req_parse(void *buf, size_t len, unsigned *txn);
+void *gps_loc_start_req_encode(struct gps_loc_start_req *loc_start_req, size_t *len);
+void gps_loc_start_req_free(struct gps_loc_start_req *loc_start_req);
+
+int gps_loc_start_req_set_session_id(struct gps_loc_start_req *loc_start_req, uint8_t val);
+int gps_loc_start_req_get_session_id(struct gps_loc_start_req *loc_start_req, uint8_t *val);
+
+int gps_loc_start_req_set_fix_recurrence(struct gps_loc_start_req *loc_start_req, uint32_t val);
+int gps_loc_start_req_get_fix_recurrence(struct gps_loc_start_req *loc_start_req, uint32_t *val);
+
+int gps_loc_start_req_set_horizontal_accuracy_level(struct gps_loc_start_req *loc_start_req, uint32_t val);
+int gps_loc_start_req_get_horizontal_accuracy_level(struct gps_loc_start_req *loc_start_req, uint32_t *val);
+
+int gps_loc_start_req_set_intermediate_report_state(struct gps_loc_start_req *loc_start_req, uint32_t val);
+int gps_loc_start_req_get_intermediate_report_state(struct gps_loc_start_req *loc_start_req, uint32_t *val);
+
+int gps_loc_start_req_set_min_interval(struct gps_loc_start_req *loc_start_req, uint32_t val);
+int gps_loc_start_req_get_min_interval(struct gps_loc_start_req *loc_start_req, uint32_t *val);
+
+int gps_loc_start_req_set_application_id(struct gps_loc_start_req *loc_start_req, struct gps_loc_application_id *val);
+struct gps_loc_application_id *gps_loc_start_req_get_application_id(struct gps_loc_start_req *loc_start_req);
+
+int gps_loc_start_req_set_config_altitude_assumed(struct gps_loc_start_req *loc_start_req, uint32_t val);
+int gps_loc_start_req_get_config_altitude_assumed(struct gps_loc_start_req *loc_start_req, uint32_t *val);
+
+int gps_loc_start_req_set_min_intermediate_position_report_interval(struct gps_loc_start_req *loc_start_req, uint32_t val);
+int gps_loc_start_req_get_min_intermediate_position_report_interval(struct gps_loc_start_req *loc_start_req, uint32_t *val);
+
+/*
+ * gps_loc_start_resp message
+ */
+struct gps_loc_start_resp *gps_loc_start_resp_alloc(unsigned txn);
+struct gps_loc_start_resp *gps_loc_start_resp_parse(void *buf, size_t len, unsigned *txn);
+void *gps_loc_start_resp_encode(struct gps_loc_start_resp *loc_start_resp, size_t *len);
+void gps_loc_start_resp_free(struct gps_loc_start_resp *loc_start_resp);
+
+int gps_loc_start_resp_set_result(struct gps_loc_start_resp *loc_start_resp, struct gps_qmi_result *val);
+struct gps_qmi_result *gps_loc_start_resp_get_result(struct gps_loc_start_resp *loc_start_resp);
+
+/*
+ * gps_loc_stop_req message
+ */
+struct gps_loc_stop_req *gps_loc_stop_req_alloc(unsigned txn);
+struct gps_loc_stop_req *gps_loc_stop_req_parse(void *buf, size_t len, unsigned *txn);
+void *gps_loc_stop_req_encode(struct gps_loc_stop_req *loc_stop_req, size_t *len);
+void gps_loc_stop_req_free(struct gps_loc_stop_req *loc_stop_req);
+
+int gps_loc_stop_req_set_session_id(struct gps_loc_stop_req *loc_stop_req, uint8_t val);
+int gps_loc_stop_req_get_session_id(struct gps_loc_stop_req *loc_stop_req, uint8_t *val);
+
+/*
+ * gps_loc_stop_resp message
+ */
+struct gps_loc_stop_resp *gps_loc_stop_resp_alloc(unsigned txn);
+struct gps_loc_stop_resp *gps_loc_stop_resp_parse(void *buf, size_t len, unsigned *txn);
+void *gps_loc_stop_resp_encode(struct gps_loc_stop_resp *loc_stop_resp, size_t *len);
+void gps_loc_stop_resp_free(struct gps_loc_stop_resp *loc_stop_resp);
+
+int gps_loc_stop_resp_set_result(struct gps_loc_stop_resp *loc_stop_resp, struct gps_qmi_result *val);
+struct gps_qmi_result *gps_loc_stop_resp_get_result(struct gps_loc_stop_resp *loc_stop_resp);
+
+/*
+ * gps_loc_reg_events_req message
+ */
+struct gps_loc_reg_events_req *gps_loc_reg_events_req_alloc(unsigned txn);
+struct gps_loc_reg_events_req *gps_loc_reg_events_req_parse(void *buf, size_t len, unsigned *txn);
+void *gps_loc_reg_events_req_encode(struct gps_loc_reg_events_req *loc_reg_events_req, size_t *len);
+void gps_loc_reg_events_req_free(struct gps_loc_reg_events_req *loc_reg_events_req);
+
+int gps_loc_reg_events_req_set_event_reg_mask(struct gps_loc_reg_events_req *loc_reg_events_req, uint64_t val);
+int gps_loc_reg_events_req_get_event_reg_mask(struct gps_loc_reg_events_req *loc_reg_events_req, uint64_t *val);
+
+/*
+ * gps_loc_reg_events_resp message
+ */
+struct gps_loc_reg_events_resp *gps_loc_reg_events_resp_alloc(unsigned txn);
+struct gps_loc_reg_events_resp *gps_loc_reg_events_resp_parse(void *buf, size_t len, unsigned *txn);
+void *gps_loc_reg_events_resp_encode(struct gps_loc_reg_events_resp *loc_reg_events_resp, size_t *len);
+void gps_loc_reg_events_resp_free(struct gps_loc_reg_events_resp *loc_reg_events_resp);
+
+int gps_loc_reg_events_resp_set_result(struct gps_loc_reg_events_resp *loc_reg_events_resp, struct gps_qmi_result *val);
+struct gps_qmi_result *gps_loc_reg_events_resp_get_result(struct gps_loc_reg_events_resp *loc_reg_events_resp);
+
+/*
+ * gps_loc_event_nmea_ind message
+ */
+struct gps_loc_event_nmea_ind *gps_loc_event_nmea_ind_alloc(unsigned txn);
+struct gps_loc_event_nmea_ind *gps_loc_event_nmea_ind_parse(void *buf, size_t len, unsigned *txn);
+void *gps_loc_event_nmea_ind_encode(struct gps_loc_event_nmea_ind *loc_event_nmea_ind, size_t *len);
+void gps_loc_event_nmea_ind_free(struct gps_loc_event_nmea_ind *loc_event_nmea_ind);
+
+int gps_loc_event_nmea_ind_set_nmea(struct gps_loc_event_nmea_ind *loc_event_nmea_ind, char *buf, size_t len);
+int gps_loc_event_nmea_ind_get_nmea(struct gps_loc_event_nmea_ind *loc_event_nmea_ind, char *buf, size_t buflen);
+
+#endif
diff --git a/qmi_gps.qmi b/qmi_gps.qmi
new file mode 100644
index 0000000..8572cde
--- /dev/null
+++ b/qmi_gps.qmi
@@ -0,0 +1,69 @@
+package gps;
+
+const QMI_GPS_RESULT_SUCCESS = 0;
+const QMI_GPS_RESULT_FAILURE = 1;
+
+const QMI_GPS_ERR_NONE = 0;
+const QMI_GPS_ERR_INTERNAL = 3;
+const QMI_GPS_ERR_MALFORMED_MSG = 1;
+const QMI_GPS_QMI_ERR_NO_MEMORY = 2;
+const QMI_GPS_QMI_ERR_INVALID_HANDLE = 9;
+
+const QMI_GPS_LOC_START = 0x22;
+const QMI_GPS_LOC_STOP = 0x23;
+const QMI_GPS_LOC_REG_EVENTS = 0x21;
+const QMI_GPS_LOC_NMEA_IND = 0x26;
+
+const QMI_LOC_MAX_APP_ID_NAME_LENGTH_V02 = 32;
+const QMI_LOC_MAX_APP_ID_PROVIDER_LENGTH_V02 = 24;
+const QMI_LOC_MAX_APP_ID_VERSION_LENGTH_V02 = 8;
+
+
+struct qmi_result {
+ u16 result;
+ u16 error;
+};
+
+struct loc_application_id {
+ string application_provider;
+ string application_name;
+ u8 application_version_valid;
+ string application_version;
+};
+
+request loc_start_req {
+ required u8 session_id = 1;
+ optional u32 fix_recurrence = 0x10;
+ optional u32 horizontal_accuracy_level = 0x11;
+ optional u32 intermediate_report_state = 0x12;
+ optional u32 min_interval = 0x13;
+ optional loc_application_id application_id = 0x14;
+ optional u32 config_altitude_assumed = 0x15;
+ optional u32 min_intermediate_position_report_interval = 0x16;
+} = 0x22;
+
+response loc_start_resp {
+ required qmi_result result = 2;
+} = 0x22;
+
+request loc_stop_req {
+ required u8 session_id = 1;
+} = 0x23;
+
+response loc_stop_resp {
+ required qmi_result result = 2;
+} = 0x23;
+
+request loc_reg_events_req {
+ required u64 event_reg_mask = 1;
+} = 0x21;
+
+response loc_reg_events_resp {
+ required qmi_result result = 2;
+} = 0x21;
+
+indication loc_event_nmea_ind {
+ required string nmea = 1;
+} = 0x26;
+
+
diff --git a/qmi_tlv.c b/qmi_tlv.c
new file mode 100644
index 0000000..75446d8
--- /dev/null
+++ b/qmi_tlv.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "qmi_gps.h"
+
+struct qmi_packet {
+ uint8_t flags;
+ uint16_t txn_id;
+ uint16_t msg_id;
+ uint16_t msg_len;
+ uint8_t data[];
+} __attribute__((__packed__));
+
+struct qmi_tlv {
+ void *allocated;
+ void *buf;
+ size_t size;
+ int error;
+};
+
+struct qmi_tlv_item {
+ uint8_t key;
+ uint16_t len;
+ uint8_t data[];
+} __attribute__((__packed__));
+
+struct qmi_tlv *qmi_tlv_init(unsigned txn, unsigned msg_id, unsigned msg_type)
+{
+ struct qmi_packet *pkt;
+ struct qmi_tlv *tlv;
+
+ tlv = malloc(sizeof(struct qmi_tlv));
+ memset(tlv, 0, sizeof(struct qmi_tlv));
+
+ tlv->size = sizeof(struct qmi_packet);
+ tlv->allocated = malloc(tlv->size);
+ tlv->buf = tlv->allocated;
+
+ pkt = tlv->buf;
+ pkt->flags = msg_type;
+ pkt->txn_id = txn;
+ pkt->msg_id = msg_id;
+ pkt->msg_len = 0;
+
+ return tlv;
+}
+
+struct qmi_tlv *qmi_tlv_decode(void *buf, size_t len, unsigned *txn, unsigned msg_type)
+{
+ struct qmi_packet *pkt = buf;
+ struct qmi_tlv *tlv;
+
+ if (pkt->flags != msg_type)
+ return NULL;
+
+ tlv = malloc(sizeof(struct qmi_tlv));
+ memset(tlv, 0, sizeof(struct qmi_tlv));
+
+ tlv->buf = buf;
+ tlv->size = len;
+
+ if (txn)
+ *txn = pkt->txn_id;
+
+ return tlv;
+}
+
+void *qmi_tlv_encode(struct qmi_tlv *tlv, size_t *len)
+{
+
+ struct qmi_packet *pkt;
+
+ if (!tlv || tlv->error)
+ return NULL;
+
+ pkt = tlv->buf;
+ pkt->msg_len = tlv->size - sizeof(struct qmi_packet);
+
+ *len = tlv->size;
+ return tlv->buf;
+}
+
+void qmi_tlv_free(struct qmi_tlv *tlv)
+{
+ free(tlv->allocated);
+ free(tlv);
+}
+
+static struct qmi_tlv_item *qmi_tlv_get_item(struct qmi_tlv *tlv, unsigned id)
+{
+ struct qmi_tlv_item *item;
+ struct qmi_packet *pkt;
+ unsigned offset = 0;
+ void *pkt_data;
+
+ pkt = tlv->buf;
+ pkt_data = pkt->data;
+
+ while (offset < tlv->size) {
+ item = pkt_data + offset;
+ if (item->key == id)
+ return pkt_data + offset;
+
+ offset += sizeof(struct qmi_tlv_item) + item->len;
+ }
+ return NULL;
+}
+
+void *qmi_tlv_get(struct qmi_tlv *tlv, unsigned id, size_t *len)
+{
+ struct qmi_tlv_item *item;
+
+ item = qmi_tlv_get_item(tlv, id);
+ if (!item)
+ return NULL;
+
+ *len = item->len;
+ return item->data;
+}
+
+void *qmi_tlv_get_array(struct qmi_tlv *tlv, unsigned id, unsigned len_size, size_t *len, size_t *size)
+{
+ struct qmi_tlv_item *item;
+ unsigned count;
+ void *ptr;
+
+ item = qmi_tlv_get_item(tlv, id);
+ if (!item)
+ return NULL;
+
+ ptr = item->data;
+ switch (len_size) {
+ case 4:
+ count = *(uint32_t*)ptr++;
+ break;
+ case 2:
+ count = *(uint16_t*)ptr++;
+ break;
+ case 1:
+ count = *(uint8_t*)ptr++;
+ break;
+ }
+
+ *len = count;
+ *size = (item->len - len_size) / count;
+
+ return ptr;
+}
+
+static struct qmi_tlv_item *qmi_tlv_alloc_item(struct qmi_tlv *tlv, unsigned id, size_t len)
+{
+ struct qmi_tlv_item *item;
+ size_t new_size;
+ bool migrate;
+ void *newp;
+
+ /* If using user provided buffer, migrate data */
+ migrate = !tlv->allocated;
+
+ new_size = tlv->size + sizeof(struct qmi_tlv_item) + len;
+ newp = realloc(tlv->allocated, new_size);
+ if (!newp)
+ return NULL;
+
+ if (migrate)
+ memcpy(newp, tlv->buf, tlv->size);
+
+ item = newp + tlv->size;
+ item->key = id;
+ item->len = len;
+
+ tlv->buf = tlv->allocated = newp;
+ tlv->size = new_size;
+
+ return item;
+}
+
+int qmi_tlv_set(struct qmi_tlv *tlv, unsigned id, void *buf, size_t len)
+{
+ struct qmi_tlv_item *item;
+
+ if (!tlv)
+ return -EINVAL;
+
+ item = qmi_tlv_alloc_item(tlv, id, len);
+ if (!item) {
+ tlv->error = ENOMEM;
+ return -ENOMEM;
+ }
+
+ memcpy(item->data, buf, len);
+
+ return 0;
+}
+
+int qmi_tlv_set_array(struct qmi_tlv *tlv, unsigned id, unsigned len_size, void *buf, size_t len, size_t size)
+{
+ struct qmi_tlv_item *item;
+ size_t array_size;
+ void *ptr;
+
+ if (!tlv)
+ return -EINVAL;
+
+ array_size = len * size;
+ item = qmi_tlv_alloc_item(tlv, id, len_size + array_size);
+ if (!item) {
+ tlv->error = ENOMEM;
+ return -ENOMEM;
+ }
+
+ ptr = item->data;
+
+ switch (len_size) {
+ case 4:
+ *(uint32_t*)ptr++ = len;
+ break;
+ case 2:
+ *(uint16_t*)ptr++ = len;
+ break;
+ case 1:
+ *(uint8_t*)ptr++ = len;
+ break;
+ }
+ memcpy(ptr, buf, array_size);
+
+ return 0;
+}
+
+
diff --git a/util.c b/util.c
new file mode 100644
index 0000000..4669f85
--- /dev/null
+++ b/util.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <stdint.h>
+#include <stdio.h>
+#include "util.h"
+
+static uint8_t to_hex(uint8_t ch)
+{
+ ch &= 0xf;
+ return ch <= 9 ? '0' + ch : 'a' + ch - 10;
+}
+
+void print_hex_dump(const char *prefix, const void *buf, size_t len)
+{
+ const uint8_t *ptr = buf;
+ size_t linelen;
+ uint8_t ch;
+ char line[16 * 3 + 16 + 1];
+ int li;
+ int i;
+ int j;
+
+ for (i = 0; i < len; i += 16) {
+ linelen = MIN(16, len - i);
+ li = 0;
+
+ for (j = 0; j < linelen; j++) {
+ ch = ptr[i + j];
+ line[li++] = to_hex(ch >> 4);
+ line[li++] = to_hex(ch);
+ line[li++] = ' ';
+ }
+
+ for (; j < 16; j++) {
+ line[li++] = ' ';
+ line[li++] = ' ';
+ line[li++] = ' ';
+ }
+
+ for (j = 0; j < linelen; j++) {
+ ch = ptr[i + j];
+ line[li++] = isprint(ch) ? ch : '.';
+ }
+
+ line[li] = '\0';
+
+ printf("%s %04x: %s\n", prefix, i, line);
+ }
+}
diff --git a/util.h b/util.h
new file mode 100644
index 0000000..a9c2242
--- /dev/null
+++ b/util.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __UTIL_H__
+#define __UTIL_H__
+
+#define MIN(x, y) ((x) < (y) ? (x) : (y))
+#define MAX(x, y) ((x) > (y) ? (x) : (y))
+
+void print_hex_dump(const char *prefix, const void *buf, size_t len);
+
+#endif