* [PATCH BlueZ 2/4] replay: Add timing functionality
2012-08-09 17:36 [PATCH BlueZ 1/4] replay: Add initial version of replay tool Anton Weber
@ 2012-08-09 17:36 ` Anton Weber
2012-08-09 17:36 ` [PATCH BlueZ 3/4] replay: Add emulation support Anton Weber
` (3 subsequent siblings)
4 siblings, 0 replies; 14+ messages in thread
From: Anton Weber @ 2012-08-09 17:36 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anton Weber
Add -d parameter that lets user choose between two timing modes.
delta: use time difference between two packets for delay
none: no delay
Add -m parameter to specify a factor that multiplies delays in
delta timing mode.
Add -t parameter to set the epoll timeout when receiving packets.
---
Makefile.tools | 3 +-
tools/replay/hciseq.c | 53 ++++++++++++++++++++++++++++
tools/replay/hciseq.h | 23 +++++++++++++
tools/replay/main.c | 56 +++++++++++++++++++++++++++++-
tools/replay/main.h | 21 +-----------
tools/replay/time.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++
tools/replay/time.h | 31 +++++++++++++++++
7 files changed, 256 insertions(+), 22 deletions(-)
create mode 100644 tools/replay/hciseq.c
create mode 100644 tools/replay/time.c
create mode 100644 tools/replay/time.h
diff --git a/Makefile.tools b/Makefile.tools
index 4a3aca5..6767300 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -70,7 +70,8 @@ emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \
emulator/btdev.h emulator/btdev.c
tools_replay_btreplay_SOURCES = tools/replay/main.h tools/replay/main.c \
- tools/replay/hciseq.h \
+ tools/replay/hciseq.h tools/replay/hciseq.c \
+ tools/replay/time.h tools/replay/time.c \
monitor/packet.h monitor/packet.c \
monitor/btsnoop.h monitor/btsnoop.c \
monitor/control.h monitor/control.c \
diff --git a/tools/replay/hciseq.c b/tools/replay/hciseq.c
new file mode 100644
index 0000000..8b24264
--- /dev/null
+++ b/tools/replay/hciseq.c
@@ -0,0 +1,53 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ * Copyright (C) 2012 Anton Weber <ant@antweb.me>
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "hciseq.h"
+#include "time.h"
+#include "monitor/bt.h"
+
+void calc_rel_ts(struct hciseq_list *seq)
+{
+ struct timeval start;
+ struct hciseq_node *tmp;
+
+ start = seq->current->frame->ts;
+ tmp = seq->current;
+
+ /* first packet */
+ tmp->attr->ts_rel.tv_sec = 0;
+ tmp->attr->ts_rel.tv_usec = 0;
+ tmp->attr->ts_diff.tv_sec = 0;
+ tmp->attr->ts_diff.tv_usec = 0;
+
+ while (tmp->next != NULL) {
+ timeval_diff(&tmp->next->frame->ts, &start,
+ &tmp->next->attr->ts_rel);
+ timeval_diff(&tmp->next->frame->ts, &tmp->frame->ts,
+ &tmp->next->attr->ts_diff);
+ tmp = tmp->next;
+ }
+}
diff --git a/tools/replay/hciseq.h b/tools/replay/hciseq.h
index bf953cd..f9fe7c8 100644
--- a/tools/replay/hciseq.h
+++ b/tools/replay/hciseq.h
@@ -22,6 +22,25 @@
*
*/
+struct frame {
+ void *data;
+ uint32_t data_len;
+ void *ptr;
+ uint32_t len;
+ uint16_t dev_id;
+ uint8_t in;
+ uint8_t master;
+ uint16_t handle;
+ uint16_t cid;
+ uint16_t num;
+ uint8_t dlci;
+ uint8_t channel;
+ unsigned long flags;
+ struct timeval ts;
+ int pppdump_fd;
+ int audio_fd;
+};
+
enum hciseq_action {
HCISEQ_ACTION_REPLAY = 0,
};
@@ -33,6 +52,8 @@ struct hciseq_list {
};
struct hciseq_attr {
+ struct timeval ts_rel;
+ struct timeval ts_diff;
enum hciseq_action action;
};
@@ -41,3 +62,5 @@ struct hciseq_node {
struct hciseq_node *next;
struct hciseq_attr *attr;
};
+
+void calc_rel_ts(struct hciseq_list *seq);
diff --git a/tools/replay/main.c b/tools/replay/main.c
index 8019151..49ea21d 100644
--- a/tools/replay/main.c
+++ b/tools/replay/main.c
@@ -37,8 +37,10 @@
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
+#include <sys/time.h>
#include "main.h"
+#include "time.h"
#include "lib/bluetooth.h"
#include "lib/hci.h"
#include "monitor/bt.h"
@@ -49,16 +51,22 @@
#define MAX_EPOLL_EVENTS 1
#define MAX_MSG 128
+#define TIMING_NONE 0
+#define TIMING_DELTA 1
+
static struct hciseq_list dumpseq;
static int fd;
static int pos = 1;
static int skipped = 0;
+static struct timeval start;
static int epoll_fd;
static struct epoll_event epoll_event;
static int timeout = -1;
+static int timing = TIMING_NONE;
+static double factor = 1;
static bool verbose = false;
static inline int read_n(int fd, char *buf, int len)
@@ -313,6 +321,10 @@ static bool process_in()
if (n < 0) {
perror("Could not receive\n");
return false;
+ } else if (n == 0) {
+ printf("[%4d/%4d] Timeout\n", pos, dumpseq.len);
+ skipped++;
+ return true;
}
/* is this the packet in the sequence? */
@@ -353,9 +365,29 @@ static bool process_out()
static void process()
{
+ struct timeval last, last_diff;
+ __useconds_t delay;
bool processed;
+ gettimeofday(&last, NULL);
do {
+ /* delay */
+ if (timing == TIMING_DELTA) {
+ /* consider exec time of process_out()/process_in() */
+ get_timeval_passed(&last, &last_diff);
+ if (timeval_cmp(&dumpseq.current->attr->ts_diff, &last_diff) >= 0) {
+ delay = timeval_diff(&dumpseq.current->attr->ts_diff,
+ &last_diff, NULL);
+ delay *= factor;
+ if (usleep(delay) == -1)
+ printf("Delay failed\n");
+ } else {
+ /* exec time was longer than delay */
+ printf("Packet delay - processing previous packet took longer than recorded time difference\n");
+ }
+ gettimeofday(&last, NULL);
+ }
+
if (dumpseq.current->frame->in == 1)
processed = process_out();
else
@@ -417,12 +449,18 @@ static void usage(void)
printf("hcireplay - Bluetooth replayer\n"
"Usage:\thcireplay-client [options] file...\n"
"options:\n"
+ "\t-d, --timing={none|delta} Specify timing mode\n"
+ "\t-m, --factor=<value> Use timing modifier\n"
+ "\t-t, --timeout=<value> Use timeout when receiving\n"
"\t-v, --verbose Enable verbose output\n"
"\t --version Give version information\n"
"\t --help Give a short usage message\n");
}
static const struct option main_options[] = {
+ {"timing", required_argument, NULL, 'd'},
+ {"factor", required_argument, NULL, 'm'},
+ {"timeout", required_argument, NULL, 't'},
{"verbose", no_argument, NULL, 'v'},
{"version", no_argument, NULL, 'V'},
{"help", no_argument, NULL, 'H'},
@@ -437,12 +475,25 @@ int main(int argc, char *argv[])
while (1) {
int opt;
- opt = getopt_long(argc, argv, "v",
+ opt = getopt_long(argc, argv, "d:m:t:v",
main_options, NULL);
if (opt < 0)
break;
switch (opt) {
+ case 'd':
+ if (!strcmp(optarg, "none"))
+ timing = TIMING_NONE;
+ else if (!strcmp(optarg, "delta"))
+ timing = TIMING_DELTA;
+
+ break;
+ case 'm':
+ factor = atof(optarg);
+ break;
+ case 't':
+ timeout = atoi(optarg);
+ break;
case 'v':
verbose = true;
break;
@@ -478,6 +529,9 @@ int main(int argc, char *argv[])
}
}
dumpseq.current = dumpseq.frames;
+ calc_rel_ts(&dumpseq);
+
+ gettimeofday(&start, NULL);
/*
* make sure we open the interface after parsing
diff --git a/tools/replay/main.h b/tools/replay/main.h
index d80deec..2223789 100644
--- a/tools/replay/main.h
+++ b/tools/replay/main.h
@@ -42,23 +42,4 @@ struct btsnoop_pkt {
} __attribute__ ((packed));
#define BTSNOOP_PKT_SIZE (sizeof(struct btsnoop_pkt))
-static uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00 };
-
-struct frame {
- void *data;
- uint32_t data_len;
- void *ptr;
- uint32_t len;
- uint16_t dev_id;
- uint8_t in;
- uint8_t master;
- uint16_t handle;
- uint16_t cid;
- uint16_t num;
- uint8_t dlci;
- uint8_t channel;
- unsigned long flags;
- struct timeval ts;
- int pppdump_fd;
- int audio_fd;
-};
+uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00 };
diff --git a/tools/replay/time.c b/tools/replay/time.c
new file mode 100644
index 0000000..029501a
--- /dev/null
+++ b/tools/replay/time.c
@@ -0,0 +1,91 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ * Copyright (C) 2012 Anton Weber <ant@antweb.me>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdlib.h>
+#include <sys/time.h>
+
+#include "time.h"
+
+/*
+ * Adjust timeval structs to make sure usec difference is not negative
+ * see http://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html
+ */
+void timeval_adjust_usec(struct timeval *l, struct timeval *r) {
+ int tmpsec;
+
+ if (r->tv_usec > l->tv_usec) {
+ tmpsec = (r->tv_usec - l->tv_usec) / 1000000 + 1;
+ r->tv_sec += tmpsec;
+ r->tv_usec -= 1000000 * tmpsec;
+ }
+
+ if ((l->tv_usec - r->tv_usec) > 1000000) {
+ tmpsec = (l->tv_usec - r->tv_usec) / 1000000;
+ r->tv_sec -= tmpsec;
+ r->tv_usec += 1000000 * tmpsec;
+ }
+}
+
+__useconds_t
+timeval_diff(struct timeval *l, struct timeval *r, struct timeval *diff)
+{
+ static struct timeval tmp;
+
+ timeval_adjust_usec(l, r);
+
+ /* use local variable if we only need return value */
+ if (diff == NULL)
+ diff = &tmp;
+
+ diff->tv_sec = l->tv_sec - r->tv_sec;
+ diff->tv_usec = l->tv_usec - r->tv_usec;
+
+ return (diff->tv_sec * 1000000) + diff->tv_usec;
+}
+
+int timeval_cmp(struct timeval *l, struct timeval *r)
+{
+ timeval_adjust_usec(l, r);
+
+ if (l->tv_sec > r->tv_sec) {
+ return 1;
+ } else if (l->tv_sec < r->tv_sec) {
+ return -1;
+ } else {
+ if (l->tv_usec > r->tv_usec)
+ return 1;
+ else if (l->tv_usec < r->tv_usec)
+ return -1;
+ else
+ return 0;
+ }
+}
+
+inline __useconds_t
+get_timeval_passed(struct timeval *since, struct timeval *diff)
+{
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+
+ return timeval_diff(&now, since, diff);
+}
diff --git a/tools/replay/time.h b/tools/replay/time.h
new file mode 100644
index 0000000..0c876a4
--- /dev/null
+++ b/tools/replay/time.h
@@ -0,0 +1,31 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ * Copyright (C) 2012 Anton Weber <ant@antweb.me>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+void timeval_adjust_usec(struct timeval *l, struct timeval *r);
+
+__useconds_t
+timeval_diff(struct timeval *l, struct timeval *r, struct timeval *diff);
+
+int timeval_cmp(struct timeval *l, struct timeval *r);
+
+inline __useconds_t
+ get_timeval_passed(struct timeval *since, struct timeval *diff);
--
1.7.9.5
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH BlueZ 3/4] replay: Add emulation support
2012-08-09 17:36 [PATCH BlueZ 1/4] replay: Add initial version of replay tool Anton Weber
2012-08-09 17:36 ` [PATCH BlueZ 2/4] replay: Add timing functionality Anton Weber
@ 2012-08-09 17:36 ` Anton Weber
2012-08-09 17:36 ` [PATCH BlueZ 4/4] replay: Add config file parser and skip action Anton Weber
` (2 subsequent siblings)
4 siblings, 0 replies; 14+ messages in thread
From: Anton Weber @ 2012-08-09 17:36 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anton Weber
Integrate emulator so it can process selected packets instead of
replaying them from the sequence. The behaviour is controlled
through the action attribute in hciseq_attr.
If set to HCISEQ_ACTION_EMULATE, hcireplay will use the emulator
to process the packet automatically instead of replaying it.
HCISEQ_ACTION_REPLAY keeps the previous behaviour and replays the
packet using the dump file.
---
Makefile.tools | 1 +
tools/replay/hciseq.h | 1 +
tools/replay/main.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++---
3 files changed, 51 insertions(+), 3 deletions(-)
diff --git a/Makefile.tools b/Makefile.tools
index 6767300..8b3c8d8 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -76,6 +76,7 @@ tools_replay_btreplay_SOURCES = tools/replay/main.h tools/replay/main.c \
monitor/btsnoop.h monitor/btsnoop.c \
monitor/control.h monitor/control.c \
monitor/mainloop.h monitor/mainloop.c \
+ emulator/btdev.h emulator/btdev.c \
lib/hci.h
tools_replay_btreplay_LDADD = lib/libbluetooth-private.la
diff --git a/tools/replay/hciseq.h b/tools/replay/hciseq.h
index f9fe7c8..1454147 100644
--- a/tools/replay/hciseq.h
+++ b/tools/replay/hciseq.h
@@ -43,6 +43,7 @@ struct frame {
enum hciseq_action {
HCISEQ_ACTION_REPLAY = 0,
+ HCISEQ_ACTION_EMULATE = 1
};
struct hciseq_list {
diff --git a/tools/replay/main.c b/tools/replay/main.c
index 49ea21d..f108b25 100644
--- a/tools/replay/main.c
+++ b/tools/replay/main.c
@@ -43,6 +43,7 @@
#include "time.h"
#include "lib/bluetooth.h"
#include "lib/hci.h"
+#include "emulator/btdev.h"
#include "monitor/bt.h"
#include "monitor/btsnoop.h"
#include "monitor/control.h"
@@ -69,6 +70,8 @@ static int timing = TIMING_NONE;
static double factor = 1;
static bool verbose = false;
+static struct btdev *btdev;
+
static inline int read_n(int fd, char *buf, int len)
{
int t = 0, w;
@@ -250,6 +253,34 @@ static int recv_frm(int fd, struct frame *frm)
return n;
}
+static void btdev_send(const void *data, uint16_t len, void *user_data)
+{
+ struct frame frm;
+ static void* tmpdata = NULL;
+
+ /* copy data so we respect 'const' qualifier */
+ if(tmpdata == NULL)
+ tmpdata = malloc(HCI_MAX_FRAME_SIZE);
+
+ memcpy(tmpdata, data, len);
+
+ frm.data = tmpdata;
+ frm.len = len;
+ frm.data_len = len;
+ frm.in = 1;
+ printf("[Emulator ] ");
+ dump_frame(&frm);
+ send_frm(&frm);
+}
+
+static void btdev_recv(struct frame *frm)
+{
+ frm->in = 0;
+ printf("[Emulator ] ");
+ dump_frame(frm);
+ btdev_receive_h4(btdev, frm->data, frm->data_len);
+}
+
static bool check_match(struct frame *l, struct frame *r, char *msg)
{
uint8_t type_l = ((const uint8_t *) l->data)[0];
@@ -332,11 +363,16 @@ static bool process_in()
match = check_match(dumpseq.current->frame, &frm, msg);
/* process packet if match */
- if (match)
+ if (match) {
printf("[%4d/%4d] ", pos, dumpseq.len);
- else
- printf("[ Unknown ] %s\n ", msg);
+ if (dumpseq.current->attr->action == HCISEQ_ACTION_EMULATE) {
+ btdev_recv(&frm);
+ return true;
+ }
+ } else {
+ printf("[ Unknown ] %s\n ", msg);
+ }
dump_frame(&frm);
return match;
@@ -346,6 +382,11 @@ static bool process_out()
{
uint8_t pkt_type;
+ /* emulator sends response automatically */
+ if (dumpseq.current->attr->action == HCISEQ_ACTION_EMULATE) {
+ return 1;
+ }
+
pkt_type = ((const uint8_t *) dumpseq.current->frame->data)[0];
switch (pkt_type) {
@@ -531,6 +572,10 @@ int main(int argc, char *argv[])
dumpseq.current = dumpseq.frames;
calc_rel_ts(&dumpseq);
+ /* init emulator */
+ btdev = btdev_create(0);
+ btdev_set_send_handler(btdev, btdev_send, NULL);
+
gettimeofday(&start, NULL);
/*
@@ -548,6 +593,7 @@ int main(int argc, char *argv[])
process();
vhci_close();
+ btdev_destroy(btdev);
delete_list();
printf("Terminating\n");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH BlueZ 4/4] replay: Add config file parser and skip action
2012-08-09 17:36 [PATCH BlueZ 1/4] replay: Add initial version of replay tool Anton Weber
2012-08-09 17:36 ` [PATCH BlueZ 2/4] replay: Add timing functionality Anton Weber
2012-08-09 17:36 ` [PATCH BlueZ 3/4] replay: Add emulation support Anton Weber
@ 2012-08-09 17:36 ` Anton Weber
2012-08-13 18:34 ` [PATCH BlueZ v2 1/4] replay: Add initial version of replay tool Anton Weber
2012-09-05 14:14 ` [PATCH BlueZ v3 1/6] replay: Add initial version of replay tool Anton Weber
4 siblings, 0 replies; 14+ messages in thread
From: Anton Weber @ 2012-08-09 17:36 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anton Weber
Add support for config files to manipulate the loaded sequence.
A config file can be specified with the new -c command line parameter.
The config syntax is:
scope attr=value[,attr=value,...]
e.g.:
all delta=300,action=replay
1-4 action=emulate
1 delta=500
HCI_EVT_0x0e delta=0
HCI_CMD_0x03|0x0003 action=emulate
HCI_ACL action=replay
The inital version supports the attributes delta and action and
all scope types shown in the example.
HCISEQ_ACTION_SKIP allows to skip a packet in the sequence.
The action can be set in config files using "action=skip".
---
Makefile.tools | 1 +
tools/replay/config-parser.c | 506 ++++++++++++++++++++++++++++++++++++++++++
tools/replay/config-parser.h | 36 +++
tools/replay/hciseq.h | 3 +-
tools/replay/main.c | 117 +++++++++-
tools/replay/main.h | 2 -
6 files changed, 661 insertions(+), 4 deletions(-)
create mode 100644 tools/replay/config-parser.c
create mode 100644 tools/replay/config-parser.h
diff --git a/Makefile.tools b/Makefile.tools
index 8b3c8d8..ba2db00 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -72,6 +72,7 @@ emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \
tools_replay_btreplay_SOURCES = tools/replay/main.h tools/replay/main.c \
tools/replay/hciseq.h tools/replay/hciseq.c \
tools/replay/time.h tools/replay/time.c \
+ tools/replay/config-parser.h tools/replay/config-parser.c \
monitor/packet.h monitor/packet.c \
monitor/btsnoop.h monitor/btsnoop.c \
monitor/control.h monitor/control.c \
diff --git a/tools/replay/config-parser.c b/tools/replay/config-parser.c
new file mode 100644
index 0000000..5219eb9
--- /dev/null
+++ b/tools/replay/config-parser.c
@@ -0,0 +1,506 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ * Copyright (C) 2012 Anton Weber <ant@antweb.me>
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <sys/time.h>
+
+#include "config-parser.h"
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "monitor/bt.h"
+
+#define MAXLINE 128
+#define MAX_ATTR_KEY 32
+#define MAX_ATTR_VAL 32
+
+static struct hciseq_list *seq;
+static struct hciseq_type_cfg *type_cfg;
+
+static bool verbose;
+static int line;
+
+struct scope_list {
+ struct scope_node *head;
+};
+
+struct scope_node {
+ int pos;
+ struct hciseq_attr *attr;
+ struct scope_node *next;
+};
+
+struct attr_list {
+ struct attr_node *head;
+};
+
+struct attr_node {
+ char key[MAX_ATTR_KEY];
+ char val[MAX_ATTR_VAL];
+ struct attr_node *next;
+};
+
+static void attr_list_delete(struct attr_list *list)
+{
+ struct attr_node *node, *next;
+
+ if (list == NULL)
+ return;
+
+ node = list->head;
+ free(list);
+ while (node != NULL) {
+ next = node->next;
+ free(node);
+ node = next;
+ }
+}
+
+static void scope_list_delete(struct scope_list *list)
+{
+ struct scope_node *node, *next;
+
+ if (list == NULL)
+ return;
+
+ node = list->head;
+ free(list);
+ while (node != NULL) {
+ next = node->next;
+ free(node);
+ node = next;
+ }
+}
+
+static struct attr_list *parse_attrstr(char *attrstr)
+{
+ struct attr_list *list = NULL;
+ struct attr_node *node;
+ char *res;
+
+ do {
+ if (list == NULL) {
+ if ((res = strtok(attrstr, "=")) == NULL) {
+ /* nothing to parse */
+ return NULL;
+ }
+
+ list = malloc(sizeof(*list));
+ node = malloc(sizeof(*node));
+ list->head = node;
+ } else {
+ if ((res = strtok(NULL, "=")) == NULL) {
+ /* nothing left to parse */
+ break;
+ }
+
+ node->next = malloc(sizeof(*node));
+ node = node->next;
+ }
+
+ strncpy(node->key, res, sizeof(node->key));
+ node->key[sizeof(node->key) - 1] = '\0';
+
+ if ((res = strtok(NULL, ",")) == NULL) {
+ fprintf(stderr, "Invalid attribute");
+ goto err;
+ }
+ strncpy(node->val, res, sizeof(node->val));
+ node->val[sizeof(node->val) - 1] = '\0';
+
+ node->next = NULL;
+ } while (res != NULL);
+
+ return list;
+
+err:
+ attr_list_delete(list);
+ return NULL;
+}
+
+static int apply_attr(struct scope_node *scope_node,
+ struct attr_list *list)
+{
+ struct attr_node *attr_node = list->head;
+ struct hciseq_attr *attr = scope_node->attr;
+ long lval;
+
+ while (attr_node != NULL) {
+ if (strcmp(attr_node->key, "delta") == 0) {
+ /* delta */
+ lval = strtol(attr_node->val, NULL, 10);
+ if (errno == ERANGE || errno == EINVAL)
+ return 1;
+
+ if (verbose) {
+ printf("\t[%d] set delta to %ld\n",
+ scope_node->pos, lval);
+ }
+
+ attr->ts_diff.tv_sec = 0;
+ attr->ts_diff.tv_usec = lval;
+ } else if (strcmp(attr_node->key, "action") == 0) {
+ /* action */
+ if (strcmp(attr_node->val, "replay") == 0) {
+ lval = HCISEQ_ACTION_REPLAY;
+ if (verbose)
+ printf("\t[%d] set action to 'replay'\n",
+ scope_node->pos);
+ } else if (strcmp(attr_node->val, "emulate") == 0) {
+ lval = HCISEQ_ACTION_EMULATE;
+ if (verbose)
+ printf("\t[%d] set action to 'emulate'\n",
+ scope_node->pos);
+ } else if (strcmp(attr_node->val, "skip") == 0) {
+ lval = HCISEQ_ACTION_SKIP;
+ if (verbose)
+ printf("\t[%d] set action to 'skip'\n",
+ scope_node->pos);
+ } else {
+ return 1;
+ }
+
+ attr->action = lval;
+ }
+
+ attr_node = attr_node->next;
+ }
+
+ return 0;
+}
+
+static int apply_attr_scope(struct scope_list *scope,
+ struct attr_list *attr)
+{
+ struct scope_node *node = scope->head;
+
+ while (node != NULL) {
+ apply_attr(node, attr);
+ node = node->next;
+ }
+
+ return 0;
+}
+
+static struct scope_list *get_scope_range(int from, int to)
+{
+ struct scope_list *list = NULL;
+ struct scope_node *scope_node;
+ struct hciseq_node *seq_node = seq->current;
+ int pos = 1;
+
+ /* forward to 'from' */
+ while (pos < from) {
+ seq_node = seq_node->next;
+ pos++;
+ }
+
+ /* create scope list for range */
+ while (pos <= to) {
+ if (verbose)
+ printf("\tadd packet [%d]\n", pos);
+
+ if (list == NULL) {
+ list = malloc(sizeof(*list));
+ scope_node = malloc(sizeof(*scope_node));
+ list->head = scope_node;
+ } else {
+ scope_node->next = malloc(sizeof(*scope_node));
+ scope_node = scope_node->next;
+ }
+ scope_node->attr = seq_node->attr;
+ scope_node->pos = pos;
+ scope_node->next = NULL;
+
+ seq_node = seq_node->next;
+ pos++;
+ }
+
+ return list;
+}
+
+static struct scope_list *get_scope_type(uint8_t type, void *filter1,
+ void *filter2)
+{
+ struct scope_list *list = NULL;
+ struct scope_node *scope_node;
+ struct hciseq_node *seq_node = seq->current;
+ uint16_t opcode, node_opcode;
+ uint8_t node_ogf, ogf = 0x00;
+ uint16_t node_ocf, ocf = 0x0000;
+ uint8_t node_evt, evt = 0x00;
+ bool match;
+ int pos = 1;
+ struct hciseq_attr *attr;
+
+ if (type == BT_H4_CMD_PKT) {
+ ogf = *((uint8_t *) filter1);
+ ocf = *((uint16_t *) filter2);
+ opcode = cmd_opcode_pack(ogf, ocf);
+
+ if (opcode > 0x2FFF) {
+ attr = NULL;
+ } else {
+ attr = type_cfg->cmd[opcode];
+ if (attr == NULL) {
+ attr = malloc(sizeof(*attr));
+ type_cfg->cmd[opcode] = attr;
+ }
+ }
+ } else if (type == BT_H4_EVT_PKT) {
+ evt = *((uint8_t *) filter1);
+ attr = type_cfg->evt[evt];
+
+ if (attr == NULL) {
+ attr = malloc(sizeof(*attr));
+ type_cfg->evt[evt] = attr;
+ }
+ } else if (type == BT_H4_ACL_PKT) {
+ attr = type_cfg->acl;
+ if (attr == NULL) {
+ attr = malloc(sizeof(*attr));
+ type_cfg->acl = attr;
+ }
+ } else {
+ attr = NULL;
+ }
+
+ /* add matching packets in sequence */
+ while (seq_node != NULL) {
+ match = false;
+ if (((uint8_t *) seq_node->frame->data)[0] == type) {
+ if (type == BT_H4_CMD_PKT) {
+ node_opcode = *((uint16_t *)
+ (seq_node->frame->data + 1));
+ node_ogf = cmd_opcode_ogf(node_opcode);
+ node_ocf = cmd_opcode_ocf(node_opcode);
+ if (node_ogf == ogf && node_ocf == ocf)
+ match = true;
+ } else if (type == BT_H4_EVT_PKT) {
+ node_evt = ((uint8_t *) seq_node->frame->data)[1];
+ if (evt == node_evt)
+ match = true;
+ } else if (type == BT_H4_ACL_PKT) {
+ match = true;
+ }
+ }
+
+ if (match) {
+ if (verbose)
+ printf("\tadd packet [%d]\n", pos);
+
+ if (list == NULL) {
+ list = malloc(sizeof(*list));
+ scope_node = malloc(sizeof(*scope_node));
+ list->head = scope_node;
+ } else {
+ scope_node->next = malloc(sizeof(*scope_node));
+ scope_node = scope_node->next;
+ }
+ scope_node->attr = seq_node->attr;
+ scope_node->pos = pos;
+ scope_node->next = NULL;
+ }
+ seq_node = seq_node->next;
+ pos++;
+ }
+
+ /* add type config */
+ if (attr != NULL) {
+ if (list == NULL) {
+ list = malloc(sizeof(*list));
+ scope_node = malloc(sizeof(*scope_node));
+ list->head = scope_node;
+ } else {
+ scope_node->next = malloc(sizeof(*scope_node));
+ scope_node = scope_node->next;
+ }
+
+ scope_node->attr = attr;
+ scope_node->pos = 0;
+ scope_node->next = NULL;
+ }
+
+ return list;
+}
+
+static int parse_line(char *buf)
+{
+ char *scopestr, *attrstr;
+ struct scope_list *scope_list = NULL;
+ struct attr_list *attr_list;
+ uint8_t evt, ogf;
+ uint16_t ocf;
+ char *res;
+ int from, to;
+
+ line++;
+
+ /* split line into scope and attributes */
+ if ((scopestr = strtok(buf, " ")) == NULL)
+ return 1;
+
+ if ((attrstr = strtok(NULL, "\n")) == NULL)
+ return 1;
+
+ if (verbose)
+ printf("Parsing scope (%s)\n", scopestr);
+
+ if (strcmp(scopestr, "all") == 0) {
+ if (verbose)
+ printf("\tadd all\n");
+
+ scope_list = get_scope_range(0, seq->len);
+ } else if ((strncmp(scopestr, "HCI_", 4) == 0) && strlen(scopestr) >= 7) {
+ /* make sure scopestr is at least 7 chars long, so we can check for HCI_XXX */
+
+ if (strncmp(scopestr + 4, "ACL", 3) == 0) {
+ /* scope is HCI_ACL */
+ if (verbose)
+ printf("\tadd all HCI ACL data packets:");
+
+ scope_list = get_scope_type(BT_H4_ACL_PKT, NULL, NULL);
+ } else if (strncmp(scopestr + 4, "CMD", 3) == 0) {
+ /* scope is HCI_CMD_
+ * length must be exactly 19 (e.g. HCI_CMD_0x03|0x0003) */
+ if (strlen(scopestr) != 19 || scopestr[12] != '|')
+ return 1;
+
+ if (sscanf(scopestr + 8, "0x%2hhx", &ogf) <= 0)
+ return 1;
+
+ if (sscanf(scopestr + 13, "0x%4hx", &ocf) <= 0)
+ return 1;
+
+ if (verbose)
+ printf("\tadd all HCI command packets with opcode (0x%2.2x|0x%4.4x):\n",
+ ogf, ocf);
+
+ scope_list = get_scope_type(BT_H4_CMD_PKT, &ogf, &ocf);
+ } else if (strncmp(scopestr + 4, "EVT", 3) == 0) {
+ /* scope is CMD_EVT_
+ * length must be exactly 12 (e.g. HCI_EVT_0x0e) */
+ if (strlen(scopestr) != 12)
+ return 1;
+
+ if (sscanf(scopestr + 8, "0x%2hhx", &evt) <= 0)
+ return 1;
+
+ if (verbose)
+ printf("\tadd all HCI event packets with event type (0x%2.2x):\n",
+ evt);
+
+ scope_list = get_scope_type(BT_H4_EVT_PKT, &evt, NULL);
+ }
+ } else if (scopestr[0] >= 48 || scopestr[0] <= 57) {
+ /* first char is a digit */
+ if ((res = strtok(scopestr, "-")) == NULL)
+ return 1;
+
+ from = atoi(res);
+ if (from <= 0)
+ return 1;
+
+ if ((res = strtok(NULL, ":")) == NULL) {
+ /* just one packet */
+ if (verbose)
+ printf("\tadd packet single packet\n");
+
+ scope_list = get_scope_range(from, from);
+ } else {
+ /* range */
+ to = atoi(res);
+ if (to > seq->len)
+ return 1;
+
+ if (verbose)
+ printf("\tadd packets %d to %d\n", from, to);
+
+ scope_list = get_scope_range(from, to);
+ }
+
+ }
+
+ if (verbose)
+ printf("Parsing attributes (%s)\n", attrstr);
+
+ if ((attr_list = parse_attrstr(attrstr)) == NULL) {
+ return 1;
+ }
+
+ if (scope_list != NULL) {
+ apply_attr_scope(scope_list, attr_list);
+ scope_list_delete(scope_list);
+ } else {
+ if (verbose)
+ printf("Empty scope, skipping\n");
+ }
+
+ attr_list_delete(attr_list);
+
+ return 0;
+}
+
+int parse_config(char *path, struct hciseq_list *_seq,
+ struct hciseq_type_cfg *_type_cfg, bool _verbose)
+{
+ char *buf;
+ FILE *file;
+
+ seq = _seq;
+ type_cfg = _type_cfg;
+ verbose = _verbose;
+ line = 0;
+
+ printf("Reading config file...\n");
+
+ buf = malloc(sizeof(char) * MAXLINE);
+ if (buf == NULL) {
+ fprintf(stderr, "Failed to allocate buffer\n");
+ return 1;
+ }
+
+ if ((file = fopen(path, "r")) == NULL) {
+ perror("Failed to open config file");
+ return 1;
+ }
+
+ while (fgets(buf, MAXLINE, file) != NULL) {
+ if (parse_line(buf)) {
+ fprintf(stderr, "Error parsing config file - line %d\n",
+ line);
+ free(buf);
+ return 1;
+ }
+ }
+
+ printf("Done\n\n");
+ fclose(file);
+ free(buf);
+
+ return 0;
+}
diff --git a/tools/replay/config-parser.h b/tools/replay/config-parser.h
new file mode 100644
index 0000000..265628d
--- /dev/null
+++ b/tools/replay/config-parser.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ * Copyright (C) 2012 Anton Weber <ant@antweb.me>
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "hciseq.h"
+
+struct hciseq_type_cfg {
+ struct hciseq_attr *cmd[9216]; /*
+ * opcodes 0x0000 - 0x23FF
+ * (OGF 0x01 - 0x08)
+ */
+ struct hciseq_attr *evt[256]; /* events 0x00 - 0xFF */
+ struct hciseq_attr *acl;
+};
+
+int parse_config(char *path, struct hciseq_list *_seq,
+ struct hciseq_type_cfg *_type_cfg, bool _verbose);
diff --git a/tools/replay/hciseq.h b/tools/replay/hciseq.h
index 1454147..fa589a3 100644
--- a/tools/replay/hciseq.h
+++ b/tools/replay/hciseq.h
@@ -43,7 +43,8 @@ struct frame {
enum hciseq_action {
HCISEQ_ACTION_REPLAY = 0,
- HCISEQ_ACTION_EMULATE = 1
+ HCISEQ_ACTION_EMULATE = 1,
+ HCISEQ_ACTION_SKIP = 2
};
struct hciseq_list {
diff --git a/tools/replay/main.c b/tools/replay/main.c
index f108b25..9763b7a 100644
--- a/tools/replay/main.c
+++ b/tools/replay/main.c
@@ -41,6 +41,7 @@
#include "main.h"
#include "time.h"
+#include "config-parser.h"
#include "lib/bluetooth.h"
#include "lib/hci.h"
#include "emulator/btdev.h"
@@ -56,6 +57,7 @@
#define TIMING_DELTA 1
static struct hciseq_list dumpseq;
+static struct hciseq_type_cfg type_cfg;
static int fd;
static int pos = 1;
@@ -281,6 +283,35 @@ static void btdev_recv(struct frame *frm)
btdev_receive_h4(btdev, frm->data, frm->data_len);
}
+static struct hciseq_attr *get_type_attr(struct frame *frm)
+{
+ uint8_t pkt_type = ((const uint8_t *) frm->data)[0];
+ uint16_t opcode;
+ uint8_t evt;
+
+ switch (pkt_type) {
+ case BT_H4_CMD_PKT:
+ opcode = *((uint16_t *) (frm->data + 1));
+ if (opcode > 0x2FFF)
+ return NULL;
+ return type_cfg.cmd[opcode];
+ case BT_H4_EVT_PKT:
+ evt = *((uint8_t *) (frm->data + 1));
+
+ /* use attributes of opcode for 'Command Complete' events */
+ if (evt == 0x0e) {
+ opcode = *((uint16_t *) (frm->data + 4));
+ return type_cfg.cmd[opcode];
+ }
+
+ return type_cfg.evt[evt];
+ case BT_H4_ACL_PKT:
+ return type_cfg.acl;
+ default:
+ return NULL;
+ }
+}
+
static bool check_match(struct frame *l, struct frame *r, char *msg)
{
uint8_t type_l = ((const uint8_t *) l->data)[0];
@@ -341,6 +372,7 @@ static bool process_in()
{
static struct frame frm;
static uint8_t data[HCI_MAX_FRAME_SIZE];
+ struct hciseq_attr *attr;
int n;
bool match;
char msg[MAX_MSG];
@@ -362,6 +394,38 @@ static bool process_in()
msg[0] = '\0';
match = check_match(dumpseq.current->frame, &frm, msg);
+ /* check type config */
+ attr = get_type_attr(&frm);
+ if (attr != NULL) {
+ if (attr->action == HCISEQ_ACTION_SKIP) {
+ if (match) {
+ printf("[%4d/%4d] SKIPPING\n", pos,
+ dumpseq.len);
+ return 1;
+ } else {
+ printf("[ Unknown ] %s\n ",
+ msg);
+ dump_frame(&frm);
+ printf(" SKIPPING\n");
+ return 0;
+ }
+ }
+ if (attr->action == HCISEQ_ACTION_EMULATE) {
+ if (match) {
+ printf("[%4d/%4d] EMULATING\n", pos,
+ dumpseq.len);
+ } else {
+ printf("[ Unknown ] %s\n ",
+ msg);
+ printf("EMULATING\n");
+ }
+
+ btdev_recv(&frm);
+
+ return match;
+ }
+ }
+
/* process packet if match */
if (match) {
printf("[%4d/%4d] ", pos, dumpseq.len);
@@ -381,12 +445,20 @@ static bool process_in()
static bool process_out()
{
uint8_t pkt_type;
+ struct hciseq_attr *attr;
/* emulator sends response automatically */
if (dumpseq.current->attr->action == HCISEQ_ACTION_EMULATE) {
return 1;
}
+ /* use type config if set */
+ attr = get_type_attr(dumpseq.current->frame);
+ if (attr != NULL) {
+ if (attr->action == HCISEQ_ACTION_SKIP || attr->action == HCISEQ_ACTION_EMULATE)
+ return true;
+ }
+
pkt_type = ((const uint8_t *) dumpseq.current->frame->data)[0];
switch (pkt_type) {
@@ -412,6 +484,15 @@ static void process()
gettimeofday(&last, NULL);
do {
+ if (dumpseq.current->attr->action == HCISEQ_ACTION_SKIP) {
+ printf("[%4d/%4d] SKIPPING\n ", pos,
+ dumpseq.len);
+ dump_frame(dumpseq.current->frame);
+ dumpseq.current = dumpseq.current->next;
+ pos++;
+ continue;
+ }
+
/* delay */
if (timing == TIMING_DELTA) {
/* consider exec time of process_out()/process_in() */
@@ -485,6 +566,19 @@ static void delete_list()
}
}
+static void delete_type_cfg()
+{
+ int i;
+
+ for (i = 0; i < 12288; i++) {
+ free(type_cfg.cmd[i]);
+ }
+ for (i = 0; i < 256; i++) {
+ free(type_cfg.evt[i]);
+ }
+ free(type_cfg.acl);
+}
+
static void usage(void)
{
printf("hcireplay - Bluetooth replayer\n"
@@ -493,6 +587,7 @@ static void usage(void)
"\t-d, --timing={none|delta} Specify timing mode\n"
"\t-m, --factor=<value> Use timing modifier\n"
"\t-t, --timeout=<value> Use timeout when receiving\n"
+ "\t-c, --config=<file> Use config file\n"
"\t-v, --verbose Enable verbose output\n"
"\t --version Give version information\n"
"\t --help Give a short usage message\n");
@@ -502,6 +597,7 @@ static const struct option main_options[] = {
{"timing", required_argument, NULL, 'd'},
{"factor", required_argument, NULL, 'm'},
{"timeout", required_argument, NULL, 't'},
+ {"config", required_argument, NULL, 'c'},
{"verbose", no_argument, NULL, 'v'},
{"version", no_argument, NULL, 'V'},
{"help", no_argument, NULL, 'H'},
@@ -512,11 +608,12 @@ int main(int argc, char *argv[])
{
int dumpfd;
int i;
+ char *config = NULL;
while (1) {
int opt;
- opt = getopt_long(argc, argv, "d:m:t:v",
+ opt = getopt_long(argc, argv, "d:m:t:c:v",
main_options, NULL);
if (opt < 0)
break;
@@ -535,6 +632,9 @@ int main(int argc, char *argv[])
case 't':
timeout = atoi(optarg);
break;
+ case 'c':
+ config = optarg;
+ break;
case 'v':
verbose = true;
break;
@@ -572,6 +672,20 @@ int main(int argc, char *argv[])
dumpseq.current = dumpseq.frames;
calc_rel_ts(&dumpseq);
+ /* init type config */
+ for (i = 0; i < 9216; i++)
+ type_cfg.cmd[i] = NULL;
+ for (i = 0; i < 256; i++)
+ type_cfg.evt[i] = NULL;
+ type_cfg.acl = NULL;
+
+ if (config != NULL) {
+ if (parse_config(config, &dumpseq, &type_cfg, verbose)) {
+ vhci_close();
+ return 1;
+ }
+ }
+
/* init emulator */
btdev = btdev_create(0);
btdev_set_send_handler(btdev, btdev_send, NULL);
@@ -595,6 +709,7 @@ int main(int argc, char *argv[])
vhci_close();
btdev_destroy(btdev);
delete_list();
+ delete_type_cfg();
printf("Terminating\n");
return EXIT_SUCCESS;
diff --git a/tools/replay/main.h b/tools/replay/main.h
index 2223789..e8b7365 100644
--- a/tools/replay/main.h
+++ b/tools/replay/main.h
@@ -23,8 +23,6 @@
*
*/
-#include "hciseq.h"
-
struct btsnoop_hdr {
uint8_t id[8]; /* Identification Pattern */
uint32_t version; /* Version Number = 1 */
--
1.7.9.5
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH BlueZ v2 1/4] replay: Add initial version of replay tool
2012-08-09 17:36 [PATCH BlueZ 1/4] replay: Add initial version of replay tool Anton Weber
` (2 preceding siblings ...)
2012-08-09 17:36 ` [PATCH BlueZ 4/4] replay: Add config file parser and skip action Anton Weber
@ 2012-08-13 18:34 ` Anton Weber
2012-08-13 18:34 ` [PATCH BlueZ v2 2/4] replay: Add timing functionality Anton Weber
` (2 more replies)
2012-09-05 14:14 ` [PATCH BlueZ v3 1/6] replay: Add initial version of replay tool Anton Weber
4 siblings, 3 replies; 14+ messages in thread
From: Anton Weber @ 2012-08-13 18:34 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anton Weber
This utility uses a VHCI interface to simulate HCI traffic from
a recorded scenario. It reads packets from BTSnoop dump files and
replays them on the virtual interface.
It is meant as a debugging tool that allows to investigate problems
with particular controllers and Bluetooth hardware on other system
configurations.
---
v1 -> v2: File header check for "btsnoop" string incorrectly returned
failure on return value 0 instead of !=0
.gitignore | 1 +
Makefile.tools | 12 +-
tools/replay/hciseq.h | 43 +++++
tools/replay/main.c | 501 +++++++++++++++++++++++++++++++++++++++++++++++++
tools/replay/main.h | 64 +++++++
5 files changed, 620 insertions(+), 1 deletion(-)
create mode 100644 tools/replay/hciseq.h
create mode 100644 tools/replay/main.c
create mode 100644 tools/replay/main.h
diff --git a/.gitignore b/.gitignore
index 38318cd..d53e266 100644
--- a/.gitignore
+++ b/.gitignore
@@ -87,6 +87,7 @@ unit/test-eir
tools/btmgmt
monitor/btmon
emulator/btvirt
+tools/replay/btreplay
doc/*.bak
doc/*.stamp
diff --git a/Makefile.tools b/Makefile.tools
index 5579b86..4a3aca5 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -50,7 +50,7 @@ tools_ppporc_LDADD = lib/libbluetooth-private.la
tools_hcieventmask_LDADD = lib/libbluetooth-private.la
-noinst_PROGRAMS += tools/btmgmt monitor/btmon emulator/btvirt
+noinst_PROGRAMS += tools/btmgmt monitor/btmon emulator/btvirt tools/replay/btreplay
tools_btmgmt_SOURCES = tools/btmgmt.c src/glib-helper.c
tools_btmgmt_LDADD = lib/libbluetooth-private.la @GLIB_LIBS@
@@ -69,6 +69,16 @@ emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \
emulator/vhci.h emulator/vhci.c \
emulator/btdev.h emulator/btdev.c
+tools_replay_btreplay_SOURCES = tools/replay/main.h tools/replay/main.c \
+ tools/replay/hciseq.h \
+ monitor/packet.h monitor/packet.c \
+ monitor/btsnoop.h monitor/btsnoop.c \
+ monitor/control.h monitor/control.c \
+ monitor/mainloop.h monitor/mainloop.c \
+ lib/hci.h
+
+tools_replay_btreplay_LDADD = lib/libbluetooth-private.la
+
if READLINE
bin_PROGRAMS += attrib/gatttool
diff --git a/tools/replay/hciseq.h b/tools/replay/hciseq.h
new file mode 100644
index 0000000..bf953cd
--- /dev/null
+++ b/tools/replay/hciseq.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ * Copyright (C) 2012 Anton Weber <ant@antweb.me>
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+enum hciseq_action {
+ HCISEQ_ACTION_REPLAY = 0,
+};
+
+struct hciseq_list {
+ struct hciseq_node *frames;
+ struct hciseq_node *current;
+ int len;
+};
+
+struct hciseq_attr {
+ enum hciseq_action action;
+};
+
+struct hciseq_node {
+ struct frame *frame;
+ struct hciseq_node *next;
+ struct hciseq_attr *attr;
+};
diff --git a/tools/replay/main.c b/tools/replay/main.c
new file mode 100644
index 0000000..509d968
--- /dev/null
+++ b/tools/replay/main.c
@@ -0,0 +1,501 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ * Copyright (C) 2012 Anton Weber <ant@antweb.me>
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2000-2002 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2003-2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/epoll.h>
+#include <sys/ioctl.h>
+
+#include "main.h"
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "monitor/bt.h"
+#include "monitor/btsnoop.h"
+#include "monitor/control.h"
+#include "monitor/packet.h"
+
+#define MAX_EPOLL_EVENTS 1
+#define MAX_MSG 128
+
+static struct hciseq_list dumpseq;
+
+static int fd;
+static int pos = 1;
+static int skipped = 0;
+
+static int epoll_fd;
+static struct epoll_event epoll_event;
+
+static int timeout = -1;
+static bool verbose = false;
+
+static inline int read_n(int fd, char *buf, int len)
+{
+ int t = 0, w;
+
+ while (len > 0) {
+ w = read(fd, buf, len);
+ if (w < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return -1;
+ } else if (w == 0) {
+ return 0;
+ }
+
+ len -= w;
+ buf += w;
+ t += w;
+ }
+
+ return t;
+}
+
+static int
+parse_btsnoop(int fd, struct frame *frm, struct btsnoop_hdr *hdr)
+{
+ struct btsnoop_pkt pkt;
+ uint8_t pkt_type;
+ uint64_t ts;
+ int n;
+
+ n = read_n(fd, (void *) &pkt, BTSNOOP_PKT_SIZE);
+ if (n < 0)
+ return -1;
+ else if (n == 0)
+ return 0;
+
+ switch (ntohl(hdr->type)) {
+ case 1001:
+ if (ntohl(pkt.flags) & 0x02) {
+ if (ntohl(pkt.flags) & 0x01)
+ pkt_type = HCI_EVENT_PKT;
+ else
+ pkt_type = HCI_COMMAND_PKT;
+ } else
+ pkt_type = HCI_ACLDATA_PKT;
+
+ ((uint8_t *) frm->data)[0] = pkt_type;
+
+ frm->data_len = ntohl(pkt.len) + 1;
+ n = read_n(fd, frm->data + 1, frm->data_len - 1);
+ break;
+
+ case 1002:
+ frm->data_len = ntohl(pkt.len);
+ n = read_n(fd, frm->data, frm->data_len);
+ break;
+ }
+
+ frm->in = ntohl(pkt.flags) & 0x01;
+ ts = ntoh64(pkt.ts) - 0x00E03AB44A676000ll;
+ frm->ts.tv_sec = (ts / 1000000ll) + 946684800ll;
+ frm->ts.tv_usec = ts % 1000000ll;
+
+ return n;
+}
+
+static int parse_dump(int fd, struct hciseq_list *seq)
+{
+ struct frame *frm;
+ struct btsnoop_hdr bh;
+ int n, count;
+ struct hciseq_node *nodeptr, *last;
+
+ last = seq->current;
+
+ /* read BTSnoop header once */
+ if (read_n(fd, (void *) &bh, BTSNOOP_HDR_SIZE) != BTSNOOP_HDR_SIZE)
+ return -1;
+
+ /* check for "btsnoop" string in header */
+ if (memcmp(bh.id, btsnoop_id, sizeof(btsnoop_id)) != 0)
+ return -1;
+
+ count = seq->len;
+ while (1) {
+ frm = malloc(sizeof(*frm));
+ frm->data = malloc(HCI_MAX_FRAME_SIZE);
+
+ n = parse_btsnoop(fd, frm, &bh);
+ if (n <= 0) {
+ free(frm->data);
+ free(frm);
+ return n;
+ }
+
+ frm->ptr = frm->data;
+ frm->len = frm->data_len;
+
+ nodeptr = malloc(sizeof(*nodeptr));
+ nodeptr->frame = frm;
+ nodeptr->attr = malloc(sizeof(*nodeptr->attr));
+ nodeptr->attr->action = HCISEQ_ACTION_REPLAY;
+
+ if (last == NULL)
+ seq->frames = nodeptr;
+ else
+ last->next = nodeptr;
+
+ last = nodeptr;
+ nodeptr->next = NULL;
+ seq->len = ++count;
+ }
+
+ return 0;
+}
+
+static void dump_frame(struct frame *frm)
+{
+ struct timeval tv;
+ uint8_t pkt_type;
+
+ gettimeofday(&tv, NULL);
+
+ pkt_type = ((const uint8_t *) frm->data)[0];
+ switch (pkt_type) {
+ case BT_H4_CMD_PKT:
+ packet_hci_command(&tv, 0x00, frm->data + 1,
+ frm->data_len - 1);
+ break;
+ case BT_H4_EVT_PKT:
+ packet_hci_event(&tv, 0x00, frm->data + 1,
+ frm->data_len - 1);
+ break;
+ case BT_H4_ACL_PKT:
+ if (frm->in)
+ packet_hci_acldata(&tv, 0x00, 0x01,
+ frm->data + 1,
+ frm->data_len - 1);
+ else
+ packet_hci_acldata(&tv, 0x00, 0x00,
+ frm->data + 1,
+ frm->data_len - 1);
+ break;
+ default:
+ //TODO: raw dump
+ break;
+ }
+}
+
+static int send_frm(struct frame *frm)
+{
+ return write(fd, frm->data, frm->data_len);
+}
+
+static int recv_frm(int fd, struct frame *frm)
+{
+ int i, n;
+ int nevs;
+ uint8_t buf[HCI_MAX_FRAME_SIZE];
+ struct epoll_event ev[MAX_EPOLL_EVENTS];
+
+ nevs = epoll_wait(epoll_fd, ev, MAX_EPOLL_EVENTS, timeout);
+ if (nevs < 0)
+ return -1;
+ else if (nevs == 0)
+ return 0;
+
+ for (i = 0; i < nevs; i++) {
+ if (ev[i].events & (EPOLLERR | EPOLLHUP))
+ return -1;
+
+ n = read(fd, (void *) &buf, HCI_MAX_FRAME_SIZE);
+ if (n > 0) {
+ memcpy(frm->data, buf, n);
+ frm->data_len = n;
+ }
+ }
+
+ return n;
+}
+
+static bool check_match(struct frame *l, struct frame *r, char *msg)
+{
+ uint8_t type_l = ((const uint8_t *) l->data)[0];
+ uint8_t type_r = ((const uint8_t *) r->data)[0];
+ uint16_t opcode_l, opcode_r;
+ uint8_t evt_l, evt_r;
+
+ if (type_l != type_r) {
+ snprintf(msg, MAX_MSG,
+ "! Wrong packet type - expected (0x%2.2x), was (0x%2.2x)",
+ type_l, type_r);
+ return false;
+ }
+
+ switch (type_l) {
+ case BT_H4_CMD_PKT:
+ opcode_l = *((uint16_t *) (l->data + 1));
+ opcode_r = *((uint16_t *) (r->data + 1));
+ if (opcode_l != opcode_r) {
+ snprintf(msg, MAX_MSG,
+ "! Wrong opcode - expected (0x%2.2x|0x%4.4x), was (0x%2.2x|0x%4.4x)",
+ cmd_opcode_ogf(opcode_l),
+ cmd_opcode_ocf(opcode_l),
+ cmd_opcode_ogf(opcode_r),
+ cmd_opcode_ocf(opcode_r));
+ return false;
+ } else {
+ return true;
+ }
+ case BT_H4_EVT_PKT:
+ evt_l = *((uint8_t *) (l->data + 1));
+ evt_r = *((uint8_t *) (r->data + 1));
+ if (evt_l != evt_r) {
+ snprintf(msg, MAX_MSG,
+ "! Wrong event type - expected (0x%2.2x), was (0x%2.2x)",
+ evt_l, evt_r);
+ return false;
+ } else {
+ return true;
+ }
+ case BT_H4_ACL_PKT:
+ if (l->data_len != r->data_len)
+ return false;
+
+ return memcmp(l->data, r->data, l->data_len) == 0;
+ default:
+ snprintf(msg, MAX_MSG, "! Unknown packet type (0x%2.2x)",
+ type_l);
+
+ if (l->data_len != r->data_len)
+ return false;
+
+ return memcmp(l->data, r->data, l->data_len) == 0;
+ }
+}
+
+static bool process_in()
+{
+ static struct frame frm;
+ static uint8_t data[HCI_MAX_FRAME_SIZE];
+ int n;
+ bool match;
+ char msg[MAX_MSG];
+
+ frm.data = &data;
+ frm.ptr = frm.data;
+
+ n = recv_frm(fd, &frm);
+ if (n < 0) {
+ perror("Could not receive\n");
+ return false;
+ }
+
+ /* is this the packet in the sequence? */
+ msg[0] = '\0';
+ match = check_match(dumpseq.current->frame, &frm, msg);
+
+ /* process packet if match */
+ if (match)
+ printf("[%4d/%4d] ", pos, dumpseq.len);
+ else
+ printf("[ Unknown ] %s\n ", msg);
+
+ dump_frame(&frm);
+
+ return match;
+}
+
+static bool process_out()
+{
+ uint8_t pkt_type;
+
+ pkt_type = ((const uint8_t *) dumpseq.current->frame->data)[0];
+
+ switch (pkt_type) {
+ case BT_H4_EVT_PKT:
+ case BT_H4_ACL_PKT:
+ printf("[%4d/%4d] ", pos, dumpseq.len);
+ dump_frame(dumpseq.current->frame);
+ send_frm(dumpseq.current->frame);
+ break;
+ default:
+ printf("Unsupported packet 0x%2.2x\n", pkt_type);
+ break;
+ }
+
+ return true;
+}
+
+static void process()
+{
+ bool processed;
+
+ do {
+ if (dumpseq.current->frame->in == 1)
+ processed = process_out();
+ else
+ processed = process_in();
+
+ if (processed) {
+ dumpseq.current = dumpseq.current->next;
+ pos++;
+ }
+ } while (dumpseq.current != NULL);
+
+ printf("Done\n");
+ printf("Processed %d out of %d\n", dumpseq.len - skipped,
+ dumpseq.len);
+}
+
+static int vhci_open()
+{
+ fd = open("/dev/vhci", O_RDWR | O_NONBLOCK);
+ epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+ if (epoll_fd < 0)
+ return -1;
+
+ epoll_event.events = EPOLLIN;
+ epoll_event.data.fd = fd;
+
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD,
+ epoll_event.data.fd, &epoll_event) < 0) {
+ return -1;
+ }
+
+ return fd;
+}
+
+static int vhci_close()
+{
+ epoll_ctl(epoll_fd, EPOLL_CTL_DEL, epoll_event.data.fd, NULL);
+ return close(fd);
+}
+
+static void delete_list()
+{
+ struct hciseq_node *node, *tmp;
+
+ node = dumpseq.frames;
+ while (node != NULL) {
+ tmp = node;
+ node = node->next;
+
+ free(tmp->frame->data);
+ free(tmp->frame);
+ free(tmp->attr);
+ free(tmp);
+ }
+}
+
+static void usage(void)
+{
+ printf("hcireplay - Bluetooth replayer\n"
+ "Usage:\thcireplay-client [options] file...\n"
+ "options:\n"
+ "\t-v, --verbose Enable verbose output\n"
+ "\t --version Give version information\n"
+ "\t --help Give a short usage message\n");
+}
+
+static const struct option main_options[] = {
+ {"verbose", no_argument, NULL, 'v'},
+ {"version", no_argument, NULL, 'V'},
+ {"help", no_argument, NULL, 'H'},
+ {}
+};
+
+int main(int argc, char *argv[])
+{
+ int dumpfd;
+ int i;
+
+ while (1) {
+ int opt;
+
+ opt = getopt_long(argc, argv, "v",
+ main_options, NULL);
+ if (opt < 0)
+ break;
+
+ switch (opt) {
+ case 'v':
+ verbose = true;
+ break;
+ case 'V':
+ printf("%s\n", VERSION);
+ return EXIT_SUCCESS;
+ case 'H':
+ usage();
+ return EXIT_SUCCESS;
+ default:
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (optind >= argc) {
+ usage();
+ return EXIT_FAILURE;
+ }
+
+ dumpseq.current = NULL;
+ dumpseq.frames = NULL;
+ for (i = optind; i < argc; i++) {
+ dumpfd = open(argv[i], O_RDONLY);
+ if (dumpfd < 0) {
+ perror("Failed to open dump file");
+ return EXIT_FAILURE;
+ }
+
+ if (parse_dump(dumpfd, &dumpseq) < 0) {
+ fprintf(stderr, "Error parsing dump file\n");
+ vhci_close();
+ return EXIT_FAILURE;
+ }
+ }
+ dumpseq.current = dumpseq.frames;
+
+ /*
+ * make sure we open the interface after parsing
+ * through all files so we can start without delay
+ */
+ fd = vhci_open();
+ if (fd < 0) {
+ perror("Failed to open VHCI interface");
+ return EXIT_FAILURE;
+ }
+
+ printf("Running\n");
+
+ process();
+
+ vhci_close();
+ delete_list();
+ printf("Terminating\n");
+
+ return EXIT_SUCCESS;
+}
diff --git a/tools/replay/main.h b/tools/replay/main.h
new file mode 100644
index 0000000..d80deec
--- /dev/null
+++ b/tools/replay/main.h
@@ -0,0 +1,64 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ * Copyright (C) 2012 Anton Weber <ant@antweb.me>
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2000-2002 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2003-2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "hciseq.h"
+
+struct btsnoop_hdr {
+ uint8_t id[8]; /* Identification Pattern */
+ uint32_t version; /* Version Number = 1 */
+ uint32_t type; /* Datalink Type */
+} __attribute__ ((packed));
+#define BTSNOOP_HDR_SIZE (sizeof(struct btsnoop_hdr))
+
+struct btsnoop_pkt {
+ uint32_t size; /* Original Length */
+ uint32_t len; /* Included Length */
+ uint32_t flags; /* Packet Flags */
+ uint32_t drops; /* Cumulative Drops */
+ uint64_t ts; /* Timestamp microseconds */
+ uint8_t data[0]; /* Packet Data */
+} __attribute__ ((packed));
+#define BTSNOOP_PKT_SIZE (sizeof(struct btsnoop_pkt))
+
+static uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00 };
+
+struct frame {
+ void *data;
+ uint32_t data_len;
+ void *ptr;
+ uint32_t len;
+ uint16_t dev_id;
+ uint8_t in;
+ uint8_t master;
+ uint16_t handle;
+ uint16_t cid;
+ uint16_t num;
+ uint8_t dlci;
+ uint8_t channel;
+ unsigned long flags;
+ struct timeval ts;
+ int pppdump_fd;
+ int audio_fd;
+};
--
1.7.9.5
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH BlueZ v2 2/4] replay: Add timing functionality
2012-08-13 18:34 ` [PATCH BlueZ v2 1/4] replay: Add initial version of replay tool Anton Weber
@ 2012-08-13 18:34 ` Anton Weber
2012-08-13 18:34 ` [PATCH BlueZ v2 3/4] replay: Add emulation support Anton Weber
2012-08-13 18:34 ` [PATCH BlueZ v2 4/4] replay: Add config file parser and skip action Anton Weber
2 siblings, 0 replies; 14+ messages in thread
From: Anton Weber @ 2012-08-13 18:34 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anton Weber
Add -d parameter that lets user choose between two timing modes.
delta: use time difference between two packets for delay
none: no delay
Add -m parameter to specify a factor that multiplies delays in
delta timing mode.
Add -t parameter to set the epoll timeout when receiving packets.
---
Makefile.tools | 3 +-
tools/replay/hciseq.c | 53 ++++++++++++++++++++++++++++
tools/replay/hciseq.h | 23 +++++++++++++
tools/replay/main.c | 56 +++++++++++++++++++++++++++++-
tools/replay/main.h | 21 +-----------
tools/replay/time.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++
tools/replay/time.h | 31 +++++++++++++++++
7 files changed, 256 insertions(+), 22 deletions(-)
create mode 100644 tools/replay/hciseq.c
create mode 100644 tools/replay/time.c
create mode 100644 tools/replay/time.h
diff --git a/Makefile.tools b/Makefile.tools
index 4a3aca5..6767300 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -70,7 +70,8 @@ emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \
emulator/btdev.h emulator/btdev.c
tools_replay_btreplay_SOURCES = tools/replay/main.h tools/replay/main.c \
- tools/replay/hciseq.h \
+ tools/replay/hciseq.h tools/replay/hciseq.c \
+ tools/replay/time.h tools/replay/time.c \
monitor/packet.h monitor/packet.c \
monitor/btsnoop.h monitor/btsnoop.c \
monitor/control.h monitor/control.c \
diff --git a/tools/replay/hciseq.c b/tools/replay/hciseq.c
new file mode 100644
index 0000000..8b24264
--- /dev/null
+++ b/tools/replay/hciseq.c
@@ -0,0 +1,53 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ * Copyright (C) 2012 Anton Weber <ant@antweb.me>
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "hciseq.h"
+#include "time.h"
+#include "monitor/bt.h"
+
+void calc_rel_ts(struct hciseq_list *seq)
+{
+ struct timeval start;
+ struct hciseq_node *tmp;
+
+ start = seq->current->frame->ts;
+ tmp = seq->current;
+
+ /* first packet */
+ tmp->attr->ts_rel.tv_sec = 0;
+ tmp->attr->ts_rel.tv_usec = 0;
+ tmp->attr->ts_diff.tv_sec = 0;
+ tmp->attr->ts_diff.tv_usec = 0;
+
+ while (tmp->next != NULL) {
+ timeval_diff(&tmp->next->frame->ts, &start,
+ &tmp->next->attr->ts_rel);
+ timeval_diff(&tmp->next->frame->ts, &tmp->frame->ts,
+ &tmp->next->attr->ts_diff);
+ tmp = tmp->next;
+ }
+}
diff --git a/tools/replay/hciseq.h b/tools/replay/hciseq.h
index bf953cd..f9fe7c8 100644
--- a/tools/replay/hciseq.h
+++ b/tools/replay/hciseq.h
@@ -22,6 +22,25 @@
*
*/
+struct frame {
+ void *data;
+ uint32_t data_len;
+ void *ptr;
+ uint32_t len;
+ uint16_t dev_id;
+ uint8_t in;
+ uint8_t master;
+ uint16_t handle;
+ uint16_t cid;
+ uint16_t num;
+ uint8_t dlci;
+ uint8_t channel;
+ unsigned long flags;
+ struct timeval ts;
+ int pppdump_fd;
+ int audio_fd;
+};
+
enum hciseq_action {
HCISEQ_ACTION_REPLAY = 0,
};
@@ -33,6 +52,8 @@ struct hciseq_list {
};
struct hciseq_attr {
+ struct timeval ts_rel;
+ struct timeval ts_diff;
enum hciseq_action action;
};
@@ -41,3 +62,5 @@ struct hciseq_node {
struct hciseq_node *next;
struct hciseq_attr *attr;
};
+
+void calc_rel_ts(struct hciseq_list *seq);
diff --git a/tools/replay/main.c b/tools/replay/main.c
index 509d968..578936f 100644
--- a/tools/replay/main.c
+++ b/tools/replay/main.c
@@ -37,8 +37,10 @@
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
+#include <sys/time.h>
#include "main.h"
+#include "time.h"
#include "lib/bluetooth.h"
#include "lib/hci.h"
#include "monitor/bt.h"
@@ -49,16 +51,22 @@
#define MAX_EPOLL_EVENTS 1
#define MAX_MSG 128
+#define TIMING_NONE 0
+#define TIMING_DELTA 1
+
static struct hciseq_list dumpseq;
static int fd;
static int pos = 1;
static int skipped = 0;
+static struct timeval start;
static int epoll_fd;
static struct epoll_event epoll_event;
static int timeout = -1;
+static int timing = TIMING_NONE;
+static double factor = 1;
static bool verbose = false;
static inline int read_n(int fd, char *buf, int len)
@@ -313,6 +321,10 @@ static bool process_in()
if (n < 0) {
perror("Could not receive\n");
return false;
+ } else if (n == 0) {
+ printf("[%4d/%4d] Timeout\n", pos, dumpseq.len);
+ skipped++;
+ return true;
}
/* is this the packet in the sequence? */
@@ -353,9 +365,29 @@ static bool process_out()
static void process()
{
+ struct timeval last, last_diff;
+ __useconds_t delay;
bool processed;
+ gettimeofday(&last, NULL);
do {
+ /* delay */
+ if (timing == TIMING_DELTA) {
+ /* consider exec time of process_out()/process_in() */
+ get_timeval_passed(&last, &last_diff);
+ if (timeval_cmp(&dumpseq.current->attr->ts_diff, &last_diff) >= 0) {
+ delay = timeval_diff(&dumpseq.current->attr->ts_diff,
+ &last_diff, NULL);
+ delay *= factor;
+ if (usleep(delay) == -1)
+ printf("Delay failed\n");
+ } else {
+ /* exec time was longer than delay */
+ printf("Packet delay - processing previous packet took longer than recorded time difference\n");
+ }
+ gettimeofday(&last, NULL);
+ }
+
if (dumpseq.current->frame->in == 1)
processed = process_out();
else
@@ -417,12 +449,18 @@ static void usage(void)
printf("hcireplay - Bluetooth replayer\n"
"Usage:\thcireplay-client [options] file...\n"
"options:\n"
+ "\t-d, --timing={none|delta} Specify timing mode\n"
+ "\t-m, --factor=<value> Use timing modifier\n"
+ "\t-t, --timeout=<value> Use timeout when receiving\n"
"\t-v, --verbose Enable verbose output\n"
"\t --version Give version information\n"
"\t --help Give a short usage message\n");
}
static const struct option main_options[] = {
+ {"timing", required_argument, NULL, 'd'},
+ {"factor", required_argument, NULL, 'm'},
+ {"timeout", required_argument, NULL, 't'},
{"verbose", no_argument, NULL, 'v'},
{"version", no_argument, NULL, 'V'},
{"help", no_argument, NULL, 'H'},
@@ -437,12 +475,25 @@ int main(int argc, char *argv[])
while (1) {
int opt;
- opt = getopt_long(argc, argv, "v",
+ opt = getopt_long(argc, argv, "d:m:t:v",
main_options, NULL);
if (opt < 0)
break;
switch (opt) {
+ case 'd':
+ if (!strcmp(optarg, "none"))
+ timing = TIMING_NONE;
+ else if (!strcmp(optarg, "delta"))
+ timing = TIMING_DELTA;
+
+ break;
+ case 'm':
+ factor = atof(optarg);
+ break;
+ case 't':
+ timeout = atoi(optarg);
+ break;
case 'v':
verbose = true;
break;
@@ -478,6 +529,9 @@ int main(int argc, char *argv[])
}
}
dumpseq.current = dumpseq.frames;
+ calc_rel_ts(&dumpseq);
+
+ gettimeofday(&start, NULL);
/*
* make sure we open the interface after parsing
diff --git a/tools/replay/main.h b/tools/replay/main.h
index d80deec..2223789 100644
--- a/tools/replay/main.h
+++ b/tools/replay/main.h
@@ -42,23 +42,4 @@ struct btsnoop_pkt {
} __attribute__ ((packed));
#define BTSNOOP_PKT_SIZE (sizeof(struct btsnoop_pkt))
-static uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00 };
-
-struct frame {
- void *data;
- uint32_t data_len;
- void *ptr;
- uint32_t len;
- uint16_t dev_id;
- uint8_t in;
- uint8_t master;
- uint16_t handle;
- uint16_t cid;
- uint16_t num;
- uint8_t dlci;
- uint8_t channel;
- unsigned long flags;
- struct timeval ts;
- int pppdump_fd;
- int audio_fd;
-};
+uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00 };
diff --git a/tools/replay/time.c b/tools/replay/time.c
new file mode 100644
index 0000000..029501a
--- /dev/null
+++ b/tools/replay/time.c
@@ -0,0 +1,91 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ * Copyright (C) 2012 Anton Weber <ant@antweb.me>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdlib.h>
+#include <sys/time.h>
+
+#include "time.h"
+
+/*
+ * Adjust timeval structs to make sure usec difference is not negative
+ * see http://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html
+ */
+void timeval_adjust_usec(struct timeval *l, struct timeval *r) {
+ int tmpsec;
+
+ if (r->tv_usec > l->tv_usec) {
+ tmpsec = (r->tv_usec - l->tv_usec) / 1000000 + 1;
+ r->tv_sec += tmpsec;
+ r->tv_usec -= 1000000 * tmpsec;
+ }
+
+ if ((l->tv_usec - r->tv_usec) > 1000000) {
+ tmpsec = (l->tv_usec - r->tv_usec) / 1000000;
+ r->tv_sec -= tmpsec;
+ r->tv_usec += 1000000 * tmpsec;
+ }
+}
+
+__useconds_t
+timeval_diff(struct timeval *l, struct timeval *r, struct timeval *diff)
+{
+ static struct timeval tmp;
+
+ timeval_adjust_usec(l, r);
+
+ /* use local variable if we only need return value */
+ if (diff == NULL)
+ diff = &tmp;
+
+ diff->tv_sec = l->tv_sec - r->tv_sec;
+ diff->tv_usec = l->tv_usec - r->tv_usec;
+
+ return (diff->tv_sec * 1000000) + diff->tv_usec;
+}
+
+int timeval_cmp(struct timeval *l, struct timeval *r)
+{
+ timeval_adjust_usec(l, r);
+
+ if (l->tv_sec > r->tv_sec) {
+ return 1;
+ } else if (l->tv_sec < r->tv_sec) {
+ return -1;
+ } else {
+ if (l->tv_usec > r->tv_usec)
+ return 1;
+ else if (l->tv_usec < r->tv_usec)
+ return -1;
+ else
+ return 0;
+ }
+}
+
+inline __useconds_t
+get_timeval_passed(struct timeval *since, struct timeval *diff)
+{
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+
+ return timeval_diff(&now, since, diff);
+}
diff --git a/tools/replay/time.h b/tools/replay/time.h
new file mode 100644
index 0000000..0c876a4
--- /dev/null
+++ b/tools/replay/time.h
@@ -0,0 +1,31 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ * Copyright (C) 2012 Anton Weber <ant@antweb.me>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+void timeval_adjust_usec(struct timeval *l, struct timeval *r);
+
+__useconds_t
+timeval_diff(struct timeval *l, struct timeval *r, struct timeval *diff);
+
+int timeval_cmp(struct timeval *l, struct timeval *r);
+
+inline __useconds_t
+ get_timeval_passed(struct timeval *since, struct timeval *diff);
--
1.7.9.5
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH BlueZ v2 3/4] replay: Add emulation support
2012-08-13 18:34 ` [PATCH BlueZ v2 1/4] replay: Add initial version of replay tool Anton Weber
2012-08-13 18:34 ` [PATCH BlueZ v2 2/4] replay: Add timing functionality Anton Weber
@ 2012-08-13 18:34 ` Anton Weber
2012-08-13 18:34 ` [PATCH BlueZ v2 4/4] replay: Add config file parser and skip action Anton Weber
2 siblings, 0 replies; 14+ messages in thread
From: Anton Weber @ 2012-08-13 18:34 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anton Weber
Integrate emulator so it can process selected packets instead of
replaying them from the sequence. The behaviour is controlled
through the action attribute in hciseq_attr.
If set to HCISEQ_ACTION_EMULATE, hcireplay will use the emulator
to process the packet automatically instead of replaying it.
HCISEQ_ACTION_REPLAY keeps the previous behaviour and replays the
packet using the dump file.
---
Makefile.tools | 1 +
tools/replay/hciseq.h | 1 +
tools/replay/main.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++---
3 files changed, 51 insertions(+), 3 deletions(-)
diff --git a/Makefile.tools b/Makefile.tools
index 6767300..8b3c8d8 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -76,6 +76,7 @@ tools_replay_btreplay_SOURCES = tools/replay/main.h tools/replay/main.c \
monitor/btsnoop.h monitor/btsnoop.c \
monitor/control.h monitor/control.c \
monitor/mainloop.h monitor/mainloop.c \
+ emulator/btdev.h emulator/btdev.c \
lib/hci.h
tools_replay_btreplay_LDADD = lib/libbluetooth-private.la
diff --git a/tools/replay/hciseq.h b/tools/replay/hciseq.h
index f9fe7c8..1454147 100644
--- a/tools/replay/hciseq.h
+++ b/tools/replay/hciseq.h
@@ -43,6 +43,7 @@ struct frame {
enum hciseq_action {
HCISEQ_ACTION_REPLAY = 0,
+ HCISEQ_ACTION_EMULATE = 1
};
struct hciseq_list {
diff --git a/tools/replay/main.c b/tools/replay/main.c
index 578936f..b80eb7f 100644
--- a/tools/replay/main.c
+++ b/tools/replay/main.c
@@ -43,6 +43,7 @@
#include "time.h"
#include "lib/bluetooth.h"
#include "lib/hci.h"
+#include "emulator/btdev.h"
#include "monitor/bt.h"
#include "monitor/btsnoop.h"
#include "monitor/control.h"
@@ -69,6 +70,8 @@ static int timing = TIMING_NONE;
static double factor = 1;
static bool verbose = false;
+static struct btdev *btdev;
+
static inline int read_n(int fd, char *buf, int len)
{
int t = 0, w;
@@ -250,6 +253,34 @@ static int recv_frm(int fd, struct frame *frm)
return n;
}
+static void btdev_send(const void *data, uint16_t len, void *user_data)
+{
+ struct frame frm;
+ static void* tmpdata = NULL;
+
+ /* copy data so we respect 'const' qualifier */
+ if(tmpdata == NULL)
+ tmpdata = malloc(HCI_MAX_FRAME_SIZE);
+
+ memcpy(tmpdata, data, len);
+
+ frm.data = tmpdata;
+ frm.len = len;
+ frm.data_len = len;
+ frm.in = 1;
+ printf("[Emulator ] ");
+ dump_frame(&frm);
+ send_frm(&frm);
+}
+
+static void btdev_recv(struct frame *frm)
+{
+ frm->in = 0;
+ printf("[Emulator ] ");
+ dump_frame(frm);
+ btdev_receive_h4(btdev, frm->data, frm->data_len);
+}
+
static bool check_match(struct frame *l, struct frame *r, char *msg)
{
uint8_t type_l = ((const uint8_t *) l->data)[0];
@@ -332,11 +363,16 @@ static bool process_in()
match = check_match(dumpseq.current->frame, &frm, msg);
/* process packet if match */
- if (match)
+ if (match) {
printf("[%4d/%4d] ", pos, dumpseq.len);
- else
- printf("[ Unknown ] %s\n ", msg);
+ if (dumpseq.current->attr->action == HCISEQ_ACTION_EMULATE) {
+ btdev_recv(&frm);
+ return true;
+ }
+ } else {
+ printf("[ Unknown ] %s\n ", msg);
+ }
dump_frame(&frm);
return match;
@@ -346,6 +382,11 @@ static bool process_out()
{
uint8_t pkt_type;
+ /* emulator sends response automatically */
+ if (dumpseq.current->attr->action == HCISEQ_ACTION_EMULATE) {
+ return 1;
+ }
+
pkt_type = ((const uint8_t *) dumpseq.current->frame->data)[0];
switch (pkt_type) {
@@ -531,6 +572,10 @@ int main(int argc, char *argv[])
dumpseq.current = dumpseq.frames;
calc_rel_ts(&dumpseq);
+ /* init emulator */
+ btdev = btdev_create(0);
+ btdev_set_send_handler(btdev, btdev_send, NULL);
+
gettimeofday(&start, NULL);
/*
@@ -548,6 +593,7 @@ int main(int argc, char *argv[])
process();
vhci_close();
+ btdev_destroy(btdev);
delete_list();
printf("Terminating\n");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH BlueZ v2 4/4] replay: Add config file parser and skip action
2012-08-13 18:34 ` [PATCH BlueZ v2 1/4] replay: Add initial version of replay tool Anton Weber
2012-08-13 18:34 ` [PATCH BlueZ v2 2/4] replay: Add timing functionality Anton Weber
2012-08-13 18:34 ` [PATCH BlueZ v2 3/4] replay: Add emulation support Anton Weber
@ 2012-08-13 18:34 ` Anton Weber
2 siblings, 0 replies; 14+ messages in thread
From: Anton Weber @ 2012-08-13 18:34 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anton Weber
Add support for config files to manipulate the loaded sequence.
A config file can be specified with the new -c command line parameter.
The config syntax is:
scope attr=value[,attr=value,...]
e.g.:
all delta=300,action=replay
1-4 action=emulate
1 delta=500
HCI_EVT_0x0e delta=0
HCI_CMD_0x03|0x0003 action=emulate
HCI_ACL action=replay
The inital version supports the attributes delta and action and
all scope types shown in the example.
HCISEQ_ACTION_SKIP allows to skip a packet in the sequence.
The action can be set in config files using "action=skip".
---
v1 -> v2: delete_type_cfg() was using an outdated number of entries when
freeing type_cfg.cmd[]
Makefile.tools | 1 +
tools/replay/config-parser.c | 506 ++++++++++++++++++++++++++++++++++++++++++
tools/replay/config-parser.h | 36 +++
tools/replay/hciseq.h | 3 +-
tools/replay/main.c | 117 +++++++++-
tools/replay/main.h | 2 -
6 files changed, 661 insertions(+), 4 deletions(-)
create mode 100644 tools/replay/config-parser.c
create mode 100644 tools/replay/config-parser.h
diff --git a/Makefile.tools b/Makefile.tools
index 8b3c8d8..ba2db00 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -72,6 +72,7 @@ emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \
tools_replay_btreplay_SOURCES = tools/replay/main.h tools/replay/main.c \
tools/replay/hciseq.h tools/replay/hciseq.c \
tools/replay/time.h tools/replay/time.c \
+ tools/replay/config-parser.h tools/replay/config-parser.c \
monitor/packet.h monitor/packet.c \
monitor/btsnoop.h monitor/btsnoop.c \
monitor/control.h monitor/control.c \
diff --git a/tools/replay/config-parser.c b/tools/replay/config-parser.c
new file mode 100644
index 0000000..5219eb9
--- /dev/null
+++ b/tools/replay/config-parser.c
@@ -0,0 +1,506 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ * Copyright (C) 2012 Anton Weber <ant@antweb.me>
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <sys/time.h>
+
+#include "config-parser.h"
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "monitor/bt.h"
+
+#define MAXLINE 128
+#define MAX_ATTR_KEY 32
+#define MAX_ATTR_VAL 32
+
+static struct hciseq_list *seq;
+static struct hciseq_type_cfg *type_cfg;
+
+static bool verbose;
+static int line;
+
+struct scope_list {
+ struct scope_node *head;
+};
+
+struct scope_node {
+ int pos;
+ struct hciseq_attr *attr;
+ struct scope_node *next;
+};
+
+struct attr_list {
+ struct attr_node *head;
+};
+
+struct attr_node {
+ char key[MAX_ATTR_KEY];
+ char val[MAX_ATTR_VAL];
+ struct attr_node *next;
+};
+
+static void attr_list_delete(struct attr_list *list)
+{
+ struct attr_node *node, *next;
+
+ if (list == NULL)
+ return;
+
+ node = list->head;
+ free(list);
+ while (node != NULL) {
+ next = node->next;
+ free(node);
+ node = next;
+ }
+}
+
+static void scope_list_delete(struct scope_list *list)
+{
+ struct scope_node *node, *next;
+
+ if (list == NULL)
+ return;
+
+ node = list->head;
+ free(list);
+ while (node != NULL) {
+ next = node->next;
+ free(node);
+ node = next;
+ }
+}
+
+static struct attr_list *parse_attrstr(char *attrstr)
+{
+ struct attr_list *list = NULL;
+ struct attr_node *node;
+ char *res;
+
+ do {
+ if (list == NULL) {
+ if ((res = strtok(attrstr, "=")) == NULL) {
+ /* nothing to parse */
+ return NULL;
+ }
+
+ list = malloc(sizeof(*list));
+ node = malloc(sizeof(*node));
+ list->head = node;
+ } else {
+ if ((res = strtok(NULL, "=")) == NULL) {
+ /* nothing left to parse */
+ break;
+ }
+
+ node->next = malloc(sizeof(*node));
+ node = node->next;
+ }
+
+ strncpy(node->key, res, sizeof(node->key));
+ node->key[sizeof(node->key) - 1] = '\0';
+
+ if ((res = strtok(NULL, ",")) == NULL) {
+ fprintf(stderr, "Invalid attribute");
+ goto err;
+ }
+ strncpy(node->val, res, sizeof(node->val));
+ node->val[sizeof(node->val) - 1] = '\0';
+
+ node->next = NULL;
+ } while (res != NULL);
+
+ return list;
+
+err:
+ attr_list_delete(list);
+ return NULL;
+}
+
+static int apply_attr(struct scope_node *scope_node,
+ struct attr_list *list)
+{
+ struct attr_node *attr_node = list->head;
+ struct hciseq_attr *attr = scope_node->attr;
+ long lval;
+
+ while (attr_node != NULL) {
+ if (strcmp(attr_node->key, "delta") == 0) {
+ /* delta */
+ lval = strtol(attr_node->val, NULL, 10);
+ if (errno == ERANGE || errno == EINVAL)
+ return 1;
+
+ if (verbose) {
+ printf("\t[%d] set delta to %ld\n",
+ scope_node->pos, lval);
+ }
+
+ attr->ts_diff.tv_sec = 0;
+ attr->ts_diff.tv_usec = lval;
+ } else if (strcmp(attr_node->key, "action") == 0) {
+ /* action */
+ if (strcmp(attr_node->val, "replay") == 0) {
+ lval = HCISEQ_ACTION_REPLAY;
+ if (verbose)
+ printf("\t[%d] set action to 'replay'\n",
+ scope_node->pos);
+ } else if (strcmp(attr_node->val, "emulate") == 0) {
+ lval = HCISEQ_ACTION_EMULATE;
+ if (verbose)
+ printf("\t[%d] set action to 'emulate'\n",
+ scope_node->pos);
+ } else if (strcmp(attr_node->val, "skip") == 0) {
+ lval = HCISEQ_ACTION_SKIP;
+ if (verbose)
+ printf("\t[%d] set action to 'skip'\n",
+ scope_node->pos);
+ } else {
+ return 1;
+ }
+
+ attr->action = lval;
+ }
+
+ attr_node = attr_node->next;
+ }
+
+ return 0;
+}
+
+static int apply_attr_scope(struct scope_list *scope,
+ struct attr_list *attr)
+{
+ struct scope_node *node = scope->head;
+
+ while (node != NULL) {
+ apply_attr(node, attr);
+ node = node->next;
+ }
+
+ return 0;
+}
+
+static struct scope_list *get_scope_range(int from, int to)
+{
+ struct scope_list *list = NULL;
+ struct scope_node *scope_node;
+ struct hciseq_node *seq_node = seq->current;
+ int pos = 1;
+
+ /* forward to 'from' */
+ while (pos < from) {
+ seq_node = seq_node->next;
+ pos++;
+ }
+
+ /* create scope list for range */
+ while (pos <= to) {
+ if (verbose)
+ printf("\tadd packet [%d]\n", pos);
+
+ if (list == NULL) {
+ list = malloc(sizeof(*list));
+ scope_node = malloc(sizeof(*scope_node));
+ list->head = scope_node;
+ } else {
+ scope_node->next = malloc(sizeof(*scope_node));
+ scope_node = scope_node->next;
+ }
+ scope_node->attr = seq_node->attr;
+ scope_node->pos = pos;
+ scope_node->next = NULL;
+
+ seq_node = seq_node->next;
+ pos++;
+ }
+
+ return list;
+}
+
+static struct scope_list *get_scope_type(uint8_t type, void *filter1,
+ void *filter2)
+{
+ struct scope_list *list = NULL;
+ struct scope_node *scope_node;
+ struct hciseq_node *seq_node = seq->current;
+ uint16_t opcode, node_opcode;
+ uint8_t node_ogf, ogf = 0x00;
+ uint16_t node_ocf, ocf = 0x0000;
+ uint8_t node_evt, evt = 0x00;
+ bool match;
+ int pos = 1;
+ struct hciseq_attr *attr;
+
+ if (type == BT_H4_CMD_PKT) {
+ ogf = *((uint8_t *) filter1);
+ ocf = *((uint16_t *) filter2);
+ opcode = cmd_opcode_pack(ogf, ocf);
+
+ if (opcode > 0x2FFF) {
+ attr = NULL;
+ } else {
+ attr = type_cfg->cmd[opcode];
+ if (attr == NULL) {
+ attr = malloc(sizeof(*attr));
+ type_cfg->cmd[opcode] = attr;
+ }
+ }
+ } else if (type == BT_H4_EVT_PKT) {
+ evt = *((uint8_t *) filter1);
+ attr = type_cfg->evt[evt];
+
+ if (attr == NULL) {
+ attr = malloc(sizeof(*attr));
+ type_cfg->evt[evt] = attr;
+ }
+ } else if (type == BT_H4_ACL_PKT) {
+ attr = type_cfg->acl;
+ if (attr == NULL) {
+ attr = malloc(sizeof(*attr));
+ type_cfg->acl = attr;
+ }
+ } else {
+ attr = NULL;
+ }
+
+ /* add matching packets in sequence */
+ while (seq_node != NULL) {
+ match = false;
+ if (((uint8_t *) seq_node->frame->data)[0] == type) {
+ if (type == BT_H4_CMD_PKT) {
+ node_opcode = *((uint16_t *)
+ (seq_node->frame->data + 1));
+ node_ogf = cmd_opcode_ogf(node_opcode);
+ node_ocf = cmd_opcode_ocf(node_opcode);
+ if (node_ogf == ogf && node_ocf == ocf)
+ match = true;
+ } else if (type == BT_H4_EVT_PKT) {
+ node_evt = ((uint8_t *) seq_node->frame->data)[1];
+ if (evt == node_evt)
+ match = true;
+ } else if (type == BT_H4_ACL_PKT) {
+ match = true;
+ }
+ }
+
+ if (match) {
+ if (verbose)
+ printf("\tadd packet [%d]\n", pos);
+
+ if (list == NULL) {
+ list = malloc(sizeof(*list));
+ scope_node = malloc(sizeof(*scope_node));
+ list->head = scope_node;
+ } else {
+ scope_node->next = malloc(sizeof(*scope_node));
+ scope_node = scope_node->next;
+ }
+ scope_node->attr = seq_node->attr;
+ scope_node->pos = pos;
+ scope_node->next = NULL;
+ }
+ seq_node = seq_node->next;
+ pos++;
+ }
+
+ /* add type config */
+ if (attr != NULL) {
+ if (list == NULL) {
+ list = malloc(sizeof(*list));
+ scope_node = malloc(sizeof(*scope_node));
+ list->head = scope_node;
+ } else {
+ scope_node->next = malloc(sizeof(*scope_node));
+ scope_node = scope_node->next;
+ }
+
+ scope_node->attr = attr;
+ scope_node->pos = 0;
+ scope_node->next = NULL;
+ }
+
+ return list;
+}
+
+static int parse_line(char *buf)
+{
+ char *scopestr, *attrstr;
+ struct scope_list *scope_list = NULL;
+ struct attr_list *attr_list;
+ uint8_t evt, ogf;
+ uint16_t ocf;
+ char *res;
+ int from, to;
+
+ line++;
+
+ /* split line into scope and attributes */
+ if ((scopestr = strtok(buf, " ")) == NULL)
+ return 1;
+
+ if ((attrstr = strtok(NULL, "\n")) == NULL)
+ return 1;
+
+ if (verbose)
+ printf("Parsing scope (%s)\n", scopestr);
+
+ if (strcmp(scopestr, "all") == 0) {
+ if (verbose)
+ printf("\tadd all\n");
+
+ scope_list = get_scope_range(0, seq->len);
+ } else if ((strncmp(scopestr, "HCI_", 4) == 0) && strlen(scopestr) >= 7) {
+ /* make sure scopestr is at least 7 chars long, so we can check for HCI_XXX */
+
+ if (strncmp(scopestr + 4, "ACL", 3) == 0) {
+ /* scope is HCI_ACL */
+ if (verbose)
+ printf("\tadd all HCI ACL data packets:");
+
+ scope_list = get_scope_type(BT_H4_ACL_PKT, NULL, NULL);
+ } else if (strncmp(scopestr + 4, "CMD", 3) == 0) {
+ /* scope is HCI_CMD_
+ * length must be exactly 19 (e.g. HCI_CMD_0x03|0x0003) */
+ if (strlen(scopestr) != 19 || scopestr[12] != '|')
+ return 1;
+
+ if (sscanf(scopestr + 8, "0x%2hhx", &ogf) <= 0)
+ return 1;
+
+ if (sscanf(scopestr + 13, "0x%4hx", &ocf) <= 0)
+ return 1;
+
+ if (verbose)
+ printf("\tadd all HCI command packets with opcode (0x%2.2x|0x%4.4x):\n",
+ ogf, ocf);
+
+ scope_list = get_scope_type(BT_H4_CMD_PKT, &ogf, &ocf);
+ } else if (strncmp(scopestr + 4, "EVT", 3) == 0) {
+ /* scope is CMD_EVT_
+ * length must be exactly 12 (e.g. HCI_EVT_0x0e) */
+ if (strlen(scopestr) != 12)
+ return 1;
+
+ if (sscanf(scopestr + 8, "0x%2hhx", &evt) <= 0)
+ return 1;
+
+ if (verbose)
+ printf("\tadd all HCI event packets with event type (0x%2.2x):\n",
+ evt);
+
+ scope_list = get_scope_type(BT_H4_EVT_PKT, &evt, NULL);
+ }
+ } else if (scopestr[0] >= 48 || scopestr[0] <= 57) {
+ /* first char is a digit */
+ if ((res = strtok(scopestr, "-")) == NULL)
+ return 1;
+
+ from = atoi(res);
+ if (from <= 0)
+ return 1;
+
+ if ((res = strtok(NULL, ":")) == NULL) {
+ /* just one packet */
+ if (verbose)
+ printf("\tadd packet single packet\n");
+
+ scope_list = get_scope_range(from, from);
+ } else {
+ /* range */
+ to = atoi(res);
+ if (to > seq->len)
+ return 1;
+
+ if (verbose)
+ printf("\tadd packets %d to %d\n", from, to);
+
+ scope_list = get_scope_range(from, to);
+ }
+
+ }
+
+ if (verbose)
+ printf("Parsing attributes (%s)\n", attrstr);
+
+ if ((attr_list = parse_attrstr(attrstr)) == NULL) {
+ return 1;
+ }
+
+ if (scope_list != NULL) {
+ apply_attr_scope(scope_list, attr_list);
+ scope_list_delete(scope_list);
+ } else {
+ if (verbose)
+ printf("Empty scope, skipping\n");
+ }
+
+ attr_list_delete(attr_list);
+
+ return 0;
+}
+
+int parse_config(char *path, struct hciseq_list *_seq,
+ struct hciseq_type_cfg *_type_cfg, bool _verbose)
+{
+ char *buf;
+ FILE *file;
+
+ seq = _seq;
+ type_cfg = _type_cfg;
+ verbose = _verbose;
+ line = 0;
+
+ printf("Reading config file...\n");
+
+ buf = malloc(sizeof(char) * MAXLINE);
+ if (buf == NULL) {
+ fprintf(stderr, "Failed to allocate buffer\n");
+ return 1;
+ }
+
+ if ((file = fopen(path, "r")) == NULL) {
+ perror("Failed to open config file");
+ return 1;
+ }
+
+ while (fgets(buf, MAXLINE, file) != NULL) {
+ if (parse_line(buf)) {
+ fprintf(stderr, "Error parsing config file - line %d\n",
+ line);
+ free(buf);
+ return 1;
+ }
+ }
+
+ printf("Done\n\n");
+ fclose(file);
+ free(buf);
+
+ return 0;
+}
diff --git a/tools/replay/config-parser.h b/tools/replay/config-parser.h
new file mode 100644
index 0000000..265628d
--- /dev/null
+++ b/tools/replay/config-parser.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ * Copyright (C) 2012 Anton Weber <ant@antweb.me>
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "hciseq.h"
+
+struct hciseq_type_cfg {
+ struct hciseq_attr *cmd[9216]; /*
+ * opcodes 0x0000 - 0x23FF
+ * (OGF 0x01 - 0x08)
+ */
+ struct hciseq_attr *evt[256]; /* events 0x00 - 0xFF */
+ struct hciseq_attr *acl;
+};
+
+int parse_config(char *path, struct hciseq_list *_seq,
+ struct hciseq_type_cfg *_type_cfg, bool _verbose);
diff --git a/tools/replay/hciseq.h b/tools/replay/hciseq.h
index 1454147..fa589a3 100644
--- a/tools/replay/hciseq.h
+++ b/tools/replay/hciseq.h
@@ -43,7 +43,8 @@ struct frame {
enum hciseq_action {
HCISEQ_ACTION_REPLAY = 0,
- HCISEQ_ACTION_EMULATE = 1
+ HCISEQ_ACTION_EMULATE = 1,
+ HCISEQ_ACTION_SKIP = 2
};
struct hciseq_list {
diff --git a/tools/replay/main.c b/tools/replay/main.c
index b80eb7f..b9597be 100644
--- a/tools/replay/main.c
+++ b/tools/replay/main.c
@@ -41,6 +41,7 @@
#include "main.h"
#include "time.h"
+#include "config-parser.h"
#include "lib/bluetooth.h"
#include "lib/hci.h"
#include "emulator/btdev.h"
@@ -56,6 +57,7 @@
#define TIMING_DELTA 1
static struct hciseq_list dumpseq;
+static struct hciseq_type_cfg type_cfg;
static int fd;
static int pos = 1;
@@ -281,6 +283,35 @@ static void btdev_recv(struct frame *frm)
btdev_receive_h4(btdev, frm->data, frm->data_len);
}
+static struct hciseq_attr *get_type_attr(struct frame *frm)
+{
+ uint8_t pkt_type = ((const uint8_t *) frm->data)[0];
+ uint16_t opcode;
+ uint8_t evt;
+
+ switch (pkt_type) {
+ case BT_H4_CMD_PKT:
+ opcode = *((uint16_t *) (frm->data + 1));
+ if (opcode > 0x2FFF)
+ return NULL;
+ return type_cfg.cmd[opcode];
+ case BT_H4_EVT_PKT:
+ evt = *((uint8_t *) (frm->data + 1));
+
+ /* use attributes of opcode for 'Command Complete' events */
+ if (evt == 0x0e) {
+ opcode = *((uint16_t *) (frm->data + 4));
+ return type_cfg.cmd[opcode];
+ }
+
+ return type_cfg.evt[evt];
+ case BT_H4_ACL_PKT:
+ return type_cfg.acl;
+ default:
+ return NULL;
+ }
+}
+
static bool check_match(struct frame *l, struct frame *r, char *msg)
{
uint8_t type_l = ((const uint8_t *) l->data)[0];
@@ -341,6 +372,7 @@ static bool process_in()
{
static struct frame frm;
static uint8_t data[HCI_MAX_FRAME_SIZE];
+ struct hciseq_attr *attr;
int n;
bool match;
char msg[MAX_MSG];
@@ -362,6 +394,38 @@ static bool process_in()
msg[0] = '\0';
match = check_match(dumpseq.current->frame, &frm, msg);
+ /* check type config */
+ attr = get_type_attr(&frm);
+ if (attr != NULL) {
+ if (attr->action == HCISEQ_ACTION_SKIP) {
+ if (match) {
+ printf("[%4d/%4d] SKIPPING\n", pos,
+ dumpseq.len);
+ return 1;
+ } else {
+ printf("[ Unknown ] %s\n ",
+ msg);
+ dump_frame(&frm);
+ printf(" SKIPPING\n");
+ return 0;
+ }
+ }
+ if (attr->action == HCISEQ_ACTION_EMULATE) {
+ if (match) {
+ printf("[%4d/%4d] EMULATING\n", pos,
+ dumpseq.len);
+ } else {
+ printf("[ Unknown ] %s\n ",
+ msg);
+ printf("EMULATING\n");
+ }
+
+ btdev_recv(&frm);
+
+ return match;
+ }
+ }
+
/* process packet if match */
if (match) {
printf("[%4d/%4d] ", pos, dumpseq.len);
@@ -381,12 +445,20 @@ static bool process_in()
static bool process_out()
{
uint8_t pkt_type;
+ struct hciseq_attr *attr;
/* emulator sends response automatically */
if (dumpseq.current->attr->action == HCISEQ_ACTION_EMULATE) {
return 1;
}
+ /* use type config if set */
+ attr = get_type_attr(dumpseq.current->frame);
+ if (attr != NULL) {
+ if (attr->action == HCISEQ_ACTION_SKIP || attr->action == HCISEQ_ACTION_EMULATE)
+ return true;
+ }
+
pkt_type = ((const uint8_t *) dumpseq.current->frame->data)[0];
switch (pkt_type) {
@@ -412,6 +484,15 @@ static void process()
gettimeofday(&last, NULL);
do {
+ if (dumpseq.current->attr->action == HCISEQ_ACTION_SKIP) {
+ printf("[%4d/%4d] SKIPPING\n ", pos,
+ dumpseq.len);
+ dump_frame(dumpseq.current->frame);
+ dumpseq.current = dumpseq.current->next;
+ pos++;
+ continue;
+ }
+
/* delay */
if (timing == TIMING_DELTA) {
/* consider exec time of process_out()/process_in() */
@@ -485,6 +566,19 @@ static void delete_list()
}
}
+static void delete_type_cfg()
+{
+ int i;
+
+ for (i = 0; i < 9216; i++) {
+ free(type_cfg.cmd[i]);
+ }
+ for (i = 0; i < 256; i++) {
+ free(type_cfg.evt[i]);
+ }
+ free(type_cfg.acl);
+}
+
static void usage(void)
{
printf("hcireplay - Bluetooth replayer\n"
@@ -493,6 +587,7 @@ static void usage(void)
"\t-d, --timing={none|delta} Specify timing mode\n"
"\t-m, --factor=<value> Use timing modifier\n"
"\t-t, --timeout=<value> Use timeout when receiving\n"
+ "\t-c, --config=<file> Use config file\n"
"\t-v, --verbose Enable verbose output\n"
"\t --version Give version information\n"
"\t --help Give a short usage message\n");
@@ -502,6 +597,7 @@ static const struct option main_options[] = {
{"timing", required_argument, NULL, 'd'},
{"factor", required_argument, NULL, 'm'},
{"timeout", required_argument, NULL, 't'},
+ {"config", required_argument, NULL, 'c'},
{"verbose", no_argument, NULL, 'v'},
{"version", no_argument, NULL, 'V'},
{"help", no_argument, NULL, 'H'},
@@ -512,11 +608,12 @@ int main(int argc, char *argv[])
{
int dumpfd;
int i;
+ char *config = NULL;
while (1) {
int opt;
- opt = getopt_long(argc, argv, "d:m:t:v",
+ opt = getopt_long(argc, argv, "d:m:t:c:v",
main_options, NULL);
if (opt < 0)
break;
@@ -535,6 +632,9 @@ int main(int argc, char *argv[])
case 't':
timeout = atoi(optarg);
break;
+ case 'c':
+ config = optarg;
+ break;
case 'v':
verbose = true;
break;
@@ -572,6 +672,20 @@ int main(int argc, char *argv[])
dumpseq.current = dumpseq.frames;
calc_rel_ts(&dumpseq);
+ /* init type config */
+ for (i = 0; i < 9216; i++)
+ type_cfg.cmd[i] = NULL;
+ for (i = 0; i < 256; i++)
+ type_cfg.evt[i] = NULL;
+ type_cfg.acl = NULL;
+
+ if (config != NULL) {
+ if (parse_config(config, &dumpseq, &type_cfg, verbose)) {
+ vhci_close();
+ return 1;
+ }
+ }
+
/* init emulator */
btdev = btdev_create(0);
btdev_set_send_handler(btdev, btdev_send, NULL);
@@ -595,6 +709,7 @@ int main(int argc, char *argv[])
vhci_close();
btdev_destroy(btdev);
delete_list();
+ delete_type_cfg();
printf("Terminating\n");
return EXIT_SUCCESS;
diff --git a/tools/replay/main.h b/tools/replay/main.h
index 2223789..e8b7365 100644
--- a/tools/replay/main.h
+++ b/tools/replay/main.h
@@ -23,8 +23,6 @@
*
*/
-#include "hciseq.h"
-
struct btsnoop_hdr {
uint8_t id[8]; /* Identification Pattern */
uint32_t version; /* Version Number = 1 */
--
1.7.9.5
^ permalink raw reply related [flat|nested] 14+ messages in thread
* [PATCH BlueZ v3 1/6] replay: Add initial version of replay tool
2012-08-09 17:36 [PATCH BlueZ 1/4] replay: Add initial version of replay tool Anton Weber
` (3 preceding siblings ...)
2012-08-13 18:34 ` [PATCH BlueZ v2 1/4] replay: Add initial version of replay tool Anton Weber
@ 2012-09-05 14:14 ` Anton Weber
2012-09-05 14:14 ` [PATCH BlueZ v3 2/6] replay: Add timing functionality Anton Weber
` (4 more replies)
4 siblings, 5 replies; 14+ messages in thread
From: Anton Weber @ 2012-09-05 14:14 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anton Weber
This utility uses a VHCI interface to simulate HCI traffic from
a recorded scenario. It reads packets from BTSnoop dump files and
replays them on the virtual interface.
It is meant as a debugging tool that allows to investigate problems
with particular controllers and Bluetooth hardware on other system
configurations.
---
.gitignore | 1 +
Makefile.tools | 12 +-
tools/replay/hciseq.h | 43 +++++
tools/replay/main.c | 501 +++++++++++++++++++++++++++++++++++++++++++++++++
tools/replay/main.h | 64 +++++++
5 files changed, 620 insertions(+), 1 deletion(-)
create mode 100644 tools/replay/hciseq.h
create mode 100644 tools/replay/main.c
create mode 100644 tools/replay/main.h
diff --git a/.gitignore b/.gitignore
index 38318cd..d53e266 100644
--- a/.gitignore
+++ b/.gitignore
@@ -87,6 +87,7 @@ unit/test-eir
tools/btmgmt
monitor/btmon
emulator/btvirt
+tools/replay/btreplay
doc/*.bak
doc/*.stamp
diff --git a/Makefile.tools b/Makefile.tools
index 5579b86..4a3aca5 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -50,7 +50,7 @@ tools_ppporc_LDADD = lib/libbluetooth-private.la
tools_hcieventmask_LDADD = lib/libbluetooth-private.la
-noinst_PROGRAMS += tools/btmgmt monitor/btmon emulator/btvirt
+noinst_PROGRAMS += tools/btmgmt monitor/btmon emulator/btvirt tools/replay/btreplay
tools_btmgmt_SOURCES = tools/btmgmt.c src/glib-helper.c
tools_btmgmt_LDADD = lib/libbluetooth-private.la @GLIB_LIBS@
@@ -69,6 +69,16 @@ emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \
emulator/vhci.h emulator/vhci.c \
emulator/btdev.h emulator/btdev.c
+tools_replay_btreplay_SOURCES = tools/replay/main.h tools/replay/main.c \
+ tools/replay/hciseq.h \
+ monitor/packet.h monitor/packet.c \
+ monitor/btsnoop.h monitor/btsnoop.c \
+ monitor/control.h monitor/control.c \
+ monitor/mainloop.h monitor/mainloop.c \
+ lib/hci.h
+
+tools_replay_btreplay_LDADD = lib/libbluetooth-private.la
+
if READLINE
bin_PROGRAMS += attrib/gatttool
diff --git a/tools/replay/hciseq.h b/tools/replay/hciseq.h
new file mode 100644
index 0000000..bf953cd
--- /dev/null
+++ b/tools/replay/hciseq.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ * Copyright (C) 2012 Anton Weber <ant@antweb.me>
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+enum hciseq_action {
+ HCISEQ_ACTION_REPLAY = 0,
+};
+
+struct hciseq_list {
+ struct hciseq_node *frames;
+ struct hciseq_node *current;
+ int len;
+};
+
+struct hciseq_attr {
+ enum hciseq_action action;
+};
+
+struct hciseq_node {
+ struct frame *frame;
+ struct hciseq_node *next;
+ struct hciseq_attr *attr;
+};
diff --git a/tools/replay/main.c b/tools/replay/main.c
new file mode 100644
index 0000000..509d968
--- /dev/null
+++ b/tools/replay/main.c
@@ -0,0 +1,501 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ * Copyright (C) 2012 Anton Weber <ant@antweb.me>
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2000-2002 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2003-2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/epoll.h>
+#include <sys/ioctl.h>
+
+#include "main.h"
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "monitor/bt.h"
+#include "monitor/btsnoop.h"
+#include "monitor/control.h"
+#include "monitor/packet.h"
+
+#define MAX_EPOLL_EVENTS 1
+#define MAX_MSG 128
+
+static struct hciseq_list dumpseq;
+
+static int fd;
+static int pos = 1;
+static int skipped = 0;
+
+static int epoll_fd;
+static struct epoll_event epoll_event;
+
+static int timeout = -1;
+static bool verbose = false;
+
+static inline int read_n(int fd, char *buf, int len)
+{
+ int t = 0, w;
+
+ while (len > 0) {
+ w = read(fd, buf, len);
+ if (w < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return -1;
+ } else if (w == 0) {
+ return 0;
+ }
+
+ len -= w;
+ buf += w;
+ t += w;
+ }
+
+ return t;
+}
+
+static int
+parse_btsnoop(int fd, struct frame *frm, struct btsnoop_hdr *hdr)
+{
+ struct btsnoop_pkt pkt;
+ uint8_t pkt_type;
+ uint64_t ts;
+ int n;
+
+ n = read_n(fd, (void *) &pkt, BTSNOOP_PKT_SIZE);
+ if (n < 0)
+ return -1;
+ else if (n == 0)
+ return 0;
+
+ switch (ntohl(hdr->type)) {
+ case 1001:
+ if (ntohl(pkt.flags) & 0x02) {
+ if (ntohl(pkt.flags) & 0x01)
+ pkt_type = HCI_EVENT_PKT;
+ else
+ pkt_type = HCI_COMMAND_PKT;
+ } else
+ pkt_type = HCI_ACLDATA_PKT;
+
+ ((uint8_t *) frm->data)[0] = pkt_type;
+
+ frm->data_len = ntohl(pkt.len) + 1;
+ n = read_n(fd, frm->data + 1, frm->data_len - 1);
+ break;
+
+ case 1002:
+ frm->data_len = ntohl(pkt.len);
+ n = read_n(fd, frm->data, frm->data_len);
+ break;
+ }
+
+ frm->in = ntohl(pkt.flags) & 0x01;
+ ts = ntoh64(pkt.ts) - 0x00E03AB44A676000ll;
+ frm->ts.tv_sec = (ts / 1000000ll) + 946684800ll;
+ frm->ts.tv_usec = ts % 1000000ll;
+
+ return n;
+}
+
+static int parse_dump(int fd, struct hciseq_list *seq)
+{
+ struct frame *frm;
+ struct btsnoop_hdr bh;
+ int n, count;
+ struct hciseq_node *nodeptr, *last;
+
+ last = seq->current;
+
+ /* read BTSnoop header once */
+ if (read_n(fd, (void *) &bh, BTSNOOP_HDR_SIZE) != BTSNOOP_HDR_SIZE)
+ return -1;
+
+ /* check for "btsnoop" string in header */
+ if (memcmp(bh.id, btsnoop_id, sizeof(btsnoop_id)) != 0)
+ return -1;
+
+ count = seq->len;
+ while (1) {
+ frm = malloc(sizeof(*frm));
+ frm->data = malloc(HCI_MAX_FRAME_SIZE);
+
+ n = parse_btsnoop(fd, frm, &bh);
+ if (n <= 0) {
+ free(frm->data);
+ free(frm);
+ return n;
+ }
+
+ frm->ptr = frm->data;
+ frm->len = frm->data_len;
+
+ nodeptr = malloc(sizeof(*nodeptr));
+ nodeptr->frame = frm;
+ nodeptr->attr = malloc(sizeof(*nodeptr->attr));
+ nodeptr->attr->action = HCISEQ_ACTION_REPLAY;
+
+ if (last == NULL)
+ seq->frames = nodeptr;
+ else
+ last->next = nodeptr;
+
+ last = nodeptr;
+ nodeptr->next = NULL;
+ seq->len = ++count;
+ }
+
+ return 0;
+}
+
+static void dump_frame(struct frame *frm)
+{
+ struct timeval tv;
+ uint8_t pkt_type;
+
+ gettimeofday(&tv, NULL);
+
+ pkt_type = ((const uint8_t *) frm->data)[0];
+ switch (pkt_type) {
+ case BT_H4_CMD_PKT:
+ packet_hci_command(&tv, 0x00, frm->data + 1,
+ frm->data_len - 1);
+ break;
+ case BT_H4_EVT_PKT:
+ packet_hci_event(&tv, 0x00, frm->data + 1,
+ frm->data_len - 1);
+ break;
+ case BT_H4_ACL_PKT:
+ if (frm->in)
+ packet_hci_acldata(&tv, 0x00, 0x01,
+ frm->data + 1,
+ frm->data_len - 1);
+ else
+ packet_hci_acldata(&tv, 0x00, 0x00,
+ frm->data + 1,
+ frm->data_len - 1);
+ break;
+ default:
+ //TODO: raw dump
+ break;
+ }
+}
+
+static int send_frm(struct frame *frm)
+{
+ return write(fd, frm->data, frm->data_len);
+}
+
+static int recv_frm(int fd, struct frame *frm)
+{
+ int i, n;
+ int nevs;
+ uint8_t buf[HCI_MAX_FRAME_SIZE];
+ struct epoll_event ev[MAX_EPOLL_EVENTS];
+
+ nevs = epoll_wait(epoll_fd, ev, MAX_EPOLL_EVENTS, timeout);
+ if (nevs < 0)
+ return -1;
+ else if (nevs == 0)
+ return 0;
+
+ for (i = 0; i < nevs; i++) {
+ if (ev[i].events & (EPOLLERR | EPOLLHUP))
+ return -1;
+
+ n = read(fd, (void *) &buf, HCI_MAX_FRAME_SIZE);
+ if (n > 0) {
+ memcpy(frm->data, buf, n);
+ frm->data_len = n;
+ }
+ }
+
+ return n;
+}
+
+static bool check_match(struct frame *l, struct frame *r, char *msg)
+{
+ uint8_t type_l = ((const uint8_t *) l->data)[0];
+ uint8_t type_r = ((const uint8_t *) r->data)[0];
+ uint16_t opcode_l, opcode_r;
+ uint8_t evt_l, evt_r;
+
+ if (type_l != type_r) {
+ snprintf(msg, MAX_MSG,
+ "! Wrong packet type - expected (0x%2.2x), was (0x%2.2x)",
+ type_l, type_r);
+ return false;
+ }
+
+ switch (type_l) {
+ case BT_H4_CMD_PKT:
+ opcode_l = *((uint16_t *) (l->data + 1));
+ opcode_r = *((uint16_t *) (r->data + 1));
+ if (opcode_l != opcode_r) {
+ snprintf(msg, MAX_MSG,
+ "! Wrong opcode - expected (0x%2.2x|0x%4.4x), was (0x%2.2x|0x%4.4x)",
+ cmd_opcode_ogf(opcode_l),
+ cmd_opcode_ocf(opcode_l),
+ cmd_opcode_ogf(opcode_r),
+ cmd_opcode_ocf(opcode_r));
+ return false;
+ } else {
+ return true;
+ }
+ case BT_H4_EVT_PKT:
+ evt_l = *((uint8_t *) (l->data + 1));
+ evt_r = *((uint8_t *) (r->data + 1));
+ if (evt_l != evt_r) {
+ snprintf(msg, MAX_MSG,
+ "! Wrong event type - expected (0x%2.2x), was (0x%2.2x)",
+ evt_l, evt_r);
+ return false;
+ } else {
+ return true;
+ }
+ case BT_H4_ACL_PKT:
+ if (l->data_len != r->data_len)
+ return false;
+
+ return memcmp(l->data, r->data, l->data_len) == 0;
+ default:
+ snprintf(msg, MAX_MSG, "! Unknown packet type (0x%2.2x)",
+ type_l);
+
+ if (l->data_len != r->data_len)
+ return false;
+
+ return memcmp(l->data, r->data, l->data_len) == 0;
+ }
+}
+
+static bool process_in()
+{
+ static struct frame frm;
+ static uint8_t data[HCI_MAX_FRAME_SIZE];
+ int n;
+ bool match;
+ char msg[MAX_MSG];
+
+ frm.data = &data;
+ frm.ptr = frm.data;
+
+ n = recv_frm(fd, &frm);
+ if (n < 0) {
+ perror("Could not receive\n");
+ return false;
+ }
+
+ /* is this the packet in the sequence? */
+ msg[0] = '\0';
+ match = check_match(dumpseq.current->frame, &frm, msg);
+
+ /* process packet if match */
+ if (match)
+ printf("[%4d/%4d] ", pos, dumpseq.len);
+ else
+ printf("[ Unknown ] %s\n ", msg);
+
+ dump_frame(&frm);
+
+ return match;
+}
+
+static bool process_out()
+{
+ uint8_t pkt_type;
+
+ pkt_type = ((const uint8_t *) dumpseq.current->frame->data)[0];
+
+ switch (pkt_type) {
+ case BT_H4_EVT_PKT:
+ case BT_H4_ACL_PKT:
+ printf("[%4d/%4d] ", pos, dumpseq.len);
+ dump_frame(dumpseq.current->frame);
+ send_frm(dumpseq.current->frame);
+ break;
+ default:
+ printf("Unsupported packet 0x%2.2x\n", pkt_type);
+ break;
+ }
+
+ return true;
+}
+
+static void process()
+{
+ bool processed;
+
+ do {
+ if (dumpseq.current->frame->in == 1)
+ processed = process_out();
+ else
+ processed = process_in();
+
+ if (processed) {
+ dumpseq.current = dumpseq.current->next;
+ pos++;
+ }
+ } while (dumpseq.current != NULL);
+
+ printf("Done\n");
+ printf("Processed %d out of %d\n", dumpseq.len - skipped,
+ dumpseq.len);
+}
+
+static int vhci_open()
+{
+ fd = open("/dev/vhci", O_RDWR | O_NONBLOCK);
+ epoll_fd = epoll_create1(EPOLL_CLOEXEC);
+ if (epoll_fd < 0)
+ return -1;
+
+ epoll_event.events = EPOLLIN;
+ epoll_event.data.fd = fd;
+
+ if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD,
+ epoll_event.data.fd, &epoll_event) < 0) {
+ return -1;
+ }
+
+ return fd;
+}
+
+static int vhci_close()
+{
+ epoll_ctl(epoll_fd, EPOLL_CTL_DEL, epoll_event.data.fd, NULL);
+ return close(fd);
+}
+
+static void delete_list()
+{
+ struct hciseq_node *node, *tmp;
+
+ node = dumpseq.frames;
+ while (node != NULL) {
+ tmp = node;
+ node = node->next;
+
+ free(tmp->frame->data);
+ free(tmp->frame);
+ free(tmp->attr);
+ free(tmp);
+ }
+}
+
+static void usage(void)
+{
+ printf("hcireplay - Bluetooth replayer\n"
+ "Usage:\thcireplay-client [options] file...\n"
+ "options:\n"
+ "\t-v, --verbose Enable verbose output\n"
+ "\t --version Give version information\n"
+ "\t --help Give a short usage message\n");
+}
+
+static const struct option main_options[] = {
+ {"verbose", no_argument, NULL, 'v'},
+ {"version", no_argument, NULL, 'V'},
+ {"help", no_argument, NULL, 'H'},
+ {}
+};
+
+int main(int argc, char *argv[])
+{
+ int dumpfd;
+ int i;
+
+ while (1) {
+ int opt;
+
+ opt = getopt_long(argc, argv, "v",
+ main_options, NULL);
+ if (opt < 0)
+ break;
+
+ switch (opt) {
+ case 'v':
+ verbose = true;
+ break;
+ case 'V':
+ printf("%s\n", VERSION);
+ return EXIT_SUCCESS;
+ case 'H':
+ usage();
+ return EXIT_SUCCESS;
+ default:
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (optind >= argc) {
+ usage();
+ return EXIT_FAILURE;
+ }
+
+ dumpseq.current = NULL;
+ dumpseq.frames = NULL;
+ for (i = optind; i < argc; i++) {
+ dumpfd = open(argv[i], O_RDONLY);
+ if (dumpfd < 0) {
+ perror("Failed to open dump file");
+ return EXIT_FAILURE;
+ }
+
+ if (parse_dump(dumpfd, &dumpseq) < 0) {
+ fprintf(stderr, "Error parsing dump file\n");
+ vhci_close();
+ return EXIT_FAILURE;
+ }
+ }
+ dumpseq.current = dumpseq.frames;
+
+ /*
+ * make sure we open the interface after parsing
+ * through all files so we can start without delay
+ */
+ fd = vhci_open();
+ if (fd < 0) {
+ perror("Failed to open VHCI interface");
+ return EXIT_FAILURE;
+ }
+
+ printf("Running\n");
+
+ process();
+
+ vhci_close();
+ delete_list();
+ printf("Terminating\n");
+
+ return EXIT_SUCCESS;
+}
diff --git a/tools/replay/main.h b/tools/replay/main.h
new file mode 100644
index 0000000..d80deec
--- /dev/null
+++ b/tools/replay/main.h
@@ -0,0 +1,64 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ * Copyright (C) 2012 Anton Weber <ant@antweb.me>
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2000-2002 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2003-2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "hciseq.h"
+
+struct btsnoop_hdr {
+ uint8_t id[8]; /* Identification Pattern */
+ uint32_t version; /* Version Number = 1 */
+ uint32_t type; /* Datalink Type */
+} __attribute__ ((packed));
+#define BTSNOOP_HDR_SIZE (sizeof(struct btsnoop_hdr))
+
+struct btsnoop_pkt {
+ uint32_t size; /* Original Length */
+ uint32_t len; /* Included Length */
+ uint32_t flags; /* Packet Flags */
+ uint32_t drops; /* Cumulative Drops */
+ uint64_t ts; /* Timestamp microseconds */
+ uint8_t data[0]; /* Packet Data */
+} __attribute__ ((packed));
+#define BTSNOOP_PKT_SIZE (sizeof(struct btsnoop_pkt))
+
+static uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00 };
+
+struct frame {
+ void *data;
+ uint32_t data_len;
+ void *ptr;
+ uint32_t len;
+ uint16_t dev_id;
+ uint8_t in;
+ uint8_t master;
+ uint16_t handle;
+ uint16_t cid;
+ uint16_t num;
+ uint8_t dlci;
+ uint8_t channel;
+ unsigned long flags;
+ struct timeval ts;
+ int pppdump_fd;
+ int audio_fd;
+};
--
1.7.9.5
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH BlueZ v3 2/6] replay: Add timing functionality
2012-09-05 14:14 ` [PATCH BlueZ v3 1/6] replay: Add initial version of replay tool Anton Weber
@ 2012-09-05 14:14 ` Anton Weber
2012-09-05 14:14 ` [PATCH BlueZ v3 3/6] replay: Add emulation support Anton Weber
` (3 subsequent siblings)
4 siblings, 0 replies; 14+ messages in thread
From: Anton Weber @ 2012-09-05 14:14 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anton Weber
Add -d parameter that lets user choose between two delay modes.
delta: use time difference between two packets for delay
none: no delay
Add -m parameter to specify a factor that multiplies delays in
delta delay mode.
Add -t parameter to set the epoll timeout when receiving packets.
---
v2 -> v3: Rename long parameters timing and factor
Makefile.tools | 3 +-
tools/replay/hciseq.c | 53 ++++++++++++++++++++++++++++
tools/replay/hciseq.h | 23 +++++++++++++
tools/replay/main.c | 62 ++++++++++++++++++++++++++++++---
tools/replay/main.h | 21 +-----------
tools/replay/time.c | 91 +++++++++++++++++++++++++++++++++++++++++++++++++
tools/replay/time.h | 31 +++++++++++++++++
7 files changed, 259 insertions(+), 25 deletions(-)
create mode 100644 tools/replay/hciseq.c
create mode 100644 tools/replay/time.c
create mode 100644 tools/replay/time.h
diff --git a/Makefile.tools b/Makefile.tools
index 4a3aca5..6767300 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -70,7 +70,8 @@ emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \
emulator/btdev.h emulator/btdev.c
tools_replay_btreplay_SOURCES = tools/replay/main.h tools/replay/main.c \
- tools/replay/hciseq.h \
+ tools/replay/hciseq.h tools/replay/hciseq.c \
+ tools/replay/time.h tools/replay/time.c \
monitor/packet.h monitor/packet.c \
monitor/btsnoop.h monitor/btsnoop.c \
monitor/control.h monitor/control.c \
diff --git a/tools/replay/hciseq.c b/tools/replay/hciseq.c
new file mode 100644
index 0000000..8b24264
--- /dev/null
+++ b/tools/replay/hciseq.c
@@ -0,0 +1,53 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ * Copyright (C) 2012 Anton Weber <ant@antweb.me>
+ * Copyright (C) 2011-2012 Intel Corporation
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+
+#include "hciseq.h"
+#include "time.h"
+#include "monitor/bt.h"
+
+void calc_rel_ts(struct hciseq_list *seq)
+{
+ struct timeval start;
+ struct hciseq_node *tmp;
+
+ start = seq->current->frame->ts;
+ tmp = seq->current;
+
+ /* first packet */
+ tmp->attr->ts_rel.tv_sec = 0;
+ tmp->attr->ts_rel.tv_usec = 0;
+ tmp->attr->ts_diff.tv_sec = 0;
+ tmp->attr->ts_diff.tv_usec = 0;
+
+ while (tmp->next != NULL) {
+ timeval_diff(&tmp->next->frame->ts, &start,
+ &tmp->next->attr->ts_rel);
+ timeval_diff(&tmp->next->frame->ts, &tmp->frame->ts,
+ &tmp->next->attr->ts_diff);
+ tmp = tmp->next;
+ }
+}
diff --git a/tools/replay/hciseq.h b/tools/replay/hciseq.h
index bf953cd..f9fe7c8 100644
--- a/tools/replay/hciseq.h
+++ b/tools/replay/hciseq.h
@@ -22,6 +22,25 @@
*
*/
+struct frame {
+ void *data;
+ uint32_t data_len;
+ void *ptr;
+ uint32_t len;
+ uint16_t dev_id;
+ uint8_t in;
+ uint8_t master;
+ uint16_t handle;
+ uint16_t cid;
+ uint16_t num;
+ uint8_t dlci;
+ uint8_t channel;
+ unsigned long flags;
+ struct timeval ts;
+ int pppdump_fd;
+ int audio_fd;
+};
+
enum hciseq_action {
HCISEQ_ACTION_REPLAY = 0,
};
@@ -33,6 +52,8 @@ struct hciseq_list {
};
struct hciseq_attr {
+ struct timeval ts_rel;
+ struct timeval ts_diff;
enum hciseq_action action;
};
@@ -41,3 +62,5 @@ struct hciseq_node {
struct hciseq_node *next;
struct hciseq_attr *attr;
};
+
+void calc_rel_ts(struct hciseq_list *seq);
diff --git a/tools/replay/main.c b/tools/replay/main.c
index 509d968..e026d98 100644
--- a/tools/replay/main.c
+++ b/tools/replay/main.c
@@ -37,8 +37,10 @@
#include <unistd.h>
#include <sys/epoll.h>
#include <sys/ioctl.h>
+#include <sys/time.h>
#include "main.h"
+#include "time.h"
#include "lib/bluetooth.h"
#include "lib/hci.h"
#include "monitor/bt.h"
@@ -49,16 +51,22 @@
#define MAX_EPOLL_EVENTS 1
#define MAX_MSG 128
+#define TIMING_NONE 0
+#define TIMING_DELTA 1
+
static struct hciseq_list dumpseq;
static int fd;
static int pos = 1;
static int skipped = 0;
+static struct timeval start;
static int epoll_fd;
static struct epoll_event epoll_event;
static int timeout = -1;
+static int timing = TIMING_NONE;
+static double factor = 1;
static bool verbose = false;
static inline int read_n(int fd, char *buf, int len)
@@ -313,6 +321,10 @@ static bool process_in()
if (n < 0) {
perror("Could not receive\n");
return false;
+ } else if (n == 0) {
+ printf("[%4d/%4d] Timeout\n", pos, dumpseq.len);
+ skipped++;
+ return true;
}
/* is this the packet in the sequence? */
@@ -353,9 +365,29 @@ static bool process_out()
static void process()
{
+ struct timeval last, last_diff;
+ __useconds_t delay;
bool processed;
+ gettimeofday(&last, NULL);
do {
+ /* delay */
+ if (timing == TIMING_DELTA) {
+ /* consider exec time of process_out()/process_in() */
+ get_timeval_passed(&last, &last_diff);
+ if (timeval_cmp(&dumpseq.current->attr->ts_diff, &last_diff) >= 0) {
+ delay = timeval_diff(&dumpseq.current->attr->ts_diff,
+ &last_diff, NULL);
+ delay *= factor;
+ if (usleep(delay) == -1)
+ printf("Delay failed\n");
+ } else {
+ /* exec time was longer than delay */
+ printf("Packet delay - processing previous packet took longer than recorded time difference\n");
+ }
+ gettimeofday(&last, NULL);
+ }
+
if (dumpseq.current->frame->in == 1)
processed = process_out();
else
@@ -417,12 +449,18 @@ static void usage(void)
printf("hcireplay - Bluetooth replayer\n"
"Usage:\thcireplay-client [options] file...\n"
"options:\n"
- "\t-v, --verbose Enable verbose output\n"
- "\t --version Give version information\n"
- "\t --help Give a short usage message\n");
+ "\t-d, --delay-mode={none|delta} Specify delay mode (default is none)\n"
+ "\t-m, --delay-modifier=N Set delay modifier to N (default is 1)\n"
+ "\t-t, --timeout=N Set timeout to N milliseconds when receiving packets from host\n"
+ "\t-v, --verbose Enable verbose output\n"
+ "\t --version Give version information\n"
+ "\t --help Give a short usage message\n");
}
static const struct option main_options[] = {
+ {"delay-mode", required_argument, NULL, 'd'},
+ {"delay-modifier", required_argument, NULL, 'm'},
+ {"timeout", required_argument, NULL, 't'},
{"verbose", no_argument, NULL, 'v'},
{"version", no_argument, NULL, 'V'},
{"help", no_argument, NULL, 'H'},
@@ -437,12 +475,25 @@ int main(int argc, char *argv[])
while (1) {
int opt;
- opt = getopt_long(argc, argv, "v",
+ opt = getopt_long(argc, argv, "d:m:t:v",
main_options, NULL);
if (opt < 0)
break;
switch (opt) {
+ case 'd':
+ if (!strcmp(optarg, "none"))
+ timing = TIMING_NONE;
+ else if (!strcmp(optarg, "delta"))
+ timing = TIMING_DELTA;
+
+ break;
+ case 'm':
+ factor = atof(optarg);
+ break;
+ case 't':
+ timeout = atoi(optarg);
+ break;
case 'v':
verbose = true;
break;
@@ -478,6 +529,9 @@ int main(int argc, char *argv[])
}
}
dumpseq.current = dumpseq.frames;
+ calc_rel_ts(&dumpseq);
+
+ gettimeofday(&start, NULL);
/*
* make sure we open the interface after parsing
diff --git a/tools/replay/main.h b/tools/replay/main.h
index d80deec..2223789 100644
--- a/tools/replay/main.h
+++ b/tools/replay/main.h
@@ -42,23 +42,4 @@ struct btsnoop_pkt {
} __attribute__ ((packed));
#define BTSNOOP_PKT_SIZE (sizeof(struct btsnoop_pkt))
-static uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00 };
-
-struct frame {
- void *data;
- uint32_t data_len;
- void *ptr;
- uint32_t len;
- uint16_t dev_id;
- uint8_t in;
- uint8_t master;
- uint16_t handle;
- uint16_t cid;
- uint16_t num;
- uint8_t dlci;
- uint8_t channel;
- unsigned long flags;
- struct timeval ts;
- int pppdump_fd;
- int audio_fd;
-};
+uint8_t btsnoop_id[] = { 0x62, 0x74, 0x73, 0x6e, 0x6f, 0x6f, 0x70, 0x00 };
diff --git a/tools/replay/time.c b/tools/replay/time.c
new file mode 100644
index 0000000..029501a
--- /dev/null
+++ b/tools/replay/time.c
@@ -0,0 +1,91 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ * Copyright (C) 2012 Anton Weber <ant@antweb.me>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdlib.h>
+#include <sys/time.h>
+
+#include "time.h"
+
+/*
+ * Adjust timeval structs to make sure usec difference is not negative
+ * see http://www.gnu.org/software/libc/manual/html_node/Elapsed-Time.html
+ */
+void timeval_adjust_usec(struct timeval *l, struct timeval *r) {
+ int tmpsec;
+
+ if (r->tv_usec > l->tv_usec) {
+ tmpsec = (r->tv_usec - l->tv_usec) / 1000000 + 1;
+ r->tv_sec += tmpsec;
+ r->tv_usec -= 1000000 * tmpsec;
+ }
+
+ if ((l->tv_usec - r->tv_usec) > 1000000) {
+ tmpsec = (l->tv_usec - r->tv_usec) / 1000000;
+ r->tv_sec -= tmpsec;
+ r->tv_usec += 1000000 * tmpsec;
+ }
+}
+
+__useconds_t
+timeval_diff(struct timeval *l, struct timeval *r, struct timeval *diff)
+{
+ static struct timeval tmp;
+
+ timeval_adjust_usec(l, r);
+
+ /* use local variable if we only need return value */
+ if (diff == NULL)
+ diff = &tmp;
+
+ diff->tv_sec = l->tv_sec - r->tv_sec;
+ diff->tv_usec = l->tv_usec - r->tv_usec;
+
+ return (diff->tv_sec * 1000000) + diff->tv_usec;
+}
+
+int timeval_cmp(struct timeval *l, struct timeval *r)
+{
+ timeval_adjust_usec(l, r);
+
+ if (l->tv_sec > r->tv_sec) {
+ return 1;
+ } else if (l->tv_sec < r->tv_sec) {
+ return -1;
+ } else {
+ if (l->tv_usec > r->tv_usec)
+ return 1;
+ else if (l->tv_usec < r->tv_usec)
+ return -1;
+ else
+ return 0;
+ }
+}
+
+inline __useconds_t
+get_timeval_passed(struct timeval *since, struct timeval *diff)
+{
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+
+ return timeval_diff(&now, since, diff);
+}
diff --git a/tools/replay/time.h b/tools/replay/time.h
new file mode 100644
index 0000000..0c876a4
--- /dev/null
+++ b/tools/replay/time.h
@@ -0,0 +1,31 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ * Copyright (C) 2012 Anton Weber <ant@antweb.me>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+void timeval_adjust_usec(struct timeval *l, struct timeval *r);
+
+__useconds_t
+timeval_diff(struct timeval *l, struct timeval *r, struct timeval *diff);
+
+int timeval_cmp(struct timeval *l, struct timeval *r);
+
+inline __useconds_t
+ get_timeval_passed(struct timeval *since, struct timeval *diff);
--
1.7.9.5
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH BlueZ v3 3/6] replay: Add emulation support
2012-09-05 14:14 ` [PATCH BlueZ v3 1/6] replay: Add initial version of replay tool Anton Weber
2012-09-05 14:14 ` [PATCH BlueZ v3 2/6] replay: Add timing functionality Anton Weber
@ 2012-09-05 14:14 ` Anton Weber
2012-09-05 14:14 ` [PATCH BlueZ v3 4/6] replay: Add config file parser and skip action Anton Weber
` (2 subsequent siblings)
4 siblings, 0 replies; 14+ messages in thread
From: Anton Weber @ 2012-09-05 14:14 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anton Weber
Integrate emulator so it can process selected packets instead of
replaying them from the sequence. The behaviour is controlled
through the action attribute in hciseq_attr.
If set to HCISEQ_ACTION_EMULATE, hcireplay will use the emulator
to process the packet automatically instead of replaying it.
HCISEQ_ACTION_REPLAY keeps the previous behaviour and replays the
packet using the dump file.
---
Makefile.tools | 1 +
tools/replay/hciseq.h | 1 +
tools/replay/main.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++---
3 files changed, 51 insertions(+), 3 deletions(-)
diff --git a/Makefile.tools b/Makefile.tools
index 6767300..8b3c8d8 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -76,6 +76,7 @@ tools_replay_btreplay_SOURCES = tools/replay/main.h tools/replay/main.c \
monitor/btsnoop.h monitor/btsnoop.c \
monitor/control.h monitor/control.c \
monitor/mainloop.h monitor/mainloop.c \
+ emulator/btdev.h emulator/btdev.c \
lib/hci.h
tools_replay_btreplay_LDADD = lib/libbluetooth-private.la
diff --git a/tools/replay/hciseq.h b/tools/replay/hciseq.h
index f9fe7c8..1454147 100644
--- a/tools/replay/hciseq.h
+++ b/tools/replay/hciseq.h
@@ -43,6 +43,7 @@ struct frame {
enum hciseq_action {
HCISEQ_ACTION_REPLAY = 0,
+ HCISEQ_ACTION_EMULATE = 1
};
struct hciseq_list {
diff --git a/tools/replay/main.c b/tools/replay/main.c
index e026d98..dc281b0 100644
--- a/tools/replay/main.c
+++ b/tools/replay/main.c
@@ -43,6 +43,7 @@
#include "time.h"
#include "lib/bluetooth.h"
#include "lib/hci.h"
+#include "emulator/btdev.h"
#include "monitor/bt.h"
#include "monitor/btsnoop.h"
#include "monitor/control.h"
@@ -69,6 +70,8 @@ static int timing = TIMING_NONE;
static double factor = 1;
static bool verbose = false;
+static struct btdev *btdev;
+
static inline int read_n(int fd, char *buf, int len)
{
int t = 0, w;
@@ -250,6 +253,34 @@ static int recv_frm(int fd, struct frame *frm)
return n;
}
+static void btdev_send(const void *data, uint16_t len, void *user_data)
+{
+ struct frame frm;
+ static void* tmpdata = NULL;
+
+ /* copy data so we respect 'const' qualifier */
+ if(tmpdata == NULL)
+ tmpdata = malloc(HCI_MAX_FRAME_SIZE);
+
+ memcpy(tmpdata, data, len);
+
+ frm.data = tmpdata;
+ frm.len = len;
+ frm.data_len = len;
+ frm.in = 1;
+ printf("[Emulator ] ");
+ dump_frame(&frm);
+ send_frm(&frm);
+}
+
+static void btdev_recv(struct frame *frm)
+{
+ frm->in = 0;
+ printf("[Emulator ] ");
+ dump_frame(frm);
+ btdev_receive_h4(btdev, frm->data, frm->data_len);
+}
+
static bool check_match(struct frame *l, struct frame *r, char *msg)
{
uint8_t type_l = ((const uint8_t *) l->data)[0];
@@ -332,11 +363,16 @@ static bool process_in()
match = check_match(dumpseq.current->frame, &frm, msg);
/* process packet if match */
- if (match)
+ if (match) {
printf("[%4d/%4d] ", pos, dumpseq.len);
- else
- printf("[ Unknown ] %s\n ", msg);
+ if (dumpseq.current->attr->action == HCISEQ_ACTION_EMULATE) {
+ btdev_recv(&frm);
+ return true;
+ }
+ } else {
+ printf("[ Unknown ] %s\n ", msg);
+ }
dump_frame(&frm);
return match;
@@ -346,6 +382,11 @@ static bool process_out()
{
uint8_t pkt_type;
+ /* emulator sends response automatically */
+ if (dumpseq.current->attr->action == HCISEQ_ACTION_EMULATE) {
+ return 1;
+ }
+
pkt_type = ((const uint8_t *) dumpseq.current->frame->data)[0];
switch (pkt_type) {
@@ -531,6 +572,10 @@ int main(int argc, char *argv[])
dumpseq.current = dumpseq.frames;
calc_rel_ts(&dumpseq);
+ /* init emulator */
+ btdev = btdev_create(0);
+ btdev_set_send_handler(btdev, btdev_send, NULL);
+
gettimeofday(&start, NULL);
/*
@@ -548,6 +593,7 @@ int main(int argc, char *argv[])
process();
vhci_close();
+ btdev_destroy(btdev);
delete_list();
printf("Terminating\n");
--
1.7.9.5
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH BlueZ v3 4/6] replay: Add config file parser and skip action
2012-09-05 14:14 ` [PATCH BlueZ v3 1/6] replay: Add initial version of replay tool Anton Weber
2012-09-05 14:14 ` [PATCH BlueZ v3 2/6] replay: Add timing functionality Anton Weber
2012-09-05 14:14 ` [PATCH BlueZ v3 3/6] replay: Add emulation support Anton Weber
@ 2012-09-05 14:14 ` Anton Weber
2012-09-05 14:14 ` [PATCH BlueZ v3 5/6] replay: Add man page Anton Weber
2012-09-05 14:14 ` [PATCH BlueZ v3 6/6] replay: Add tutorial Anton Weber
4 siblings, 0 replies; 14+ messages in thread
From: Anton Weber @ 2012-09-05 14:14 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anton Weber
Add support for config files to manipulate the loaded sequence.
A config file can be specified with the new -c command line parameter.
The config syntax is:
scope key=value[,key=value,...]
e.g.:
all delta=300,action=replay
1-4 action=emulate
1 delta=500
HCI_EVT_0x0e delta=0
HCI_CMD_0x03|0x0003 action=emulate
HCI_ACL action=replay
The inital version supports the keys delta and action and
all scope types shown in the example.
HCISEQ_ACTION_SKIP allows to skip a packet in the sequence.
The action can be set in config files using "action=skip".
---
Makefile.tools | 1 +
tools/replay/config-parser.c | 506 ++++++++++++++++++++++++++++++++++++++++++
tools/replay/config-parser.h | 36 +++
tools/replay/hciseq.h | 3 +-
tools/replay/main.c | 117 +++++++++-
tools/replay/main.h | 2 -
6 files changed, 661 insertions(+), 4 deletions(-)
create mode 100644 tools/replay/config-parser.c
create mode 100644 tools/replay/config-parser.h
diff --git a/Makefile.tools b/Makefile.tools
index 8b3c8d8..ba2db00 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -72,6 +72,7 @@ emulator_btvirt_SOURCES = emulator/main.c monitor/bt.h \
tools_replay_btreplay_SOURCES = tools/replay/main.h tools/replay/main.c \
tools/replay/hciseq.h tools/replay/hciseq.c \
tools/replay/time.h tools/replay/time.c \
+ tools/replay/config-parser.h tools/replay/config-parser.c \
monitor/packet.h monitor/packet.c \
monitor/btsnoop.h monitor/btsnoop.c \
monitor/control.h monitor/control.c \
diff --git a/tools/replay/config-parser.c b/tools/replay/config-parser.c
new file mode 100644
index 0000000..5219eb9
--- /dev/null
+++ b/tools/replay/config-parser.c
@@ -0,0 +1,506 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ * Copyright (C) 2012 Anton Weber <ant@antweb.me>
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <sys/time.h>
+
+#include "config-parser.h"
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "monitor/bt.h"
+
+#define MAXLINE 128
+#define MAX_ATTR_KEY 32
+#define MAX_ATTR_VAL 32
+
+static struct hciseq_list *seq;
+static struct hciseq_type_cfg *type_cfg;
+
+static bool verbose;
+static int line;
+
+struct scope_list {
+ struct scope_node *head;
+};
+
+struct scope_node {
+ int pos;
+ struct hciseq_attr *attr;
+ struct scope_node *next;
+};
+
+struct attr_list {
+ struct attr_node *head;
+};
+
+struct attr_node {
+ char key[MAX_ATTR_KEY];
+ char val[MAX_ATTR_VAL];
+ struct attr_node *next;
+};
+
+static void attr_list_delete(struct attr_list *list)
+{
+ struct attr_node *node, *next;
+
+ if (list == NULL)
+ return;
+
+ node = list->head;
+ free(list);
+ while (node != NULL) {
+ next = node->next;
+ free(node);
+ node = next;
+ }
+}
+
+static void scope_list_delete(struct scope_list *list)
+{
+ struct scope_node *node, *next;
+
+ if (list == NULL)
+ return;
+
+ node = list->head;
+ free(list);
+ while (node != NULL) {
+ next = node->next;
+ free(node);
+ node = next;
+ }
+}
+
+static struct attr_list *parse_attrstr(char *attrstr)
+{
+ struct attr_list *list = NULL;
+ struct attr_node *node;
+ char *res;
+
+ do {
+ if (list == NULL) {
+ if ((res = strtok(attrstr, "=")) == NULL) {
+ /* nothing to parse */
+ return NULL;
+ }
+
+ list = malloc(sizeof(*list));
+ node = malloc(sizeof(*node));
+ list->head = node;
+ } else {
+ if ((res = strtok(NULL, "=")) == NULL) {
+ /* nothing left to parse */
+ break;
+ }
+
+ node->next = malloc(sizeof(*node));
+ node = node->next;
+ }
+
+ strncpy(node->key, res, sizeof(node->key));
+ node->key[sizeof(node->key) - 1] = '\0';
+
+ if ((res = strtok(NULL, ",")) == NULL) {
+ fprintf(stderr, "Invalid attribute");
+ goto err;
+ }
+ strncpy(node->val, res, sizeof(node->val));
+ node->val[sizeof(node->val) - 1] = '\0';
+
+ node->next = NULL;
+ } while (res != NULL);
+
+ return list;
+
+err:
+ attr_list_delete(list);
+ return NULL;
+}
+
+static int apply_attr(struct scope_node *scope_node,
+ struct attr_list *list)
+{
+ struct attr_node *attr_node = list->head;
+ struct hciseq_attr *attr = scope_node->attr;
+ long lval;
+
+ while (attr_node != NULL) {
+ if (strcmp(attr_node->key, "delta") == 0) {
+ /* delta */
+ lval = strtol(attr_node->val, NULL, 10);
+ if (errno == ERANGE || errno == EINVAL)
+ return 1;
+
+ if (verbose) {
+ printf("\t[%d] set delta to %ld\n",
+ scope_node->pos, lval);
+ }
+
+ attr->ts_diff.tv_sec = 0;
+ attr->ts_diff.tv_usec = lval;
+ } else if (strcmp(attr_node->key, "action") == 0) {
+ /* action */
+ if (strcmp(attr_node->val, "replay") == 0) {
+ lval = HCISEQ_ACTION_REPLAY;
+ if (verbose)
+ printf("\t[%d] set action to 'replay'\n",
+ scope_node->pos);
+ } else if (strcmp(attr_node->val, "emulate") == 0) {
+ lval = HCISEQ_ACTION_EMULATE;
+ if (verbose)
+ printf("\t[%d] set action to 'emulate'\n",
+ scope_node->pos);
+ } else if (strcmp(attr_node->val, "skip") == 0) {
+ lval = HCISEQ_ACTION_SKIP;
+ if (verbose)
+ printf("\t[%d] set action to 'skip'\n",
+ scope_node->pos);
+ } else {
+ return 1;
+ }
+
+ attr->action = lval;
+ }
+
+ attr_node = attr_node->next;
+ }
+
+ return 0;
+}
+
+static int apply_attr_scope(struct scope_list *scope,
+ struct attr_list *attr)
+{
+ struct scope_node *node = scope->head;
+
+ while (node != NULL) {
+ apply_attr(node, attr);
+ node = node->next;
+ }
+
+ return 0;
+}
+
+static struct scope_list *get_scope_range(int from, int to)
+{
+ struct scope_list *list = NULL;
+ struct scope_node *scope_node;
+ struct hciseq_node *seq_node = seq->current;
+ int pos = 1;
+
+ /* forward to 'from' */
+ while (pos < from) {
+ seq_node = seq_node->next;
+ pos++;
+ }
+
+ /* create scope list for range */
+ while (pos <= to) {
+ if (verbose)
+ printf("\tadd packet [%d]\n", pos);
+
+ if (list == NULL) {
+ list = malloc(sizeof(*list));
+ scope_node = malloc(sizeof(*scope_node));
+ list->head = scope_node;
+ } else {
+ scope_node->next = malloc(sizeof(*scope_node));
+ scope_node = scope_node->next;
+ }
+ scope_node->attr = seq_node->attr;
+ scope_node->pos = pos;
+ scope_node->next = NULL;
+
+ seq_node = seq_node->next;
+ pos++;
+ }
+
+ return list;
+}
+
+static struct scope_list *get_scope_type(uint8_t type, void *filter1,
+ void *filter2)
+{
+ struct scope_list *list = NULL;
+ struct scope_node *scope_node;
+ struct hciseq_node *seq_node = seq->current;
+ uint16_t opcode, node_opcode;
+ uint8_t node_ogf, ogf = 0x00;
+ uint16_t node_ocf, ocf = 0x0000;
+ uint8_t node_evt, evt = 0x00;
+ bool match;
+ int pos = 1;
+ struct hciseq_attr *attr;
+
+ if (type == BT_H4_CMD_PKT) {
+ ogf = *((uint8_t *) filter1);
+ ocf = *((uint16_t *) filter2);
+ opcode = cmd_opcode_pack(ogf, ocf);
+
+ if (opcode > 0x2FFF) {
+ attr = NULL;
+ } else {
+ attr = type_cfg->cmd[opcode];
+ if (attr == NULL) {
+ attr = malloc(sizeof(*attr));
+ type_cfg->cmd[opcode] = attr;
+ }
+ }
+ } else if (type == BT_H4_EVT_PKT) {
+ evt = *((uint8_t *) filter1);
+ attr = type_cfg->evt[evt];
+
+ if (attr == NULL) {
+ attr = malloc(sizeof(*attr));
+ type_cfg->evt[evt] = attr;
+ }
+ } else if (type == BT_H4_ACL_PKT) {
+ attr = type_cfg->acl;
+ if (attr == NULL) {
+ attr = malloc(sizeof(*attr));
+ type_cfg->acl = attr;
+ }
+ } else {
+ attr = NULL;
+ }
+
+ /* add matching packets in sequence */
+ while (seq_node != NULL) {
+ match = false;
+ if (((uint8_t *) seq_node->frame->data)[0] == type) {
+ if (type == BT_H4_CMD_PKT) {
+ node_opcode = *((uint16_t *)
+ (seq_node->frame->data + 1));
+ node_ogf = cmd_opcode_ogf(node_opcode);
+ node_ocf = cmd_opcode_ocf(node_opcode);
+ if (node_ogf == ogf && node_ocf == ocf)
+ match = true;
+ } else if (type == BT_H4_EVT_PKT) {
+ node_evt = ((uint8_t *) seq_node->frame->data)[1];
+ if (evt == node_evt)
+ match = true;
+ } else if (type == BT_H4_ACL_PKT) {
+ match = true;
+ }
+ }
+
+ if (match) {
+ if (verbose)
+ printf("\tadd packet [%d]\n", pos);
+
+ if (list == NULL) {
+ list = malloc(sizeof(*list));
+ scope_node = malloc(sizeof(*scope_node));
+ list->head = scope_node;
+ } else {
+ scope_node->next = malloc(sizeof(*scope_node));
+ scope_node = scope_node->next;
+ }
+ scope_node->attr = seq_node->attr;
+ scope_node->pos = pos;
+ scope_node->next = NULL;
+ }
+ seq_node = seq_node->next;
+ pos++;
+ }
+
+ /* add type config */
+ if (attr != NULL) {
+ if (list == NULL) {
+ list = malloc(sizeof(*list));
+ scope_node = malloc(sizeof(*scope_node));
+ list->head = scope_node;
+ } else {
+ scope_node->next = malloc(sizeof(*scope_node));
+ scope_node = scope_node->next;
+ }
+
+ scope_node->attr = attr;
+ scope_node->pos = 0;
+ scope_node->next = NULL;
+ }
+
+ return list;
+}
+
+static int parse_line(char *buf)
+{
+ char *scopestr, *attrstr;
+ struct scope_list *scope_list = NULL;
+ struct attr_list *attr_list;
+ uint8_t evt, ogf;
+ uint16_t ocf;
+ char *res;
+ int from, to;
+
+ line++;
+
+ /* split line into scope and attributes */
+ if ((scopestr = strtok(buf, " ")) == NULL)
+ return 1;
+
+ if ((attrstr = strtok(NULL, "\n")) == NULL)
+ return 1;
+
+ if (verbose)
+ printf("Parsing scope (%s)\n", scopestr);
+
+ if (strcmp(scopestr, "all") == 0) {
+ if (verbose)
+ printf("\tadd all\n");
+
+ scope_list = get_scope_range(0, seq->len);
+ } else if ((strncmp(scopestr, "HCI_", 4) == 0) && strlen(scopestr) >= 7) {
+ /* make sure scopestr is at least 7 chars long, so we can check for HCI_XXX */
+
+ if (strncmp(scopestr + 4, "ACL", 3) == 0) {
+ /* scope is HCI_ACL */
+ if (verbose)
+ printf("\tadd all HCI ACL data packets:");
+
+ scope_list = get_scope_type(BT_H4_ACL_PKT, NULL, NULL);
+ } else if (strncmp(scopestr + 4, "CMD", 3) == 0) {
+ /* scope is HCI_CMD_
+ * length must be exactly 19 (e.g. HCI_CMD_0x03|0x0003) */
+ if (strlen(scopestr) != 19 || scopestr[12] != '|')
+ return 1;
+
+ if (sscanf(scopestr + 8, "0x%2hhx", &ogf) <= 0)
+ return 1;
+
+ if (sscanf(scopestr + 13, "0x%4hx", &ocf) <= 0)
+ return 1;
+
+ if (verbose)
+ printf("\tadd all HCI command packets with opcode (0x%2.2x|0x%4.4x):\n",
+ ogf, ocf);
+
+ scope_list = get_scope_type(BT_H4_CMD_PKT, &ogf, &ocf);
+ } else if (strncmp(scopestr + 4, "EVT", 3) == 0) {
+ /* scope is CMD_EVT_
+ * length must be exactly 12 (e.g. HCI_EVT_0x0e) */
+ if (strlen(scopestr) != 12)
+ return 1;
+
+ if (sscanf(scopestr + 8, "0x%2hhx", &evt) <= 0)
+ return 1;
+
+ if (verbose)
+ printf("\tadd all HCI event packets with event type (0x%2.2x):\n",
+ evt);
+
+ scope_list = get_scope_type(BT_H4_EVT_PKT, &evt, NULL);
+ }
+ } else if (scopestr[0] >= 48 || scopestr[0] <= 57) {
+ /* first char is a digit */
+ if ((res = strtok(scopestr, "-")) == NULL)
+ return 1;
+
+ from = atoi(res);
+ if (from <= 0)
+ return 1;
+
+ if ((res = strtok(NULL, ":")) == NULL) {
+ /* just one packet */
+ if (verbose)
+ printf("\tadd packet single packet\n");
+
+ scope_list = get_scope_range(from, from);
+ } else {
+ /* range */
+ to = atoi(res);
+ if (to > seq->len)
+ return 1;
+
+ if (verbose)
+ printf("\tadd packets %d to %d\n", from, to);
+
+ scope_list = get_scope_range(from, to);
+ }
+
+ }
+
+ if (verbose)
+ printf("Parsing attributes (%s)\n", attrstr);
+
+ if ((attr_list = parse_attrstr(attrstr)) == NULL) {
+ return 1;
+ }
+
+ if (scope_list != NULL) {
+ apply_attr_scope(scope_list, attr_list);
+ scope_list_delete(scope_list);
+ } else {
+ if (verbose)
+ printf("Empty scope, skipping\n");
+ }
+
+ attr_list_delete(attr_list);
+
+ return 0;
+}
+
+int parse_config(char *path, struct hciseq_list *_seq,
+ struct hciseq_type_cfg *_type_cfg, bool _verbose)
+{
+ char *buf;
+ FILE *file;
+
+ seq = _seq;
+ type_cfg = _type_cfg;
+ verbose = _verbose;
+ line = 0;
+
+ printf("Reading config file...\n");
+
+ buf = malloc(sizeof(char) * MAXLINE);
+ if (buf == NULL) {
+ fprintf(stderr, "Failed to allocate buffer\n");
+ return 1;
+ }
+
+ if ((file = fopen(path, "r")) == NULL) {
+ perror("Failed to open config file");
+ return 1;
+ }
+
+ while (fgets(buf, MAXLINE, file) != NULL) {
+ if (parse_line(buf)) {
+ fprintf(stderr, "Error parsing config file - line %d\n",
+ line);
+ free(buf);
+ return 1;
+ }
+ }
+
+ printf("Done\n\n");
+ fclose(file);
+ free(buf);
+
+ return 0;
+}
diff --git a/tools/replay/config-parser.h b/tools/replay/config-parser.h
new file mode 100644
index 0000000..265628d
--- /dev/null
+++ b/tools/replay/config-parser.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ * Copyright (C) 2012 Anton Weber <ant@antweb.me>
+ * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "hciseq.h"
+
+struct hciseq_type_cfg {
+ struct hciseq_attr *cmd[9216]; /*
+ * opcodes 0x0000 - 0x23FF
+ * (OGF 0x01 - 0x08)
+ */
+ struct hciseq_attr *evt[256]; /* events 0x00 - 0xFF */
+ struct hciseq_attr *acl;
+};
+
+int parse_config(char *path, struct hciseq_list *_seq,
+ struct hciseq_type_cfg *_type_cfg, bool _verbose);
diff --git a/tools/replay/hciseq.h b/tools/replay/hciseq.h
index 1454147..fa589a3 100644
--- a/tools/replay/hciseq.h
+++ b/tools/replay/hciseq.h
@@ -43,7 +43,8 @@ struct frame {
enum hciseq_action {
HCISEQ_ACTION_REPLAY = 0,
- HCISEQ_ACTION_EMULATE = 1
+ HCISEQ_ACTION_EMULATE = 1,
+ HCISEQ_ACTION_SKIP = 2
};
struct hciseq_list {
diff --git a/tools/replay/main.c b/tools/replay/main.c
index dc281b0..d4f748f 100644
--- a/tools/replay/main.c
+++ b/tools/replay/main.c
@@ -41,6 +41,7 @@
#include "main.h"
#include "time.h"
+#include "config-parser.h"
#include "lib/bluetooth.h"
#include "lib/hci.h"
#include "emulator/btdev.h"
@@ -56,6 +57,7 @@
#define TIMING_DELTA 1
static struct hciseq_list dumpseq;
+static struct hciseq_type_cfg type_cfg;
static int fd;
static int pos = 1;
@@ -281,6 +283,35 @@ static void btdev_recv(struct frame *frm)
btdev_receive_h4(btdev, frm->data, frm->data_len);
}
+static struct hciseq_attr *get_type_attr(struct frame *frm)
+{
+ uint8_t pkt_type = ((const uint8_t *) frm->data)[0];
+ uint16_t opcode;
+ uint8_t evt;
+
+ switch (pkt_type) {
+ case BT_H4_CMD_PKT:
+ opcode = *((uint16_t *) (frm->data + 1));
+ if (opcode > 0x2FFF)
+ return NULL;
+ return type_cfg.cmd[opcode];
+ case BT_H4_EVT_PKT:
+ evt = *((uint8_t *) (frm->data + 1));
+
+ /* use attributes of opcode for 'Command Complete' events */
+ if (evt == 0x0e) {
+ opcode = *((uint16_t *) (frm->data + 4));
+ return type_cfg.cmd[opcode];
+ }
+
+ return type_cfg.evt[evt];
+ case BT_H4_ACL_PKT:
+ return type_cfg.acl;
+ default:
+ return NULL;
+ }
+}
+
static bool check_match(struct frame *l, struct frame *r, char *msg)
{
uint8_t type_l = ((const uint8_t *) l->data)[0];
@@ -341,6 +372,7 @@ static bool process_in()
{
static struct frame frm;
static uint8_t data[HCI_MAX_FRAME_SIZE];
+ struct hciseq_attr *attr;
int n;
bool match;
char msg[MAX_MSG];
@@ -362,6 +394,38 @@ static bool process_in()
msg[0] = '\0';
match = check_match(dumpseq.current->frame, &frm, msg);
+ /* check type config */
+ attr = get_type_attr(&frm);
+ if (attr != NULL) {
+ if (attr->action == HCISEQ_ACTION_SKIP) {
+ if (match) {
+ printf("[%4d/%4d] SKIPPING\n", pos,
+ dumpseq.len);
+ return 1;
+ } else {
+ printf("[ Unknown ] %s\n ",
+ msg);
+ dump_frame(&frm);
+ printf(" SKIPPING\n");
+ return 0;
+ }
+ }
+ if (attr->action == HCISEQ_ACTION_EMULATE) {
+ if (match) {
+ printf("[%4d/%4d] EMULATING\n", pos,
+ dumpseq.len);
+ } else {
+ printf("[ Unknown ] %s\n ",
+ msg);
+ printf("EMULATING\n");
+ }
+
+ btdev_recv(&frm);
+
+ return match;
+ }
+ }
+
/* process packet if match */
if (match) {
printf("[%4d/%4d] ", pos, dumpseq.len);
@@ -381,12 +445,20 @@ static bool process_in()
static bool process_out()
{
uint8_t pkt_type;
+ struct hciseq_attr *attr;
/* emulator sends response automatically */
if (dumpseq.current->attr->action == HCISEQ_ACTION_EMULATE) {
return 1;
}
+ /* use type config if set */
+ attr = get_type_attr(dumpseq.current->frame);
+ if (attr != NULL) {
+ if (attr->action == HCISEQ_ACTION_SKIP || attr->action == HCISEQ_ACTION_EMULATE)
+ return true;
+ }
+
pkt_type = ((const uint8_t *) dumpseq.current->frame->data)[0];
switch (pkt_type) {
@@ -412,6 +484,15 @@ static void process()
gettimeofday(&last, NULL);
do {
+ if (dumpseq.current->attr->action == HCISEQ_ACTION_SKIP) {
+ printf("[%4d/%4d] SKIPPING\n ", pos,
+ dumpseq.len);
+ dump_frame(dumpseq.current->frame);
+ dumpseq.current = dumpseq.current->next;
+ pos++;
+ continue;
+ }
+
/* delay */
if (timing == TIMING_DELTA) {
/* consider exec time of process_out()/process_in() */
@@ -485,6 +566,19 @@ static void delete_list()
}
}
+static void delete_type_cfg()
+{
+ int i;
+
+ for (i = 0; i < 9216; i++) {
+ free(type_cfg.cmd[i]);
+ }
+ for (i = 0; i < 256; i++) {
+ free(type_cfg.evt[i]);
+ }
+ free(type_cfg.acl);
+}
+
static void usage(void)
{
printf("hcireplay - Bluetooth replayer\n"
@@ -493,6 +587,7 @@ static void usage(void)
"\t-d, --delay-mode={none|delta} Specify delay mode (default is none)\n"
"\t-m, --delay-modifier=N Set delay modifier to N (default is 1)\n"
"\t-t, --timeout=N Set timeout to N milliseconds when receiving packets from host\n"
+ "\t-c, --config=<file> Use config file\n"
"\t-v, --verbose Enable verbose output\n"
"\t --version Give version information\n"
"\t --help Give a short usage message\n");
@@ -502,6 +597,7 @@ static const struct option main_options[] = {
{"delay-mode", required_argument, NULL, 'd'},
{"delay-modifier", required_argument, NULL, 'm'},
{"timeout", required_argument, NULL, 't'},
+ {"config", required_argument, NULL, 'c'},
{"verbose", no_argument, NULL, 'v'},
{"version", no_argument, NULL, 'V'},
{"help", no_argument, NULL, 'H'},
@@ -512,11 +608,12 @@ int main(int argc, char *argv[])
{
int dumpfd;
int i;
+ char *config = NULL;
while (1) {
int opt;
- opt = getopt_long(argc, argv, "d:m:t:v",
+ opt = getopt_long(argc, argv, "d:m:t:c:v",
main_options, NULL);
if (opt < 0)
break;
@@ -535,6 +632,9 @@ int main(int argc, char *argv[])
case 't':
timeout = atoi(optarg);
break;
+ case 'c':
+ config = optarg;
+ break;
case 'v':
verbose = true;
break;
@@ -572,6 +672,20 @@ int main(int argc, char *argv[])
dumpseq.current = dumpseq.frames;
calc_rel_ts(&dumpseq);
+ /* init type config */
+ for (i = 0; i < 9216; i++)
+ type_cfg.cmd[i] = NULL;
+ for (i = 0; i < 256; i++)
+ type_cfg.evt[i] = NULL;
+ type_cfg.acl = NULL;
+
+ if (config != NULL) {
+ if (parse_config(config, &dumpseq, &type_cfg, verbose)) {
+ vhci_close();
+ return 1;
+ }
+ }
+
/* init emulator */
btdev = btdev_create(0);
btdev_set_send_handler(btdev, btdev_send, NULL);
@@ -595,6 +709,7 @@ int main(int argc, char *argv[])
vhci_close();
btdev_destroy(btdev);
delete_list();
+ delete_type_cfg();
printf("Terminating\n");
return EXIT_SUCCESS;
diff --git a/tools/replay/main.h b/tools/replay/main.h
index 2223789..e8b7365 100644
--- a/tools/replay/main.h
+++ b/tools/replay/main.h
@@ -23,8 +23,6 @@
*
*/
-#include "hciseq.h"
-
struct btsnoop_hdr {
uint8_t id[8]; /* Identification Pattern */
uint32_t version; /* Version Number = 1 */
--
1.7.9.5
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH BlueZ v3 5/6] replay: Add man page
2012-09-05 14:14 ` [PATCH BlueZ v3 1/6] replay: Add initial version of replay tool Anton Weber
` (2 preceding siblings ...)
2012-09-05 14:14 ` [PATCH BlueZ v3 4/6] replay: Add config file parser and skip action Anton Weber
@ 2012-09-05 14:14 ` Anton Weber
2012-09-05 14:14 ` [PATCH BlueZ v3 6/6] replay: Add tutorial Anton Weber
4 siblings, 0 replies; 14+ messages in thread
From: Anton Weber @ 2012-09-05 14:14 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anton Weber
---
Makefile.tools | 6 +-
tools/replay/btreplay.1 | 148 +++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 152 insertions(+), 2 deletions(-)
create mode 100644 tools/replay/btreplay.1
diff --git a/Makefile.tools b/Makefile.tools
index ba2db00..8b1e3de 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -94,11 +94,13 @@ endif
dist_man_MANS += tools/rfcomm.1 tools/l2ping.8 \
tools/hciattach.8 tools/hciconfig.8 \
- tools/hcitool.1 tools/sdptool.1 tools/ciptool.1
+ tools/hcitool.1 tools/sdptool.1 tools/ciptool.1 \
+ tools/replay/btreplay.1
else
EXTRA_DIST += tools/rfcomm.1 tools/l2ping.8 \
tools/hciattach.8 tools/hciconfig.8 \
- tools/hcitool.1 tools/sdptool.1 tools/ciptool.1
+ tools/hcitool.1 tools/sdptool.1 tools/ciptool.1 \
+ tools/replay/btreplay.1
endif
CLEANFILES += tools/lexer.c tools/parser.c tools/parser.h
diff --git a/tools/replay/btreplay.1 b/tools/replay/btreplay.1
new file mode 100644
index 0000000..36fd6c7
--- /dev/null
+++ b/tools/replay/btreplay.1
@@ -0,0 +1,148 @@
+.TH BTREPLAY 1 "August 2012" BlueZ "Linux System Administration "
+
+.SH NAME
+btreplay \- Bluetooth Replayer
+
+.SH SYNOPSIS
+.B btreplay
+.RB [\| \-d
+.IR none|delta \|]
+.RB [\| \-m
+.IR factor \|]
+.RB [\| \-t
+.IR timeout \|]
+.RB [\| \-c
+.IR config-file \|]
+.RB [\| \-v \|]
+.RI "" file " ..."
+
+.SH DESCRIPTION
+.B btreplay
+uses a VHCI interface to simulate HCI traffic from a recorded scenario. It
+reads packets from BTSnoop dump files and replays them on the virtual
+interface.
+.PP
+.TP
+.B "Delay modes"
+The delay mode handles the delay between two packets in the replay process.
+.br
+Following delay modes are supported:
+
+.BR "delta" ": use time difference between two packets (delta value) from the"
+dump for delay
+.br
+.BR "none" ": no delay"
+.TP
+.B "Delay modifier"
+Allows to speed up or slow down the overall packet delay. When used with
+delta delay mode, each delta value in the sequence is multiplied with the
+specified factor.
+.TP
+.B "Packet action"
+The action defines how packets are handled within the replay process.
+.BR "" "The default action is " "replay" "."
+
+.BR "replay" ": Replay packet from dump file"
+.br
+.BR "skip" ": Skip packet"
+.br
+.BR "emulate" ": Forward packet to emulator"
+.TP
+.B "Config file"
+A config file can alter attributes and actions for specific packets in the
+sequence or packet types.
+
+.SH OPTIONS
+.TP
+.BI "\-d, --delay-mode=" "none|delta"
+.RI "Specify delay mode (default is " "none" ")"
+.TP
+.BI "\-m, --delay-modifier=" "N"
+Set delay modifier to N (default is 1)
+.TP
+.BI "\-t, --timeout=" "N"
+.RI "Set timeout to " "N" " milliseconds when receiving packets from host."
+.br
+Set to -1 by default (no timeout)
+.TP
+.BI "\-c, --config=" "config-file"
+Use config file
+.TP
+.BI "\-v, --verbose"
+Enable verbose output
+.TP
+.BI \--version
+Give version information
+.TP
+.BI \--help
+Give a short usage message
+
+.SH CONFIG FILE FORMAT
+The config parser expects one definition per line. Each line has the syntax
+
+.in +4n
+.nf
+.I "scope key=value[,key=value,...]"
+.fi
+.in
+
+and must be terminated by EOF or '\\n'.
+
+.SS Scopes
+The scope defines which packets are affected by the configuration. All scopes
+that do not specify a particular position in the sequence are used as a type
+filter (i.e. they also apply to all incoming and outgoing packets, even if they
+are not in the sequence).
+.TP
+.B all
+All packets in the sequence
+.TP
+.B N
+Single packet
+.TP
+.B N-M
+Packet range (e.g. 2-10, from packet 2 to 10)
+.TP
+.B HCI_ACL
+ACL data packets
+.TP
+.B HCI_EVT_0xXX
+Event code
+.br
+(e.g. HCI_EVT_0x0e, all packets with event code 0x0e (Command Complete))
+.TP
+.B HCI_CMD_0xXX|0xXXXX
+Opcode
+.br
+(e.g. 0x03|0x0003, all packets with OGF 0x03 and OCF 0x0003 (Reset)).
+.br
+Also applies to corresponding Command Complete event packets.
+.PP
+(N and M being the packet position in the sequence, starting with 1)
+
+.SS Properties
+The properties define the configuration that should be applied to the scope.
+.RB "Every property has a " "key"
+.RI " and a " "value" " delimited by an equals sign."
+.TP
+.BI "action="replay|skip|emulate
+Set packet action
+.TP
+.BI "delta="value
+Override delta value (used in delta delay mode)
+
+.SS Example configuration
+all delta=300,action=replay
+.br
+1-4 action=emulate
+.br
+1 delta=500
+.br
+HCI_EVT_0x0e delta=0
+.br
+HCI_CMD_0x03|0x0003 action=emulate
+.br
+HCI_ACL action=replay
+.br
+.SH AUTHOR
+Written by Anton Weber <ant@antweb.me>
--
1.7.9.5
^ permalink raw reply related [flat|nested] 14+ messages in thread* [PATCH BlueZ v3 6/6] replay: Add tutorial
2012-09-05 14:14 ` [PATCH BlueZ v3 1/6] replay: Add initial version of replay tool Anton Weber
` (3 preceding siblings ...)
2012-09-05 14:14 ` [PATCH BlueZ v3 5/6] replay: Add man page Anton Weber
@ 2012-09-05 14:14 ` Anton Weber
4 siblings, 0 replies; 14+ messages in thread
From: Anton Weber @ 2012-09-05 14:14 UTC (permalink / raw)
To: linux-bluetooth; +Cc: Anton Weber
Add short tutorial to show example usage of btreplay.
---
doc/btreplay-tutorial.txt | 60 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 60 insertions(+)
create mode 100644 doc/btreplay-tutorial.txt
diff --git a/doc/btreplay-tutorial.txt b/doc/btreplay-tutorial.txt
new file mode 100644
index 0000000..61e96b6
--- /dev/null
+++ b/doc/btreplay-tutorial.txt
@@ -0,0 +1,60 @@
+BlueZ Bluetooth Replayer Tutorial
+*********************************
+
+Copyright (C) 2012 Anton Weber <ant@antweb.me>
+
+
+Create dump
+===========
+A dump can be recorded using the -b option of btmon.
+The best way to record a suitable dump is to disable the Bluetooth controller
+before recording. That way btmon also records the initialisation sequence.
+
+# sudo btmon -b testdump
+<plug in / enable Bluetooth contoller>
+<connect Bluetooth mouse>
+<move mouse around>
+<CTRL+C>
+
+
+Replay dump
+===========
+
+First make sure the VHCI driver is loaded:
+
+# sudo modprobe hci_vhci
+# ls /dev | grep vhci
+
+Now the dump can be replayed with btreplay. When the same machine is used to
+replay the dump, it is recommended to unplug / disable the Bluetooth controller
+to avoid conflicts.
+
+The simplest way to use the Bluetooth Replayer is to run
+
+# sudo btreplay testdump
+
+Some scenarios require minimal delay during the execution to be replayed
+correctly (such as the example above).
+One solution is to use the delta timing mode that adds delays according to the
+time difference between packets in the dump file.
+
+# sudo btreplay -d delta testdump
+
+Running btreplay with a dump from another machine can cause some issues with
+the initialisation sequence. The host might send out packets in a different
+order.
+btreplay will show several packets marked as "[Unknown] ! Wrong opcode" and
+the replay process can get stuck.
+When these packets are not relevant to the actual scenario, they can be ignored
+using a config file. For example, they can be passed on to the emulator.
+To do so, set the action for these packet types to 'emulate' in a config file.
+
+Example config contents:
+
+HCI_CMD_0x03|0x0001 action=emulate
+HCI_CMD_0x03|0x0018 action=emulate
+
+
+and run btreplay with this config file.
+
+# sudo btreplay -d delta -c testconfig testdump
--
1.7.9.5
^ permalink raw reply related [flat|nested] 14+ messages in thread