diff options
author | Bjorn Andersson <bjorn.andersson@linaro.org> | 2016-05-10 22:22:50 -0700 |
---|---|---|
committer | Bjorn Andersson <bjorn.andersson@linaro.org> | 2016-05-10 22:22:50 -0700 |
commit | 8dbff020d2881985c94f8e1de268bc0668f109b9 (patch) | |
tree | 4607bb6507d7fd0cfa9939a31f6a3dff18b3def7 | |
parent | b374fe4aabf77a7f2f59e1db5a08f21afb656c74 (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-- | Makefile | 20 | ||||
-rw-r--r-- | gps_proxy.c | 441 | ||||
-rw-r--r-- | gps_proxy.h | 54 | ||||
-rw-r--r-- | qmi_gps.c | 464 | ||||
-rw-r--r-- | qmi_gps.h | 181 | ||||
-rw-r--r-- | qmi_gps.qmi | 69 | ||||
-rw-r--r-- | qmi_tlv.c | 262 | ||||
-rw-r--r-- | util.c | 77 | ||||
-rw-r--r-- | util.h | 38 |
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; +} + + @@ -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); + } +} @@ -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 |