hcidump.c revision f62210cb6c06984210bb58a409327aa9b5ea7d08
1/*
2	HCIDump - HCI packet analyzer
3	Copyright (C) 2000-2001 Maxim Krasnyansky <maxk@qualcomm.com>
4
5	This program is free software; you can redistribute it and/or modify
6	it under the terms of the GNU General Public License version 2 as
7	published by the Free Software Foundation;
8
9	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
10	OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
11	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
12	IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY CLAIM,
13	OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
14	RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
15	NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
16	USE OR PERFORMANCE OF THIS SOFTWARE.
17
18	ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, COPYRIGHTS,
19	TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS SOFTWARE IS DISCLAIMED.
20*/
21
22/*
23 * $Id$
24 */
25
26#include <stdio.h>
27#include <stdlib.h>
28#include <unistd.h>
29#include <termios.h>
30#include <fcntl.h>
31#include <sys/ioctl.h>
32#include <sys/socket.h>
33#include <sys/types.h>
34#include <sys/uio.h>
35#include <errno.h>
36#include <string.h>
37
38#include <asm/types.h>
39
40#include <bluetooth/bluetooth.h>
41#include <bluetooth/hci.h>
42#include <bluetooth/l2cap.h>
43#include <bluetooth/hci_lib.h>
44
45#include <pwd.h>
46#include <argp.h>
47
48#include "parser.h"
49#include "hcidump.h"
50
51/* Default options */
52static int  device;
53static int  snap_len = SNAP_LEN;
54static int  mode = PARSE;
55static long flags;
56static long filter;
57static char *dump_file;
58
59static void process_frames(int dev, int sock, int file)
60{
61	struct cmsghdr *cmsg;
62	struct msghdr msg;
63	struct iovec  iv;
64	struct dump_hdr *dh;
65	struct frame frm;
66	char *buf, *ctrl;
67
68	if (snap_len < SNAP_LEN)
69		snap_len = SNAP_LEN;
70
71	if (!(buf = malloc(snap_len + DUMP_HDR_SIZE))) {
72		perror("Can't allocate data buffer");
73		exit(1);
74	}
75	dh = (void *) buf;
76	frm.data = buf + DUMP_HDR_SIZE;
77
78	if (!(ctrl = malloc(100))) {
79		perror("Can't allocate control buffer");
80		exit(1);
81	}
82
83	printf("device: hci%d snap_len: %d filter: 0x%lx\n",
84		dev, snap_len, filter);
85
86	while (1) {
87		iv.iov_base = frm.data;
88		iv.iov_len  = snap_len;
89
90		msg.msg_iov = &iv;
91		msg.msg_iovlen = 1;
92		msg.msg_control = ctrl;
93		msg.msg_controllen = 100;
94
95		if ((frm.data_len = recvmsg(sock, &msg, 0)) < 0) {
96			perror("Receive failed");
97			exit(1);
98		}
99
100		/* Process control message */
101		frm.in = 0;
102		cmsg = CMSG_FIRSTHDR(&msg);
103		while (cmsg) {
104			switch (cmsg->cmsg_type) {
105			case HCI_CMSG_DIR:
106				frm.in = *((int *)CMSG_DATA(cmsg));
107				break;
108			case HCI_CMSG_TSTAMP:
109				frm.ts = *((struct timeval *)CMSG_DATA(cmsg));
110				break;
111			}
112			cmsg = CMSG_NXTHDR(&msg, cmsg);
113		}
114
115		frm.ptr = frm.data;
116		frm.len = frm.data_len;
117
118		switch (mode) {
119		case WRITE:
120			/* Save dump */
121			dh->len = htobs(frm.data_len);
122			dh->in  = frm.in;
123			dh->ts_sec  = htobl(frm.ts.tv_sec);
124			dh->ts_usec = htobl(frm.ts.tv_usec);
125			if (write_n(file, buf, frm.data_len + DUMP_HDR_SIZE) < 0) {
126				perror("Write error");
127				exit(1);
128			}
129			break;
130
131		default:
132			/* Parse and print */
133			parse(&frm);
134			break;
135		}
136	}
137}
138
139static void read_dump(int file)
140{
141	struct dump_hdr dh;
142	struct frame frm;
143	int err;
144
145	if (!(frm.data = malloc(HCI_MAX_FRAME_SIZE))) {
146		perror("Can't allocate data buffer");
147		exit(1);
148	}
149
150	while (1) {
151		if ((err = read_n(file, (void *) &dh, DUMP_HDR_SIZE)) < 0)
152			goto failed;
153		if (!err) return;
154
155		frm.data_len = btohs(dh.len);
156
157		if ((err = read_n(file, frm.data, frm.data_len)) < 0)
158			goto failed;
159		if (!err) return;
160
161		frm.ptr = frm.data;
162		frm.len = frm.data_len;
163		frm.in  = dh.in;
164		frm.ts.tv_sec  = btohl(dh.ts_sec);
165		frm.ts.tv_usec = btohl(dh.ts_usec);
166
167		parse(&frm);
168	}
169
170failed:
171	perror("Read failed");
172	exit(1);
173}
174
175static int open_file(char *file, int mode)
176{
177	int f, flags;
178
179	if (mode == WRITE)
180		flags = O_WRONLY | O_CREAT | O_APPEND;
181	else
182		flags = O_RDONLY;
183
184	if ((f = open(file, flags)) < 0) {
185		perror("Can't open output file");
186		exit(1);
187	}
188	return f;
189}
190
191static int open_socket(int dev)
192{
193	struct sockaddr_hci addr;
194	struct hci_filter flt;
195	int s, opt;
196
197	/* Create HCI socket */
198	if ((s=socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) {
199		perror("Can't create HCI socket");
200		exit(1);
201	}
202
203	opt = 1;
204	if (setsockopt(s, SOL_HCI, HCI_DATA_DIR, &opt, sizeof(opt)) < 0) {
205		perror("Can't enable data direction info");
206		exit(1);
207	}
208
209	opt = 1;
210	if (setsockopt(s, SOL_HCI, HCI_TIME_STAMP, &opt, sizeof(opt)) < 0) {
211		perror("Can't enable time stamp");
212		exit(1);
213	}
214
215	/* Setup filter */
216	hci_filter_clear(&flt);
217	hci_filter_all_ptypes(&flt);
218	hci_filter_all_events(&flt);
219	if (setsockopt(s, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
220		perror("Can't set HCI filter");
221		exit(1);
222	}
223
224	/* Bind socket to the HCI device */
225	addr.hci_family = AF_BLUETOOTH;
226	addr.hci_dev = dev;
227	if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
228		printf("Can't attach to device hci%d. %s(%d)\n",
229					dev, strerror(errno), errno);
230		exit(1);
231	}
232	return s;
233}
234
235const char *argp_program_version = "HCIDump "VERSION;
236const char *argp_program_bug_address = "<bluez-users@lists.sf.net>";
237
238static struct argp_option options[] = {
239	{"device", 	'i', "hci_dev", 0, "HCI device", 0  },
240	{"snap-len", 	's', "len",  0, "Snap len (in bytes)", 1 },
241	{"save-dump",	'w', "file", 0, "Save dump to a file", 2 },
242	{"read-dump",	'r', "file", 0, "Read dump from a file", 2 },
243	{"ts", 		't', 0,  0, "Display time stamps", 2 },
244	{"hex", 	'x', 0,  0, "Dump data in hex", 3 },
245	{"ascii", 	'a', 0,  0, "Dump data in ascii", 3 },
246	{"raw", 	'R', 0,  0, "Raw mode", 4 },
247	{ 0 }
248};
249
250static struct {
251	char *name;
252	int  flag;
253} filters[] = {
254	{ "hci",    FILT_HCI    },
255	{ "l2cap",  FILT_L2CAP  },
256	{ "sco",    FILT_SCO    },
257	{ "rfcomm", FILT_RFCOMM },
258	{ "sdp",    FILT_SDP    },
259	{ "bnep",   FILT_BNEP	},
260	{ 0 }
261};
262
263static void parse_filter(struct argp_state *state)
264{
265	int i,n;
266
267	for (i=state->next; i<state->argc; i++) {
268		for (n=0; filters[n].name; n++) {
269			if (!strcmp(filters[n].name, state->argv[i])) {
270				filter |= filters[n].flag;
271				break;
272			}
273		}
274	}
275}
276
277static error_t parse_opt(int key, char *arg, struct argp_state *state)
278{
279	switch (key) {
280		case 'i':
281			device = atoi(arg+3);
282			break;
283
284		case 'x':
285			flags |= DUMP_HEX;
286			break;
287
288		case 'a':
289			flags |= DUMP_ASCII;
290			break;
291
292		case 's':
293			snap_len = atoi(arg);
294			break;
295
296		case 't':
297			flags |= DUMP_TSTAMP;
298			break;
299
300		case 'R':
301			flags |= DUMP_RAW;
302			break;
303
304		case 'r':
305			mode = READ;
306			dump_file = strdup(arg);
307			break;
308
309		case 'w':
310			mode = WRITE;
311			dump_file = strdup(arg);
312			break;
313
314		case ARGP_KEY_ARGS:
315			parse_filter(state);
316			break;
317
318		default:
319			return ARGP_ERR_UNKNOWN;
320	}
321	return 0;
322}
323
324static struct argp arg_parser = {
325	options,
326	parse_opt,
327	"[filter]",
328	"HCIDump - HCI packet analyzer ver " VERSION
329};
330
331int main(int argc, char *argv[])
332{
333	argp_parse(&arg_parser, argc, argv, 0, NULL, NULL);
334
335	printf("HCIDump - HCI packet analyzer ver %s.\n", VERSION);
336
337	/* Default settings */
338	if (!filter)
339		filter = ~0L;
340
341	switch (mode) {
342	case PARSE:
343		init_parser(flags, filter);
344		process_frames(device, open_socket(device), -1);
345		break;
346
347	case WRITE:
348		process_frames(device, open_socket(device), open_file(dump_file, mode));
349		break;
350
351	case READ:
352		init_parser(flags, filter);
353		read_dump(open_file(dump_file, mode));
354		break;
355	}
356	return 0;
357}
358