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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include "file_wrappers.h"
33 * Symbian's btsnoop format is derived from Sun's snoop format.
34 * See RFC 1761 for a description of the "snoop" file format.
37 /* Magic number in "btsnoop" files. */
38 static const char btsnoop_magic[] = {
39 'b', 't', 's', 'n', 'o', 'o', 'p', '\0'
42 /* "btsnoop" file header (minus magic number). */
44 guint32 version; /* version number (should be 1) */
45 guint32 datalink; /* datalink type */
48 /* "btsnoop" record header. */
49 struct btsnooprec_hdr {
50 guint32 orig_len; /* actual length of packet */
51 guint32 incl_len; /* number of octets captured in file */
52 guint32 flags; /* packet flags */
53 guint32 cum_drops; /* cumulative number of dropped packets */
54 gint64 ts_usec; /* timestamp microseconds */
57 /* H1 is unframed data with the packet type encoded in the flags field of capture header */
58 /* It can be used for any datalink by placing logging above the datalink layer of HCI */
59 #define KHciLoggerDatalinkTypeH1 1001
60 /* H4 is the serial HCI with packet type encoded in the first byte of each packet */
61 #define KHciLoggerDatalinkTypeH4 1002
62 /* CSR's PPP derived bluecore serial protocol - in practice we log in H1 format after deframing */
63 #define KHciLoggerDatalinkTypeBCSP 1003
64 /* H5 is the official three wire serial protocol derived from BCSP*/
65 #define KHciLoggerDatalinkTypeH5 1004
67 #define KHciLoggerHostToController 0
68 #define KHciLoggerControllerToHost 0x00000001
69 #define KHciLoggerACLDataFrame 0
70 #define KHciLoggerCommandOrEvent 0x00000002
72 const gint64 KUnixTimeBase = G_GINT64_CONSTANT(0x00dcddb30f2f8000); /* offset from symbian - unix time */
74 static gboolean btsnoop_read(wtap *wth, int *err, gchar **err_info,
76 static gboolean btsnoop_seek_read(wtap *wth, gint64 seek_off,
77 struct wtap_pkthdr *phdr, guint8 *pd, int length,
78 int *err, gchar **err_info);
79 static gboolean snoop_read_rec_data(FILE_T fh, guint8 *pd, int length, int *err,
82 int btsnoop_open(wtap *wth, int *err, gchar **err_info)
85 char magic[sizeof btsnoop_magic];
86 struct btsnoop_hdr hdr;
88 int file_encap=WTAP_ENCAP_UNKNOWN;
90 /* Read in the string that should be at the start of a "btsnoop" file */
91 errno = WTAP_ERR_CANT_READ;
92 bytes_read = file_read(magic, sizeof magic, wth->fh);
93 if (bytes_read != sizeof magic) {
94 *err = file_error(wth->fh, err_info);
95 if (*err != 0 && *err != WTAP_ERR_SHORT_READ)
100 if (memcmp(magic, btsnoop_magic, sizeof btsnoop_magic) != 0) {
104 /* Read the rest of the header. */
105 errno = WTAP_ERR_CANT_READ;
106 bytes_read = file_read(&hdr, sizeof hdr, wth->fh);
107 if (bytes_read != sizeof hdr) {
108 *err = file_error(wth->fh, err_info);
110 *err = WTAP_ERR_SHORT_READ;
115 * Make sure it's a version we support.
117 hdr.version = g_ntohl(hdr.version);
118 if (hdr.version != 1) {
119 *err = WTAP_ERR_UNSUPPORTED;
120 *err_info = g_strdup_printf("btsnoop: version %u unsupported", hdr.version);
124 hdr.datalink = g_ntohl(hdr.datalink);
125 switch (hdr.datalink) {
126 case KHciLoggerDatalinkTypeH1:
127 file_encap=WTAP_ENCAP_BLUETOOTH_HCI;
129 case KHciLoggerDatalinkTypeBCSP:
130 *err = WTAP_ERR_UNSUPPORTED;
131 *err_info = g_strdup_printf("btsnoop: BCSP capture logs unsupported");
133 case KHciLoggerDatalinkTypeH5:
134 *err = WTAP_ERR_UNSUPPORTED;
135 *err_info = g_strdup_printf("btsnoop: H5 capture logs unsupported");
137 case KHciLoggerDatalinkTypeH4:
138 file_encap=WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR;
141 *err = WTAP_ERR_UNSUPPORTED;
142 *err_info = g_strdup_printf("btsnoop: datalink type %u unknown or unsupported", hdr.datalink);
146 wth->subtype_read = btsnoop_read;
147 wth->subtype_seek_read = btsnoop_seek_read;
148 wth->file_encap = file_encap;
149 wth->snapshot_length = 0; /* not available in header */
150 wth->tsprecision = WTAP_FILE_TSPREC_USEC;
151 wth->file_type = WTAP_FILE_BTSNOOP;
155 static gboolean btsnoop_read(wtap *wth, int *err, gchar **err_info,
162 struct btsnooprec_hdr hdr;
165 /* As the send/receive flag is stored in the middle of the capture header
166 but needs to go in the pseudo header for wiretap, the header needs to be reread
167 in the seek_read function*/
168 *data_offset = file_tell(wth->fh);
170 /* Read record header. */
171 errno = WTAP_ERR_CANT_READ;
172 bytes_read = file_read(&hdr, sizeof hdr, wth->fh);
173 if (bytes_read != sizeof hdr) {
174 *err = file_error(wth->fh, err_info);
175 if (*err == 0 && bytes_read != 0)
176 *err = WTAP_ERR_SHORT_READ;
180 packet_size = g_ntohl(hdr.incl_len);
181 orig_size = g_ntohl(hdr.orig_len);
182 flags = g_ntohl(hdr.flags);
183 if (packet_size > WTAP_MAX_PACKET_SIZE) {
185 * Probably a corrupt capture file; don't blow up trying
186 * to allocate space for an immensely-large packet.
188 *err = WTAP_ERR_BAD_FILE;
189 *err_info = g_strdup_printf("btsnoop: File has %u-byte packet, bigger than maximum of %u",
190 packet_size, WTAP_MAX_PACKET_SIZE);
194 buffer_assure_space(wth->frame_buffer, packet_size);
195 if (!snoop_read_rec_data(wth->fh, buffer_start_ptr(wth->frame_buffer),
196 packet_size, err, err_info)) {
197 return FALSE; /* Read error */
200 ts = GINT64_FROM_BE(hdr.ts_usec);
203 wth->phdr.presence_flags = WTAP_HAS_TS|WTAP_HAS_CAP_LEN;
204 wth->phdr.ts.secs = (guint)(ts / 1000000);
205 wth->phdr.ts.nsecs = (guint)((ts % 1000000) * 1000);
206 wth->phdr.caplen = packet_size;
207 wth->phdr.len = orig_size;
208 if(wth->file_encap == WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR)
210 wth->phdr.pseudo_header.p2p.sent = (flags & KHciLoggerControllerToHost) ? FALSE : TRUE;
212 else if(wth->file_encap == WTAP_ENCAP_BLUETOOTH_HCI)
214 wth->phdr.pseudo_header.bthci.sent = (flags & KHciLoggerControllerToHost) ? FALSE : TRUE;
215 if(flags & KHciLoggerCommandOrEvent)
217 if(wth->phdr.pseudo_header.bthci.sent)
219 wth->phdr.pseudo_header.bthci.channel = BTHCI_CHANNEL_COMMAND;
223 wth->phdr.pseudo_header.bthci.channel = BTHCI_CHANNEL_EVENT;
228 wth->phdr.pseudo_header.bthci.channel = BTHCI_CHANNEL_ACL;
234 static gboolean btsnoop_seek_read(wtap *wth, gint64 seek_off,
235 struct wtap_pkthdr *phdr, guint8 *pd, int length,
236 int *err, gchar **err_info) {
237 union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
239 struct btsnooprec_hdr hdr;
241 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
244 /* Read record header. */
245 errno = WTAP_ERR_CANT_READ;
246 bytes_read = file_read(&hdr, sizeof hdr, wth->random_fh);
247 if (bytes_read != sizeof hdr) {
248 *err = file_error(wth->random_fh, err_info);
249 if (*err == 0 && bytes_read != 0)
250 *err = WTAP_ERR_SHORT_READ;
253 flags = g_ntohl(hdr.flags);
256 * Read the packet data.
258 if (!snoop_read_rec_data(wth->random_fh, pd, length, err, err_info))
259 return FALSE; /* failed */
261 if(wth->file_encap == WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR)
263 pseudo_header->p2p.sent = (flags & KHciLoggerControllerToHost) ? FALSE : TRUE;
265 else if(wth->file_encap == WTAP_ENCAP_BLUETOOTH_HCI)
267 pseudo_header->bthci.sent = (flags & KHciLoggerControllerToHost) ? FALSE : TRUE;
268 if(flags & KHciLoggerCommandOrEvent)
270 if(pseudo_header->bthci.sent)
272 pseudo_header->bthci.channel = BTHCI_CHANNEL_COMMAND;
276 pseudo_header->bthci.channel = BTHCI_CHANNEL_EVENT;
281 pseudo_header->bthci.channel = BTHCI_CHANNEL_ACL;
288 snoop_read_rec_data(FILE_T fh, guint8 *pd, int length, int *err,
293 errno = WTAP_ERR_CANT_READ;
294 bytes_read = file_read(pd, length, fh);
296 if (bytes_read != length) {
297 *err = file_error(fh, err_info);
299 *err = WTAP_ERR_SHORT_READ;
305 /* Returns 0 if we could write the specified encapsulation type,
306 an error indication otherwise. */
307 int btsnoop_dump_can_write_encap(int encap)
309 /* Per-packet encapsulations aren't supported. */
310 if (encap == WTAP_ENCAP_PER_PACKET)
311 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
313 /* XXX - for now we only support WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR */
314 if (encap != WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR)
315 return WTAP_ERR_UNSUPPORTED_ENCAP;
320 struct hci_flags_mapping
327 static const struct hci_flags_mapping hci_flags[] =
329 { 0x02, TRUE, KHciLoggerHostToController|KHciLoggerACLDataFrame }, /* HCI_H4_TYPE_ACL */
330 { 0x02, FALSE, KHciLoggerControllerToHost|KHciLoggerACLDataFrame }, /* HCI_H4_TYPE_ACL */
331 { 0x01, TRUE, KHciLoggerHostToController|KHciLoggerCommandOrEvent }, /* HCI_H4_TYPE_CMD */
332 { 0x04, FALSE, KHciLoggerControllerToHost|KHciLoggerCommandOrEvent }, /* HCI_H4_TYPE_EVT */
335 static guint8 btsnoop_lookup_flags(guint8 hci_type, gboolean sent, guint8 *flags)
339 for (i=0; i < G_N_ELEMENTS(hci_flags); ++i)
341 if (hci_flags[i].hci_type == hci_type &&
342 hci_flags[i].sent == sent)
344 *flags = hci_flags[i].flags;
351 static gboolean btsnoop_dump_partial_rec_hdr(wtap_dumper *wdh _U_,
352 const struct wtap_pkthdr *phdr,
353 const union wtap_pseudo_header *pseudo_header,
354 const guint8 *pd, int *err,
355 struct btsnooprec_hdr *rec_hdr)
361 if (!btsnoop_lookup_flags(*pd, pseudo_header->p2p.sent, &flags)) {
362 *err = WTAP_ERR_UNSUPPORTED;
366 nsecs = phdr->ts.nsecs;
367 ts_usec = ((gint64) phdr->ts.secs * 1000000) + (nsecs / 1000);
368 ts_usec += KUnixTimeBase;
370 rec_hdr->flags = GUINT32_TO_BE(flags);
371 rec_hdr->cum_drops = GUINT32_TO_BE(0);
372 rec_hdr->ts_usec = GINT64_TO_BE(ts_usec);
377 /* FIXME: How do we support multiple backends?*/
378 static gboolean btsnoop_dump_h1(wtap_dumper *wdh,
379 const struct wtap_pkthdr *phdr,
380 const guint8 *pd, int *err)
382 const union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
383 struct btsnooprec_hdr rec_hdr;
385 if (!btsnoop_dump_partial_rec_hdr(wdh, phdr, pseudo_header, pd, err, &rec_hdr))
388 rec_hdr.incl_len = GUINT32_TO_BE(phdr->caplen-1);
389 rec_hdr.orig_len = GUINT32_TO_BE(phdr->len-1);
391 if (!wtap_dump_file_write(wdh, &rec_hdr, sizeof rec_hdr, err))
394 wdh->bytes_dumped += sizeof rec_hdr;
396 /* Skip HCI packet type */
399 if (!wtap_dump_file_write(wdh, pd, phdr->caplen-1, err))
402 wdh->bytes_dumped += phdr->caplen-1;
407 static gboolean btsnoop_dump_h4(wtap_dumper *wdh,
408 const struct wtap_pkthdr *phdr,
409 const guint8 *pd, int *err)
411 const union wtap_pseudo_header *pseudo_header = &phdr->pseudo_header;
412 struct btsnooprec_hdr rec_hdr;
414 if (!btsnoop_dump_partial_rec_hdr(wdh, phdr, pseudo_header, pd, err, &rec_hdr))
417 rec_hdr.incl_len = GUINT32_TO_BE(phdr->caplen);
418 rec_hdr.orig_len = GUINT32_TO_BE(phdr->len);
420 if (!wtap_dump_file_write(wdh, &rec_hdr, sizeof rec_hdr, err))
423 wdh->bytes_dumped += sizeof rec_hdr;
425 if (!wtap_dump_file_write(wdh, pd, phdr->caplen, err))
428 wdh->bytes_dumped += phdr->caplen;
433 /* FIXME: How do we support multiple backends?*/
434 gboolean btsnoop_dump_open_h1(wtap_dumper *wdh, int *err)
436 struct btsnoop_hdr file_hdr;
438 /* This is a libpcap file */
439 wdh->subtype_write = btsnoop_dump_h1;
440 wdh->subtype_close = NULL;
442 /* Write the file header. */
443 switch (wdh->file_type) {
445 case WTAP_FILE_BTSNOOP:
446 wdh->tsprecision = WTAP_FILE_TSPREC_USEC;
450 /* We should never get here - our open routine
451 should only get called for the types above. */
452 *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
456 if (!wtap_dump_file_write(wdh, btsnoop_magic, sizeof btsnoop_magic, err))
459 wdh->bytes_dumped += sizeof btsnoop_magic;
461 /* current "btsnoop" format is 1 */
462 file_hdr.version = GUINT32_TO_BE(1);
463 /* HCI type encoded in first byte */
464 file_hdr.datalink = GUINT32_TO_BE(KHciLoggerDatalinkTypeH1);
466 if (!wtap_dump_file_write(wdh, &file_hdr, sizeof file_hdr, err))
469 wdh->bytes_dumped += sizeof file_hdr;
474 /* Returns TRUE on success, FALSE on failure; sets "*err" to an error code on
476 gboolean btsnoop_dump_open_h4(wtap_dumper *wdh, int *err)
478 struct btsnoop_hdr file_hdr;
480 /* This is a libpcap file */
481 wdh->subtype_write = btsnoop_dump_h4;
482 wdh->subtype_close = NULL;
484 /* Write the file header. */
485 switch (wdh->file_type) {
487 case WTAP_FILE_BTSNOOP:
488 wdh->tsprecision = WTAP_FILE_TSPREC_USEC;
492 /* We should never get here - our open routine
493 should only get called for the types above. */
494 *err = WTAP_ERR_UNSUPPORTED_FILE_TYPE;
498 if (!wtap_dump_file_write(wdh, btsnoop_magic, sizeof btsnoop_magic, err))
501 wdh->bytes_dumped += sizeof btsnoop_magic;
503 /* current "btsnoop" format is 1 */
504 file_hdr.version = GUINT32_TO_BE(1);
505 /* HCI type encoded in first byte */
506 file_hdr.datalink = GUINT32_TO_BE(KHciLoggerDatalinkTypeH4);
508 if (!wtap_dump_file_write(wdh, &file_hdr, sizeof file_hdr, err))
511 wdh->bytes_dumped += sizeof file_hdr;