hcidump.c revision dda78ee2b52b86b6008d109db63253fc955304e7
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
44#include <pwd.h>
45#include <argp.h>
46
47#include "parser.h"
48#include "hcidump.h"
49
50/* Default options */
51static int  device;
52static int  snap_len = SNAP_LEN;
53static int  mode  = PARSE;
54static long flags;
55static char *dump_file;
56
57static void process_frames(int dev, int sock, int file)
58{
59	struct cmsghdr *cmsg;
60	struct msghdr msg;
61	struct iovec  iv;
62	struct dump_hdr *dh;
63	struct frame frm;
64	char *buf, *ctrl;
65
66	if (snap_len < SNAP_LEN)
67		snap_len = SNAP_LEN;
68
69	if (!(buf = malloc(snap_len + DUMP_HDR_SIZE))) {
70		perror("Can't allocate data buffer");
71		exit(1);
72	}
73	dh = (void *) buf;
74	frm.data = buf + DUMP_HDR_SIZE;
75
76	if (!(ctrl = malloc(100))) {
77		perror("Can't allocate control buffer");
78		exit(1);
79	}
80
81	printf("device: hci%d snap_len: %d filter: none\n", dev, snap_len);
82
83	while (1) {
84		iv.iov_base = frm.data;
85		iv.iov_len  = snap_len;
86
87		msg.msg_iov = &iv;
88		msg.msg_iovlen = 1;
89		msg.msg_control = ctrl;
90		msg.msg_controllen = 100;
91
92		if ((frm.data_len = recvmsg(sock, &msg, 0)) < 0) {
93			perror("Receive failed");
94			exit(1);
95		}
96
97		/* Process control message */
98		frm.in = 0;
99		cmsg = CMSG_FIRSTHDR(&msg);
100		while (cmsg) {
101			switch (cmsg->cmsg_type) {
102			case HCI_CMSG_DIR:
103				frm.in = *((int *)CMSG_DATA(cmsg));
104				break;
105			}
106			cmsg = CMSG_NXTHDR(&msg, cmsg);
107		}
108
109		frm.ptr = frm.data;
110		frm.len = frm.data_len;
111
112		switch (mode) {
113		case RAW:
114			/* Print raw dump */
115      			printf("%c ", (frm.in ? '>' : '<'));
116			raw_dump(0, &frm);
117			fflush(stdout);
118			break;
119
120		case WRITE:
121			/* Save dump */
122			dh->len = __cpu_to_le16(frm.data_len);
123			dh->in  = frm.in;
124			if (write_n(file, buf, frm.data_len + DUMP_HDR_SIZE) < 0) {
125				perror("Write error");
126				exit(1);
127			}
128			break;
129
130		default:
131			/* Parse and print */
132			parse(&frm);
133			break;
134		}
135	}
136}
137
138static void read_dump(int file)
139{
140	struct dump_hdr dh;
141	struct frame frm;
142	int err;
143
144	if (!(frm.data = malloc(HCI_MAX_FRAME_SIZE))) {
145		perror("Can't allocate data buffer");
146		exit(1);
147	}
148
149	while (1) {
150		if ((err = read_n(file, (void *) &dh, DUMP_HDR_SIZE)) < 0)
151			goto failed;
152		if (!err) return;
153
154		frm.data_len = __le16_to_cpu(dh.len);
155
156		if ((err = read_n(file, frm.data, frm.data_len)) < 0)
157			goto failed;
158		if (!err) return;
159
160		frm.ptr = frm.data;
161		frm.len = frm.data_len;
162		frm.in  = dh.in;
163
164		parse(&frm);
165	}
166
167failed:
168	perror("Read failed");
169	exit(1);
170}
171
172static int open_file(char *file, int mode)
173{
174	int f, flags;
175
176	if (mode == WRITE)
177		flags = O_WRONLY | O_CREAT | O_APPEND;
178	else
179		flags = O_RDONLY;
180
181	if ((f = open(file, flags)) < 0) {
182		perror("Can't open output file");
183		exit(1);
184	}
185	return f;
186}
187
188static int open_socket(int dev)
189{
190	struct sockaddr_hci addr;
191	struct hci_filter flt;
192	int s, opt;
193
194	/* Create HCI socket */
195	if ((s=socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI)) < 0) {
196		perror("Can't create HCI socket");
197		exit(1);
198	}
199
200	opt = 1;
201	if (setsockopt(s, SOL_HCI, HCI_DATA_DIR, &opt, sizeof(opt)) < 0) {
202		perror("Can't enable data direction info");
203		exit(1);
204	}
205
206	/* Setup filter */
207	flt.type_mask  = ~0;      // All packet types
208	flt.event_mask[0] = ~0L;  // All events
209	flt.event_mask[1] = ~0L;
210	if (setsockopt(s, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
211		perror("Can't set HCI filter");
212		exit(1);
213	}
214
215	/* Bind socket to the HCI device */
216	addr.hci_family = AF_BLUETOOTH;
217	addr.hci_dev = dev;
218	if (bind(s, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
219		printf("Can't attach to device hci%d. %s(%d)\n",
220					dev, strerror(errno), errno);
221		exit(1);
222	}
223	return s;
224}
225
226const char *argp_program_version = "HCIDump "VERSION;
227const char *argp_program_bug_address = "<bluez-users@lists.sf.net>";
228
229static struct argp_option options[] = {
230	{"device", 	'i', "hci_dev", 0, "HCI device", 0  },
231	{"snap-len", 	's', "len",  0, "Snap len (in bytes)", 1 },
232	{"save-dump",	'w', "file", 0, "Save dump to a file", 2 },
233	{"read-dump",	'r', "file", 0, "Read dump from a file", 2 },
234	{"hex", 	'h', 0,  0, "Dump data in hex", 3 },
235	{"ascii", 	'a', 0,  0, "Dump data in ascii", 3 },
236	{"raw", 	'R', 0,  0, "Raw mode", 3 },
237	{ 0 }
238};
239
240static error_t parse_opt(int key, char *arg, struct argp_state *state)
241{
242	switch (key) {
243		case 'i':
244			device = atoi(arg+3);
245			break;
246
247		case 'h':
248			flags |= DUMP_HEX;
249			break;
250
251		case 'a':
252			flags |= DUMP_ASCII;
253			break;
254
255		case 's':
256			snap_len = atoi(arg);
257			break;
258
259		case 'R':
260			flags |= DUMP_HEX;
261			mode = RAW;
262			break;
263
264		case 'r':
265			mode = READ;
266			dump_file = strdup(arg);
267			break;
268
269		case 'w':
270			mode = WRITE;
271			dump_file = strdup(arg);
272			break;
273
274		default:
275			return ARGP_ERR_UNKNOWN;
276	}
277	return 0;
278}
279
280static struct argp parser = {
281	options,
282	parse_opt,
283	"",
284	"HCIDump - HCI packet analyzer ver " VERSION
285};
286
287int main(int argc, char *argv[])
288{
289	argp_parse(&parser, argc, argv, 0, NULL, NULL);
290
291	printf("HCIDump - HCI packet analyzer ver %s.\n", VERSION);
292
293	switch (mode) {
294	case RAW:
295	case PARSE:
296		init_parser(flags);
297		process_frames(device, open_socket(device), -1);
298		break;
299
300	case WRITE:
301		process_frames(device, open_socket(device), open_file(dump_file, mode));
302		break;
303
304	case READ:
305		init_parser(flags);
306		read_dump(open_file(dump_file, mode));
307		break;
308	}
309	return 0;
310}
311