mirror of
https://github.com/linux-msm/qmic.git
synced 2024-11-23 09:44:06 +08:00
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>
This commit is contained in:
parent
739fbdc423
commit
241fff829e
17
Makefile
Normal file
17
Makefile
Normal file
@ -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)
|
||||
|
344
qmi_message.c
Normal file
344
qmi_message.c
Normal file
@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
156
qmi_struct.c
Normal file
156
qmi_struct.c
Normal file
@ -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);
|
||||
}
|
||||
}
|
142
qmi_tlv.c
Normal file
142
qmi_tlv.c
Normal file
@ -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;
|
||||
}
|
||||
|
||||
|
372
qmic.c
Normal file
372
qmic.c
Normal file
@ -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;
|
||||
}
|
71
qmic.h
Normal file
71
qmic.h
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user