#include #include #include #include #include #include "tdc_store.h" #include "tdcproto.h" #include "variable_tab.h" #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) static struct tdc_var * var_map[256]; static GByteArray *stream; void get_varvalue(struct tdc_var *var, char *buf, int size, int viewmode) { switch ((var->flags & TDC_TYPEMASK) | ((viewmode & 0x01) << 8)) { case TDC_UNSIGNED: switch (var->flags & TDC_SIZEMASK) { case 1: snprintf(buf, size, "%u", var->data_uint8); break; case 2: snprintf(buf, size, "%u", var->data_uint16); break; case 4: snprintf(buf, size, "%u", var->data_uint32); break; case 8: snprintf(buf, size, "%llu", var->data_uint64); break; default: snprintf(buf, size, "???"); break; } break; case TDC_UNSIGNED | 0x100: switch (var->flags & TDC_SIZEMASK) { case 1: snprintf(buf, size, "0x%02x", var->data_uint8); break; case 2: snprintf(buf, size, "0x%04x", var->data_uint16); break; case 4: snprintf(buf, size, "0x%08x", var->data_uint32); break; case 8: snprintf(buf, size, "0x%16llx", var->data_uint64); break; default: snprintf(buf, size, "???"); break; } break; case TDC_SIGNED: switch (var->flags & TDC_SIZEMASK) { case 1: snprintf(buf, size, "%d", (int8_t)var->data_uint8); break; case 2: snprintf(buf, size, "%d", (int16_t)var->data_uint16); break; case 4: snprintf(buf, size, "%d", (int32_t)var->data_uint32); break; case 8: snprintf(buf, size, "%lld", (int64_t)var->data_uint64); break; default: snprintf(buf, size, "???"); break; } break; case TDC_SIGNED | 0x100: switch (var->flags & TDC_SIZEMASK) { case 1: snprintf(buf, size, "0x%02x", (int8_t)var->data_uint8); break; case 2: snprintf(buf, size, "0x%04x", (int16_t)var->data_uint16); break; case 4: snprintf(buf, size, "0x%08x", (int32_t)var->data_uint32); break; case 8: snprintf(buf, size, "0x%16llx", (int64_t)var->data_uint64); break; default: snprintf(buf, size, "???"); break; } break; case TDC_FP: case TDC_FP | 0x100: switch (var->flags & TDC_SIZEMASK) { case sizeof(float): snprintf(buf, size, "%.8f", var->data_float); break; case sizeof(double): snprintf(buf, size, "%.12f", var->data_double); break; default: snprintf(buf, size, "???"); break; } break; case TDC_FIXED: case TDC_FIXED | 0x100: snprintf(buf, size, "???"); break; } } void get_vartype(struct tdc_var *var, char *buf, int size) { int pos; int width = (var->flags & TDC_SIZEMASK); switch (var->flags & TDC_TYPEMASK) { case TDC_UNSIGNED: pos = snprintf(buf, size, "uint%d_t", width * 8); break; case TDC_SIGNED: pos = snprintf(buf, size, "int%d_t", width * 8); break; case TDC_FP: if (width == sizeof(float)) pos = snprintf(buf, size, "float"); else if (size == sizeof(double)) pos = snprintf(buf, size, "double"); else pos = snprintf(buf, size, "fp%d_t", width * 8); break; case TDC_FIXED: pos = snprintf(buf, size, "fix%d_t", width * 8); break; default: pos = snprintf(buf, size, "???"); break; } if (var->flags & TDC_READONLY) pos = snprintf(buf + pos, size - pos, " (ro)"); } static void destroy_var(int id) { g_free(var_map[id]); var_map[id] = NULL; } static void create_var(int id, uint32_t flags, char *name, int len) { id &= 0xFF; if (var_map[id] != NULL) destroy_var(id); var_map[id] = g_malloc0(sizeof(struct tdc_var) + len + 1); var_map[id]->id = id; var_map[id]->flags = flags; strncpy(var_map[id]->name, name, len); var_map[id]->name[len] = '\0'; vartab_add_var(var_map[id]); } static void update_var(int id, uint8_t *data, int len) { memcpy(&(var_map[id & 0xFF]->data_uint8), data, len); } #if 0 static char * stream_print(void) { int pos = 0, i = 0, j; char *buf = g_malloc(stream->len * 4 + 64); while (pos < stream->len) { i += sprintf(buf + i, "%04X: ", pos); for (j = 0; j < 16; j++) { if (pos + j < stream->len) i += sprintf(buf + i, "%02X", stream->data[pos + j]); else i += sprintf(buf + i, " "); if (j % 2) buf[i++] = ' '; } for (j = 0; j < 16; j++) { if (pos + j < stream->len) { unsigned char val = stream->data[pos + j]; if (val >= 0x20 && val < 0x80) buf[i++] = val; else buf[i++] = '.'; } else { buf[i++] = ' '; } } pos += 16; buf[i++] = '\r'; buf[i++] = '\n'; } buf[i] = 0; return buf; } #endif static int parse_size_helper(int available, int packet_size, int expected_base_size, int packet_data_size, const char *name) { /* not enough data in buffer */ if (available < expected_base_size) { printf("received incomplete %s\n", name); return 0; } /* retest, after we know that packet_data_size is valid */ if (available < (expected_base_size + packet_data_size)) { printf("received incomplete %s\n", name); return 0; } /* packet has wrong size */ if (packet_size != (expected_base_size + packet_data_size)) { printf("received invalid %s\n", name); return -1; } // printf("received %s\n", name); return packet_size; } static int parse(int fd) { int address = (stream->data[0] & TDC_ADDRMASK) >> 4; int remove = 1; switch (stream->data[0] & (TDC_REPLY | TDC_OPCODEMASK)) { case TDC_HELLO: { struct tdc_pkt_header *pkt = (struct tdc_pkt_header *)stream->data; remove = parse_size_helper(stream->len, pkt->size, sizeof(*pkt), 0, "TDC_HELLO"); /* do nothing, we'll never receive TDC_HELLOs */ } break; case (TDC_HELLO | TDC_REPLY): { struct tdc_hello_reply *pkt = (struct tdc_hello_reply *)stream->data; remove = parse_size_helper(stream->len, pkt->size, sizeof(*pkt), 0, "TDC_HELLO REPLY"); if (remove <= 0) break; printf("ADDR%d: '%32s'\n", address, pkt->name); struct tdc_pkt_header pkt2; pkt2.cmd = (address << 4) | TDC_GETVARS; pkt2.size = sizeof(pkt2); write(fd, &pkt2, pkt2.size); } break; case TDC_GETVARS: { struct tdc_pkt_header *pkt = (struct tdc_pkt_header *)stream->data; remove = parse_size_helper(stream->len, pkt->size, sizeof(*pkt), 0, "TDC_GETVARS"); /* do nothing, we'll never receive TDC_GETVARSs */ } break; case (TDC_GETVARS | TDC_REPLY): { struct tdc_getvars_reply *pkt = (struct tdc_getvars_reply *)stream->data; remove = parse_size_helper(stream->len, pkt->size, sizeof(*pkt), pkt->name_len, "TDC_GETVARS REPLY"); if (remove <= 0) break; create_var(pkt->id, pkt->flags, pkt->name, pkt->name_len); printf("create_var(%d, 0x%x, '%s', %d)\n", pkt->id, var_map[pkt->id]->flags, var_map[pkt->id]->name, pkt->name_len); } break; case TDC_GETVALUE: { struct tdc_getvalue_request *pkt = (struct tdc_getvalue_request *)stream->data; remove = parse_size_helper(stream->len, pkt->size, sizeof(*pkt), 0, "TDC_GETVALUE"); /* do nothing, we'll never receive TDC_GETVALUEs */ } break; case (TDC_GETVALUE | TDC_REPLY): { struct tdc_getvalue_reply *pkt = (struct tdc_getvalue_reply *)stream->data; /* check base packet */ remove = parse_size_helper(stream->len, pkt->size, sizeof(*pkt), 0, "TDC_GETVALUE REPLY"); if (remove <= 0) break; if (var_map[pkt->id & 0xFF] == NULL) { printf("unknown variable %d\n", pkt->id); break; } int varsize = (var_map[pkt->id & 0xFF]->flags & TDC_SIZEMASK); remove = parse_size_helper(stream->len, pkt->size, sizeof(*pkt), varsize, "TDC_GETVALUE REPLY"); // TODO: adjust remove on error (valid reply with invalid data size) update_var(pkt->id, pkt->data, varsize); } break; case TDC_SETVALUE: { struct tdc_setvalue_request *pkt = (struct tdc_setvalue_request *)stream->data; remove = parse_size_helper(stream->len, pkt->size, sizeof(*pkt), 0, "TDC_SETVALUE"); if (remove <= 0) break; if (var_map[pkt->id & 0xFF] == NULL) { printf("unknown variable %d\n", pkt->id); break; } int varsize = (var_map[pkt->id & 0xFF]->flags & TDC_SIZEMASK); remove = parse_size_helper(stream->len, pkt->size, sizeof(*pkt), varsize, "TDC_GETVALUE REPLY"); // TODO: adjust remove on error (valid reply with invalid data size) /* do nothing, we'll never receive TDC_SETVALUEs */ } break; case (TDC_SETVALUE | TDC_REPLY): break; case TDC_REQVALUES: { struct tdc_reqvalues_request *pkt = (struct tdc_reqvalues_request *)stream->data; remove = parse_size_helper(stream->len, pkt->size, sizeof(*pkt), 0, "TDC_REQVALUES"); /* do nothing, we'll never receive TDC_REQVALUESs */ } break; case (TDC_REQVALUES | TDC_REPLY): { struct tdc_reqvalues_reply *pkt = (struct tdc_reqvalues_reply *)stream->data; remove = parse_size_helper(stream->len, pkt->size, sizeof(*pkt), 0, "TDC_REQVALUES"); if (remove <= 0) break; // TODO: output pkt->timestamp, pkt->cnt } break; case TDC_TERMINAL: case (TDC_TERMINAL | TDC_REPLY): break; } if (remove != 0) g_byte_array_remove_range(stream, 0, (remove > 0) ? remove : -remove); return remove; } int tdc_parse_data(int fd) { char buf[256]; int len = read(fd, buf, sizeof(buf)); if (len > 0) { g_byte_array_append(stream, (guint8 *)buf, len); while (stream->len >= 2) { if (parse(fd) == 0) break; } } return len; } void tdc_flush_vars(void) { int i; for (i = 0; i < ARRAY_SIZE(var_map); i++) { if (var_map[i] == NULL) continue; destroy_var(i); } if (stream->len) g_byte_array_remove_range(stream, 0, stream->len); } void tdc_send_hello(int fd) { int addr; for (addr = 1; addr < 8; addr++) { struct tdc_pkt_header pkt; pkt.cmd = (addr << 4) | TDC_HELLO; pkt.size = sizeof(pkt); write(fd, &pkt, pkt.size); } } void tdc_init(void) { stream = g_byte_array_new(); }