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