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, guchar *pd, int length,
80 int *err, gchar **err_info);
81 static gboolean snoop_read_rec_data(FILE_T fh, guchar *pd, int length, int *err);
83 int btsnoop_open(wtap *wth, int *err, gchar **err_info _U_)
86 char magic[sizeof btsnoop_magic];
87 struct btsnoop_hdr hdr;
89 int file_encap=WTAP_ENCAP_UNKNOWN;
91 /* Read in the string that should be at the start of a "btsnoop" file */
92 errno = WTAP_ERR_CANT_READ;
93 bytes_read = file_read(magic, 1, sizeof magic, wth->fh);
94 if (bytes_read != sizeof magic) {
95 *err = file_error(wth->fh);
100 wth->data_offset += sizeof magic;
102 if (memcmp(magic, btsnoop_magic, sizeof btsnoop_magic) != 0) {
106 /* Read the rest of the header. */
107 errno = WTAP_ERR_CANT_READ;
108 bytes_read = file_read(&hdr, 1, sizeof hdr, wth->fh);
109 if (bytes_read != sizeof hdr) {
110 *err = file_error(wth->fh);
115 wth->data_offset += sizeof hdr;
118 * Make sure it's a version we support.
120 hdr.version = g_ntohl(hdr.version);
121 if (hdr.version != 1) {
122 *err = WTAP_ERR_UNSUPPORTED;
123 *err_info = g_strdup_printf("btsnoop: version %u unsupported", hdr.version);
127 hdr.datalink = g_ntohl(hdr.datalink);
128 switch (hdr.datalink) {
129 case KHciLoggerDatalinkTypeH1:
130 file_encap=WTAP_ENCAP_BLUETOOTH_HCI;
132 case KHciLoggerDatalinkTypeBCSP:
133 *err = WTAP_ERR_UNSUPPORTED;
134 *err_info = g_strdup_printf("btsnoop: BCSP capture logs unsupported");
136 case KHciLoggerDatalinkTypeH5:
137 *err = WTAP_ERR_UNSUPPORTED;
138 *err_info = g_strdup_printf("btsnoop: H5 capture logs unsupported");
140 case KHciLoggerDatalinkTypeH4:
141 file_encap=WTAP_ENCAP_BLUETOOTH_H4;
144 *err = WTAP_ERR_UNSUPPORTED;
145 *err_info = g_strdup_printf("btsnoop: datalink type %u unknown or unsupported", hdr.datalink);
149 wth->subtype_read = btsnoop_read;
150 wth->subtype_seek_read = btsnoop_seek_read;
151 wth->file_encap = file_encap;
152 wth->snapshot_length = 0; /* not available in header */
153 wth->tsprecision = WTAP_FILE_TSPREC_USEC;
154 wth->file_type = WTAP_FILE_BTSNOOP;
158 static gboolean btsnoop_read(wtap *wth, int *err, gchar **err_info,
165 struct btsnooprec_hdr hdr;
168 /* As the send/receive flag is stored in the middle of the capture header
169 but needs to go in the pseudo header for wiretap, the header needs to be reread
170 in the seek_read function*/
171 *data_offset = wth->data_offset;
173 /* Read record header. */
174 errno = WTAP_ERR_CANT_READ;
175 bytes_read = file_read(&hdr, 1, sizeof hdr, wth->fh);
176 if (bytes_read != sizeof hdr) {
177 *err = file_error(wth->fh);
178 if (*err == 0 && bytes_read != 0)
179 *err = WTAP_ERR_SHORT_READ;
182 wth->data_offset += sizeof hdr;
184 packet_size = g_ntohl(hdr.incl_len);
185 orig_size = g_ntohl(hdr.orig_len);
186 flags = g_ntohl(hdr.flags);
187 if (packet_size > WTAP_MAX_PACKET_SIZE) {
189 * Probably a corrupt capture file; don't blow up trying
190 * to allocate space for an immensely-large packet.
192 *err = WTAP_ERR_BAD_RECORD;
193 *err_info = g_strdup_printf("btsnoop: File has %u-byte packet, bigger than maximum of %u",
194 packet_size, WTAP_MAX_PACKET_SIZE);
198 buffer_assure_space(wth->frame_buffer, packet_size);
199 if (!snoop_read_rec_data(wth->fh, buffer_start_ptr(wth->frame_buffer),
201 return FALSE; /* Read error */
203 wth->data_offset += packet_size;
205 ts = GINT64_FROM_BE(hdr.ts_usec);
208 wth->phdr.ts.secs = (guint)(ts / 1000000);
209 wth->phdr.ts.nsecs = (guint)((ts % 1000000) * 1000);
210 wth->phdr.caplen = packet_size;
211 wth->phdr.len = orig_size;
212 if(wth->file_encap == WTAP_ENCAP_BLUETOOTH_H4)
214 wth->pseudo_header.p2p.sent = (flags & KHciLoggerControllerToHost) ? FALSE : TRUE;
216 else if(wth->file_encap == WTAP_ENCAP_BLUETOOTH_HCI)
218 wth->pseudo_header.bthci.sent = (flags & KHciLoggerControllerToHost) ? FALSE : TRUE;
219 if(flags & KHciLoggerCommandOrEvent)
221 if(wth->pseudo_header.bthci.sent)
223 wth->pseudo_header.bthci.channel = BTHCI_CHANNEL_COMMAND;
227 wth->pseudo_header.bthci.channel = BTHCI_CHANNEL_EVENT;
232 wth->pseudo_header.bthci.channel = BTHCI_CHANNEL_ACL;
238 static gboolean btsnoop_seek_read(wtap *wth, gint64 seek_off,
239 union wtap_pseudo_header *pseudo_header, guchar *pd, int length,
240 int *err, gchar **err_info _U_) {
242 struct btsnooprec_hdr hdr;
244 if (file_seek(wth->random_fh, seek_off, SEEK_SET, err) == -1)
247 /* Read record header. */
248 errno = WTAP_ERR_CANT_READ;
249 bytes_read = file_read(&hdr, 1, sizeof hdr, wth->random_fh);
250 if (bytes_read != sizeof hdr) {
251 *err = file_error(wth->random_fh);
252 if (*err == 0 && bytes_read != 0)
253 *err = WTAP_ERR_SHORT_READ;
256 flags = g_ntohl(hdr.flags);
259 * Read the packet data.
261 if (!snoop_read_rec_data(wth->random_fh, pd, length, err))
262 return FALSE; /* failed */
264 if(wth->file_encap == WTAP_ENCAP_BLUETOOTH_H4)
266 pseudo_header->p2p.sent = (flags & KHciLoggerControllerToHost) ? FALSE : TRUE;
268 else if(wth->file_encap == WTAP_ENCAP_BLUETOOTH_HCI)
270 pseudo_header->bthci.sent = (flags & KHciLoggerControllerToHost) ? FALSE : TRUE;
271 if(flags & KHciLoggerCommandOrEvent)
273 if(pseudo_header->bthci.sent)
275 pseudo_header->bthci.channel = BTHCI_CHANNEL_COMMAND;
279 pseudo_header->bthci.channel = BTHCI_CHANNEL_EVENT;
284 pseudo_header->bthci.channel = BTHCI_CHANNEL_ACL;
291 snoop_read_rec_data(FILE_T fh, guchar *pd, int length, int *err)
295 errno = WTAP_ERR_CANT_READ;
296 bytes_read = file_read(pd, 1, length, fh);
298 if (bytes_read != length) {
299 *err = file_error(fh);
301 *err = WTAP_ERR_SHORT_READ;
307 /* Returns 0 if we could write the specified encapsulation type,
308 an error indication otherwise. */
309 int btsnoop_dump_can_write_encap(int encap)
311 /* Per-packet encapsulations aren't supported. */
312 if (encap == WTAP_ENCAP_PER_PACKET)
313 return WTAP_ERR_ENCAP_PER_PACKET_UNSUPPORTED;
315 /* XXX - for now we only support WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR */
316 if (encap != WTAP_ENCAP_BLUETOOTH_H4_WITH_PHDR)
317 return WTAP_ERR_UNSUPPORTED_ENCAP;
322 struct hci_flags_mapping
329 static const struct hci_flags_mapping hci_flags[] =
331 { 0x02, TRUE, KHciLoggerHostToController|KHciLoggerACLDataFrame }, /* HCI_H4_TYPE_ACL */
332 { 0x02, FALSE, KHciLoggerControllerToHost|KHciLoggerACLDataFrame }, /* HCI_H4_TYPE_ACL */
333 { 0x01, TRUE, KHciLoggerHostToController|KHciLoggerCommandOrEvent }, /* HCI_H4_TYPE_CMD */
334 { 0x04, FALSE, KHciLoggerControllerToHost|KHciLoggerCommandOrEvent }, /* HCI_H4_TYPE_EVT */
337 static guint8 btsnoop_lookup_flags(guint8 hci_type, gboolean sent, guint8 *flags)
341 for (i=0; i < G_N_ELEMENTS(hci_flags); ++i)
343 if (hci_flags[i].hci_type == hci_type &&
344 hci_flags[i].sent == sent)
346 *flags = hci_flags[i].flags;
353 static gboolean btsnoop_dump_partial_rec_hdr(wtap_dumper *wdh _U_,
354 const struct wtap_pkthdr *phdr,
355 const union wtap_pseudo_header *pseudo_header,
356 const guchar *pd, int *err,
357 struct btsnooprec_hdr *rec_hdr)
362 if (!btsnoop_lookup_flags(*pd, pseudo_header->p2p.sent, &flags)) {
363 *err = WTAP_ERR_UNSUPPORTED;
367 ts_usec = ((gint64) phdr->ts.secs * 1000000) + ((gint64) phdr->ts.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 union wtap_pseudo_header *pseudo_header,
381 const guchar *pd, int *err)
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 union wtap_pseudo_header *pseudo_header,
410 const guchar *pd, int *err)
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, gboolean cant_seek _U_, 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, gboolean cant_seek _U_, 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;