6 * Copyright (c) 1998 by Gilbert Ramirez <gram@alumni.rice.edu>
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 #include "file_wrappers.h"
35 * Symbian's btsnoop format is derived from Sun's snoop format.
36 * See RFC 1761 for a description of the "snoop" file format.
39 /* Magic number in "btsnoop" files. */
40 static const char btsnoop_magic[] = {
41 'b', 't', 's', 'n', 'o', 'o', 'p', '\0'
44 /* "btsnoop" file header (minus magic number). */
46 guint32 version; /* version number (should be 1) */
47 guint32 datalink; /* datalink type */
50 /* "btsnoop" record header. */
51 struct btsnooprec_hdr {
52 guint32 orig_len; /* actual length of packet */
53 guint32 incl_len; /* number of octets captured in file */
54 guint32 flags; /* packet flags */
55 guint32 cum_drops; /* cumulative number of dropped packets */
56 gint64 ts_usec; /* timestamp microseconds */
59 /* H1 is unframed data with the packet type encoded in the flags field of capture header */
60 /* It can be used for any datalink by placing logging above the datalink layer of HCI */
61 #define KHciLoggerDatalinkTypeH1 1001
62 /* H4 is the serial HCI with packet type encoded in the first byte of each packet */
63 #define KHciLoggerDatalinkTypeH4 1002
64 /* CSR's PPP derived bluecore serial protocol - in practice we log in H1 format after deframing */
65 #define KHciLoggerDatalinkTypeBCSP 1003
66 /* H5 is the official three wire serial protocol derived from BCSP*/
67 #define KHciLoggerDatalinkTypeH5 1004
69 #define KHciLoggerHostToController 0
70 #define KHciLoggerControllerToHost 0x00000001
71 #define KHciLoggerACLDataFrame 0
72 #define KHciLoggerCommandOrEvent 0x00000002
74 const gint64 KUnixTimeBase = G_GINT64_CONSTANT(0x00dcddb30f2f8000); /* offset from symbian - unix time */
76 static gboolean btsnoop_read(wtap *wth, int *err, gchar **err_info,
78 static gboolean btsnoop_seek_read(wtap *wth, gint64 seek_off,
79 union wtap_pseudo_header *pseudo_header, guint8 *pd, int length,
80 int *err, gchar **err_info);
81 static gboolean snoop_read_rec_data(FILE_T fh, guint8 *pd, int length, int *err,
84 int btsnoop_open(wtap *wth, int *err, gchar **err_info)
87 char magic[sizeof btsnoop_magic];
88 struct btsnoop_hdr hdr;
90 int file_encap=WTAP_ENCAP_UNKNOWN;
92 /* Read in the string that should be at the start of a "btsnoop" file */
93 errno = WTAP_ERR_CANT_READ;
94 bytes_read = file_read(magic, sizeof magic, wth->fh);
95 if (bytes_read != sizeof magic) {
96 *err = file_error(wth->fh, err_info);
101 wth->data_offset += sizeof magic;
103 if (memcmp(magic, btsnoop_magic, sizeof btsnoop_magic) != 0) {
107 /* Read the rest of the header. */
108 errno = WTAP_ERR_CANT_READ;
109 bytes_read = file_read(&hdr, sizeof hdr, wth->fh);
110 if (bytes_read != sizeof hdr) {
111 *err = file_error(wth->fh, err_info);
116 wth->data_offset += sizeof hdr;
119 * Make sure it's a version we support.
121 hdr.version = g_ntohl(hdr.version);
122 if (hdr.version != 1) {
123 *err = WTAP_ERR_UNSUPPORTED;
124 *err_info = g_strdup_printf("btsnoop: version %u unsupported", hdr.version);
128 hdr.datalink = g_ntohl(hdr.datalink);
129 switch (hdr.datalink) {
130 case KHciLoggerDatalinkTypeH1:
131 file_encap=WTAP_ENCAP_BLUETOOTH_HCI;
133 case KHciLoggerDatalinkTypeBCSP:
134 *err = WTAP_ERR_UNSUPPORTED;
135 *err_info = g_strdup_printf("btsnoop: BCSP capture logs unsupported");
137 case KHciLoggerDatalinkTypeH5:
138 *err = WTAP_ERR_UNSUPPORTED;
139 *err_info = g_strdup_printf("btsnoop: H5 capture logs unsupported");
141 case KHciLoggerDatalinkTypeH4:
142 file_encap=WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR;
145 *err = WTAP_ERR_UNSUPPORTED;
146 *err_info = g_strdup_printf("btsnoop: datalink type %u unknown or unsupported", hdr.datalink);
150 wth->subtype_read = btsnoop_read;
151 wth->subtype_seek_read = btsnoop_seek_read;
152 wth->file_encap = file_encap;
153 wth->snapshot_length = 0; /* not available in header */
154 wth->tsprecision = WTAP_FILE_TSPREC_USEC;
155 wth->file_type = WTAP_FILE_BTSNOOP;
159 static gboolean btsnoop_read(wtap *wth, int *err, gchar **err_info,
166 struct btsnooprec_hdr hdr;
169 /* As the send/receive flag is stored in the middle of the capture header
170 but needs to go in the pseudo header for wiretap, the header needs to be reread
171 in the seek_read function*/
172 *data_offset = wth->data_offset;
174 /* Read record header. */
175 errno = WTAP_ERR_CANT_READ;
176 bytes_read = file_read(&hdr, sizeof hdr, wth->fh);
177 if (bytes_read != sizeof hdr) {
178 *err = file_error(wth->fh, err_info);
179 if (*err == 0 && bytes_read != 0)
180 *err = WTAP_ERR_SHORT_READ;
183 wth->data_offset += sizeof hdr;
185 packet_size = g_ntohl(hdr.incl_len);
186 orig_size = g_ntohl(hdr.orig_len);
187 flags = g_ntohl(hdr.flags);
188 if (packet_size > WTAP_MAX_PACKET_SIZE) {
190 * Probably a corrupt capture file; don't blow up trying
191 * to allocate space for an immensely-large packet.
193 *err = WTAP_ERR_BAD_RECORD;
194 *err_info = g_strdup_printf("btsnoop: File has %u-byte packet, bigger than maximum of %u",
195 packet_size, WTAP_MAX_PACKET_SIZE);
199 buffer_assure_space(wth->frame_buffer, packet_size);
200 if (!snoop_read_rec_data(wth->fh, buffer_start_ptr(wth->frame_buffer),
201 packet_size, err, err_info)) {
202 return FALSE; /* Read error */
204 wth->data_offset += packet_size;
206 ts = GINT64_FROM_BE(hdr.ts_usec);
209 wth->phdr.ts.secs = (guint)(ts / 1000000);
210 wth->phdr.ts.nsecs = (guint)((ts % 1000000) * 1000);
211 wth->phdr.caplen = packet_size;
212 wth->phdr.len = orig_size;
213 if(wth->file_encap == WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR)
215 wth->pseudo_header.p2p.sent = (flags & KHciLoggerControllerToHost) ? FALSE : TRUE;
217 else if(wth->file_encap == WTAP_ENCAP_BLUETOOTH_HCI)
219 wth->pseudo_header.bthci.sent = (flags & KHciLoggerControllerToHost) ? FALSE : TRUE;
220 if(flags & KHciLoggerCommandOrEvent)
222 if(wth->pseudo_header.bthci.sent)
224 wth->pseudo_header.bthci.channel = BTHCI_CHANNEL_COMMAND;
228 wth->pseudo_header.bthci.channel = BTHCI_CHANNEL_EVENT;
233 wth->pseudo_header.bthci.channel = BTHCI_CHANNEL_ACL;
239 static gboolean btsnoop_seek_read(wtap *wth, gint64 seek_off,
240 union wtap_pseudo_header *pseudo_header, guint8 *pd, int length,
241 int *err, gchar **err_info) {
243 struct btsnooprec_hdr hdr;
245 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
248 /* Read record header. */
249 errno = WTAP_ERR_CANT_READ;
250 bytes_read = file_read(&hdr, sizeof hdr, wth->random_fh);
251 if (bytes_read != sizeof hdr) {
252 *err = file_error(wth->random_fh, err_info);
253 if (*err == 0 && bytes_read != 0)
254 *err = WTAP_ERR_SHORT_READ;
257 flags = g_ntohl(hdr.flags);
260 * Read the packet data.
262 if (!snoop_read_rec_data(wth->random_fh, pd, length, err, err_info))
263 return FALSE; /* failed */
265 if(wth->file_encap == WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR)
267 pseudo_header->p2p.sent = (flags & KHciLoggerControllerToHost) ? FALSE : TRUE;
269 else if(wth->file_encap == WTAP_ENCAP_BLUETOOTH_HCI)
271 pseudo_header->bthci.sent = (flags & KHciLoggerControllerToHost) ? FALSE : TRUE;
272 if(flags & KHciLoggerCommandOrEvent)
274 if(pseudo_header->bthci.sent)
276 pseudo_header->bthci.channel = BTHCI_CHANNEL_COMMAND;
280 pseudo_header->bthci.channel = BTHCI_CHANNEL_EVENT;
285 pseudo_header->bthci.channel = BTHCI_CHANNEL_ACL;
292 snoop_read_rec_data(FILE_T fh, guint8 *pd, int length, int *err,
297 errno = WTAP_ERR_CANT_READ;
298 bytes_read = file_read(pd, length, fh);
300 if (bytes_read != length) {
301 *err = file_error(fh, err_info);
303 *err = WTAP_ERR_SHORT_READ;
309 /* Returns 0 if we could write the specified encapsulation type,
310 an error indication otherwise. */
311 int btsnoop_dump_can_write_encap(int encap)
313 /* Per-packet encapsulations aren't supported. */
314 if (encap == WTAP_ENCAP_PER_PACKET)
315 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
317 /* XXX - for now we only support WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR */
318 if (encap != WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR)
319 return WTAP_ERR_UNSUPPORTED_ENCAP;
324 struct hci_flags_mapping
331 static const struct hci_flags_mapping hci_flags[] =
333 { 0x02, TRUE, KHciLoggerHostToController|KHciLoggerACLDataFrame }, /* HCI_H4_TYPE_ACL */
334 { 0x02, FALSE, KHciLoggerControllerToHost|KHciLoggerACLDataFrame }, /* HCI_H4_TYPE_ACL */
335 { 0x01, TRUE, KHciLoggerHostToController|KHciLoggerCommandOrEvent }, /* HCI_H4_TYPE_CMD */
336 { 0x04, FALSE, KHciLoggerControllerToHost|KHciLoggerCommandOrEvent }, /* HCI_H4_TYPE_EVT */
339 static guint8 btsnoop_lookup_flags(guint8 hci_type, gboolean sent, guint8 *flags)
343 for (i=0; i < G_N_ELEMENTS(hci_flags); ++i)
345 if (hci_flags[i].hci_type == hci_type &&
346 hci_flags[i].sent == sent)
348 *flags = hci_flags[i].flags;
355 static gboolean btsnoop_dump_partial_rec_hdr(wtap_dumper *wdh _U_,
356 const struct wtap_pkthdr *phdr,
357 const union wtap_pseudo_header *pseudo_header,
358 const guint8 *pd, int *err,
359 struct btsnooprec_hdr *rec_hdr)
364 if (!btsnoop_lookup_flags(*pd, pseudo_header->p2p.sent, &flags)) {
365 *err = WTAP_ERR_UNSUPPORTED;
369 ts_usec = ((gint64) phdr->ts.secs * 1000000) + ((gint64) phdr->ts.nsecs / 1000);
370 ts_usec += KUnixTimeBase;
372 rec_hdr->flags = GUINT32_TO_BE(flags);
373 rec_hdr->cum_drops = GUINT32_TO_BE(0);
374 rec_hdr->ts_usec = GINT64_TO_BE(ts_usec);
379 /* FIXME: How do we support multiple backends?*/
380 static gboolean btsnoop_dump_h1(wtap_dumper *wdh,
381 const struct wtap_pkthdr *phdr,
382 const union wtap_pseudo_header *pseudo_header,
383 const guint8 *pd, int *err)
385 struct btsnooprec_hdr rec_hdr;
387 if (!btsnoop_dump_partial_rec_hdr(wdh, phdr, pseudo_header, pd, err, &rec_hdr))
390 rec_hdr.incl_len = GUINT32_TO_BE(phdr->caplen-1);
391 rec_hdr.orig_len = GUINT32_TO_BE(phdr->len-1);
393 if (!wtap_dump_file_write(wdh, &rec_hdr, sizeof rec_hdr, err))
396 wdh->bytes_dumped += sizeof rec_hdr;
398 /* Skip HCI packet type */
401 if (!wtap_dump_file_write(wdh, pd, phdr->caplen-1, err))
404 wdh->bytes_dumped += phdr->caplen-1;
409 static gboolean btsnoop_dump_h4(wtap_dumper *wdh,
410 const struct wtap_pkthdr *phdr,
411 const union wtap_pseudo_header *pseudo_header,
412 const guint8 *pd, int *err)
414 struct btsnooprec_hdr rec_hdr;
416 if (!btsnoop_dump_partial_rec_hdr(wdh, phdr, pseudo_header, pd, err, &rec_hdr))
419 rec_hdr.incl_len = GUINT32_TO_BE(phdr->caplen);
420 rec_hdr.orig_len = GUINT32_TO_BE(phdr->len);
422 if (!wtap_dump_file_write(wdh, &rec_hdr, sizeof rec_hdr, err))
425 wdh->bytes_dumped += sizeof rec_hdr;
427 if (!wtap_dump_file_write(wdh, pd, phdr->caplen, err))
430 wdh->bytes_dumped += phdr->caplen;
435 /* FIXME: How do we support multiple backends?*/
436 gboolean btsnoop_dump_open_h1(wtap_dumper *wdh, int *err)
438 struct btsnoop_hdr file_hdr;
440 /* This is a libpcap file */
441 wdh->subtype_write = btsnoop_dump_h1;
442 wdh->subtype_close = NULL;
444 /* Write the file header. */
445 switch (wdh->file_type) {
447 case WTAP_FILE_BTSNOOP:
448 wdh->tsprecision = WTAP_FILE_TSPREC_USEC;
452 /* We should never get here - our open routine
453 should only get called for the types above. */
454 *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
458 if (!wtap_dump_file_write(wdh, btsnoop_magic, sizeof btsnoop_magic, err))
461 wdh->bytes_dumped += sizeof btsnoop_magic;
463 /* current "btsnoop" format is 1 */
464 file_hdr.version = GUINT32_TO_BE(1);
465 /* HCI type encoded in first byte */
466 file_hdr.datalink = GUINT32_TO_BE(KHciLoggerDatalinkTypeH1);
468 if (!wtap_dump_file_write(wdh, &file_hdr, sizeof file_hdr, err))
471 wdh->bytes_dumped += sizeof file_hdr;
476 /* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
478 gboolean btsnoop_dump_open_h4(wtap_dumper *wdh, int *err)
480 struct btsnoop_hdr file_hdr;
482 /* This is a libpcap file */
483 wdh->subtype_write = btsnoop_dump_h4;
484 wdh->subtype_close = NULL;
486 /* Write the file header. */
487 switch (wdh->file_type) {
489 case WTAP_FILE_BTSNOOP:
490 wdh->tsprecision = WTAP_FILE_TSPREC_USEC;
494 /* We should never get here - our open routine
495 should only get called for the types above. */
496 *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
500 if (!wtap_dump_file_write(wdh, btsnoop_magic, sizeof btsnoop_magic, err))
503 wdh->bytes_dumped += sizeof btsnoop_magic;
505 /* current "btsnoop" format is 1 */
506 file_hdr.version = GUINT32_TO_BE(1);
507 /* HCI type encoded in first byte */
508 file_hdr.datalink = GUINT32_TO_BE(KHciLoggerDatalinkTypeH4);
510 if (!wtap_dump_file_write(wdh, &file_hdr, sizeof file_hdr, err))
513 wdh->bytes_dumped += sizeof file_hdr;