Firewire (IEEE 1394) with FreeBSD 5.0

This document describes my experiences with firewire support under FreeBSD. FreeBSD 5.0 supposedly has firewire support. For a first release, I suppose it's pretty good, but as usual, I'm having a learning curve with it. My initial use for firewire is to hook up my Sony digital camcorder. For now, I want to use it as a (really expensive) webcam. I also want to try to use a firewire external disk drive. Here are my notes as I go along. Update: FreeBSD 4.8 has been released with firewire support.

Reading around, it looks like I can pull a DV stream from the camera. There is also a library (libdv) which I can probably use to muck with the file. For my purposes (a low frame-rate web video stream), I will want to create a series of JPEG images.

Does anything work?

I was able to verify that my camera and my computer are talking. I fired off the command:

venom.coreth.com:/home/mpearce 927> fwcontrol -c 0
first quad: 0x041edb85
len: 30
bus_info0: 0x31333934
bus_info1: 0xe0644000
bus_info2: 0x08004601
bus_info3: 0x02735ab8
root_directory: len=0x0006(6) crc=0xb8ed
03(I:03) 080046 module_vendor_ID: 524358
81(L:01) 00000e text_leaf: Sony
0c(I:0c) 0083c0 node_capabilities: 33728
8d(L:0d) 000009 unknown: offset=0x0009(9)
d1(D:11) 000002 unit_directory: len=0x0002(2) crc=0xdd9e
        12(I:12) 00a02d unit_spec_ID: 41005
        13(I:13) 010001 unit_sw_version: 65537
c3(D:03) 000004 unknown: len=0x0002(2) crc=0x0f3a
        17(I:17) 380050 model_ID: 3670096
        81(L:01) 000008 text_leaf: DCR-TRV330

The 9th line shows "Sony", but that doesn't help, because my laptop is also made by Sony. The last line, however, shows "DCR-TRV330", which is the model of my camera. Looks like they are talking.

Also, I can watch the messages file (tail -f /var/log/messages), and see things happen when I plug in and unplug the firewire cable. A bus reset occurs and all the identification that goes along with it. My camera appears to do firewire in any mode but off (Camera, VCR, Memory).

The sbp(4) device

I added the sbp(4) device (SCSI over Firewire?) to my kernel, in the hopes that maybe I could mount something on the camera as if it were a filesystem (the camera has a Sony Memory Stick slot). After I did this, I discovered that the firewire system wouldn't recognize the devices on the bus (ie, the camera). I got messages like:

Mar 13 11:12:48 venom kernel: fw_sidrcv: invalid self-id packet

This did not happen when sbp wasn't in the kernel. I took it back out, and I can communicate with the camera again. It might still be possible to use the memory stick like a filesystem, but until I can figure out what is causing this problem, I have to leave the sbp device out of my kernel.

Update: the above symptom occurs on my Sony VAIO laptop running FreeBSD 5.0-RELEASE. Since then, I've tried it on a different machine running FreeBSD 4.8-RELEASE, and it works fine. So, it could be a difference in hardware or a bug in the firewire software that has since been fixed. To test this theory, I could build a 5.0 installation on the machine that works, or I could try upgrading the laptop to 5.1 when my CDs get here.

Large FAT32 Disk

Well, I thought I was going to be cool by using a large (200 GB) external firewire drive to move files around with me. My plan was to put a firewire port on every machine I own. The trick is, some of those machines are Windows machines, and I have to use a filesystem that both operating systems grok. The drive came preformatted with a FAT32 filesystem, so I thought I was set.

I got a machine upgraded to 4.8-RELEASE and plugged in the drive. It detected fine, da1 appeared, and when I mounted it, I got the message:

mountmsdosfs(): disk too big, sorry

Needless to say, I was not happy. This isn't really a firewire problem, though. I think I'll document my progress with this on another page.

I have come to understand the problem and its cause. I haven't thought of a simple solution. The bottom line is, a FAT32 filesystem under FreeBSD cannot currently be larger than 128 GB. I guess I could work around this problem by repartioning the drive into two 100 GB filesystems.

FreeBSD Firewire Information Resources

The author or coordinator of FreeBSD's firewire support appears to be Hidetoshi Shimokawa. The two best sources of information for me have been his posts to the freebsd-firewire mailing list (http://docs.freebsd.org/mail/archive/2003/freebsd-firewire/), and his web site on people.freebsd.org (http://people.freebsd.org/~simokawa/firewire/).

Capturing a DV Stream

Hidetoshi has written a utility called dvrec, which dumps a DV stream to a file. This utility can be found on his web site (see above). Looking at the mailing list archives, however, it seems that he has incorporated this functionality into fwcontrol(8). However, this option (-R) seems to be missing from the version supplied with 5.0-RELEASE.

I've used dvrec to grab some video. I have a file, but now I need to verify that it is a useful file. I need to install playdv and see what I can see. I wonder if Windows Media Player or Real Player know what to do with a DV file. I told it to capture 30 frames, which I would have thought would take one second (it was a bit longer). That yielded exactly 3600000 bytes. That is 120k per frame. A huge file, to say the least.

It worked! Neither Windows Media Player nor Real Player knew what to do with a DV file, but QuickTime did! Which makes sense, Apple is big on firewire these days. I got to see about one second of video, at 720x480 with full stereo audio. Here is a (scaled down) screen capture from my first DV capture:

Yeah, I know, the floor of my office as seen from underneath my desk is not very interesting. However, it was pretty cool given that I wasn't at my office when I captured this, I was at home :)

libdv

Now it's time to see how well libdv works, and whether I can make it do what I want to do. [You have to love API libraries that don't come with any documentation.]

Glancing through playdv (a sample program supplied with libdv), the immediately interesting bits seem to be:

#include <libdv/dv_types.h>
#include <libdv/dv.h>

dv_decoder_t    *decoder;
decoder = dv_decoder_new(TRUE, FALSE, FALSE))) goto no_decoder;

  /* Read in header of first frame to see how big frames are */
  mmap_unaligned(fd,dv_player->no_mmap,0,120000,&dv_player->mmap_region);
  if(MAP_FAILED == dv_player->mmap_region.map_start) goto map_failed;

  if(dv_parse_header(dv_player->decoder, dv_player->mmap_region.data_start)< 0)
    goto header_parse_error;

  if (dv_format_wide (dv_player->decoder))
    fprintf (stderr, "format 16:9\n");
  if (dv_format_normal (dv_player->decoder))
    fprintf (stderr, "format 4:3\n");

/* mmap crap */

dv_parse_packs (dv_player -> decoder, dv_player -> mmap_region. data_start);
dv_display_check_format (dv_player->display,
                             dv_format_wide (dv_player->decoder));

dv_report_video_error (dv_player -> decoder,
                               dv_player -> mmap_region. data_start);

/* Parse and decode video */
        dv_decode_full_frame(dv_player->decoder, dv_player->mmap_region.data_sta
rt,
                             dv_player->display->color_space,
                             dv_player->display->pixels,
                             dv_player->display->pitches);
        dv_player->decoder->prev_frame_decoded = 1;

/* This code appears to be used to dump frames as PPM images ... */
        fprintf(fp, "P6\n# CREATOR: playdv\n%d %d\n255\n",
                dv_player->display->width, dv_player->display->height);
        fwrite(dv_player->display->pixels[0],
               3, dv_player->display->width
                * dv_player->display->height, fp);

	frame_count++;

Bugger. A lot of this code uses mmap. Great for files, but I don't know if I want to be using it to read directly from the DV device.

camserv

There is a program out there called camserv which is supposed to do essentially what I want done. It is in the FreeBSD ports collection (graphics/camserv), but according to the pkg-descr, the only supported BSD input device is the bttv driver. I should check this out. That note might be out of date. Even so, it might be worth adding DV support to this application.

Bugs

I was able to (temporarily) hang the machine by reading from /dev/fw0. I was obviously not reading the correct way. Everything came unstuck when I unplugged the firewire cable. I wouldn't have been able to do this if I wasn't in group operator (or root), and this probably wouldn't have happened if nothing was plugged into the firewire port. Still, a userland process shouldn't be able to freeze an entire machine.