summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBjorn Andersson <bjorn.andersson@linaro.org>2016-02-07 09:27:50 -0800
committerBjorn Andersson <bjorn.andersson@linaro.org>2016-02-07 09:27:50 -0800
commit241fff829e1deb44d0774a9993b02d9b97b7d10f (patch)
tree144fba7861427d8eaa1eef93944d7f783b8e59a8
parent739fbdc423e43e299e22f5ac2e85d7c467909269 (diff)
qmic: Initial basic implementation
This initial implementation is capable of generating encoder and decoder accessors for messages with basic integers, strings, arrays and structs. Signed-off-by: Bjorn Andersson <bjorn.andersson@linaro.org>
-rw-r--r--Makefile17
-rw-r--r--qmi_message.c344
-rw-r--r--qmi_struct.c156
-rw-r--r--qmi_tlv.c142
-rw-r--r--qmic.c372
-rw-r--r--qmic.h71
6 files changed, 1102 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..22b999b
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,17 @@
+OUT := qmic
+
+CFLAGS := -Wall -g
+LDFLAGS :=
+
+SRCS := qmic.c qmi_message.c qmi_struct.c
+OBJS := $(SRCS:.c=.o)
+
+$(OUT): $(OBJS)
+ $(CC) $(LDFLAGS) -o $@ $^
+
+test: $(OUT)
+ ./$(OUT)
+
+clean:
+ rm -f $(OUT) $(OBJS)
+
diff --git a/qmi_message.c b/qmi_message.c
new file mode 100644
index 0000000..884538b
--- /dev/null
+++ b/qmi_message.c
@@ -0,0 +1,344 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include "qmic.h"
+
+static const char *sz_simple_types[] = {
+ [TYPE_U8] = "uint8_t",
+ [TYPE_U16] = "uint16_t",
+ [TYPE_U32] = "uint32_t",
+ [TYPE_U64] = "uint64_t",
+};
+
+struct qmi_message_member {
+ const char *name;
+ int type;
+ struct qmi_struct *qmi_struct;
+ int id;
+ bool required;
+ unsigned array;
+
+ struct qmi_message_member *next;
+};
+
+struct qmi_message {
+ enum message_type type;
+ const char *name;
+ unsigned msg_id;
+
+ struct qmi_message *next;
+
+ LIST_HEAD(qmi_message_member, members);
+};
+
+LIST_HEAD(qmi_message, qmi_messages);
+
+void qmi_message_parse(enum message_type message_type)
+{
+ struct qmi_message_member *qmm;
+ struct qmi_message *qm;
+ struct token msg_id_tok;
+ struct token type_tok;
+ struct token num_tok;
+ struct token id_tok;
+ unsigned array;
+ bool required;
+
+ token_expect(TOK_ID, &msg_id_tok);
+ token_expect('{', NULL);
+
+ qm = malloc(sizeof(struct qmi_message));
+ qm->name = msg_id_tok.str;
+ qm->type = message_type;
+
+ while (!token_accept('}', NULL)) {
+ array = 0;
+
+ if (token_accept(TOK_REQUIRED, NULL))
+ required = true;
+ else if (token_accept(TOK_OPTIONAL, NULL))
+ required = false;
+ else
+ yyerror("expected required, optional or '}'");
+
+ token_expect(TOK_TYPE, &type_tok);
+ token_expect(TOK_ID, &id_tok);
+
+ if (token_accept('[', NULL)) {
+ array = 1;
+ if (token_accept(TOK_NUM, &num_tok)) {
+ if (num_tok.num & 0xffff0000)
+ array = 4;
+ else if (num_tok.num & 0xff00)
+ array = 2;
+ }
+
+ token_expect(']', NULL);
+ }
+
+ token_expect('=', NULL);
+ token_expect(TOK_NUM, &num_tok);
+ token_expect(';', NULL);
+
+ qmm = malloc(sizeof(struct qmi_message_member));
+ qmm->name = id_tok.str;
+ qmm->type = type_tok.num;
+ qmm->qmi_struct = type_tok.qmi_struct;
+ qmm->id = num_tok.num;
+ qmm->required = required;
+ qmm->array = array;
+
+ LIST_ADD(qm->members, qmm);
+ }
+
+ if (token_accept('=', NULL)) {
+ token_expect(TOK_NUM, &num_tok);
+
+ qm->msg_id = num_tok.num;
+ }
+
+ token_expect(';', NULL);
+
+ LIST_ADD(qmi_messages, qm);
+}
+
+static void qmi_message_emit_message_type(FILE *fp,
+ const char *package,
+ const char *message)
+{
+ fprintf(fp, "struct %s_%s;\n", package, message);
+}
+
+static void qmi_message_emit_message_prototype(FILE *fp,
+ const char *package,
+ const char *message)
+{
+ fprintf(fp, "/*\n"
+ " * %1$s_%2$s message\n"
+ " */\n",
+ package, message);
+
+ fprintf(fp, "struct %1$s_%2$s *%1$s_%2$s_alloc(unsigned txn);\n",
+ package, message);
+
+ fprintf(fp, "struct %1$s_%2$s *%1$s_%2$s_parse(void *buf, size_t len, unsigned *txn);\n",
+ package, message);
+
+ fprintf(fp, "void *%1$s_%2$s_encode(struct %1$s_%2$s *%2$s, size_t *len);\n",
+ package, message);
+
+ fprintf(fp, "void %1$s_%2$s_free(struct %1$s_%2$s *%2$s);\n\n",
+ package, message);
+}
+
+static void qmi_message_emit_message(FILE *fp,
+ const char *package,
+ struct qmi_message *qm)
+{
+ fprintf(fp, "struct %1$s_%2$s *%1$s_%2$s_alloc(unsigned txn)\n"
+ "{\n"
+ " return (struct %1$s_%2$s*)qmi_tlv_init(txn, %3$d);\n"
+ "}\n\n",
+ package, qm->name, qm->msg_id);
+
+ fprintf(fp, "struct %1$s_%2$s *%1$s_%2$s_parse(void *buf, size_t len, unsigned *txn)\n"
+ "{\n"
+ " return (struct %1$s_%2$s*)qmi_tlv_decode(buf, len, txn);\n"
+ "}\n\n",
+ package, qm->name);
+
+ fprintf(fp, "void *%1$s_%2$s_encode(struct %1$s_%2$s *%2$s, size_t *len)\n"
+ "{\n"
+ " return qmi_tlv_encode((struct qmi_tlv*)%2$s, len);\n"
+ "}\n\n",
+ package, qm->name);
+
+ fprintf(fp, "void %1$s_%2$s_free(struct %1$s_%2$s *%2$s)\n"
+ "{\n"
+ " qmi_tlv_free((struct qmi_tlv*)%2$s);\n"
+ "}\n\n",
+ package, qm->name);
+}
+
+static void qmi_message_emit_simple_prototype(FILE *fp,
+ const char *package,
+ const char *message,
+ struct qmi_message_member *qmm)
+{
+ if (qmm->array) {
+ fprintf(fp, "int %1$s_%2$s_set_%3$s(struct %1$s_%2$s *%2$s, %4$s *val, size_t count);\n",
+ package, message, qmm->name, sz_simple_types[qmm->type]);
+
+ fprintf(fp, "%4$s *%1$s_%2$s_get_%3$s(struct %1$s_%2$s *%2$s, size_t *count);\n\n",
+ package, message, qmm->name, sz_simple_types[qmm->type]);
+ } else {
+ fprintf(fp, "int %1$s_%2$s_set_%3$s(struct %1$s_%2$s *%2$s, %4$s val);\n",
+ package, message, qmm->name, sz_simple_types[qmm->type]);
+
+ fprintf(fp, "int %1$s_%2$s_get_%3$s(struct %1$s_%2$s *%2$s, %4$s *val);\n\n",
+ package, message, qmm->name, sz_simple_types[qmm->type]);
+ }
+}
+
+
+static void qmi_message_emit_simple_accessors(FILE *fp,
+ const char *package,
+ const char *message,
+ struct qmi_message_member *qmm)
+{
+ if (qmm->array) {
+ fprintf(fp, "int %1$s_%2$s_set_%3$s(struct %1$s_%2$s *%2$s, %4$s *val, size_t count)\n"
+ "{\n"
+ " return qmi_tlv_set_array((struct qmi_tlv*)%2$s, %5$d, %6$d, val, count, sizeof(%4$s));\n"
+ "}\n\n",
+ package, message, qmm->name, sz_simple_types[qmm->type], qmm->id, qmm->array);
+
+ fprintf(fp, "%4$s *%1$s_%2$s_get_%3$s(struct %1$s_%2$s *%2$s, size_t *count)\n"
+ "{\n"
+ " %4$s *ptr;\n"
+ " size_t size;\n"
+ " size_t len;\n"
+ "\n"
+ " ptr = qmi_tlv_get_array((struct qmi_tlv*)%2$s, %5$d, %6$d, &len, &size);\n"
+ " if (!ptr)\n"
+ " return NULL;\n"
+ "\n"
+ " if (size != sizeof(%4$s))\n"
+ " return NULL;\n"
+ "\n"
+ " *count = len;\n"
+ " return ptr;\n"
+ "}\n\n",
+ package, message, qmm->name, sz_simple_types[qmm->type], qmm->id, qmm->array);
+ } else {
+ fprintf(fp, "int %1$s_%2$s_set_%3$s(struct %1$s_%2$s *%2$s, %4$s val)\n"
+ "{\n"
+ " return qmi_tlv_set((struct qmi_tlv*)%2$s, %5$d, &val, sizeof(%4$s));\n"
+ "}\n\n",
+ package, message, qmm->name, sz_simple_types[qmm->type], qmm->id);
+
+ fprintf(fp, "int %1$s_%2$s_get_%3$s(struct %1$s_%2$s *%2$s, %4$s *val)\n"
+ "{\n"
+ " %4$s *ptr;\n"
+ " size_t len;\n"
+ "\n"
+ " ptr = qmi_tlv_get((struct qmi_tlv*)%2$s, %5$d, &len);\n"
+ " if (!ptr)\n"
+ " return -ENOENT;\n"
+ "\n"
+ " if (len != sizeof(%4$s))\n"
+ " return -EINVAL;\n"
+ "\n"
+ " *val = *(%4$s*)ptr;\n"
+ " return 0;\n"
+ "}\n\n",
+ package, message, qmm->name, sz_simple_types[qmm->type], qmm->id);
+ }
+}
+
+static void qmi_message_emit_string_prototype(FILE *fp,
+ const char *package,
+ const char *message,
+ struct qmi_message_member *qmm)
+{
+ if (qmm->array) {
+ fprintf(stderr, "Dont' know how to encode string arrays yet");
+ exit(1);
+ } else {
+ fprintf(fp, "int %1$s_%2$s_set_%3$s(struct %1$s_%2$s *%2$s, char *buf, size_t len);\n",
+ package, message, qmm->name);
+
+ fprintf(fp, "int %1$s_%2$s_get_%3$s(struct %1$s_%2$s *%2$s, char *buf, size_t buflen);\n\n",
+ package, message, qmm->name);
+ }
+}
+
+static void qmi_message_emit_string_accessors(FILE *fp,
+ const char *package,
+ const char *message,
+ struct qmi_message_member *qmm)
+{
+ fprintf(fp, "int %1$s_%2$s_set_%3$s(struct %1$s_%2$s *%2$s, char *buf, size_t len)\n"
+ "{\n"
+ " return qmi_tlv_set((struct qmi_tlv*)%2$s, %4$d, buf, len);\n"
+ "}\n\n",
+ package, message, qmm->name, qmm->id);
+
+ fprintf(fp, "int %1$s_%2$s_get_%3$s(struct %1$s_%2$s *%2$s, char *buf, size_t buflen)\n"
+ "{\n"
+ " size_t len;\n"
+ " char *ptr;\n"
+ "\n"
+ " ptr = qmi_tlv_get((struct qmi_tlv*)open_req, %4$d, &len);\n"
+ " if (!ptr)\n"
+ " return -ENOENT;\n"
+ "\n"
+ " if (len >= buflen)\n"
+ " return -ENOMEM;\n"
+ "\n"
+ " memcpy(buf, ptr, len);\n"
+ " buf[len] = '\\0';\n"
+ " return len;\n"
+ "}\n\n",
+ package, message, qmm->name, qmm->id);
+
+}
+
+void qmi_message_source(FILE *fp, const char *package)
+{
+ struct qmi_message_member *qmm;
+ struct qmi_message *qm;
+
+ for (qm = qmi_messages.head; qm; qm = qm->next) {
+ qmi_message_emit_message(fp, package, qm);
+
+ for (qmm = qm->members.head; qmm; qmm = qmm->next)
+ switch (qmm->type) {
+ case TYPE_U8:
+ case TYPE_U16:
+ case TYPE_U32:
+ case TYPE_U64:
+ qmi_message_emit_simple_accessors(fp, package, qm->name, qmm);
+ break;
+ case TYPE_STRING:
+ qmi_message_emit_string_accessors(fp, package, qm->name, qmm);
+ break;
+ case TYPE_STRUCT:
+ qmi_struct_emit_accessors(fp, package, qm->name, qmm->name, qmm->id, qmm->array, qmm->qmi_struct);
+ break;
+ };
+ }
+}
+
+void qmi_message_header(FILE *fp, const char *package)
+{
+ struct qmi_message_member *qmm;
+ struct qmi_message *qm;
+
+ for (qm = qmi_messages.head; qm; qm = qm->next)
+ qmi_message_emit_message_type(fp, package, qm->name);
+
+ fprintf(fp, "\n");
+
+ for (qm = qmi_messages.head; qm; qm = qm->next) {
+ qmi_message_emit_message_prototype(fp, package, qm->name);
+
+ for (qmm = qm->members.head; qmm; qmm = qmm->next) {
+ switch (qmm->type) {
+ case TYPE_U8:
+ case TYPE_U16:
+ case TYPE_U32:
+ case TYPE_U64:
+ qmi_message_emit_simple_prototype(fp, package, qm->name, qmm);
+ break;
+ case TYPE_STRING:
+ qmi_message_emit_string_prototype(fp, package, qm->name, qmm);
+ break;
+ case TYPE_STRUCT:
+ qmi_struct_emit_prototype(fp, package, qm->name, qmm->name, qmm->array, qmm->qmi_struct);
+ break;
+ };
+ }
+ }
+}
diff --git a/qmi_struct.c b/qmi_struct.c
new file mode 100644
index 0000000..9953cd3
--- /dev/null
+++ b/qmi_struct.c
@@ -0,0 +1,156 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include "qmic.h"
+
+static const char *sz_simple_types[] = {
+ [TYPE_U8] = "uint8_t",
+ [TYPE_U16] = "uint16_t",
+ [TYPE_U32] = "uint32_t",
+ [TYPE_U64] = "uint64_t",
+};
+
+struct qmi_struct_member {
+ const char *name;
+ int type;
+
+ struct qmi_struct_member *next;
+};
+
+struct qmi_struct {
+ const char *name;
+
+ struct qmi_struct *next;
+
+ LIST_HEAD(qmi_struct_member, members);
+};
+
+LIST_HEAD(qmi_struct, qmi_structs);
+
+void qmi_struct_parse(void)
+{
+ struct qmi_struct_member *qsm;
+ struct token struct_id_tok;
+ struct qmi_struct *qs;
+ struct token type_tok;
+ struct token id_tok;
+
+ token_expect(TOK_ID, &struct_id_tok);
+ token_expect('{', NULL);
+
+ qs = malloc(sizeof(struct qmi_struct));
+ qs->name = struct_id_tok.str;
+
+ while (token_accept(TOK_TYPE, &type_tok)) {
+ token_expect(TOK_ID, &id_tok);
+ token_expect(';', NULL);
+
+ qsm = malloc(sizeof(struct qmi_struct_member));
+ qsm->name = id_tok.str;
+ qsm->type = type_tok.num;
+
+ LIST_ADD(qs->members, qsm);
+ }
+
+ token_expect('}', NULL);
+ token_expect(';', NULL);
+
+ LIST_ADD(qmi_structs, qs);
+
+ symbol_add(qs->name, TOK_TYPE, TYPE_STRUCT, qs);
+}
+
+void qmi_struct_header(FILE *fp, const char *package)
+{
+ struct qmi_struct_member *qsm;
+ struct qmi_struct *qs;
+
+ for (qs = qmi_structs.head; qs; qs = qs->next) {
+ fprintf(fp, "struct %s_%s {\n",
+ package, qs->name);
+ for (qsm = qs->members.head; qsm; qsm = qsm->next) {
+ fprintf(fp, "\t%s %s;\n",
+ sz_simple_types[qsm->type], qsm->name);
+ }
+ fprintf(fp, "};\n"
+ "\n");
+ }
+}
+
+void qmi_struct_emit_prototype(FILE *fp,
+ const char *package,
+ const char *message,
+ const char *member,
+ unsigned array_size,
+ struct qmi_struct *qs)
+{
+ if (array_size) {
+ fprintf(fp, "int %1$s_%2$s_set_%3$s(struct %1$s_%2$s *%2$s, struct %1$s_%4$s *val, size_t count);\n",
+ package, message, member, qs->name);
+
+ fprintf(fp, "struct %1$s_%4$s *%1$s_%2$s_get_%3$s(struct %1$s_%2$s *%2$s, size_t *count);\n\n",
+ package, message, member, qs->name);
+ } else {
+ fprintf(fp, "int %1$s_%2$s_set_%3$s(struct %1$s_%2$s *%2$s, struct %1$s_%4$s *val);\n",
+ package, message, member, qs->name);
+
+ fprintf(fp, "struct %1$s_%4$s *%1$s_%2$s_get_%3$s(struct %1$s_%2$s *%2$s);\n\n",
+ package, message, member, qs->name);
+ }
+}
+
+void qmi_struct_emit_accessors(FILE *fp,
+ const char *package,
+ const char *message,
+ const char *member,
+ int member_id,
+ unsigned array_size,
+ struct qmi_struct *qs)
+{
+ if (array_size) {
+ fprintf(fp, "int %1$s_%2$s_set_%3$s(struct %1$s_%2$s *%2$s, struct %1$s_%4$s *val, size_t count)\n"
+ "{\n"
+ " return qmi_tlv_set_array((struct qmi_tlv*)%2$s, %5$d, %6$d, val, count, sizeof(struct %1$s_%4$s));\n"
+ "}\n\n",
+ package, message, member, qs->name, member_id, array_size);
+
+ fprintf(fp, "struct %1$s_%4$s *%1$s_%2$s_get_%3$s(struct %1$s_%2$s *%2$s, size_t *count)\n"
+ "{\n"
+ " size_t size;\n"
+ " size_t len;\n"
+ " void *ptr;\n"
+ "\n"
+ " ptr = qmi_tlv_get_array((struct qmi_tlv*)%2$s, %5$d, %6$d, &len, &size);\n"
+ " if (!ptr)\n"
+ " return NULL;\n"
+ "\n"
+ " if (size != sizeof(struct %1$s_%4$s))\n"
+ " return NULL;\n"
+ "\n"
+ " *count = len;\n"
+ " return ptr;\n"
+ "}\n\n",
+ package, message, member, qs->name, member_id, array_size);
+ } else {
+ fprintf(fp, "int %1$s_%2$s_set_%3$s(struct %1$s_%2$s *%2$s, struct %1$s_%4$s *val)\n"
+ "{\n"
+ " return qmi_tlv_set((struct qmi_tlv*)%2$s, %5$d, val, sizeof(struct %1$s_%4$s));\n"
+ "}\n\n",
+ package, message, member, qs->name, member_id);
+
+ fprintf(fp, "struct %1$s_%4$s *%1$s_%2$s_get_%3$s(struct %1$s_%2$s *%2$s)\n"
+ "{\n"
+ " size_t len;\n"
+ " void *ptr;\n"
+ "\n"
+ " ptr = qmi_tlv_get((struct qmi_tlv*)%2$s, %5$d, &len);\n"
+ " if (!ptr)\n"
+ " return NULL;\n"
+ "\n"
+ " if (len != sizeof(struct %1$s_%4$s))\n"
+ " return NULL;\n"
+ "\n"
+ " return ptr;\n"
+ "}\n\n",
+ package, message, member, qs->name, member_id);
+ }
+}
diff --git a/qmi_tlv.c b/qmi_tlv.c
new file mode 100644
index 0000000..db31db8
--- /dev/null
+++ b/qmi_tlv.c
@@ -0,0 +1,142 @@
+
+struct qmi_tlv *qmi_tlv_init(void)
+{
+ struct qmi_tlv *tlv;
+
+ tlv = malloc(sizeof(struct qmi_tlv));
+ memset(tlv, 0, sizeof(struct qmi_tlv));
+
+ return tlv;
+}
+
+struct qmi_tlv *qmi_tlv_decode(void *buf, size_t len)
+{
+ struct qmi_tlv *tlv;
+
+ tlv = malloc(sizeof(struct qmi_tlv));
+ memset(tlv, 0, sizeof(struct qmi_tlv));
+
+ tlv->buf = buf;
+ tlv->size = len;
+
+ return tlv;
+}
+
+void *qmi_tlv_encode(struct qmi_tlv *tlv, size_t *len)
+{
+ *len = tlv->size;
+ return tlv->buf;
+}
+
+void qmi_tlv_free(struct qmi_tlv *tlv)
+{
+ free(tlv->allocated);
+ free(tlv);
+}
+
+static struct qmi_tlv_header *qmi_tlv_get_item(struct qmi_tlv *tlv, unsigned id)
+{
+ struct qmi_tlv_header *hdr;
+ unsigned offset = 0;
+
+ while (offset < tlv->size) {
+ hdr = tlv->buf + offset;
+ if (hdr->key == id)
+ return tlv->buf + offset;
+
+ offset += sizeof(struct qmi_tlv_header) + hdr->len;
+ }
+ return NULL;
+}
+
+void *qmi_tlv_get(struct qmi_tlv *tlv, unsigned id, size_t *len)
+{
+ struct qmi_tlv_header *hdr;
+
+ hdr = qmi_tlv_get_item(tlv, id);
+ if (!hdr)
+ return NULL;
+
+ *len = hdr->len;
+ return hdr->data;
+}
+
+void *qmi_tlv_get_array(struct qmi_tlv *tlv, unsigned id, size_t *len, size_t *size)
+{
+ struct qmi_tlv_header *hdr;
+ uint8_t count;
+ void *ptr;
+
+ hdr = qmi_tlv_get_item(tlv, id);
+ if (!hdr)
+ return NULL;
+
+ ptr = hdr->data;
+ count = *(uint8_t*)ptr++;
+
+ *len = count;
+ *size = (hdr->len - 1) / count;
+
+ return ptr;
+}
+
+static struct qmi_tlv_header *qmi_tlv_alloc_item(struct qmi_tlv *tlv, unsigned id, size_t len)
+{
+ struct qmi_tlv_header *hdr;
+ 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_header) + len;
+ newp = realloc(tlv->allocated, new_size);
+ if (!newp)
+ return NULL;
+
+ if (migrate)
+ memcpy(newp, tlv->buf, tlv->size);
+
+ hdr = newp + tlv->size;
+ hdr->key = id;
+ hdr->len = len;
+
+ tlv->buf = tlv->allocated = newp;
+ tlv->size = new_size;
+
+ return hdr;
+}
+
+int qmi_tlv_set(struct qmi_tlv *tlv, unsigned id, void *buf, size_t len)
+{
+ struct qmi_tlv_header *hdr;
+
+ hdr = qmi_tlv_alloc_item(tlv, id, len);
+ if (!hdr)
+ return -ENOMEM;
+
+ memcpy(hdr->data, buf, len);
+
+ return 0;
+}
+
+int qmi_tlv_set_array(struct qmi_tlv *tlv, unsigned id, void *buf, size_t len, size_t size)
+{
+ struct qmi_tlv_header *hdr;
+ size_t array_size;
+ void *ptr;
+
+ array_size = len * size;
+ hdr = qmi_tlv_alloc_item(tlv, id, sizeof(uint8_t) + array_size);
+ if (!hdr)
+ return -ENOMEM;
+
+ ptr = hdr->data;
+ *(uint8_t*)ptr++ = len;
+ memcpy(ptr, buf, array_size);
+
+ return 0;
+}
+
+
diff --git a/qmic.c b/qmic.c
new file mode 100644
index 0000000..db5c1b5
--- /dev/null
+++ b/qmic.c
@@ -0,0 +1,372 @@
+#include <assert.h>
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "qmic.h"
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))
+
+static char scratch_buf[128];
+static int scratch_pos;
+
+static int yyline = 1;
+
+static int input()
+{
+ static char input_buf[128];
+ static int input_pos;
+ static int input_len;
+ int ret;
+ int ch;
+
+ if (scratch_pos) {
+ ch = scratch_buf[--scratch_pos];
+ goto out;
+ }
+
+ if (input_pos < input_len) {
+ ch = input_buf[input_pos++];
+ goto out;
+ }
+
+ ret = read(0, input_buf, sizeof(input_buf));
+ if (ret <= 0)
+ return ret;
+
+ input_pos = 0;
+ input_len = ret;
+
+ ch = input_buf[input_pos++];
+out:
+ if (ch == '\n')
+ yyline++;
+ return ch;
+}
+
+static void unput(int ch)
+{
+ assert(scratch_pos < 128);
+ scratch_buf[scratch_pos++] = ch;
+
+ if (ch == '\n')
+ yyline--;
+}
+
+struct symbol {
+ int token;
+ const char *name;
+
+ int type;
+ struct qmi_struct *qmi_struct;
+
+ struct symbol *next;
+};
+
+LIST_HEAD(symbol, symbols);
+
+void symbol_add(const char *name, int token, ...)
+{
+ struct symbol *sym;
+ va_list ap;
+
+ va_start(ap, token);
+
+ sym = malloc(sizeof(struct symbol));
+ sym->token = token;
+ sym->name = name;
+ sym->next = NULL;
+
+ if (token == TOK_TYPE) {
+ sym->type = va_arg(ap, int);
+ if (sym->type == TYPE_STRUCT)
+ sym->qmi_struct = va_arg(ap, struct qmi_struct *);
+ }
+
+ LIST_ADD(symbols, sym);
+
+ va_end(ap);
+}
+
+static struct token yylex()
+{
+ struct symbol *sym;
+ struct token token = {};
+ char buf[128];
+ char *p = buf;
+ int base;
+ int ch;
+
+ while ((ch = input()) && isspace(ch))
+ ;
+
+ if (isalpha(ch)) {
+ do {
+ *p++ = ch;
+ ch = input();
+ } while (isalnum(ch) || ch == '_');
+ unput(ch);
+ *p = '\0';
+
+ token.str = strdup(buf);
+ for (sym = symbols.head; sym; sym = sym->next) {
+ if (strcmp(buf, sym->name) == 0) {
+ token.id = sym->token;
+ token.num = sym->type;
+ token.qmi_struct = sym->qmi_struct;
+ break;
+ }
+ }
+
+ if (!sym)
+ token.id = TOK_ID;
+
+ return token;
+ } else if (isdigit(ch)) {
+ do {
+ *p++ = ch;
+ ch = input();
+ } while (isdigit(ch) || (p - buf == 1 && ch == 'x'));
+ unput(ch);
+ *p = '\0';
+
+ if (buf[0] == '0' && buf[1] == 'x')
+ base = 16;
+ else if (buf[0] == '0')
+ base = 8;
+ else
+ base = 10;
+
+ token.id = TOK_NUM;
+ token.num = strtol(buf, NULL, base);
+ return token;
+ }
+
+ token.id = ch;
+ return token;
+}
+
+static struct token curr_token;
+
+void yyerror(const char *fmt, ...)
+{
+ char buf[128];
+ va_list ap;
+
+ va_start(ap, fmt);
+
+ vsprintf(buf, fmt, ap);
+ printf("parse error on line %d: %s\n", yyline, buf);
+
+ va_end(ap);
+ exit(1);
+}
+
+static void token_init(void)
+{
+ curr_token = yylex();
+}
+
+int token_accept(int id, struct token *tok)
+{
+ if (curr_token.id == id) {
+ if (tok)
+ *tok = curr_token;
+
+ curr_token = yylex();
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+void token_expect(int id, struct token *tok)
+{
+ if (!token_accept(id, tok)) {
+ switch (id) {
+ case TOK_CONST:
+ yyerror("expected const");
+ case TOK_ID:
+ yyerror("expected identifier");
+ case TOK_MESSAGE:
+ yyerror("expected message");
+ case TOK_NUM:
+ yyerror("expected num");
+ case TOK_PACKAGE:
+ yyerror("expected package");
+ case TOK_STRUCT:
+ yyerror("expected struct");
+ case TOK_TYPE:
+ yyerror("expected type");
+ case TOK_REQUIRED:
+ yyerror("expected required");
+ case TOK_OPTIONAL:
+ yyerror("expected optional");
+ default:
+ yyerror("expected '%c'", id);
+ }
+ }
+}
+
+static const char *parse_package()
+{
+ struct token tok;
+
+ if (!token_accept(TOK_ID, &tok))
+ yyerror("expected identifier");
+
+ token_expect(';', NULL);
+ return tok.str;
+}
+
+
+struct qmi_const {
+ const char *name;
+ unsigned value;
+
+ struct qmi_const *next;
+};
+
+LIST_HEAD(qmi_const, qmi_consts);
+
+static void qmi_const_parse()
+{
+ struct qmi_const *qc;
+ struct token num_tok;
+ struct token id_tok;
+
+ token_expect(TOK_ID, &id_tok);
+ token_expect('=', NULL);
+ token_expect(TOK_NUM, &num_tok);
+ token_expect(';', NULL);
+
+ qc = malloc(sizeof(struct qmi_const));
+ qc->name = id_tok.str;
+ qc->value = num_tok.num;
+ LIST_ADD(qmi_consts, qc);
+}
+
+static void qmi_const_header(FILE *fp)
+{
+ struct qmi_const *qc;
+
+ if (!qmi_consts.head)
+ return;
+
+ for (qc = qmi_consts.head; qc; qc = qc->next)
+ fprintf(fp, "#define %s %d\n", qc->name, qc->value);
+
+ fprintf(fp, "\n");
+}
+
+static void guard_header(FILE *fp, const char *package)
+{
+ char *upper;
+ char *p;
+
+ upper = p = strdup(package);
+ while (*p) {
+ *p = toupper(*p);
+ p++;
+ }
+
+ fprintf(fp, "#ifndef __QMI_%s_H__\n", upper);
+ fprintf(fp, "#define __QMI_%s_H__\n", upper);
+ fprintf(fp, "\n");
+}
+
+static void guard_footer(FILE *fp)
+{
+ fprintf(fp, "#endif\n");
+}
+
+int main(int argc, char **argv)
+{
+ const char *package = NULL;
+ struct token tok;
+ char fname[256];
+ FILE *hfp;
+ FILE *sfp;
+
+ /* PACKAGE ID<string> ';' */
+ /* CONST ID<string> '=' NUM<num> ';' */
+ /* STRUCT ID<string> '{' ... '}' ';' */
+ /* TYPE<type*> ID<string> ';' */
+ /* MESSAGE ID<string> '{' ... '}' ';' */
+ /* (REQUIRED | OPTIONAL) TYPE<type*> ID<string> '=' NUM<num> ';' */
+
+ symbol_add("const", TOK_CONST);
+ symbol_add("optional", TOK_OPTIONAL);
+ symbol_add("message", TOK_MESSAGE);
+ symbol_add("package", TOK_PACKAGE);
+ symbol_add("required", TOK_REQUIRED);
+ symbol_add("struct", TOK_STRUCT);
+ symbol_add("string", TOK_TYPE, TYPE_STRING);
+ symbol_add("u8", TOK_TYPE, TYPE_U8);
+ symbol_add("u16", TOK_TYPE, TYPE_U16);
+ symbol_add("u32", TOK_TYPE, TYPE_U32);
+ symbol_add("u64", TOK_TYPE, TYPE_U64);
+
+ token_init();
+ while (!token_accept(0, NULL)) {
+ if (token_accept(TOK_PACKAGE, NULL)) {
+ package = parse_package();
+ } else if (token_accept(TOK_CONST, NULL)) {
+ qmi_const_parse();
+ } else if (token_accept(TOK_STRUCT, NULL)) {
+ qmi_struct_parse();
+ } else if (token_accept(TOK_MESSAGE, &tok)) {
+ qmi_message_parse(tok.num);
+ } else {
+ yyerror("unexpected symbol");
+ break;
+ }
+ }
+
+ snprintf(fname, sizeof(fname), "qmi_%s.c", package);
+ sfp = fopen(fname, "w");
+ if (!sfp)
+ err(1, "failed to open %s", fname);
+
+ snprintf(fname, sizeof(fname), "qmi_%s.h", package);
+ hfp = fopen(fname, "w");
+ if (!hfp)
+ err(1, "failed to open %s", fname);
+
+ fprintf(sfp, "#include <errno.h>\n"
+ "#include <string.h>\n"
+ "#include \"qmi_%1$s.h\"\n\n",
+ package);
+ qmi_message_source(sfp, package);
+
+ guard_header(hfp, package);
+ fprintf(hfp, "#include <stdint.h>\n"
+ "#include <stdlib.h>\n\n");
+ fprintf(hfp, "struct qmi_tlv;\n"
+ "\n"
+ "struct qmi_tlv *qmi_tlv_init(unsigned txn, unsigned msg_id);\n"
+ "struct qmi_tlv *qmi_tlv_decode(void *buf, size_t len, unsigned *txn);\n"
+ "void *qmi_tlv_encode(struct qmi_tlv *tlv, size_t *len);\n"
+ "void qmi_tlv_free(struct qmi_tlv *tlv);\n"
+ "\n"
+ "void *qmi_tlv_get(struct qmi_tlv *tlv, unsigned id, size_t *len);\n"
+ "void *qmi_tlv_get_array(struct qmi_tlv *tlv, unsigned id, unsigned len_size, size_t *len, size_t *size);\n"
+ "int qmi_tlv_set(struct qmi_tlv *tlv, unsigned id, void *buf, size_t len);\n"
+ "int qmi_tlv_set_array(struct qmi_tlv *tlv, unsigned id, unsigned len_size, void *buf, size_t len, size_t size);\n"
+ "\n");
+ qmi_const_header(hfp);
+ qmi_struct_header(hfp, package);
+ qmi_message_header(hfp, package);
+ guard_footer(hfp);
+
+ fclose(hfp);
+ fclose(sfp);
+
+ return 0;
+}
diff --git a/qmic.h b/qmic.h
new file mode 100644
index 0000000..5e71259
--- /dev/null
+++ b/qmic.h
@@ -0,0 +1,71 @@
+#ifndef __QMIC_H__
+#define __QMIC_H__
+
+#include <stdbool.h>
+
+#define LIST_HEAD(type, name) \
+struct { \
+ struct type *head; \
+ struct type *tail; \
+} name;
+
+#define LIST_ADD(list, elem) \
+ if (list.tail) { \
+ list.tail->next = elem; \
+ list.tail = elem; \
+ } else { \
+ list.tail = elem; \
+ list.head = elem; \
+ }
+
+enum {
+ TOK_CONST = 256,
+ TOK_ID,
+ TOK_MESSAGE,
+ TOK_NUM,
+ TOK_PACKAGE,
+ TOK_STRUCT,
+ TOK_TYPE,
+ TOK_REQUIRED,
+ TOK_OPTIONAL,
+};
+
+enum {
+ TYPE_U8,
+ TYPE_U16,
+ TYPE_U32,
+ TYPE_U64,
+ TYPE_STRING,
+ TYPE_STRUCT
+};
+
+enum message_type {
+ MESSAGE_REQUEST,
+ MESSAGE_RESPONSE,
+ MESSAGE_INDICATION,
+};
+
+struct token {
+ int id;
+ const char *str;
+ unsigned num;
+ struct qmi_struct *qmi_struct;
+};
+
+void yyerror(const char *fmt, ...);
+
+void symbol_add(const char *name, int token, ...);
+
+int token_accept(int id, struct token *tok);
+void token_expect(int id, struct token *tok);
+
+void qmi_message_parse(enum message_type message_type);
+void qmi_message_source(FILE *fp, const char *package);
+void qmi_message_header(FILE *fp, const char *package);
+
+void qmi_struct_parse(void);
+void qmi_struct_header(FILE *fp, const char *package);
+void qmi_struct_emit_prototype(FILE *fp, const char *package, const char *message, const char *member, unsigned array_size, struct qmi_struct *qs);
+void qmi_struct_emit_accessors(FILE *fp, const char *package, const char *message, const char *member, int member_id, unsigned array_size, struct qmi_struct *qs);
+
+#endif