aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--wiretap/CMakeLists.txt4
-rw-r--r--wiretap/busmaster.c446
-rw-r--r--wiretap/busmaster.h20
-rw-r--r--wiretap/busmaster_parser.lemon453
-rw-r--r--wiretap/busmaster_priv.h136
-rw-r--r--wiretap/busmaster_scanner.l198
-rw-r--r--wiretap/candump_priv.h22
-rw-r--r--wiretap/file_access.c2
-rw-r--r--wiretap/socketcan.h38
9 files changed, 1298 insertions, 21 deletions
diff --git a/wiretap/CMakeLists.txt b/wiretap/CMakeLists.txt
index 319e677f93..720aab05ab 100644
--- a/wiretap/CMakeLists.txt
+++ b/wiretap/CMakeLists.txt
@@ -24,6 +24,7 @@ set(WIRETAP_NONGENERATED_FILES
atm.c
ber.c
btsnoop.c
+ busmaster.c
camins.c
candump.c
capsa.c
@@ -89,6 +90,7 @@ set(WIRETAP_FILES ${WIRETAP_NONGENERATED_FILES})
add_lex_files(LEX_FILES WIRETAP_FILES
ascend_scanner.l
+ busmaster_scanner.l
candump_scanner.l
k12text.l
)
@@ -98,6 +100,7 @@ add_yacc_files(YACC_FILES WIRETAP_FILES
)
add_lemon_files(LEMON_FILES WIRETAP_FILES
+ busmaster_parser.lemon
candump_parser.lemon
)
@@ -160,6 +163,7 @@ CHECKAPI(
# LEX files commented out due to use of malloc, free etc.
# ${LEX_FILES}
${YACC_FILES}
+ ${LEMON_FILES}
)
#
diff --git a/wiretap/busmaster.c b/wiretap/busmaster.c
new file mode 100644
index 0000000000..be0e96868a
--- /dev/null
+++ b/wiretap/busmaster.c
@@ -0,0 +1,446 @@
+/* busmaster.c
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Support for Busmaster log file format
+ * Copyright (c) 2019 by Maksim Salau <maksim.salau@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include <wtap-int.h>
+#include <file_wrappers.h>
+#include <epan/exported_pdu.h>
+#include <epan/dissectors/packet-socketcan.h>
+#include "busmaster.h"
+#include "busmaster_priv.h"
+#include <inttypes.h>
+#include <string.h>
+#include <errno.h>
+
+static void
+busmaster_close(wtap *wth);
+
+static gboolean
+busmaster_read(wtap *wth, wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info,
+ gint64 *data_offset);
+
+static gboolean
+busmaster_seek_read(wtap *wth, gint64 seek_off,
+ wtap_rec *rec, Buffer *buf,
+ int *err, gchar **err_info);
+
+static gboolean
+busmaster_gen_packet(wtap_rec *rec, Buffer *buf,
+ const busmaster_priv_t *priv_entry, const msg_t *msg,
+ int *err, gchar **err_info)
+{
+ time_t secs = 0;
+ guint32 nsecs = 0;
+ gboolean has_ts = FALSE;
+ gboolean is_fd = (msg->type == MSG_TYPE_STD_FD)
+ || (msg->type == MSG_TYPE_EXT_FD);
+ gboolean is_eff = (msg->type == MSG_TYPE_EXT)
+ || (msg->type == MSG_TYPE_EXT_RTR)
+ || (msg->type == MSG_TYPE_EXT_FD);
+ gboolean is_rtr = (msg->type == MSG_TYPE_STD_RTR)
+ || (msg->type == MSG_TYPE_EXT_RTR);
+ gboolean is_err = (msg->type == MSG_TYPE_ERR);
+
+ static const char *const can_proto_name = "can-hostendian";
+ static const char *const canfd_proto_name = "canfd";
+
+ const char *proto_name = is_fd ? canfd_proto_name : can_proto_name;
+ guint proto_name_length = (guint)strlen(proto_name) + 1;
+ guint header_length;
+ guint packet_length;
+ guint frame_length;
+ guint8 *buf_data;
+
+ /* Adjust proto name length to be aligned on 4 byte boundary */
+ proto_name_length += (proto_name_length % 4) ? (4 - (proto_name_length % 4)) : 0;
+
+ header_length = 4 + proto_name_length + 4;
+ frame_length = is_fd ? sizeof(canfd_frame_t) : sizeof(can_frame_t);
+ packet_length = header_length + frame_length;
+
+ ws_buffer_clean(buf);
+ ws_buffer_assure_space(buf, packet_length);
+ buf_data = ws_buffer_start_ptr(buf);
+
+ memset(buf_data, 0, packet_length);
+
+ buf_data[1] = EXP_PDU_TAG_PROTO_NAME;
+ buf_data[3] = proto_name_length;
+ memcpy(buf_data + 4, proto_name, strlen(proto_name));
+
+ if (!priv_entry)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("Header is missing");
+ return FALSE;
+ }
+
+ if (is_fd)
+ {
+ canfd_frame_t canfd_frame;
+
+ memset(&canfd_frame, 0, sizeof(canfd_frame));
+ canfd_frame.can_id = (msg->id & (is_eff ? CAN_EFF_MASK : CAN_SFF_MASK)) |
+ (is_eff ? CAN_EFF_FLAG : 0) |
+ (is_err ? CAN_ERR_FLAG : 0);
+ canfd_frame.flags = 0;
+ canfd_frame.len = msg->data.length;
+
+ memcpy(canfd_frame.data,
+ msg->data.data,
+ MIN(msg->data.length, sizeof(canfd_frame.data)));
+
+ memcpy(buf_data + header_length,
+ (guint8 *)&canfd_frame,
+ sizeof(canfd_frame));
+ }
+ else
+ {
+ can_frame_t can_frame;
+
+ memset(&can_frame, 0, sizeof(can_frame));
+ can_frame.can_id = (msg->id & (is_eff ? CAN_EFF_MASK : CAN_SFF_MASK)) |
+ (is_rtr ? CAN_RTR_FLAG : 0) |
+ (is_eff ? CAN_EFF_FLAG : 0) |
+ (is_err ? CAN_ERR_FLAG : 0);
+ can_frame.can_dlc = msg->data.length;
+
+ memcpy(can_frame.data,
+ msg->data.data,
+ MIN(msg->data.length, sizeof(can_frame.data)));
+
+ memcpy(buf_data + header_length,
+ (guint8 *)&can_frame,
+ sizeof(can_frame));
+ }
+
+ if (priv_entry->time_mode == TIME_MODE_SYSTEM)
+ {
+ struct tm tm;
+
+ tm.tm_year = priv_entry->start_date.year - 1900;
+ tm.tm_mon = priv_entry->start_date.month - 1;
+ tm.tm_mday = priv_entry->start_date.day;
+ tm.tm_hour = msg->timestamp.hours;
+ tm.tm_min = msg->timestamp.minutes;
+ tm.tm_sec = msg->timestamp.seconds;
+
+ secs = mktime(&tm);
+ nsecs = msg->timestamp.micros * 1000u;
+ has_ts = TRUE;
+ }
+ else if (priv_entry->time_mode == TIME_MODE_ABSOLUTE)
+ {
+ struct tm tm;
+ guint32 micros;
+
+ tm.tm_year = priv_entry->start_date.year - 1900;
+ tm.tm_mon = priv_entry->start_date.month - 1;
+ tm.tm_mday = priv_entry->start_date.day;
+ tm.tm_hour = priv_entry->start_time.hours;
+ tm.tm_min = priv_entry->start_time.minutes;
+ tm.tm_sec = priv_entry->start_time.seconds;
+
+ secs = mktime(&tm);
+
+ secs += msg->timestamp.hours * 3600;
+ secs += msg->timestamp.minutes * 60;
+ secs += msg->timestamp.seconds;
+
+ micros = priv_entry->start_time.micros + msg->timestamp.micros;
+ if (micros >= 1000000u)
+ {
+ micros -= 1000000u;
+ secs += 1;
+ }
+
+ nsecs = micros * 1000u;
+ has_ts = TRUE;
+ }
+
+ rec->rec_type = REC_TYPE_PACKET;
+ rec->presence_flags = has_ts ? WTAP_HAS_TS : 0;
+ rec->ts.secs = secs;
+ rec->ts.nsecs = nsecs;
+
+ rec->rec_header.packet_header.caplen = packet_length;
+ rec->rec_header.packet_header.len = packet_length;
+
+ return TRUE;
+}
+
+static log_entry_type_t
+busmaster_parse(FILE_T fh, busmaster_state_t *state, int *err, char **err_info)
+{
+ gboolean ok;
+ gint64 seek_off;
+
+ busmaster_debug_printf("%s: Running busmaster file decoder\n", G_STRFUNC);
+
+ state->fh = fh;
+
+ do
+ {
+ if (file_eof(fh))
+ return LOG_ENTRY_EOF;
+
+ seek_off = file_tell(fh);
+ busmaster_debug_printf("%s: Starting parser at offset %" PRIi64 "\n",
+ G_STRFUNC, seek_off);
+ state->file_bytes_read = 0;
+ ok = run_busmaster_parser(state, err, err_info);
+
+ /* Rewind the file to the offset we have finished parsing */
+ busmaster_debug_printf("%s: Rewinding to offset %" PRIi64 "\n",
+ G_STRFUNC, seek_off + state->file_bytes_read);
+ if (file_seek(fh, seek_off + state->file_bytes_read, SEEK_SET, err) == -1)
+ {
+ g_free(*err_info);
+ *err = errno;
+ *err_info = g_strdup(g_strerror(errno));
+ return LOG_ENTRY_ERROR;
+ }
+ }
+ while (ok && state->entry_type == LOG_ENTRY_NONE);
+
+ if (!ok)
+ return LOG_ENTRY_ERROR;
+
+ busmaster_debug_printf("%s: Success\n", G_STRFUNC);
+
+ return state->entry_type;
+}
+
+wtap_open_return_val
+busmaster_open(wtap *wth, int *err, char **err_info)
+{
+ busmaster_state_t state;
+ log_entry_type_t entry;
+
+ busmaster_debug_printf("%s: Trying to open with busmaster log reader\n",
+ G_STRFUNC);
+
+ /* Rewind to the beginning */
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ memset(&state, 0, sizeof(state));
+ entry = busmaster_parse(wth->fh, &state, err, err_info);
+
+ g_free(*err_info);
+ *err_info = NULL;
+ *err = 0;
+
+ if (entry != LOG_ENTRY_HEADER)
+ return WTAP_OPEN_NOT_MINE;
+
+ /* Rewind to the beginning, so busmaster_read may read from the very beginning */
+ if (file_seek(wth->fh, 0, SEEK_SET, err) == -1)
+ return WTAP_OPEN_ERROR;
+
+ busmaster_debug_printf("%s: That's a busmaster log\n", G_STRFUNC);
+
+ wth->priv = NULL;
+ wth->subtype_close = busmaster_close;
+ wth->subtype_read = busmaster_read;
+ wth->subtype_seek_read = busmaster_seek_read;
+ wth->file_type_subtype = WTAP_FILE_TYPE_SUBTYPE_UNKNOWN;
+ wth->file_encap = WTAP_ENCAP_WIRESHARK_UPPER_PDU;
+ wth->file_tsprec = WTAP_TSPREC_USEC;
+
+ return WTAP_OPEN_MINE;
+}
+
+static void
+busmaster_close(wtap *wth)
+{
+ busmaster_debug_printf("%s\n", G_STRFUNC);
+
+ g_slist_free_full((GSList *)wth->priv, g_free);
+ wth->priv = NULL;
+}
+
+static busmaster_priv_t *
+busmaster_find_priv_entry(void *priv, gint64 offset)
+{
+ GSList *list;
+
+ for (list = (GSList *)priv; list; list = g_slist_next(list))
+ {
+ busmaster_priv_t *entry = (busmaster_priv_t *)list->data;
+
+ if (((entry->file_end_offset == -1)
+ && (g_slist_next(list) == NULL))
+ || ((offset >= entry->file_start_offset)
+ && (offset <= entry->file_end_offset)))
+ {
+ return entry;
+ }
+ }
+
+ return NULL;
+}
+
+static gboolean
+busmaster_read(wtap *wth, wtap_rec *rec, Buffer *buf, int *err, gchar **err_info,
+ gint64 *data_offset)
+{
+ log_entry_type_t entry;
+ busmaster_state_t state;
+ busmaster_priv_t *priv_entry;
+ gboolean is_msg = FALSE;
+ gboolean is_ok = TRUE;
+
+ while (!is_msg && is_ok)
+ {
+ busmaster_debug_printf("%s: offset = %" PRIi64 "\n",
+ G_STRFUNC, file_tell(wth->fh));
+
+ if (file_eof(wth->fh))
+ {
+ busmaster_debug_printf("%s: End of file detected, nothing to do here\n",
+ G_STRFUNC);
+ *err = 0;
+ *err_info = NULL;
+ return FALSE;
+ }
+
+ *data_offset = file_tell(wth->fh);
+ priv_entry = busmaster_find_priv_entry(wth->priv, *data_offset);
+
+ memset(&state, 0, sizeof(state));
+ if (priv_entry)
+ state.header = *priv_entry;
+ entry = busmaster_parse(wth->fh, &state, err, err_info);
+
+ busmaster_debug_printf("%s: analyzing output\n", G_STRFUNC);
+ switch (entry)
+ {
+ case LOG_ENTRY_EMPTY:
+ break;
+ case LOG_ENTRY_FOOTER_AND_HEADER:
+ case LOG_ENTRY_FOOTER:
+ priv_entry = (busmaster_priv_t *)g_slist_last((GSList *)wth->priv)->data;
+ if (!priv_entry)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("Header is missing");
+ return FALSE;
+ }
+ priv_entry->file_end_offset = *data_offset;
+ if (entry == LOG_ENTRY_FOOTER)
+ break;
+ /* fall-through */
+ case LOG_ENTRY_HEADER:
+ if (state.header.protocol != PROTOCOL_CAN &&
+ state.header.protocol != PROTOCOL_J1939)
+ {
+ *err = WTAP_ERR_UNSUPPORTED;
+ *err_info = g_strdup("Unsupported protocol type");
+ return FALSE;
+ }
+
+ if (wth->priv)
+ {
+ /* Check that the previous section has a footer */
+ priv_entry = (busmaster_priv_t *)g_slist_last((GSList *)wth->priv)->data;
+
+ if (priv_entry && priv_entry->file_end_offset == -1)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("Footer is missing");
+ return FALSE;
+ }
+ }
+
+ /* Start a new section */
+ priv_entry = g_new(busmaster_priv_t, 1);
+
+ priv_entry[0] = state.header;
+ priv_entry->file_start_offset = file_tell(wth->fh);
+ priv_entry->file_end_offset = -1;
+
+ wth->priv = g_slist_append((GSList *)wth->priv, priv_entry);
+ break;
+ case LOG_ENTRY_MSG:
+ is_msg = TRUE;
+ priv_entry = busmaster_find_priv_entry(wth->priv, *data_offset);
+ is_ok = busmaster_gen_packet(rec, buf, priv_entry, &state.msg, err, err_info);
+ break;
+ case LOG_ENTRY_EOF:
+ case LOG_ENTRY_ERROR:
+ case LOG_ENTRY_NONE:
+ default:
+ is_ok = FALSE;
+ break;
+ }
+ }
+
+ busmaster_debug_printf("%s: stopped at offset %" PRIi64 " with entry %d\n",
+ G_STRFUNC, file_tell(wth->fh), entry);
+
+ return is_ok;
+}
+
+static gboolean
+busmaster_seek_read(wtap *wth, gint64 seek_off, wtap_rec *rec,
+ Buffer *buf, int *err, gchar **err_info)
+{
+ busmaster_priv_t *priv_entry;
+ busmaster_state_t state;
+ log_entry_type_t entry;
+
+ busmaster_debug_printf("%s: offset = %" PRIi64 "\n", G_STRFUNC, seek_off);
+
+ priv_entry = busmaster_find_priv_entry(wth->priv, seek_off);
+ if (!priv_entry)
+ {
+ busmaster_debug_printf("%s: analyzing output\n", G_STRFUNC);
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("Malformed header");
+ return FALSE;
+ }
+
+ if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
+ return FALSE;
+
+ memset(&state, 0, sizeof(state));
+ state.header = *priv_entry;
+ entry = busmaster_parse(wth->random_fh, &state, err, err_info);
+
+ busmaster_debug_printf("%s: analyzing output\n", G_STRFUNC);
+
+ if (entry == LOG_ENTRY_ERROR || entry == LOG_ENTRY_NONE)
+ return FALSE;
+
+ if (entry != LOG_ENTRY_MSG)
+ {
+ *err = WTAP_ERR_BAD_FILE;
+ *err_info = g_strdup("Failed to read a frame");
+ return FALSE;
+ }
+
+ return busmaster_gen_packet(rec, buf, priv_entry, &state.msg, err, err_info);
+}
+
+/*
+ * Editor modelines - https://www.wireshark.org/tools/modelines.html
+ *
+ * Local variables:
+ * c-basic-offset: 4
+ * tab-width: 8
+ * indent-tabs-mode: nil
+ * End:
+ *
+ * vi: set shiftwidth=4 tabstop=8 expandtab:
+ * :indentSize=4:tabSize=8:noTabs=true:
+ */
diff --git a/wiretap/busmaster.h b/wiretap/busmaster.h
new file mode 100644
index 0000000000..59f5b22645
--- /dev/null
+++ b/wiretap/busmaster.h
@@ -0,0 +1,20 @@
+/* busmaster.h
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Support for Busmaster log file format
+ * Copyright (c) 2019 by Maksim Salau <maksim.salau@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef BUSMASTER_H__
+#define BUSMASTER_H__
+
+#include <wiretap/wtap.h>
+
+wtap_open_return_val
+busmaster_open(wtap *wth, int *err, char **err_info);
+
+#endif /* BUSMASTER_H__ */
diff --git a/wiretap/busmaster_parser.lemon b/wiretap/busmaster_parser.lemon
new file mode 100644
index 0000000000..cabe11286f
--- /dev/null
+++ b/wiretap/busmaster_parser.lemon
@@ -0,0 +1,453 @@
+%include {
+
+/* busmaster_parser.lemon
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Support for Busmaster log file format
+ * Copyright (c) 2019 by Maksim Salau <maksim.salau@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#include "config.h"
+#include <assert.h>
+#include <string.h>
+#include <wiretap/file_wrappers.h>
+#include "busmaster_priv.h"
+
+extern void *BusmasterParserAlloc(void *(*mallocProc)(size_t));
+extern void BusmasterParser(void *yyp, int yymajor, token_t yyminor, busmaster_state_t *state);
+extern void BusmasterParserFree(void *p, void (*freeProc)(void*));
+
+#if defined(BUSMASTER_DEBUG) || defined(BUSMASTER_PARSER_TRACE)
+extern void BusmasterParserTrace(FILE *TraceFILE, char *zTracePrompt);
+#undef NDEBUG
+#endif
+
+static void merge_msg_data(msg_data_t *dst, const msg_data_t *a, const msg_data_t *b)
+{
+ dst->length = a->length + b->length;
+ memcpy(&dst->data[0], &a->data[0], a->length);
+ memcpy(&dst->data[a->length], &b->data[0], b->length);
+}
+
+DIAG_OFF(unreachable-code)
+
+}
+
+%name BusmasterParser
+
+%token_prefix TOKEN_
+
+%token_type { token_t }
+
+%token_destructor
+{
+ (void)state;
+ (void)yypParser;
+ (void)yypminor;
+}
+
+%extra_argument { busmaster_state_t* state }
+
+%syntax_error
+{
+ (void)yypParser;
+ (void)yyminor;
+
+#ifdef BUSMASTER_DEBUG
+ const int n = sizeof(yyTokenName) / sizeof(yyTokenName[0]);
+ busmaster_debug_printf("%s: got token: %s\n", G_STRFUNC, yyTokenName[yymajor]);
+ for (int i = 0; i < n; ++i) {
+ int a = yy_find_shift_action((YYCODETYPE)i, yypParser->yytos->stateno);
+ if (a < YYNSTATE + YYNRULE) {
+ busmaster_debug_printf("%s: possible token: %s\n", G_STRFUNC, yyTokenName[i]);
+ }
+ }
+#endif
+
+ g_free(state->parse_error);
+ state->entry_type = LOG_ENTRY_ERROR;
+ state->parse_error = g_strdup_printf("Syntax Error");
+ busmaster_debug_printf("%s: Syntax Error\n", G_STRFUNC);
+}
+
+%parse_failure
+{
+ g_free(state->parse_error);
+ state->entry_type = LOG_ENTRY_ERROR;
+ state->parse_error = g_strdup("Parse Error");
+ busmaster_debug_printf("%s: Parse Error\n", G_STRFUNC);
+}
+
+%stack_overflow
+{
+ g_free(state->parse_error);
+ state->entry_type = LOG_ENTRY_ERROR;
+ state->parse_error = g_strdup("Parser stack overflow");
+ busmaster_debug_printf("%s: Parser stack overflow\n", G_STRFUNC);
+}
+
+%type msg_time { msg_time_t }
+%type msg_type { msg_type_t }
+%type err_msg_type { msg_type_t }
+%type msg_length { guint }
+%type msg_id { guint32 }
+
+%type ref_date { msg_date_t }
+%type ref_time { msg_time_t }
+
+%type start_time { msg_date_time_t }
+
+%type byte { guint8 }
+%type data { msg_data_t }
+%type data0 { msg_data_t }
+%type data1 { msg_data_t }
+%type data2 { msg_data_t }
+%type data3 { msg_data_t }
+%type data4 { msg_data_t }
+%type data5 { msg_data_t }
+%type data6 { msg_data_t }
+%type data7 { msg_data_t }
+%type data8 { msg_data_t }
+%type data12 { msg_data_t }
+%type data16 { msg_data_t }
+%type data20 { msg_data_t }
+%type data24 { msg_data_t }
+%type data32 { msg_data_t }
+%type data48 { msg_data_t }
+%type data64 { msg_data_t }
+
+%nonassoc INVALID_CHAR .
+%nonassoc INVALID_NUMBER .
+
+%start_symbol entry
+
+entry ::= empty_line .
+entry ::= footer_and_header .
+entry ::= header .
+entry ::= footer .
+entry ::= msg .
+entry ::= err_msg .
+entry ::= j1939_msg .
+
+empty_line ::= .
+{
+ busmaster_debug_printf("%s: EMPTY\n", G_STRFUNC);
+ state->entry_type = LOG_ENTRY_EMPTY;
+}
+
+footer_and_header ::= footer ENDL header .
+{
+ busmaster_debug_printf("%s: FOOTER AND HEADER\n", G_STRFUNC);
+ state->entry_type = LOG_ENTRY_FOOTER_AND_HEADER;
+}
+
+header ::= version ENDL maybe_lines
+ PROTOCOL_TYPE(P) ENDL maybe_lines
+ START_SESSION ENDL maybe_lines
+ start_time(S) ENDL maybe_lines
+ DATA_MODE(D) ENDL maybe_lines
+ TIME_MODE(T) ENDL anything .
+{
+ busmaster_debug_printf("%s: HEADER\n", G_STRFUNC);
+
+ state->entry_type = LOG_ENTRY_HEADER;
+ state->header.start_date = S.date;
+ state->header.start_time = S.time;
+ state->header.protocol = (protocol_t)P.v0;
+ state->header.data_mode = (data_mode_t)D.v0;
+ state->header.time_mode = (time_mode_t)T.v0;
+}
+
+version ::= HEADER_VER maybe_chars .
+
+maybe_chars ::= .
+maybe_chars ::= maybe_chars HEADER_CHAR .
+
+maybe_lines ::= .
+maybe_lines ::= maybe_lines maybe_chars ENDL .
+
+anything ::= .
+anything ::= anything HEADER_CHAR .
+anything ::= anything ENDL .
+
+start_time(R) ::= START_TIME ref_date(D) ref_time(T) .
+{
+ R.date = D;
+ R.time = T;
+}
+
+footer ::= end_time ENDL STOP_SESSION .
+{
+ busmaster_debug_printf("%s: FOOTER\n", G_STRFUNC);
+ state->entry_type = LOG_ENTRY_FOOTER;
+}
+
+end_time ::= END_TIME ref_date ref_time .
+
+/* <Time><Tx/Rx><Channel><CAN ID><Type><DLC><DataBytes> */
+msg ::= msg_time(msg_time) MSG_DIR INT msg_id(msg_id) msg_type(msg_type) msg_length(msg_length) data(msg_data) .
+{
+ msg_t msg;
+
+ /* DLC is always in DEC mode, thus we need to fix the value
+ * if it was read initially as HEX. */
+ if (state->header.data_mode == DATA_MODE_HEX)
+ {
+ msg_length = (msg_length / 16) * 10 + (msg_length % 16);
+ }
+
+ /* Fix data in RTR frames. Data may not be present,
+ * but length field is set. */
+ if (msg_type == MSG_TYPE_STD_RTR ||
+ msg_type == MSG_TYPE_EXT_RTR)
+ {
+ memset(&msg_data, 0, sizeof(msg_data));
+ msg_data.length = msg_length;
+ }
+
+ msg.timestamp = msg_time;
+ msg.id = msg_id;
+ msg.type = msg_type;
+ msg.data = msg_data;
+
+ busmaster_debug_printf("%s: MSG\n", G_STRFUNC);
+
+ state->msg = msg;
+ state->entry_type = LOG_ENTRY_MSG;
+}
+
+/* <Time><Tx/Rx><Channel><CAN ID><Type><Text> */
+err_msg ::= msg_time(msg_time) MSG_DIR INT INT err_msg_type(msg_type) .
+{
+ msg_t msg;
+
+ msg.timestamp = msg_time;
+ msg.id = 0;
+ msg.type = msg_type;
+ msg.data.length = CAN_MAX_DLEN;
+
+ memset(msg.data.data, 0, sizeof(msg.data.data));
+
+ busmaster_debug_printf("%s: ERR MSG\n", G_STRFUNC);
+
+ state->msg = msg;
+ state->entry_type = LOG_ENTRY_MSG;
+}
+
+/* <Time><Channel><CAN ID><PGN><Type><Source Node><Destination Node><Priority><Tx/Rx><DLC><DataBytes> */
+j1939_msg ::= msg_time(msg_time) INT msg_id(msg_id) INT J1939_MSG_TYPE INT INT INT MSG_DIR msg_length data(msg_data) .
+{
+ msg_t msg;
+
+ msg.timestamp = msg_time;
+ msg.id = msg_id;
+ msg.type = MSG_TYPE_EXT;
+ msg.data = msg_data;
+
+ busmaster_debug_printf("%s: J1939 MSG\n", G_STRFUNC);
+
+ state->msg = msg;
+ state->entry_type = LOG_ENTRY_MSG;
+}
+
+ref_date(R) ::= INT(D) COLON INT(M) COLON INT(Y) .
+{
+ R.year = (guint)Y.v0;
+ R.month = (guint)M.v0;
+ R.day = (guint)D.v0;
+}
+
+ref_time(R) ::= INT(H) COLON INT(M) COLON INT(S) COLON INT(U) .
+{
+ R.hours = (guint)H.v0;
+ R.minutes = (guint)M.v0;
+ R.seconds = (guint)S.v0;
+ R.micros = (guint)U.v0 * 1000;
+}
+
+msg_time(R) ::= MSG_TIME(M) .
+{
+ R.hours = (guint)M.v0;
+ R.minutes = (guint)M.v1;
+ R.seconds = (guint)M.v2;
+ R.micros = (guint)M.v3 * 100;
+}
+
+msg_id(R) ::= INT(V) .
+{
+ R = (guint)V.v0;
+}
+
+msg_length(R) ::= INT(V) .
+{
+ R = (guint)V.v0;
+}
+
+msg_type(R) ::= MSG_TYPE(V) .
+{
+ R = (msg_type_t)V.v0;
+}
+
+err_msg_type(R) ::= ERR_MSG_TYPE(V) .
+{
+ R = (msg_type_t)V.v0;
+}
+
+data(R) ::= data0(A) . { R = A; }
+data(R) ::= data1(A) . { R = A; }
+data(R) ::= data2(A) . { R = A; }
+data(R) ::= data3(A) . { R = A; }
+data(R) ::= data4(A) . { R = A; }
+data(R) ::= data5(A) . { R = A; }
+data(R) ::= data6(A) . { R = A; }
+data(R) ::= data7(A) . { R = A; }
+data(R) ::= data8(A) . { R = A; }
+data(R) ::= data12(A) . { R = A; }
+data(R) ::= data16(A) . { R = A; }
+data(R) ::= data20(A) . { R = A; }
+data(R) ::= data24(A) . { R = A; }
+data(R) ::= data32(A) . { R = A; }
+data(R) ::= data48(A) . { R = A; }
+data(R) ::= data64(A) . { R = A; }
+
+byte(R) ::= INT(A) .
+{
+ R = (guint8)A.v0;
+}
+
+data0(R) ::= .
+{
+ R.length = 0;
+}
+
+data1(R) ::= byte(A) .
+{
+ R.length = 1;
+ R.data[0] = A;
+}
+
+data2(R) ::= byte(A) byte(B) .
+{
+ R.length = 2;
+ R.data[0] = A;
+ R.data[1] = B;
+}
+
+data3(R) ::= byte(A) byte(B) byte(C) .
+{
+ R.length = 3;
+ R.data[0] = A;
+ R.data[1] = B;
+ R.data[2] = C;
+}
+
+data4(R) ::= byte(A) byte(B) byte(C) byte(D) .
+{
+ R.length = 4;
+ R.data[0] = A;
+ R.data[1] = B;
+ R.data[2] = C;
+ R.data[3] = D;
+}
+
+data5(R) ::= data4(A) data1(B) . { merge_msg_data(&R, &A, &B); }
+data6(R) ::= data4(A) data2(B) . { merge_msg_data(&R, &A, &B); }
+data7(R) ::= data4(A) data3(B) . { merge_msg_data(&R, &A, &B); }
+data8(R) ::= data4(A) data4(B) . { merge_msg_data(&R, &A, &B); }
+data12(R) ::= data8(A) data4(B) . { merge_msg_data(&R, &A, &B); }
+data16(R) ::= data8(A) data8(B) . { merge_msg_data(&R, &A, &B); }
+data20(R) ::= data16(A) data4(B) . { merge_msg_data(&R, &A, &B); }
+data24(R) ::= data16(A) data8(B) . { merge_msg_data(&R, &A, &B); }
+data32(R) ::= data16(A) data16(B) . { merge_msg_data(&R, &A, &B); }
+data48(R) ::= data32(A) data16(B) . { merge_msg_data(&R, &A, &B); }
+data64(R) ::= data32(A) data32(B) . { merge_msg_data(&R, &A, &B); }
+
+%code {
+
+DIAG_ON(unreachable-code)
+
+#include "busmaster_scanner_lex.h"
+#include "busmaster_parser.h"
+
+gboolean
+run_busmaster_parser(busmaster_state_t *state,
+ int *err, gchar **err_info)
+{
+ int lex_code;
+ yyscan_t scanner;
+ void *parser;
+
+ state->entry_type = LOG_ENTRY_NONE;
+ state->parse_error = NULL;
+ state->err = 0;
+ state->err_info = NULL;
+
+ if (busmaster_lex_init_extra(state, &scanner) != 0)
+ {
+ *err = errno;
+ *err_info = g_strdup(g_strerror(errno));
+ return FALSE;
+ }
+
+ parser = BusmasterParserAlloc(g_malloc);
+
+#ifdef BUSMASTER_PARSER_TRACE
+ BusmasterParserTrace(stdout, "BusmasterParser >> ");
+#endif
+
+ busmaster_debug_printf("%s: Starting parsing of the line\n", G_STRFUNC);
+
+ do
+ {
+ lex_code = busmaster_lex(scanner);
+
+#ifdef BUSMASTER_DEBUG
+ if (lex_code)
+ busmaster_debug_printf("%s: Feeding %s '%s'\n",
+ G_STRFUNC, yyTokenName[lex_code],
+ busmaster_get_text(scanner));
+ else
+ busmaster_debug_printf("%s: Feeding %s\n",
+ G_STRFUNC, yyTokenName[lex_code]);
+#endif
+
+ BusmasterParser(parser, lex_code, state->token, state);
+
+ if (state->err || state->err_info || state->parse_error)
+ break;
+ }
+ while (lex_code);
+
+ busmaster_debug_printf("%s: Done (%d)\n", G_STRFUNC, lex_code);
+
+ BusmasterParserFree(parser, g_free);
+ busmaster_lex_destroy(scanner);
+
+ if (state->err || state->err_info || state->parse_error)
+ {
+ if (state->err_info)
+ {
+ *err_info = state->err_info;
+ g_free(state->parse_error);
+ }
+ else
+ {
+ *err_info = state->parse_error;
+ }
+
+ if (state->err)
+ *err = state->err;
+ else
+ *err = WTAP_ERR_BAD_FILE;
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+}
diff --git a/wiretap/busmaster_priv.h b/wiretap/busmaster_priv.h
new file mode 100644
index 0000000000..36ce70e994
--- /dev/null
+++ b/wiretap/busmaster_priv.h
@@ -0,0 +1,136 @@
+/* busmaster_priv.h
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Support for Busmaster log file format
+ * Copyright (c) 2019 by Maksim Salau <maksim.salau@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef BUSMASTER_PRIV_H__
+#define BUSMASTER_PRIV_H__
+
+#include <gmodule.h>
+#include <wiretap/wtap.h>
+#include <wiretap/socketcan.h>
+#include <wsutil/ws_printf.h>
+
+//#define BUSMASTER_DEBUG
+//#define BUSMASTER_PARSER_TRACE
+
+typedef enum {
+ LOG_ENTRY_ERROR = -1,
+ LOG_ENTRY_NONE = 0,
+ LOG_ENTRY_EMPTY,
+ LOG_ENTRY_HEADER,
+ LOG_ENTRY_FOOTER,
+ LOG_ENTRY_FOOTER_AND_HEADER,
+ LOG_ENTRY_MSG,
+ LOG_ENTRY_EOF,
+} log_entry_type_t;
+
+typedef enum {
+ PROTOCOL_UNKNOWN = 0,
+ PROTOCOL_CAN,
+ PROTOCOL_LIN,
+ PROTOCOL_J1939,
+} protocol_t;
+
+typedef enum {
+ DATA_MODE_UNKNOWN = 0,
+ DATA_MODE_HEX,
+ DATA_MODE_DEC,
+} data_mode_t;
+
+typedef enum {
+ TIME_MODE_UNKNOWN = 0,
+ TIME_MODE_ABSOLUTE,
+ TIME_MODE_SYSTEM,
+ TIME_MODE_RELATIVE,
+} time_mode_t;
+
+typedef enum {
+ MSG_TYPE_STD,
+ MSG_TYPE_EXT,
+ MSG_TYPE_STD_RTR,
+ MSG_TYPE_EXT_RTR,
+ MSG_TYPE_STD_FD,
+ MSG_TYPE_EXT_FD,
+ MSG_TYPE_ERR,
+} msg_type_t;
+
+typedef struct {
+ guint year;
+ guint month;
+ guint day;
+} msg_date_t;
+
+typedef struct {
+ guint hours;
+ guint minutes;
+ guint seconds;
+ guint micros;
+} msg_time_t;
+
+typedef struct {
+ msg_date_t date;
+ msg_time_t time;
+} msg_date_time_t;
+
+typedef struct {
+ guint length;
+ guint8 data[CANFD_MAX_DLEN];
+} msg_data_t;
+
+typedef struct {
+ msg_time_t timestamp;
+ msg_type_t type;
+ guint32 id;
+ msg_data_t data;
+} msg_t;
+
+typedef struct {
+ gint64 v0;
+ gint64 v1;
+ gint64 v2;
+ gint64 v3;
+} token_t;
+
+typedef struct {
+ gint64 file_start_offset;
+ gint64 file_end_offset;
+ protocol_t protocol;
+ data_mode_t data_mode;
+ time_mode_t time_mode;
+ msg_date_t start_date;
+ msg_time_t start_time;
+} busmaster_priv_t;
+
+typedef struct {
+ FILE_T fh;
+ gint64 file_bytes_read;
+
+ gchar *parse_error;
+ int err;
+ gchar *err_info;
+
+ token_t token;
+
+ log_entry_type_t entry_type;
+ busmaster_priv_t header;
+ msg_t msg;
+} busmaster_state_t;
+
+gboolean
+run_busmaster_parser(busmaster_state_t *state,
+ int *err, gchar **err_info);
+
+#ifdef BUSMASTER_DEBUG
+#define busmaster_debug_printf(...) ws_debug_printf(__VA_ARGS__)
+#else
+#define busmaster_debug_printf(...) (void)0
+#endif
+
+#endif /* BUSMASTER_PRIV_H__ */
diff --git a/wiretap/busmaster_scanner.l b/wiretap/busmaster_scanner.l
new file mode 100644
index 0000000000..4a352b961c
--- /dev/null
+++ b/wiretap/busmaster_scanner.l
@@ -0,0 +1,198 @@
+/* busmaster_scanner.l
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Support for Busmaster log file format
+ * Copyright (c) 2019 by Maksim Salau <maksim.salau@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+%top {
+/* Include this before everything else, for various large-file definitions */
+#include "config.h"
+}
+
+%option noyywrap
+%option noinput
+%option nounput
+%option batch
+%option never-interactive
+%option nodefault
+%option prefix="busmaster_"
+%option reentrant
+%option extra-type="busmaster_state_t *"
+
+%option noyy_scan_buffer
+%option noyy_scan_bytes
+%option noyy_scan_string
+
+/*
+ * We have to override the memory allocators so that we don't get
+ * "unused argument" warnings from the yyscanner argument (which
+ * we don't use, as we have a global memory allocator).
+ *
+ * We provide, as macros, our own versions of the routines generated by Flex,
+ * which just call malloc()/realloc()/free() (as the Flex versions do),
+ * discarding the extra argument.
+ */
+%option noyyalloc
+%option noyyrealloc
+%option noyyfree
+
+%{
+
+#include <ws_diag_control.h>
+#include <wiretap/file_wrappers.h>
+#include "busmaster_parser.h"
+#include "busmaster_priv.h"
+
+#ifndef HAVE_UNISTD_H
+#define YY_NO_UNISTD_H
+#endif
+
+static int busmaster_yyinput(void *buf, unsigned int length, busmaster_state_t *state)
+{
+ int ret = file_read(buf, length, state->fh);
+
+ if (ret < 0)
+ {
+ state->err = file_error(state->fh, &state->err_info);
+ return YY_NULL;
+ }
+
+ return ret;
+}
+
+#define YY_INPUT(buf, result, max_size) \
+ do { (result) = busmaster_yyinput((buf), (max_size), yyextra); } while (0)
+
+/* Count bytes read. This is required in order to rewind the file
+ * to the beginning of the next packet, since flex reads more bytes
+ * before executing the action that does yyterminate(). */
+#define YY_USER_ACTION do { yyextra->file_bytes_read += yyleng; } while (0);
+
+/*
+ * Sleazy hack to suppress compiler warnings in yy_fatal_error().
+ */
+#define YY_EXIT_FAILURE ((void)yyscanner, 2)
+
+/*
+ * Macros for the allocators, to discard the extra argument.
+ */
+#define busmaster_alloc(size, yyscanner) (void *)malloc(size)
+#define busmaster_realloc(ptr, size, yyscanner) (void *)realloc((char *)(ptr), (size))
+#define busmaster_free(ptr, yyscanner) free((char *)(ptr))
+
+DIAG_OFF_FLEX
+
+%}
+
+SPC [ \t]+
+ENDL [\r\n][ \t\r\n]*
+INT [0-9]+
+NUM (0x)?[0-9A-Fa-f]+
+
+%x HEADER TIME
+%x HEADER_CHANNELS HEADER_DB_FILES
+
+%%
+
+<*>{SPC} ;
+<INITIAL>{ENDL} { yyterminate(); };
+<HEADER,TIME>{ENDL} { YY_FATAL_ERROR("Unterminated header statement"); }
+
+"***" { BEGIN(HEADER); }
+<HEADER,TIME>"***"{ENDL}"***" { BEGIN(HEADER); return TOKEN_ENDL; }
+<HEADER>"***"{ENDL} { BEGIN(INITIAL); yyterminate(); }
+<HEADER>"BUSMASTER" { return TOKEN_HEADER_VER; }
+<HEADER>"PROTOCOL CAN" { yyextra->token.v0 = PROTOCOL_CAN; return TOKEN_PROTOCOL_TYPE; }
+<HEADER>"PROTOCOL J1939" { yyextra->token.v0 = PROTOCOL_J1939; return TOKEN_PROTOCOL_TYPE; }
+<HEADER>"PROTOCOL LIN" { yyextra->token.v0 = PROTOCOL_LIN; return TOKEN_PROTOCOL_TYPE; }
+<HEADER>"START DATE AND TIME" { BEGIN(TIME); return TOKEN_START_TIME; }
+<HEADER>"END DATE AND TIME" { BEGIN(TIME); return TOKEN_END_TIME; }
+<HEADER>"DEC" { yyextra->token.v0 = DATA_MODE_DEC; return TOKEN_DATA_MODE; }
+<HEADER>"HEX" { yyextra->token.v0 = DATA_MODE_HEX; return TOKEN_DATA_MODE; }
+<HEADER>"ABSOLUTE MODE" { yyextra->token.v0 = TIME_MODE_ABSOLUTE; return TOKEN_TIME_MODE; }
+<HEADER>"SYSTEM MODE" { yyextra->token.v0 = TIME_MODE_SYSTEM; return TOKEN_TIME_MODE; }
+<HEADER>"RELATIVE MODE" { yyextra->token.v0 = TIME_MODE_RELATIVE; return TOKEN_TIME_MODE; }
+<HEADER>"[START LOGGING SESSION]" { return TOKEN_START_SESSION; }
+<HEADER>"[STOP LOGGING SESSION]" { return TOKEN_STOP_SESSION; }
+<HEADER>"START CHANNEL BAUD RATE***" { BEGIN(HEADER_CHANNELS); }
+<HEADER>"START DATABASE FILES (DBF/DBC)***" { BEGIN(HEADER_DB_FILES); }
+<HEADER>. { return TOKEN_HEADER_CHAR; }
+
+<HEADER_CHANNELS>"***END CHANNEL BAUD RATE***"{ENDL}"***" { BEGIN(HEADER); }
+<HEADER_CHANNELS>.+ ;
+<HEADER_CHANNELS>{ENDL} ;
+
+<HEADER_DB_FILES>"***END DATABASE FILES (DBF/DBC)***"{ENDL}"***" { BEGIN(HEADER); }
+<HEADER_DB_FILES>"***END OF DATABASE FILES (DBF/DBC)***"{ENDL}"***" { BEGIN(HEADER); }
+<HEADER_DB_FILES>.+ ;
+<HEADER_DB_FILES>{ENDL} ;
+
+<TIME>{INT} { yyextra->token.v0 = strtoul(yytext, NULL, 10); return TOKEN_INT; }
+<TIME>: { return TOKEN_COLON; }
+<TIME>. { return TOKEN_INVALID_CHAR; }
+
+<INITIAL>{INT}:{INT}:{INT}:{INT} {
+ char *endp;
+ char *strp;
+
+ yyextra->token.v0 = strtoul(yytext, &endp, 10);
+ if (*endp != ':' || endp == yytext)
+ return TOKEN_INVALID_NUMBER;
+
+ strp = endp + 1;
+ yyextra->token.v1 = strtoul(strp, &endp, 10);
+ if (*endp != ':' || endp == strp)
+ return TOKEN_INVALID_NUMBER;
+
+ strp = endp + 1;
+ yyextra->token.v2 = strtoul(strp, &endp, 10);
+ if (*endp != ':' || endp == strp)
+ return TOKEN_INVALID_NUMBER;
+
+ strp = endp + 1;
+ yyextra->token.v3 = strtoul(strp, &endp, 10);
+ if (*endp != '\0' || endp == strp)
+ return TOKEN_INVALID_NUMBER;
+
+ return TOKEN_MSG_TIME;
+ }
+
+<INITIAL>{NUM} {
+ char *endp;
+
+ if (yyextra->header.data_mode == DATA_MODE_HEX)
+ yyextra->token.v0 = strtoul(yytext, &endp, 16);
+ else if (yyextra->header.data_mode == DATA_MODE_DEC)
+ yyextra->token.v0 = strtoul(yytext, &endp, 10);
+ else
+ return TOKEN_INVALID_NUMBER;
+
+ if (*endp != '\0' || endp == yytext)
+ return TOKEN_INVALID_NUMBER;
+
+ return TOKEN_INT;
+ }
+
+<INITIAL>[RT]x { return TOKEN_MSG_DIR; }
+<INITIAL>s { yyextra->token.v0 = MSG_TYPE_STD; return TOKEN_MSG_TYPE; }
+<INITIAL>sr { yyextra->token.v0 = MSG_TYPE_STD_RTR; return TOKEN_MSG_TYPE; }
+<INITIAL>x { yyextra->token.v0 = MSG_TYPE_EXT; return TOKEN_MSG_TYPE; }
+<INITIAL>xr { yyextra->token.v0 = MSG_TYPE_EXT_RTR; return TOKEN_MSG_TYPE; }
+<INITIAL>s-fd { yyextra->token.v0 = MSG_TYPE_STD_FD; return TOKEN_MSG_TYPE; }
+<INITIAL>x-fd { yyextra->token.v0 = MSG_TYPE_EXT_FD; return TOKEN_MSG_TYPE; }
+<INITIAL>ERR.* { yyextra->token.v0 = MSG_TYPE_ERR; return TOKEN_ERR_MSG_TYPE; }
+
+<INITIAL>("NONE"|"CMD"|"RQST"|"DATA"|"BROADCAST"|"ACK"|"GRP_FUNC"|"ACL"|"RQST_ACL"|"CA"|"BAM"|"RTS"|"CTS"|"EOM"|"CON_ABORT"|"TPDT") {
+ return TOKEN_J1939_MSG_TYPE;
+}
+
+<INITIAL>. { return TOKEN_INVALID_CHAR; }
+
+%%
+
+DIAG_ON_FLEX
diff --git a/wiretap/candump_priv.h b/wiretap/candump_priv.h
index 5d3ad7b156..a505939513 100644
--- a/wiretap/candump_priv.h
+++ b/wiretap/candump_priv.h
@@ -14,31 +14,11 @@
#include <gmodule.h>
#include <wiretap/wtap.h>
+#include <wiretap/socketcan.h>
#include <epan/dissectors/packet-socketcan.h>
//#define CANDUMP_DEBUG
-#define CAN_MAX_DLEN 8
-#define CANFD_MAX_DLEN 64
-
-typedef struct can_frame {
- guint32 can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
- guint8 can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */
- guint8 __pad; /* padding */
- guint8 __res0; /* reserved / padding */
- guint8 __res1; /* reserved / padding */
- guint8 data[CAN_MAX_DLEN];
-} can_frame_t;
-
-typedef struct canfd_frame {
- guint32 can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
- guint8 len; /* frame payload length in byte (0 .. CANFD_MAX_DLEN) */
- guint8 flags; /* additional flags for CAN FD */
- guint8 __res0; /* reserved / padding */
- guint8 __res1; /* reserved / padding */
- guint8 data[CANFD_MAX_DLEN];
-} canfd_frame_t;
-
typedef struct {
guint8 length;
guint8 data[CANFD_MAX_DLEN];
diff --git a/wiretap/file_access.c b/wiretap/file_access.c
index 466dbd58fc..3c5e834e0c 100644
--- a/wiretap/file_access.c
+++ b/wiretap/file_access.c
@@ -78,6 +78,7 @@
#include "systemd_journal.h"
#include "log3gpp.h"
#include "candump.h"
+#include "busmaster.h"
/*
@@ -418,6 +419,7 @@ static const struct open_info open_info_base[] = {
{ "Android Logcat Binary format", OPEN_INFO_HEURISTIC, logcat_open, "logcat", NULL, NULL },
{ "Android Logcat Text formats", OPEN_INFO_HEURISTIC, logcat_text_open, "txt", NULL, NULL },
{ "Candump log", OPEN_INFO_HEURISTIC, candump_open, NULL, NULL, NULL },
+ { "Busmaster log", OPEN_INFO_HEURISTIC, busmaster_open, NULL, NULL, NULL },
/* ASCII trace files from Telnet sessions. */
{ "Lucent/Ascend access server trace", OPEN_INFO_HEURISTIC, ascend_open, "txt", NULL, NULL },
{ "Toshiba Compact ISDN Router snoop", OPEN_INFO_HEURISTIC, toshiba_open, "txt", NULL, NULL },
diff --git a/wiretap/socketcan.h b/wiretap/socketcan.h
new file mode 100644
index 0000000000..a287681fa7
--- /dev/null
+++ b/wiretap/socketcan.h
@@ -0,0 +1,38 @@
+/* socketcan.h
+ *
+ * Wiretap Library
+ * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
+ *
+ * Support for Busmaster log file format
+ * Copyright (c) 2019 by Maksim Salau <maksim.salau@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ */
+
+#ifndef SOCKETCAN_H__
+#define SOCKETCAN_H__
+
+#include <gmodule.h>
+
+#define CAN_MAX_DLEN 8
+#define CANFD_MAX_DLEN 64
+
+typedef struct can_frame {
+ guint32 can_id; /* 32 bit CAN_ID + EFF/RTR/ERR flags */
+ guint8 can_dlc; /* frame payload length in byte (0 .. CAN_MAX_DLEN) */
+ guint8 __pad; /* padding */
+ guint8 __res0; /* reserved / padding */
+ guint8 __res1; /* reserved / padding */
+ guint8 data[CAN_MAX_DLEN];
+} can_frame_t;
+
+typedef struct canfd_frame {
+ guint32 can_id; /* 32 bit CAN_ID + EFF flag */
+ guint8 len; /* frame payload length in byte */
+ guint8 flags; /* additional flags for CAN FD */
+ guint8 __res0; /* reserved / padding */
+ guint8 __res1; /* reserved / padding */
+ guint8 data[CANFD_MAX_DLEN];
+} canfd_frame_t;
+
+#endif /* SOCKETCAN_H__ */