From 241fff829e1deb44d0774a9993b02d9b97b7d10f Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Sun, 7 Feb 2016 09:27:50 -0800 Subject: [PATCH] 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 --- Makefile | 17 +++ qmi_message.c | 344 ++++++++++++++++++++++++++++++++++++++++++++++ qmi_struct.c | 156 +++++++++++++++++++++ qmi_tlv.c | 142 +++++++++++++++++++ qmic.c | 372 ++++++++++++++++++++++++++++++++++++++++++++++++++ qmic.h | 71 ++++++++++ 6 files changed, 1102 insertions(+) create mode 100644 Makefile create mode 100644 qmi_message.c create mode 100644 qmi_struct.c create mode 100644 qmi_tlv.c create mode 100644 qmic.c create mode 100644 qmic.h 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 +#include +#include +#include +#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 +#include +#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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 ';' */ + /* CONST ID '=' NUM ';' */ + /* STRUCT ID '{' ... '}' ';' */ + /* TYPE ID ';' */ + /* MESSAGE ID '{' ... '}' ';' */ + /* (REQUIRED | OPTIONAL) TYPE ID '=' 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 \n" + "#include \n" + "#include \"qmi_%1$s.h\"\n\n", + package); + qmi_message_source(sfp, package); + + guard_header(hfp, package); + fprintf(hfp, "#include \n" + "#include \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 + +#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