All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH BlueZ 1/4] replay: Add initial version of replay tool
@ 2012-08-09 17:36 Anton Weber
  2012-08-09 17:36 ` [PATCH BlueZ 2/4] replay: Add timing functionality Anton Weber
                   ` (4 more replies)
  0 siblings, 5 replies; 14+ messages in thread
From: Anton Weber @ 2012-08-09 17:36 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..8019151
--- /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)))
+		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 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

end of thread, other threads:[~2012-09-05 14:14 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [PATCH BlueZ 4/4] replay: Add config file parser and skip action Anton Weber
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   ` [PATCH BlueZ v2 4/4] replay: Add config file parser and skip action Anton Weber
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   ` [PATCH BlueZ v3 4/6] replay: Add config file parser and skip action 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

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.