hal: Branch 'master'

Joe Marcus Clarke marcus at kemper.freedesktop.org
Tue Nov 28 22:12:42 PST 2006


 configure.in                                |    6 
 hald/Makefile.am                            |    2 
 hald/freebsd/Makefile.am                    |   54 +
 hald/freebsd/README                         |   28 
 hald/freebsd/TODO                           |    3 
 hald/freebsd/addons/Makefile.am             |   16 
 hald/freebsd/addons/addon-storage.c         |  190 ++++++
 hald/freebsd/hf-acpi.c                      |  692 +++++++++++++++++++++++
 hald/freebsd/hf-acpi.h                      |   42 +
 hald/freebsd/hf-ata.c                       |  275 +++++++++
 hald/freebsd/hf-ata.h                       |   37 +
 hald/freebsd/hf-block.c                     |  182 ++++++
 hald/freebsd/hf-block.h                     |   38 +
 hald/freebsd/hf-computer.c                  |  179 ++++++
 hald/freebsd/hf-computer.h                  |   36 +
 hald/freebsd/hf-devd.c                      |  419 ++++++++++++++
 hald/freebsd/hf-devd.h                      |   57 +
 hald/freebsd/hf-devtree.c                   |  591 ++++++++++++++++++++
 hald/freebsd/hf-devtree.h                   |   59 +
 hald/freebsd/hf-net.c                       |  355 ++++++++++++
 hald/freebsd/hf-net.h                       |   37 +
 hald/freebsd/hf-osspec.h                    |   42 +
 hald/freebsd/hf-pci.c                       |  282 +++++++++
 hald/freebsd/hf-pci.h                       |   35 +
 hald/freebsd/hf-pcmcia.c                    |  210 +++++++
 hald/freebsd/hf-pcmcia.h                    |   39 +
 hald/freebsd/hf-scsi.c                      |  545 ++++++++++++++++++
 hald/freebsd/hf-scsi.h                      |   35 +
 hald/freebsd/hf-serial.c                    |   94 +++
 hald/freebsd/hf-serial.h                    |   35 +
 hald/freebsd/hf-sound.c                     |  224 +++++++
 hald/freebsd/hf-sound.h                     |   35 +
 hald/freebsd/hf-storage.c                   |  780 ++++++++++++++++++++++++++
 hald/freebsd/hf-storage.h                   |   42 +
 hald/freebsd/hf-usb.c                       |  827 ++++++++++++++++++++++++++++
 hald/freebsd/hf-usb.h                       |   37 +
 hald/freebsd/hf-util.c                      |  621 +++++++++++++++++++++
 hald/freebsd/hf-util.h                      |  115 +++
 hald/freebsd/hf-volume.c                    |  283 +++++++++
 hald/freebsd/hf-volume.h                    |   49 +
 hald/freebsd/libprobe/Makefile.am           |   19 
 hald/freebsd/libprobe/hfp-cdrom.c           |  238 ++++++++
 hald/freebsd/libprobe/hfp-cdrom.h           |  163 +++++
 hald/freebsd/libprobe/hfp.c                 |  270 +++++++++
 hald/freebsd/libprobe/hfp.h                 |   97 +++
 hald/freebsd/osspec.c                       |  143 ++++
 hald/freebsd/probing/Makefile.am            |   44 +
 hald/freebsd/probing/freebsd_dvd_rw_utils.c |  717 ++++++++++++++++++++++++
 hald/freebsd/probing/freebsd_dvd_rw_utils.h |   33 +
 hald/freebsd/probing/probe-hiddev.c         |  140 ++++
 hald/freebsd/probing/probe-scsi.c           |   71 ++
 hald/freebsd/probing/probe-smbios.c         |  225 +++++++
 hald/freebsd/probing/probe-storage.c        |  209 +++++++
 hald/freebsd/probing/probe-volume.c         |  545 ++++++++++++++++++
 54 files changed, 10540 insertions(+), 2 deletions(-)

New commits:
diff-tree 76c310d0efb5d463f06291cb02100b3b3ce1da71 (from 630d5a55c08fcf645983859138081be974c1f21f)
Author: Joe Marcus Clarke <marcus at FreeBSD.org>
Date:   Wed Nov 29 01:12:33 2006 -0500

    add the FreeBSD backend
    
    Add full FreeBSD support to HAL via the freebsd backend.  This work is
    the combined effort of Jean-Yves Lefort <jylefort at FreeBSD.org> and
    Joe Marcus Clarke <marcus at FreeBSD.org>.

diff --git a/configure.in b/configure.in
index bb41521..b01cae7 100644
--- a/configure.in
+++ b/configure.in
@@ -247,7 +247,7 @@ AC_CHECK_HEADERS(pci/pci.h, [
 		USE_LIBPCI=no AM_CONDITIONAL(HAVE_LIBPCI,false)])], [
 	USE_LIBPCI=no AM_CONDITIONAL(HAVE_LIBPCI,false)])
 
-AC_ARG_WITH(backend, [  --with-backend=<name>   backend to use (linux/solaris/dummy)],
+AC_ARG_WITH(backend, [  --with-backend=<name>   backend to use (linux/solaris/freebsd/dummy)],
                       [
                       backend=$withval
                       ]
@@ -559,6 +559,10 @@ hald/linux/addons/Makefile
 hald/solaris/Makefile
 hald/solaris/probing/Makefile
 hald/solaris/addons/Makefile
+hald/freebsd/Makefile
+hald/freebsd/probing/Makefile
+hald/freebsd/libprobe/Makefile
+hald/freebsd/addons/Makefile
 hald/haldaemon
 hald-runner/Makefile
 libhal/Makefile
diff --git a/hald/Makefile.am b/hald/Makefile.am
index 248f27e..e1daa08 100644
--- a/hald/Makefile.am
+++ b/hald/Makefile.am
@@ -1,6 +1,6 @@
 ## Process this file with automake to produce Makefile.in
 
-SUBDIRS = dummy linux solaris .
+SUBDIRS = dummy freebsd linux solaris .
 
 INCLUDES = \
 	-DPACKAGE_LIBEXEC_DIR=\""$(libexecdir)"\" \
diff --git a/hald/freebsd/Makefile.am b/hald/freebsd/Makefile.am
new file mode 100644
index 0000000..c4e27d6
--- /dev/null
+++ b/hald/freebsd/Makefile.am
@@ -0,0 +1,54 @@
+SUBDIRS = libprobe probing addons .
+
+INCLUDES = \
+	-DPACKAGE_SYSCONF_DIR=\""$(sysconfdir)"\" \
+	-DPACKAGE_DATA_DIR=\""$(datadir)"\" \
+	-DPACKAGE_BIN_DIR=\""$(bindir)"\" \
+	-DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \
+	-DPACKAGE_LOCALSTATEDIR=\""$(localstatedir)"\" \
+	-I$(top_srcdir) -I.. \
+	@GLIB_CFLAGS@ @DBUS_CFLAGS@
+
+if HALD_COMPILE_FREEBSD
+noinst_LTLIBRARIES = libhald_freebsd.la
+endif
+
+libhald_freebsd_la_SOURCES = \
+	hf-acpi.c	\
+	hf-acpi.h	\
+	hf-ata.c	\
+	hf-ata.h	\
+	hf-block.c	\
+	hf-block.h	\
+	hf-computer.c	\
+	hf-computer.h	\
+	hf-devd.c	\
+	hf-devd.h	\
+	hf-devtree.c	\
+	hf-devtree.h	\
+	hf-net.c	\
+	hf-net.h	\
+	hf-osspec.h	\
+	hf-pci.c	\
+	hf-pci.h	\
+	hf-pcmcia.c	\
+	hf-pcmcia.h	\
+	hf-scsi.c	\
+	hf-scsi.h	\
+	hf-serial.c	\
+	hf-serial.h	\
+	hf-sound.c	\
+	hf-sound.h	\
+	hf-storage.c	\
+	hf-storage.h	\
+	hf-usb.c	\
+	hf-usb.h	\
+	hf-util.c	\
+	hf-util.h	\
+	hf-volume.c	\
+	hf-volume.h	\
+	osspec.c
+
+libhald_freebsd_la_LDFLAGS = -lcam
+
+EXTRA_DIST = README TODO
diff --git a/hald/freebsd/README b/hald/freebsd/README
new file mode 100644
index 0000000..9daacd5
--- /dev/null
+++ b/hald/freebsd/README
@@ -0,0 +1,28 @@
+===============================================================================
+ Hardware Abstraction Layer
+ FreeBSD notes
+
+ Jean-Yves Lefort <jylefort at FreeBSD.org>
+ September 11, 2006
+===============================================================================
+
+1. Handling of atapicam devices
+
+By default, when an ATAPI device is available through both ata(4) and
+atapicam(4), the HAL daemon will use the ata interface and mark the
+virtual SCSI device as ignored (info.ignore=true).
+
+If you want the HAL daemon to use the atapicam interface for a
+particular device, add a fdi rule for ignoring the ata device (create
+/usr/local/share/hal/fdi/preprobe/20thirdparty/10-atapi-device.fdi
+with the following contents).
+
+  <?xml version="1.0" encoding="UTF-8"?>
+  <deviceinfo version="0.2">
+    <device>
+      <!-- ignore /dev/acd0; we want hald to use the atapicam interface -->
+      <match key="block.device" string="/dev/acd0">
+        <merge key="info.ignore" type="bool">true</merge>
+      </match>
+    </device>
+  </deviceinfo>
diff --git a/hald/freebsd/TODO b/hald/freebsd/TODO
new file mode 100644
index 0000000..8bc777a
--- /dev/null
+++ b/hald/freebsd/TODO
@@ -0,0 +1,3 @@
+- Fix volume_id so that it always reads a multiple of the sector
+  size. This is a restriction of raw disk devices on FreeBSD; currently
+  some volume_id probers will fail because of that.
diff --git a/hald/freebsd/addons/Makefile.am b/hald/freebsd/addons/Makefile.am
new file mode 100644
index 0000000..90b9d83
--- /dev/null
+++ b/hald/freebsd/addons/Makefile.am
@@ -0,0 +1,16 @@
+INCLUDES = \
+	-DPACKAGE_SYSCONF_DIR=\""$(sysconfdir)"\" \
+	-DPACKAGE_DATA_DIR=\""$(datadir)"\" \
+	-DPACKAGE_BIN_DIR=\""$(bindir)"\" \
+	-DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \
+	-DPACKAGE_LOCALSTATEDIR=\""$(localstatedir)"\" \
+	-I$(top_srcdir) \
+	@DBUS_CFLAGS@
+
+if HALD_COMPILE_FREEBSD
+libexec_PROGRAMS = hald-addon-storage
+endif
+
+hald_addon_storage_SOURCES = addon-storage.c
+hald_addon_storage_LDADD = \
+	$(top_builddir)/hald/freebsd/libprobe/libhald_freebsd_probe.la
diff --git a/hald/freebsd/addons/addon-storage.c b/hald/freebsd/addons/addon-storage.c
new file mode 100644
index 0000000..8a11670
--- /dev/null
+++ b/hald/freebsd/addons/addon-storage.c
@@ -0,0 +1,190 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * addon-storage.c : poll storage devices for media changes
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "libhal/libhal.h"
+
+#include "../libprobe/hfp.h"
+#include "../libprobe/hfp-cdrom.h"
+
+static struct
+{
+  const struct timeval	update_interval;
+  char			*device_file;
+  boolean		is_cdrom;
+  boolean		had_media;
+  struct timeval	next_update;
+} addon = { { 2, 0 } };
+
+/* see MMC-3 Working Draft Revision 10 */
+static boolean
+hf_addon_storage_cdrom_eject_pressed (HFPCDROM *cdrom)
+{
+  unsigned char buf[8];
+  static char ccb[16] = {
+    HFP_CDROM_GET_EVENT_STATUS_NOTIFICATION,
+    1 << 0,			/* immediate */
+    0,
+    0,
+    1 << 4,			/* notification class = media */
+    0,
+    0,
+    0,
+    HFP_N_ELEMENTS(buf)		/* allocation length LSB */
+  };
+
+  assert(cdrom != NULL);
+
+  return hfp_cdrom_send_ccb(cdrom, ccb, 10, HFP_CDROM_DIRECTION_IN, buf, sizeof(buf), NULL)
+    && buf[1] >= 6		/* event data length LSB */
+    && buf[4] == 0x1;		/* media event = eject pressed */
+}
+
+static boolean
+hf_addon_storage_update (void)
+{
+  boolean has_media = FALSE;
+
+  if (addon.is_cdrom)
+    {
+      HFPCDROM *cdrom;
+
+      cdrom = hfp_cdrom_new(addon.device_file);
+      if (cdrom)
+	{
+	  if (hfp_cdrom_test_unit_ready(cdrom))
+	    has_media = TRUE;
+
+	  if (hf_addon_storage_cdrom_eject_pressed(cdrom))
+	    {
+	      libhal_device_emit_condition(hfp_ctx, hfp_udi, "EjectPressed", "", &hfp_error);
+	      dbus_error_free(&hfp_error);
+	    }
+
+	  hfp_cdrom_free(cdrom);
+	}
+    }
+  else
+    {
+      int fd;
+
+      fd = open(addon.device_file, O_RDONLY);
+      if (fd >= 0)		/* can open = has media */
+	{
+	  has_media = TRUE;
+	  close(fd);
+	}
+    }
+
+  hfp_gettimeofday(&addon.next_update);
+  hfp_timevaladd(&addon.next_update, &addon.update_interval);
+
+  return has_media;
+}
+
+int
+main (int argc, char **argv)
+{
+  char *drive_type;
+  DBusConnection *connection;
+
+  if (! hfp_init(argc, argv))
+    goto end;
+
+  addon.device_file = getenv("HAL_PROP_BLOCK_DEVICE");
+  if (! addon.device_file)
+    goto end;
+
+  drive_type = getenv("HAL_PROP_STORAGE_DRIVE_TYPE");
+  if (! drive_type)
+    goto end;
+
+  /* give a meaningful process title for ps(1) */
+  setproctitle("%s", addon.device_file);
+
+  addon.is_cdrom = ! strcmp(drive_type, "cdrom");
+  addon.had_media = hf_addon_storage_update();
+
+  connection = libhal_ctx_get_dbus_connection(hfp_ctx);
+  assert(connection != NULL);
+
+  while (TRUE)
+    {
+      boolean has_media;
+
+      /* process dbus traffic until update interval has elapsed */
+      while (TRUE)
+	{
+	  struct timeval now;
+
+	  hfp_gettimeofday(&now);
+	  if (hfp_timevalcmp(&now, &addon.next_update, <))
+	    {
+	      struct timeval timeout;
+
+	      timeout = addon.next_update;
+	      hfp_timevalsub(&timeout, &now);
+
+	      if (timeout.tv_sec < 0) /* current time went backwards */
+		timeout = addon.update_interval;
+
+	      dbus_connection_read_write(connection, timeout.tv_sec * 1000 + timeout.tv_usec / 1000);
+	      if (! dbus_connection_get_is_connected(connection))
+		goto end;
+	    }
+	  else
+	    break;
+	}
+
+      has_media = hf_addon_storage_update();
+      if (has_media != addon.had_media)
+	{
+	  /*
+	   * FIXME: if the media was removed, we should force-unmount
+	   * all its child volumes (see linux2/addons/addon-storage.c).
+	   * However, currently (FreeBSD 6.0) umount -f is broken and
+	   * can cause kernel panics. When I tried to umount -f a
+	   * flash card after removing it, it failed with EAGAIN. It
+	   * continued to fail after I inserted the card. The system
+	   * then hung while rebooting and did not unmount my other
+	   * filesystems.
+	   */
+
+	  libhal_device_rescan(hfp_ctx, hfp_udi, &hfp_error);
+	  dbus_error_free(&hfp_error);
+	  addon.had_media = has_media;
+	}
+    }
+
+ end:
+  return 0;
+}
diff --git a/hald/freebsd/hf-acpi.c b/hald/freebsd/hf-acpi.c
new file mode 100644
index 0000000..8097dd7
--- /dev/null
+++ b/hald/freebsd/hf-acpi.c
@@ -0,0 +1,692 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-acpi.c : poll for ACPI properties
+ *
+ * Copyright (C) 2006 Joe Marcus Clarke <marcus at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <math.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <dev/acpica/acpiio.h>
+#include <glib.h>
+
+#include "../hald.h"
+#include "../hald_dbus.h"
+#include "../logger.h"
+#include "../util.h"
+#include "../util_pm.h"
+
+#include "hf-acpi.h"
+#include "hf-util.h"
+
+#ifndef ACPI_BIF_UNITS_MA
+#define ACPI_BIF_UNITS_MA	1 /* added to CURRENT on 20051023 */
+#endif
+
+#define HF_ACPIDEV			"/dev/acpi"
+
+static const struct laptop_panel_type {
+  char *access;
+  char *name;
+  char *max_sysctl;
+  int max_levels;
+#define HF_ACPI_IBM_MAX_LEVELS		8
+#define HF_ACPI_TOSHIBA_MAX_LEVELS	8
+#define HF_ACPI_SONY_MAX_LEVELS		8
+#define HF_ACPI_PANASONIC_MAX_LEVELS	16 /* XXX This is a fallback */
+#define HF_ACPI_ASUS_MAX_LEVELS		16
+#define HF_ACPI_FUJITSU_MAX_LEVELS	8
+  /* NOTE: Each new type must also be added to hf-devtree.c */
+} laptop_panel_types[] = {
+  { "ibm",		"IBM",		NULL,
+    HF_ACPI_IBM_MAX_LEVELS },
+  { "toshiba",		"Toshiba",	NULL,
+    HF_ACPI_TOSHIBA_MAX_LEVELS },
+  { "sony",		"Sony",		NULL,
+    HF_ACPI_SONY_MAX_LEVELS },
+  { "panasonic",	"Panasonic",	"hw.acpi.panasonic.lcd_brightness_max",
+    HF_ACPI_PANASONIC_MAX_LEVELS },
+  { "asus",		"Asus",		NULL,
+    HF_ACPI_ASUS_MAX_LEVELS },
+  { "fujitsu",		"Fujitsu",	NULL,
+    HF_ACPI_FUJITSU_MAX_LEVELS }
+};
+
+static const char *video_outs[] = {
+  "crt",
+  "lcd",
+  "tv",
+  "out"
+};
+
+static void
+hf_acpi_poll_acad (HalDevice *device)
+{
+  int acline;
+
+  if (! hf_get_int_sysctl(&acline, NULL, "hw.acpi.acline"))
+    return;
+
+  hal_device_property_set_bool(device, "ac_adapter.present",
+                               acline ? TRUE : FALSE);
+}
+
+static void
+hf_acpi_poll_batt (HalDevice *device)
+{
+  int fd;
+  int volt, dvolt, rate, lastfull, cap, dcap, lcap, wcap, gra1, gra2;
+  gboolean ispresent;
+  union acpi_battery_ioctl_arg battif, battst, battinfo;
+
+  battif.unit = battst.unit = battinfo.unit =
+    hal_device_property_get_int(device, "freebsd.unit");
+
+  fd = open(HF_ACPIDEV, O_RDONLY);
+  if (fd < 0)
+    {
+      HAL_WARNING(("unable to open %s: %s", HF_ACPIDEV, g_strerror(errno)));
+      return;
+    }
+
+#ifdef ACPIIO_BATT_GET_BIF
+  if (ioctl(fd, ACPIIO_BATT_GET_BIF, &battif) == -1)
+#else
+  if (ioctl(fd, ACPIIO_CMBAT_GET_BIF, &battif) == -1)
+#endif
+    {
+      HAL_WARNING(("ioctl ACPIIO_BATT_GET_BIF failed for battery %d: %s",
+        battif.unit, g_strerror(errno)));
+      goto end;
+    }
+#ifdef ACPIIO_BATT_GET_BST
+  if (ioctl(fd, ACPIIO_BATT_GET_BST, &battst) == -1)
+#else
+  if (ioctl(fd, ACPIIO_CMBAT_GET_BST, &battst) == -1)
+#endif
+    {
+      HAL_WARNING(("ioctl ACPIIO_BATT_GET_BST failed for battery %d: %s",
+        battst.unit, g_strerror(errno)));
+      goto end;
+    }
+  if (ioctl(fd, ACPIIO_BATT_GET_BATTINFO, &battinfo) == -1)
+    {
+      HAL_WARNING(("ioctl ACPIIO_BATT_GET_BATTINFO failed for battery %d: %s",
+        battinfo.unit, g_strerror(errno)));
+      goto end;
+    }
+
+  ispresent = (battst.bst.state == ACPI_BATT_STAT_NOT_PRESENT) ? FALSE : TRUE;
+  hal_device_property_set_bool(device, "battery.present", ispresent);
+
+  if (! ispresent)
+    goto end;
+
+  dvolt = battif.bif.dvol;
+  volt = battst.bst.volt;
+  cap = battst.bst.cap;
+  dcap = battif.bif.dcap;
+  rate = battst.bst.rate;
+  lastfull = battif.bif.lfcap;
+  lcap = battif.bif.lcap;
+  wcap = battif.bif.wcap;
+  gra1 = battif.bif.gra1;
+  gra2 = battif.bif.gra2;
+
+  hal_device_property_set_string(device, "battery.voltage.unit", "mV");
+  hal_device_property_set_int(device, "battery.voltage.current", volt);
+  hal_device_property_set_int(device, "battery.voltage.design", dvolt);
+
+  hal_device_property_set_int(device, "battery.reporting.design", dcap);
+  hal_device_property_set_int(device, "battery.reporting.current", cap);
+  hal_device_property_set_int(device, "battery.reporting.rate", rate);
+  hal_device_property_set_int(device, "battery.reporting.last_full", lastfull);
+  hal_device_property_set_int(device, "battery.reporting.low", lcap);
+  hal_device_property_set_int(device, "battery.reporting.warning", wcap);
+
+  hal_device_property_set_string(device, "battery.charge_level.unit", "mWh");
+
+  if (battif.bif.units == ACPI_BIF_UNITS_MA)
+    {
+       hal_device_property_set_string(device, "battery.reporting.units", "mAh");
+
+       if (dvolt <= 0)
+         dvolt = 1;
+       if (volt <= 0 || volt > dvolt)
+         volt = dvolt;
+
+       cap = (int) rint((cap * volt) / 1000.0);
+       dcap = (int) rint((dcap * volt) / 1000.0);
+       rate = (int) rint((rate * volt) / 1000.0);
+       lastfull = (int) rint((lastfull * volt) / 1000.0);
+       lcap = (int) rint((lcap * volt) / 1000.0);
+       wcap = (int) rint((wcap * volt) / 1000.0);
+       gra1 = (int) rint((gra1 * volt) / 1000.0);
+       gra2 = (int) rint((gra2 * volt) / 1000.0);
+    }
+  else
+    hal_device_property_set_string(device, "battery.reporting.unit", "mWh");
+
+  hal_device_property_set_int(device, "battery.charge_level.design", dcap);
+  hal_device_property_set_int(device, "battery.charge_level.last_full",
+                              lastfull);
+  hal_device_property_set_int(device, "battery.charge_level.current", cap);
+  hal_device_property_set_int(device, "battery.charge_level.rate", rate);
+  hal_device_property_set_int(device, "battery.charge_level.warning", wcap);
+  hal_device_property_set_int(device, "battery.charge_level.low", lcap);
+  hal_device_property_set_int(device, "battery.charge_level.granularity_1",
+                              gra1);
+  hal_device_property_set_int(device, "battery.charge_level.granularity_2",
+                              gra2);
+
+
+  hal_device_property_set_bool(device, "battery.is_rechargeable",
+                               battif.bif.btech == 0 ? FALSE : TRUE);
+  hal_device_property_set_int(device, "battery.charge_level.percentage",
+                              battinfo.battinfo.cap);
+
+  if (hal_device_property_get_bool(device, "battery.is_rechargeable"))
+    {
+      hal_device_property_set_bool(device, "battery.rechargeable.is_charging",
+                                   battinfo.battinfo.state & ACPI_BATT_STAT_CHARGING ? TRUE : FALSE);
+      hal_device_property_set_bool(device, "battery.rechargeable.is_discharging",
+                                   battinfo.battinfo.state & ACPI_BATT_STAT_DISCHARG ? TRUE : FALSE);
+    }
+
+  /* remaining time is in seconds */
+  if (battinfo.battinfo.min > 0)
+    {
+      hal_device_property_set_int(device, "battery.remaining_time",
+                              battinfo.battinfo.min * 60);
+      hal_device_property_set_bool(device, "battery.remaining_time.calculate_per_time", FALSE);
+    }
+  else
+    {
+      int remaining_time;
+
+      remaining_time = util_compute_time_remaining(hal_device_get_udi(device), rate, cap,
+		                                   lastfull,
+                                                   hal_device_property_get_bool(device, "battery.rechargeable.is_discharging"),
+		                                   hal_device_property_get_bool(device, "battery.rechargeable.is_charging"),
+		                                   hal_device_property_get_bool(device, "battery.remaining_time.calculate_per_time"));
+      if (remaining_time > 0)
+        hal_device_property_set_int(device, "battery.remaining_time",
+                                    remaining_time);
+      else
+        hal_device_property_remove(device, "battery.remaining_time");
+    }
+
+  hal_device_property_set_string(device, "info.vendor", battif.bif.oeminfo);
+
+  hal_device_property_set_string(device, "battery.vendor", battif.bif.oeminfo);
+  hal_device_property_set_string(device, "battery.model", battif.bif.model);
+  hal_device_property_set_string(device, "battery.technology", battif.bif.type);
+  hal_device_property_set_string(device, "battery.serial", battif.bif.serial);
+
+end:
+  close(fd);
+}
+
+static void
+hf_acpi_poll_video (HalDevice *device)
+{
+  const char *type;
+  int unit;
+  int level;
+
+  type = hal_device_property_get_string(device, "display_device.type");
+  if (strcmp(type, "lcd") != 0)
+    /* Only LCD device support brightness */
+    return;
+
+  unit = hal_device_property_get_int(device, "freebsd.unit");
+
+  /* This value is returned as a percent from the sysctl, and a percent
+   * is required by HAL */
+  if (hf_get_int_sysctl(&level, NULL, "dev.acpi.video.lcd%i.brightness", unit))
+    hal_device_property_set_int(device, "display_device.lcd.brightness", level);
+  else
+    /* XXX Some devices support ACPI video, but do not support setting the
+     * brightness level via ACPI.  For those, we just assume it's 100%. */
+    hal_device_property_set_int(device, "display_device.lcd.brightness", 100);
+}
+
+static void
+hf_acpi_button_update_state (HalDevice *device, gboolean isclosed)
+{
+  /* Only Lid buttons will report state changes */
+  if (strcmp(hal_device_property_get_string(device, "button.type"), "lid"))
+    return;
+
+  hal_device_property_set_bool(device, "button.has_state", TRUE);
+  hal_device_property_set_bool(device, "button.state.value", isclosed);
+}
+
+void
+hf_acpi_button_set_properties (HalDevice *device)
+{
+  const char *pnpid;
+  const char *type = NULL;
+
+  hal_device_property_set_string(device, "info.category", "button");
+  hal_device_add_capability(device, "button");
+
+  pnpid = hal_device_property_get_string(device, "pnp.id");
+  if (pnpid)
+    {
+      if (! strcmp(pnpid, "PNP0C0C"))
+	type = "power";
+      else if (! strcmp(pnpid, "PNP0C0D"))
+	type = "lid";
+      else if (! strcmp(pnpid, "PNP0C0E"))
+	type = "sleep";
+    }
+
+  if (type)
+    {
+      hal_device_property_set_string(device, "button.type", type);
+      if (! strcmp(type, "lid"))
+        {
+          char *lid_state;
+
+	  lid_state = hf_get_string_sysctl(NULL, "hw.acpi.lid_switch_state");
+	  if (lid_state && ! strcmp(lid_state, "NONE"))
+            hal_device_property_set_bool(device, "info.ignore", TRUE);
+	  g_free(lid_state);
+	}
+      /* XXX This is a bit of hack.  We can only accurately set the lid
+       * state AFTER a state event.  Therefore, we assume it's open by
+       * default. */
+      hf_acpi_button_update_state(device, FALSE);
+    }
+}
+
+void
+hf_acpi_tz_set_properties (HalDevice *device)
+{
+  hal_device_property_set_string(device, "info.category", "sensor");
+  hal_device_add_capability(device, "sensor");
+
+  hal_device_property_set_string(device, "sensor.type", "temperature");
+  hal_device_property_set_string(device, "sensor.location", "cpu");
+}
+
+void
+hf_acpi_acad_set_properties (HalDevice *device)
+{
+  hal_device_property_set_string(device, "info.category", "ac_adapter");
+  hal_device_add_capability(device, "ac_adapter");
+  if (hal_device_property_get_int(device, "freebsd.unit") > 0)
+    /* XXX We only handle one acad device since there is no way to get
+     * other devices' statuses */
+    return;
+  hf_acpi_poll_acad(device);
+}
+
+void
+hf_acpi_battery_set_properties (HalDevice *device)
+{
+    hal_device_property_set_string(device, "battery.type", "primary");
+    hal_device_property_set_string(device, "info.category", "battery");
+    hal_device_add_capability(device, "battery");
+    hf_acpi_poll_batt(device);
+}
+
+static gboolean
+hf_acpi_poll_all_acads (void)
+{
+  HalDevice *device;
+
+  /* XXX FreeBSD currently only has one AC adapter (the system AC adapter).
+   * Therefore, we ensure that only the first AC adapter will be matched. */
+  device = hal_device_store_match_key_value_string(hald_get_gdl(),
+                                                   "info.category",
+                                                   "ac_adapter");
+  if (device != NULL)
+    {
+      device_property_atomic_update_begin();
+      hf_acpi_poll_acad(device);
+      device_property_atomic_update_end();
+
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+static gboolean
+hf_acpi_poll_all_batts (void)
+{
+  GSList *l;
+  GSList *batts;
+  HalDevice *device;
+  gboolean result = FALSE;
+
+  batts = hal_device_store_match_multiple_key_value_string(hald_get_gdl(),
+                                                           "info.category",
+                                                           "battery");
+
+  HF_LIST_FOREACH(l, batts)
+    {
+      device = HAL_DEVICE(l->data);
+      device_property_atomic_update_begin();
+      hf_acpi_poll_batt(device);
+      device_property_atomic_update_end();
+      result = TRUE;
+    }
+  g_slist_free(batts);
+
+  return result;
+}
+
+static gboolean
+hf_acpi_poll_all_videos (void)
+{
+  GSList *vouts, *l;
+  HalDevice *device;
+  gboolean result = FALSE;
+
+  vouts = hal_device_store_match_multiple_key_value_string(hald_get_gdl(),
+    "info.category",
+    "display_device");
+
+  HF_LIST_FOREACH(l, vouts)
+    {
+      device = HAL_DEVICE(l->data);
+      device_property_atomic_update_begin();
+      hf_acpi_poll_video(device);
+      device_property_atomic_update_end();
+      result = TRUE;
+    }
+  g_slist_free(vouts);
+
+  return result;
+}
+
+static gboolean
+hf_acpi_poll_cb (gpointer data)
+{
+  gboolean result = FALSE;
+
+  if (hf_is_waiting)
+    return TRUE;
+
+  result |= hf_acpi_poll_all_acads();
+  result |= hf_acpi_poll_all_batts();
+  result |= hf_acpi_poll_all_videos();
+
+  if (! result)
+    return FALSE;
+
+  return TRUE;
+}
+
+static HalDevice *
+hf_acpi_video_device_new (HalDevice *parent, const char *type)
+{
+  HalDevice *device;
+  char *product;
+  int unit;
+
+  g_return_val_if_fail(HAL_IS_DEVICE(parent), NULL);
+
+  unit = hal_device_property_get_int(parent, "freebsd.unit");
+  if (! hf_has_sysctl("hw.acpi.video.%s%i.active", type, unit))
+    return NULL;
+
+  device = hf_device_new(parent);
+
+  product = g_strdup_printf("%s (%s)",
+    (hal_device_property_get_string(parent, "info.product") != NULL) ?
+    hal_device_property_get_string(parent, "info.product") : "Video Output",
+    type);
+  hal_device_property_set_string(device, "info.product", type);
+  g_free(product);
+
+  /* We need this for polling purposes */
+  hal_device_property_set_int(device, "freebsd.unit", unit);
+
+  hal_device_property_set_string(device, "info.category", "display_device");
+  hal_device_add_capability(device, "display_device");
+
+  hal_device_property_set_string(device, "display_device.type", type);
+
+  hf_device_set_full_udi(device, "%s_display_device_%s_%i", hal_device_get_udi(parent),
+                         type, unit);
+
+  hf_acpi_poll_video(device);
+
+  return device;
+}
+
+static HalDevice *
+hf_acpi_laptop_panel_new (HalDevice *parent, int max_levels,
+                          const char *max_sysctl, const char *access,
+                          const char *name)
+{
+  HalDevice *device;
+
+  g_return_val_if_fail(HAL_IS_DEVICE(parent), NULL);
+
+  device = hf_device_new(parent);
+
+  hf_device_property_set_string_printf(device, "info.product", "Laptop Panel (%s)", name);
+
+  hal_device_property_set_string(device, "info.category", "laptop_panel");
+  hal_device_add_capability(device, "laptop_panel");
+
+  hal_device_property_set_string(device, "laptop_panel.access_method", access);
+  if (max_sysctl == NULL)
+    hal_device_property_set_int(device, "laptop_panel.num_levels", max_levels);
+  else
+    {
+      int bmax;
+
+      if (hf_get_int_sysctl(&bmax, NULL, max_sysctl))
+        hal_device_property_set_int(device, "laptop_panel.num_levels", bmax);
+      else
+        hal_device_property_set_int(device, "laptop_panel.num_levels", max_levels);
+    }
+
+  hf_device_set_full_udi(device, "%s_laptop_panel_%s", hal_device_get_udi(parent), access);
+
+  return device;
+}
+
+static void
+hf_acpi_init (void)
+{
+  g_timeout_add(30000, hf_acpi_poll_cb, NULL);
+}
+
+static void
+hf_acpi_probe (void)
+{
+  GSList *video_devices, *l;
+  int i;
+
+  video_devices = hal_device_store_match_multiple_key_value_string(
+    hald_get_gdl(), "freebsd.driver", "acpi_video");
+  HF_LIST_FOREACH(l, video_devices)
+    {
+      HalDevice *parent = HAL_DEVICE(l->data);
+
+      if (! hal_device_property_get_bool(parent, "info.ignore"))
+        {
+	  int unit;
+          int j;
+
+	  unit = hal_device_property_get_int(parent, "freebsd.unit");
+
+	  for (j = 0; j < (int) G_N_ELEMENTS(video_outs); j++)
+            {
+	      if (! hf_device_store_match(hald_get_gdl(),
+                                          "display_device.type", HAL_PROPERTY_TYPE_STRING, video_outs[j],
+					  "freebsd.unit", HAL_PROPERTY_TYPE_INT32, unit,
+					  NULL)) {
+		  HalDevice *device;
+
+		  device = hf_acpi_video_device_new(parent, video_outs[j]);
+		  if (device)
+		    hf_device_preprobe_and_add(device);
+	        }
+	    }
+	  }
+      }
+  g_slist_free(video_devices);
+
+  for (i = 0; i < (int) G_N_ELEMENTS(laptop_panel_types); i++)
+    {
+      HalDevice *parent;
+      char *pname;
+
+      pname = g_strdup_printf("acpi_%s", laptop_panel_types[i].access);
+
+      /* There should only ever be one of these.  But we only care about the
+       * first one anyway. */
+      parent = hal_device_store_match_key_value_string(hald_get_gdl(),
+        "freebsd.driver", pname);
+      g_free(pname);
+
+      if (parent && ! hal_device_property_get_bool(parent, "info.ignore"))
+        {
+          if (! hal_device_store_match_key_value_string(hald_get_gdl(),
+                                                        "laptop_panel.access_method",
+							 laptop_panel_types[i].access))
+            {
+              HalDevice *panel_device;
+
+	      panel_device = hf_acpi_laptop_panel_new(parent,
+                                                      laptop_panel_types[i].max_levels,
+						      laptop_panel_types[i].max_sysctl,
+						      laptop_panel_types[i].access,
+						      laptop_panel_types[i].name);
+	      hf_device_preprobe_and_add(panel_device);
+	    }
+	}
+    }
+}
+
+static gboolean
+hf_acpi_device_rescan (HalDevice *device)
+{
+  if (hal_device_has_capability(device, "ac_adapter"))
+    hf_acpi_poll_acad(device);
+  else if (hal_device_has_capability(device, "battery"))
+    hf_acpi_poll_batt(device);
+  else if (hal_device_has_capability(device, "display_device"))
+    hf_acpi_poll_video(device);
+  else
+    return FALSE;
+
+  return TRUE;
+}
+
+static gboolean
+hf_acpi_devd_notify (const char *system,
+		     const char *subsystem,
+		     const char *type,
+		     const char *data)
+{
+  if (strcmp(system, "ACPI"))
+    return FALSE;
+
+  if (! strcmp(subsystem, "ACAD"))
+    hf_acpi_poll_all_acads();
+  else if (! strcmp(subsystem, "CMBAT"))
+    {
+      char *ptr;
+      int unit;
+
+      ptr = strstr(type, ".BAT");
+
+      if (ptr && sscanf(ptr, ".BAT%i", &unit))
+	{
+	  HalDevice *cmbat;
+
+	  cmbat = hf_device_store_match(hald_get_gdl(),
+                                        "info.category", HAL_PROPERTY_TYPE_STRING, "battery",
+					"freebsd.unit", HAL_PROPERTY_TYPE_INT32, unit,
+					NULL);
+
+	  if (cmbat)
+	    hf_acpi_poll_batt(cmbat);
+	  else
+	    hf_acpi_poll_all_batts();
+	}
+      else
+	hf_acpi_poll_all_batts();
+    }
+  else if (! strcmp(subsystem, "Lid") || ! strcmp(subsystem, "Button"))
+    {
+      HalDevice *button;
+      const char *btype = NULL;
+
+      if (! strcmp(subsystem, "Lid"))
+        btype = "lid";
+      else if (data && ! strcmp(data, "notify=0x00"))
+        btype = "power";
+      else if (data && ! strcmp(data, "notify=0x01"))
+        btype = "sleep";
+
+      if (btype)
+        {
+          button = hal_device_store_match_key_value_string(hald_get_gdl(),
+                                                           "button.type",
+                                                           btype);
+
+          if (button)
+            {
+              if (! strcmp(btype, "lid"))
+                {
+                  gboolean isclosed;
+
+		  isclosed = (data && ! strcmp(data, "notify=0x00")) ?
+                              TRUE : FALSE;
+		  device_property_atomic_update_begin();
+		  hf_acpi_button_update_state(button, isclosed);
+		  device_property_atomic_update_end();
+		}
+              device_send_signal_condition(button, "ButtonPressed", btype);
+	    }
+	}
+    }
+
+  return TRUE;
+}
+
+HFHandler hf_acpi_handler = {
+  .init =		hf_acpi_init,
+  .probe =		hf_acpi_probe,
+  .device_rescan =	hf_acpi_device_rescan
+};
+
+HFDevdHandler hf_acpi_devd_handler = {
+  .notify = hf_acpi_devd_notify
+};
diff --git a/hald/freebsd/hf-acpi.h b/hald/freebsd/hf-acpi.h
new file mode 100644
index 0000000..0bb98ef
--- /dev/null
+++ b/hald/freebsd/hf-acpi.h
@@ -0,0 +1,42 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-pci.h : poll for ACPI properties
+ *
+ * Copyright (C) 2006 Joe Marcus Clarke <marcus at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifndef _HF_ACPI_H
+#define _HF_ACPI_H
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "hf-osspec.h"
+#include "hf-devd.h"
+
+extern HFHandler hf_acpi_handler;
+extern HFDevdHandler hf_acpi_devd_handler;
+
+void hf_acpi_button_set_properties (HalDevice *device);
+void hf_acpi_acad_set_properties (HalDevice *device);
+void hf_acpi_battery_set_properties (HalDevice *device);
+void hf_acpi_tz_set_properties (HalDevice *device);
+
+#endif /* _HF_ACPI_H */
diff --git a/hald/freebsd/hf-ata.c b/hald/freebsd/hf-ata.c
new file mode 100644
index 0000000..4ac7574
--- /dev/null
+++ b/hald/freebsd/hf-ata.c
@@ -0,0 +1,275 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-ata.c : ATA support
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/ata.h>
+
+#include "../logger.h"
+
+#include "hf-ata.h"
+#include "hf-block.h"
+#include "hf-devtree.h"
+#include "hf-pci.h"
+#include "hf-storage.h"
+#include "hf-util.h"
+
+#define HF_ATA_DEVICE			"/dev/ata"
+
+static int hf_ata_fd;
+
+/* adapted from ad_describe() in sys/dev/ata/atadisk.c */
+static char *
+hf_ata_get_vendor (const char *model)
+{
+  char *sep;
+
+  g_return_val_if_fail(model != NULL, NULL);
+
+  sep = strpbrk(model, " -");
+  if (sep)
+    return g_strndup(model, sep - model);
+  else if (g_str_has_prefix(model, "ST"))
+    return g_strdup("Seagate");
+  else
+    return NULL;
+}
+
+static HalDevice *
+hf_ata_ide_device_new (HalDevice *parent, int ms)
+{
+  HalDevice *device;
+  int host;
+
+  g_return_val_if_fail(HAL_IS_DEVICE(parent), NULL);
+
+  device = hf_device_new(parent);
+
+  host = hal_device_property_get_int(parent, "ide_host.number");
+  hf_device_set_udi(device, "ide_%i_%i", host, ms);
+
+  hal_device_property_set_string(device, "info.bus", "ide");
+  hf_device_property_set_string_printf(device, "info.product", "IDE Device (%s)", ms == 0 ? "Master" : "Slave");
+
+  hal_device_property_set_int(device, "ide.host", host);
+  hal_device_property_set_int(device, "ide.channel", ms);
+
+  return device;
+}
+
+static HalDevice *
+hf_ata_block_device_new (HalDevice *parent,
+			 int ms,
+#ifdef IOCATADEVICES
+			 const struct ata_ioc_devices *devices)
+#else
+                         const struct ata_cmd *devices)
+#endif
+{
+  HalDevice *device;
+  const struct ata_params *params;
+  char *vendor;
+
+  g_return_val_if_fail(HAL_IS_DEVICE(parent), NULL);
+  g_return_val_if_fail(devices != NULL, NULL);
+
+#ifdef IOCATADEVICES
+  params = &devices->params[ms];
+#else
+  params = &devices->u.param.params[ms];
+#endif
+  vendor = hf_ata_get_vendor(params->model);
+
+  device = hf_device_new(parent);
+
+#ifdef IOCATADEVICES
+  hf_devtree_device_set_name(device, devices->name[ms]);
+#else
+  hf_devtree_device_set_name(device, devices->u.param.name[ms]);
+#endif
+
+#ifdef IOCATADEVICES
+  hf_block_device_enable(device, devices->name[ms]);
+#else
+  hf_block_device_enable(device, devices->u.param.name[ms]);
+#endif
+  hf_storage_device_enable(device);
+
+  hal_device_property_set_string(device, "info.product", params->model);
+  if (vendor)
+    hal_device_property_set_string(device, "info.vendor", vendor);
+
+  if (params->satacapabilities && params->satacapabilities != 0xffff)
+    hal_device_property_set_string(device, "storage.bus", "sata");
+  else
+    hal_device_property_set_string(device, "storage.bus", "ide");
+
+  switch (params->config & ATA_ATAPI_TYPE_MASK)
+    {
+    case ATA_ATAPI_TYPE_TAPE:
+      hf_storage_device_enable_tape(device);
+      break;
+
+    case ATA_ATAPI_TYPE_CDROM:
+    case ATA_ATAPI_TYPE_OPTICAL:
+      hf_storage_device_enable_cdrom(device);
+      break;
+    }
+
+  if ((params->support.command1 & ATA_SUPPORT_REMOVABLE) != 0)
+    hal_device_property_set_bool(device, "storage.removable", TRUE);
+
+  hal_device_property_set_string(device, "storage.physical_device", hal_device_get_udi(parent));
+  hal_device_property_set_string(device, "storage.model", params->model);
+  hal_device_property_set_string(device, "storage.vendor", vendor);
+  if (*params->serial)
+    hal_device_property_set_string(device, "storage.serial", params->serial);
+  if (*params->revision)
+    hf_device_property_set_string_printf(device, "storage.firmware_revision", "%.8s", params->revision);
+
+  g_free(vendor);
+
+  hf_block_device_complete(device, device, FALSE);
+
+  return device;
+}
+
+static void
+hf_ata_probe_devices (HalDevice *ide_host)
+{
+#ifdef IOCATADEVICES
+  struct ata_ioc_devices devices;
+#else
+  struct ata_cmd devices;
+#endif
+  int i;
+
+  g_return_if_fail(HAL_IS_DEVICE(ide_host));
+
+#ifdef IOCATADEVICES
+  devices.channel = hal_device_property_get_int(ide_host, "ide_host.number");
+  if (ioctl(hf_ata_fd, IOCATADEVICES, &devices) < 0)
+    {
+      HAL_WARNING(("unable to probe devices of ATA channel %i: %s", devices.channel, g_strerror(errno)));
+      return;
+    }
+#else
+  memset(&devices, 0, sizeof(devices));
+  devices.cmd = ATAGPARM;
+  devices.device = -1;
+  devices.channel = hal_device_property_get_int(ide_host, "ide_host.number");
+  if (ioctl(hf_ata_fd, IOCATA, &devices) < 0)
+    {
+      HAL_WARNING(("unable to probe devices of ATA channel %i: %s", devices.channel, g_strerror(errno)));
+      return;
+    }
+#endif
+
+  for (i = 0; i < 2; i++)
+#ifdef IOCATADEVICES
+    if (*devices.name[i])
+#else
+    if (*devices.u.param.name[i])
+#endif
+      {
+	HalDevice *ide_device;
+
+	ide_device = hf_device_store_match(hald_get_gdl(),
+                                           "ide.host", HAL_PROPERTY_TYPE_INT32, devices.channel,
+					   "ide.channel", HAL_PROPERTY_TYPE_INT32, i,
+					   NULL);
+
+	if (! ide_device)
+	  {
+	    ide_device = hf_ata_ide_device_new(ide_host, i);
+	    hf_device_preprobe_and_add(ide_device);
+	  }
+
+	if (! hal_device_property_get_bool(ide_device, "info.ignore")
+#ifdef IOCATADEVICES
+	    && ! hf_devtree_find_from_name(hald_get_gdl(), devices.name[i]))
+#else
+	    && ! hf_devtree_find_from_name(hald_get_gdl(), devices.u.param.name[i]))
+#endif
+	  {
+	    HalDevice *block_device;
+
+	    block_device = hf_ata_block_device_new(ide_device, i, &devices);
+	    hf_storage_device_add(block_device);
+	  }
+      }
+}
+
+static void
+hf_ata_privileged_init (void)
+{
+  hf_ata_fd = open(HF_ATA_DEVICE, O_RDONLY);
+  if (hf_ata_fd < 0)
+    HAL_INFO(("unable to open %s: %s", HF_ATA_DEVICE, g_strerror(errno)));
+}
+
+static void
+hf_ata_probe (void)
+{
+  GSList *gdl_devices;
+  GSList *l;
+
+  if (hf_ata_fd < 0)
+    return;
+
+  /* we might modify the gdl while iterating, so we must use a copy */
+  gdl_devices = g_slist_copy(hald_get_gdl()->devices);
+  HF_LIST_FOREACH(l, gdl_devices)
+    {
+      HalDevice *device = l->data;
+
+      if (hal_device_has_property(device, "ide_host.number") && ! hal_device_property_get_bool(device, "info.ignore"))
+	hf_ata_probe_devices(device);
+    }
+  g_slist_free(gdl_devices);
+}
+
+void
+hf_ata_channel_set_properties (HalDevice *device)
+{
+  int unit;
+
+  unit = hal_device_property_get_int(device, "freebsd.unit");
+
+  hf_device_set_udi(device, "ide_host_%i", unit);
+
+  hal_device_property_set_string(device, "info.bus", "ide_host");
+  hal_device_property_set_int(device, "ide_host.number", unit);
+}
+
+HFHandler hf_ata_handler = {
+  .privileged_init	= hf_ata_privileged_init,
+  .probe		= hf_ata_probe
+};
diff --git a/hald/freebsd/hf-ata.h b/hald/freebsd/hf-ata.h
new file mode 100644
index 0000000..37a5d6c
--- /dev/null
+++ b/hald/freebsd/hf-ata.h
@@ -0,0 +1,37 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-ata.h : ATA support
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifndef _HF_ATA_H
+#define _HF_ATA_H
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "hf-osspec.h"
+
+extern HFHandler hf_ata_handler;
+
+void hf_ata_channel_set_properties (HalDevice *device);
+
+#endif /* _HF_ATA_H */
diff --git a/hald/freebsd/hf-block.c b/hald/freebsd/hf-block.c
new file mode 100644
index 0000000..fd6ba03
--- /dev/null
+++ b/hald/freebsd/hf-block.c
@@ -0,0 +1,182 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-block.c : block device support
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "../logger.h"
+
+#include "hf-block.h"
+#include "hf-util.h"
+
+static gboolean
+hf_block_get_major_minor (const char *filename, int *major, int *minor)
+{
+  struct stat sb;
+
+  g_return_val_if_fail(filename != NULL, FALSE);
+  g_return_val_if_fail(major != NULL, FALSE);
+  g_return_val_if_fail(minor != NULL, FALSE);
+
+  if (stat(filename, &sb) < 0)
+    {
+      HAL_WARNING(("unable to stat %s: %s", filename, g_strerror(errno)));
+      return FALSE;
+    }
+
+  *major = major(sb.st_rdev);
+  *minor = minor(sb.st_rdev);
+
+  return TRUE;
+}
+
+/* adapted from blockdev_compute_udi() in linux2/blockdev.c */
+static void
+hf_block_device_compute_udi (HalDevice *device)
+{
+  g_return_if_fail(HAL_IS_DEVICE(device));
+
+  if (hal_device_property_get_bool(device, "block.is_volume"))
+    {
+      const char *label;
+      const char *uuid;
+
+      label = hal_device_property_get_string(device, "volume.label");
+      uuid = hal_device_property_get_string(device, "volume.uuid");
+
+      if (uuid && *uuid)
+	hf_device_set_udi(device, "volume_uuid_%s", uuid);
+      else if (label && *label)
+	hf_device_set_udi(device, "volume_label_%s", label);
+      else if (hal_device_property_get_bool(device, "volume.is_disc") &&
+	       hal_device_property_get_bool(device, "volume.disc.is_blank"))
+	/* this should be an empty CD/DVD */
+	hf_device_set_udi(device, "volume_empty_%s", hal_device_property_get_string(device, "volume.disc.type"));
+      else if (hal_device_has_property(device, "volume.partition.number")
+	       && hal_device_has_property(device, "volume.size"))
+	hf_device_set_udi(device, "volume_part%i_size_%ju",
+			  hal_device_property_get_int(device, "volume.partition.number"),
+			  hal_device_property_get_uint64(device, "volume.size"));
+      else if (hal_device_has_property(device, "volume.partition.number"))
+	hf_device_set_udi(device, "volume_part%i",
+			  hal_device_property_get_int(device, "volume.partition.number"));
+      else if (hal_device_has_property(device, "volume.size"))
+	hf_device_set_udi(device, "volume_size_%ju",
+			  hal_device_property_get_uint64(device, "volume.size"));
+      else
+	hf_device_set_full_udi(device, "%s_volume", hal_device_property_get_string(device, "info.parent"));
+    }
+  else if (hal_device_has_capability(device, "storage"))
+    {
+      const char *model;
+      const char *serial;
+      const char *physical_device;
+
+      model = hal_device_property_get_string(device, "storage.model");
+      serial = hal_device_property_get_string(device, "storage.serial");
+      physical_device = hal_device_property_get_string(device, "storage.physical_device");
+
+      if (serial && *serial)
+	hf_device_set_udi(device, "storage_serial_%s", serial);
+      else if (model && *model)
+	hf_device_set_udi(device, "storage_model_%s", model);
+      else if (physical_device && *physical_device)
+	hf_device_set_full_udi(device, "%s_storage", physical_device);
+      else
+	hf_device_set_full_udi(device, "%s_storage", hal_device_property_get_string(device, "info.parent"));
+    }
+  else
+    hf_device_set_full_udi(device, "%s_block", hal_device_property_get_string(device, "info.parent"));
+}
+
+static void
+hf_block_device_compute_product (HalDevice *device)
+{
+g_return_if_fail(HAL_IS_DEVICE(device));
+
+  if (hal_device_has_property(device, "info.product"))
+    return;
+
+  if (hal_device_property_get_bool(device, "block.is_volume"))
+    {
+      const char *str;
+
+      if ((str = hal_device_property_get_string(device, "volume.label")) && *str)
+	hal_device_property_set_string(device, "info.product", str);
+      else if ((str = hal_device_property_get_string(device, "volume.fstype")) && *str)
+	hf_device_property_set_string_printf(device, "info.product", "Volume (%s)", str);
+      else if ((str = hal_device_property_get_string(device, "volume.fsusage")) && ! strcmp(str, "unused"))
+	hal_device_property_set_string(device, "info.product", "Volume (Unused)");
+      else
+	hal_device_property_set_string(device, "info.product", "Volume");
+    }
+  else if (hal_device_has_capability(device, "storage"))
+    hal_device_property_set_string(device, "info.product", "Storage Device");
+  else
+    hal_device_property_set_string(device, "info.product", "Block Device");
+}
+
+void
+hf_block_device_enable (HalDevice *device, const char *devname)
+{
+  int major;
+  int minor;
+
+  g_return_if_fail(HAL_IS_DEVICE(device));
+  g_return_if_fail(devname != NULL);
+
+  hal_device_add_capability(device, "block");
+
+  hal_device_property_set_string(device, "info.bus", "block");
+  hal_device_property_set_string(device, "info.category", "block"); /* FIXME? */
+
+  hf_device_property_set_string_printf(device, "block.device", "/dev/%s", devname);
+  if (hf_block_get_major_minor(hal_device_property_get_string(device, "block.device"), &major, &minor))
+    {
+      hal_device_property_set_int(device, "block.major", major);
+      hal_device_property_set_int(device, "block.minor", minor);
+    }
+}
+
+void
+hf_block_device_complete (HalDevice *device,
+			  HalDevice *storage_device,
+			  gboolean is_volume)
+{
+  g_return_if_fail(HAL_IS_DEVICE(device));
+  g_return_if_fail(HAL_IS_DEVICE(storage_device));
+
+  hal_device_property_set_bool(device, "block.is_volume", is_volume);
+
+  hf_block_device_compute_udi(device);
+  hf_block_device_compute_product(device);
+
+  /* set this last, in case device == storage_device */
+  hal_device_copy_property(storage_device, "info.udi", device, "block.storage_device");
+}
diff --git a/hald/freebsd/hf-block.h b/hald/freebsd/hf-block.h
new file mode 100644
index 0000000..c7cca48
--- /dev/null
+++ b/hald/freebsd/hf-block.h
@@ -0,0 +1,38 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-block.h : block device support
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifndef _HF_BLOCK_H
+#define _HF_BLOCK_H
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "../hald.h"
+
+void hf_block_device_enable (HalDevice *device, const char *devname);
+void hf_block_device_complete (HalDevice *device,
+			       HalDevice *storage_device,
+			       gboolean is_volume);
+
+#endif /* _HF_BLOCK_H */
diff --git a/hald/freebsd/hf-computer.c b/hald/freebsd/hf-computer.c
new file mode 100644
index 0000000..37496e9
--- /dev/null
+++ b/hald/freebsd/hf-computer.c
@@ -0,0 +1,179 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-computer.c : the root computer device
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ * Copyright (C) 2006 Joe Marcus Clarke <marcus at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <string.h>
+#include <sys/utsname.h>
+
+#include "../hald.h"
+
+#include "hf-computer.h"
+#include "hf-util.h"
+
+static void
+hf_computer_device_probe (HalDevice *device)
+{
+  const char *chassis_type;
+  const char *formfactor;
+  const char *sys_manufacturer;
+  const char *sys_product;
+  const char *sys_version;
+
+  hf_runner_run_sync(device, 0, "hald-probe-smbios", NULL);
+
+  sys_manufacturer = hal_device_property_get_string(device, "smbios.system.manufacturer");
+  sys_product = hal_device_property_get_string(device, "smbios.system.product");
+  sys_version = hal_device_property_get_string(device, "smbios.system.version");
+
+  if (sys_manufacturer && sys_product && sys_version)
+    {
+      hal_device_property_set_string(device, "system.vendor", sys_manufacturer);
+
+      if (strcmp(sys_version, "Not Specified"))
+	hf_device_property_set_string_printf(device, "system.product", "%s %s", sys_product, sys_version);
+      else
+	hal_device_property_set_string(device, "system.product", sys_product);
+    }
+
+  chassis_type = hal_device_property_get_string(device, "smbios.chassis.type");
+  formfactor = hal_device_property_get_string(device, "system.formfactor");
+
+  if (chassis_type && (! formfactor || ! strcmp(formfactor, "unknown")))
+    {
+      int i;
+      /* Map the chassis type from dmidecode.c to a sensible type used in hal
+       *
+       * See also 3.3.4.1 of the "System Management BIOS Reference Specification,
+       * Version 2.3.4" document, available from http://www.dmtf.org/standards/smbios.
+       *
+       * TODO: figure out WTF the mapping should be; "Lunch Box"? Give me a break :-)
+       */
+      const char *chassis_map[] = {
+	"Other",			"unknown",
+	"Unknown",			"unknown",
+	"Desktop",			"desktop",
+	"Low Profile Desktop",		"desktop",
+	"Pizza Box",			"server",
+	"Mini Tower",			"desktop",
+	"Tower",			"desktop",
+	"Portable",			"laptop",
+	"Laptop",			"laptop",
+	"Notebook",			"laptop",
+	"Hand Held",			"handheld",
+	"Docking Station",		"laptop",
+	"All In One",			"unknown",
+	"Sub Notebook",			"laptop",
+	"Space-saving",			"unknown",
+	"Lunch Box",			"unknown",
+	"Main Server Chassis",		"server",
+	"Expansion Chassis",		"unknown",
+	"Sub Chassis",			"unknown",
+	"Bus Expansion Chassis",	"unknown",
+	"Peripheral Chassis",		"unknown",
+	"RAID Chassis",			"unknown",
+	"Rack Mount Chassis",		"unknown",
+	"Sealed-case PC",		"unknown",
+	"Multi-system",			"unknown"
+      };
+
+      for (i = 0; i < (int) G_N_ELEMENTS(chassis_map); i += 2)
+	if (! strcmp(chassis_map[i], chassis_type))
+	  {
+	    hal_device_property_set_string(device, "system.formfactor", chassis_map[i + 1]);
+	    break;
+	  }
+    }
+}
+
+gboolean
+hf_computer_device_add (void)
+{
+  HalDevice *device;
+  struct utsname un;
+  const char *power_type = NULL;
+  gboolean can_suspend_to_ram = FALSE;
+  gboolean can_suspend_to_disk = FALSE;
+  gboolean should_decode_dmi = FALSE;
+
+  if (hal_device_store_find(hald_get_gdl(), HF_COMPUTER))
+    return TRUE;
+
+  device = hal_device_new();
+  hf_device_set_udi(device, "computer");
+  hal_device_property_set_string(device, "info.bus", "unknown");
+  hal_device_property_set_string(device, "info.product", "Computer");
+
+  if (uname(&un) == 0)
+    {
+      hal_device_property_set_string(device, "system.kernel.name", un.sysname);
+      hal_device_property_set_string(device, "system.kernel.version", un.release);
+      hal_device_property_set_string(device, "system.kernel.machine", un.machine);
+    }
+
+  hal_device_property_set_string(device, "system.formfactor", "unknown");
+
+  if (hf_has_sysctl("dev.acpi.0.%%driver"))
+    {
+      char *states;
+
+      power_type = "acpi";
+      should_decode_dmi = TRUE;
+
+      states = hf_get_string_sysctl(NULL, "hw.acpi.supported_sleep_state");
+      if (states)
+	{
+	  char **elements;
+
+	  elements = g_strsplit(states, " ", 0);
+	  g_free(states);
+
+	  can_suspend_to_ram = hf_strv_find(elements, "S2") != -1 || hf_strv_find(elements, "S3") != -1;
+	  can_suspend_to_disk = hf_strv_find(elements, "S4") != -1;
+	  g_strfreev(elements);
+	}
+    }
+  /* FIXME apm, pmu, ... */
+
+  hal_device_property_set_string(device, "power_management.type", power_type);
+  hal_device_property_set_bool(device, "power_management.can_suspend", can_suspend_to_ram);
+  hal_device_property_set_bool(device, "power_management.can_hibernate", can_suspend_to_disk);
+
+  /* XXX: These following two keys are deprecated. */
+  hal_device_property_set_bool(device, "power_management.can_suspend_to_ram", can_suspend_to_ram);
+  hal_device_property_set_bool(device, "power_management.can_suspend_to_disk", can_suspend_to_disk);
+
+  if (hf_device_preprobe(device))
+    {
+      if (should_decode_dmi)
+	hf_computer_device_probe(device);
+      hf_device_add(device);
+
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
diff --git a/hald/freebsd/hf-computer.h b/hald/freebsd/hf-computer.h
new file mode 100644
index 0000000..66ca9df
--- /dev/null
+++ b/hald/freebsd/hf-computer.h
@@ -0,0 +1,36 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-computer.h : the root computer device
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ * Copyright (C) 2006 Joe Marcus Clarke <marcus at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifndef _HF_COMPUTER_H
+#define _HF_COMPUTER_H
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <glib.h>
+
+gboolean hf_computer_device_add (void);
+
+#endif /* _HF_COMPUTER_H */
diff --git a/hald/freebsd/hf-devd.c b/hald/freebsd/hf-devd.c
new file mode 100644
index 0000000..383a38b
--- /dev/null
+++ b/hald/freebsd/hf-devd.c
@@ -0,0 +1,419 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-devd.c : process devd events
+ *
+ * Copyright (C) 2006 Joe Marcus Clarke <marcus at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "../logger.h"
+#include "../osspec.h"
+
+#include "hf-devd.h"
+#include "hf-devtree.h"
+#include "hf-acpi.h"
+#include "hf-net.h"
+#include "hf-pcmcia.h"
+#include "hf-usb.h"
+#include "hf-util.h"
+
+#define HF_DEVD_SOCK_PATH		"/var/run/devd.pipe"
+
+#define HF_DEVD_EVENT_NOTIFY		'!'
+#define HF_DEVD_EVENT_ADD		'+'
+#define HF_DEVD_EVENT_REMOVE		'-'
+#define HF_DEVD_EVENT_NOMATCH		'?'
+
+static HFDevdHandler *handlers[] = {
+  &hf_usb_devd_handler,
+  &hf_net_devd_handler,
+  &hf_acpi_devd_handler,
+  &hf_pcmcia_devd_handler
+};
+
+static GHashTable *
+hf_devd_parse_params (const char *str)
+{
+  GHashTable *params;
+  char **pairs;
+  int i;
+
+  g_return_val_if_fail(str != NULL, FALSE);
+
+  params = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+
+  pairs = g_strsplit(str, " ", 0);
+  for (i = 0; pairs[i]; i++)
+    {
+      char *equal;
+
+      equal = strchr(pairs[i], '=');
+      g_hash_table_insert(params,
+			  equal ? g_strndup(pairs[i], equal - pairs[i]) : g_strdup(pairs[i]),
+			  equal ? g_strdup(equal + 1) : NULL);
+    }
+  g_strfreev(pairs);
+
+  return params;
+}
+
+static gboolean
+hf_devd_parse_add_remove (const char *event,
+			  char **name,
+			  GHashTable **params,
+			  GHashTable **at,
+			  char **parent)
+{
+  char *params_ptr;
+  char *at_ptr;
+  char *parent_ptr;
+
+  /*
++ugen0 vendor=0x0a12 product=0x0001 devclass=0xe0 devsubclass=0x01 release=0x0525 sernum="" at port=0 vendor=0x0a12 product=0x0001 devclass=0xe0 devsubclass=0x01 release=0x0525 sernum="" on uhub3
+
+-ugen0 vendor=0x0a12 product=0x0001 devclass=0xe0 devsubclass=0x01 release=0x0525 sernum="" at port=0 vendor=0x0a12 product=0x0001 devclass=0xe0 devsubclass=0x01 release=0x0525 sernum="" on uhub3
+  */
+
+  g_return_val_if_fail(event != NULL, FALSE);
+  g_return_val_if_fail(name != NULL, FALSE);
+  g_return_val_if_fail(params != NULL, FALSE);
+  g_return_val_if_fail(at != NULL, FALSE);
+  g_return_val_if_fail(parent != NULL, FALSE);
+
+  if ((params_ptr = strchr(event, ' '))
+      && (at_ptr = strstr(params_ptr + 1, " at "))
+      && (parent_ptr = strstr(at_ptr + 4, " on ")))
+    {
+      char *params_str;
+      char *at_str;
+
+      *name = g_strndup(event, params_ptr - event);
+      params_str = g_strndup(params_ptr + 1, at_ptr - params_ptr - 1);
+      at_str = g_strndup(at_ptr + 4, parent_ptr - at_ptr - 4);
+      *parent = g_strdup(parent_ptr + 4);
+
+      if (! strcmp(*parent, ".")) /* sys/kern/subr_bus.c */
+	{
+	  g_free(*parent);
+	  *parent = NULL;
+	}
+
+      *params = hf_devd_parse_params(params_str);
+      g_free(params_str);
+
+      *at = hf_devd_parse_params(at_str);
+      g_free(at_str);
+
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+static gboolean
+hf_devd_parse_nomatch (const char *event,
+                       GHashTable **at,
+		       char **parent)
+{
+  char *at_ptr;
+  char *parent_ptr;
+
+  /*
+? at port=0 vendor=0x0a12 product=0x0001 devclass=0xe0 devsubclass=0x01 release=0x0525 sernum="" on uhub3
+  */
+
+  g_return_val_if_fail(event != NULL, FALSE);
+  g_return_val_if_fail(at != NULL, FALSE);
+  g_return_val_if_fail(parent != NULL, FALSE);
+
+  if ((at_ptr = strstr(event, " at "))
+     && (parent_ptr = strstr(at_ptr + 4, " on ")))
+    {
+      char *at_str;
+
+      at_str = g_strndup(at_ptr + 4, parent_ptr - at_ptr - 4);
+      *parent = g_strdup(parent_ptr + 4);
+
+      if (! strcmp(*parent, ".")) /* sys/kern/subr_bus.c */
+        {
+          g_free(*parent);
+	  *parent = NULL;
+	}
+
+      *at = hf_devd_parse_params(at_str);
+      g_free(at_str);
+
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+static gboolean
+hf_devd_parse_notify (const char *event,
+		      char **system,
+		      char **subsystem,
+		      char **type,
+		      char **data)
+{
+  char **items;
+  gboolean status = FALSE;
+
+  /*
+!system=IFNET subsystem=bge0 type=LINK_DOWN
+  */
+
+  g_return_val_if_fail(event != NULL, FALSE);
+  g_return_val_if_fail(system != NULL, FALSE);
+  g_return_val_if_fail(subsystem != NULL, FALSE);
+  g_return_val_if_fail(type != NULL, FALSE);
+  g_return_val_if_fail(data != NULL, FALSE);
+
+  items = g_strsplit(event, " ", 0);
+  if (g_strv_length(items) < 3)
+    goto end;
+
+  if (! g_str_has_prefix(items[0], "system=") ||
+      ! g_str_has_prefix(items[1], "subsystem=") ||
+      ! g_str_has_prefix(items[2], "type="))
+    goto end;
+
+  *system = g_strdup(items[0] + 7);
+  *subsystem = g_strdup(items[1] + 10);
+  *type = g_strdup(items[2] + 5);
+  *data = g_strdup(items[3]);	/* may be NULL */
+
+  status = TRUE;
+
+ end:
+  g_strfreev(items);
+  return status;
+}
+
+static void
+hf_devd_process_add_event (const char *name,
+			   GHashTable *params,
+			   GHashTable *at,
+			   const char *parent)
+{
+  int i;
+
+  g_return_if_fail(name != NULL);
+  g_return_if_fail(params != NULL);
+  g_return_if_fail(at != NULL);
+
+  for (i = 0; i < (int) G_N_ELEMENTS(handlers); i++)
+    if (handlers[i]->add && handlers[i]->add(name, params, at, parent))
+      return;
+
+  /* no match, default action: probe to catch the new device */
+
+  osspec_probe();
+}
+
+static void
+hf_devd_process_remove_event (const char *name,
+			      GHashTable *params,
+			      GHashTable *at,
+			      const char *parent)
+{
+  int i;
+  HalDevice *device;
+
+  g_return_if_fail(name != NULL);
+  g_return_if_fail(params != NULL);
+  g_return_if_fail(at != NULL);
+
+  for (i = 0; i < (int) G_N_ELEMENTS(handlers); i++)
+    if (handlers[i]->remove && handlers[i]->remove(name, params, at, parent))
+      return;
+
+  /* no match, default action: remove the device and its children */
+
+  device = hf_devtree_find_from_name(hald_get_gdl(), name);
+  if (device)
+    hf_device_remove_tree(device);
+}
+
+static void
+hf_devd_process_notify_event (const char *system,
+			      const char *subsystem,
+			      const char *type,
+			      const char *data)
+{
+  int i;
+
+  g_return_if_fail(system != NULL);
+  g_return_if_fail(subsystem != NULL);
+  g_return_if_fail(type != NULL);
+  g_return_if_fail(data != NULL);
+
+  for (i = 0; i < (int) G_N_ELEMENTS(handlers); i++)
+    if (handlers[i]->notify && handlers[i]->notify(system, subsystem, type, data))
+      return;
+
+  /* no default action */
+}
+
+static void
+hf_devd_process_nomatch_event (GHashTable *at, const char *parent)
+{
+  int i;
+
+  g_return_if_fail(at != NULL);
+
+  for (i = 0; i < (int) G_N_ELEMENTS(handlers); i++)
+    if (handlers[i]->nomatch && handlers[i]->nomatch(at, parent))
+      return;
+}
+
+static void
+hf_devd_process_event (const char *event)
+{
+  g_return_if_fail(event != NULL);
+
+  HAL_INFO(("received devd event: %s", event));
+
+  switch (event[0])
+    {
+    case HF_DEVD_EVENT_ADD:
+    case HF_DEVD_EVENT_REMOVE:
+      {
+	char *name;
+	GHashTable *params;
+	GHashTable *at;
+	char *parent;
+
+	if (! hf_devd_parse_add_remove(event + 1, &name, &params, &at, &parent))
+	  goto malformed;
+
+	if (event[0] == HF_DEVD_EVENT_ADD)
+	  hf_devd_process_add_event(name, params, at, parent);
+	else
+	  hf_devd_process_remove_event(name, params, at, parent);
+
+	g_free(name);
+	g_hash_table_destroy(params);
+	g_hash_table_destroy(at);
+	g_free(parent);
+      }
+      return;
+
+    case HF_DEVD_EVENT_NOTIFY:
+      {
+	char *system;
+	char *subsystem;
+	char *type;
+	char *data;
+
+	if (! hf_devd_parse_notify(event + 1, &system, &subsystem, &type, &data))
+	  goto malformed;
+
+	hf_devd_process_notify_event(system, subsystem, type, data);
+
+	g_free(system);
+	g_free(subsystem);
+	g_free(type);
+	g_free(data);
+      }
+      return;
+
+    case HF_DEVD_EVENT_NOMATCH:
+      {
+        GHashTable *at;
+        char *parent;
+
+        if (! hf_devd_parse_nomatch(event + 1, &at, &parent))
+          goto malformed;
+
+	hf_devd_process_nomatch_event(at, parent);
+
+        g_hash_table_destroy(at);
+        g_free(parent);
+      }
+      return;
+    }
+
+ malformed:
+  HAL_WARNING(("malformed devd event: %s", event));
+}
+
+static gboolean
+hf_devd_event_cb (GIOChannel *source, GIOCondition condition,
+                      gpointer user_data)
+{
+  char *event;
+  gsize terminator;
+
+  if (hf_is_waiting)
+    return TRUE;
+
+  if (g_io_channel_read_line(source, &event, NULL, &terminator, NULL) == G_IO_STATUS_NORMAL)
+    {
+      event[terminator] = 0;
+      hf_devd_process_event(event);
+      g_free(event);
+    }
+
+  return TRUE;
+}
+
+static void
+hf_devd_init (void)
+{
+  int event_fd;
+  struct sockaddr_un addr;
+
+  event_fd = socket(PF_UNIX, SOCK_STREAM, 0);
+  if (event_fd < 0)
+    {
+      HAL_WARNING(("failed to create event socket: %s", g_strerror(errno)));
+      return;
+    }
+
+  addr.sun_family = AF_UNIX;
+  strncpy(addr.sun_path, HF_DEVD_SOCK_PATH, sizeof(addr.sun_path));
+  if (connect(event_fd, (struct sockaddr *)&addr, sizeof(addr)) == 0)
+    {
+      GIOChannel *channel;
+
+      channel = g_io_channel_unix_new(event_fd);
+      g_io_add_watch(channel, G_IO_IN, hf_devd_event_cb, NULL);
+    }
+  else
+    {
+      HAL_WARNING(("failed to connect to %s: %s", HF_DEVD_SOCK_PATH,
+                   g_strerror(errno)));
+      close(event_fd);
+    }
+}
+
+HFHandler hf_devd_handler = {
+  .init = hf_devd_init
+};
diff --git a/hald/freebsd/hf-devd.h b/hald/freebsd/hf-devd.h
new file mode 100644
index 0000000..a5b28b6
--- /dev/null
+++ b/hald/freebsd/hf-devd.h
@@ -0,0 +1,57 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-devd.h : process devd events
+ *
+ * Copyright (C) 2006 Joe Marcus Clarke <marcus at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifndef _HF_DEVD_H
+#define _HF_DEVD_H
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <glib.h>
+
+#include "hf-osspec.h"
+
+extern HFHandler hf_devd_handler;
+
+typedef struct
+{
+  /* return TRUE to consume the event and stop processing */
+
+  gboolean (*add)	(const char *name,
+			 GHashTable *params,	/* values may be NULL */
+			 GHashTable *at,	/* values may be NULL */
+			 const char *parent);	/* may be NULL */
+  gboolean (*remove)	(const char *name,
+			 GHashTable *params,	/* values may be NULL */
+			 GHashTable *at,	/* values may be NULL */
+			 const char *parent);	/* may be NULL */
+  gboolean (*notify)	(const char *system,
+			 const char *subsystem,
+			 const char *type,
+			 const char *data);	/* may be NULL */
+  gboolean (*nomatch)	(GHashTable *at,	/* values may be NULL */
+			 const char *parent);	/* may be NULL */
+} HFDevdHandler;
+
+#endif /* _HF_DEVD_H */
diff --git a/hald/freebsd/hf-devtree.c b/hald/freebsd/hf-devtree.c
new file mode 100644
index 0000000..7660cac
--- /dev/null
+++ b/hald/freebsd/hf-devtree.c
@@ -0,0 +1,591 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-devtree.c : generic device tree support
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include "../ids.h"
+#include "../logger.h"
+
+#include "hf-devtree.h"
+#include "hf-acpi.h"
+#include "hf-ata.h"
+#include "hf-block.h"
+#include "hf-pcmcia.h"
+#include "hf-storage.h"
+#include "hf-util.h"
+
+typedef struct
+{
+  const char	*driver;
+  void		(*set_properties)	(HalDevice *device);
+} Handler;
+
+typedef struct
+{
+  const Handler	*handler;
+  int		unit;
+} DeviceInfo;
+
+static gboolean
+hf_devtree_parse_name (const char *name,
+		       char **driver,
+		       int *unit)
+{
+  char *_driver;
+  int _unit;
+  gboolean status = FALSE;
+
+  g_return_val_if_fail(name != NULL, FALSE);
+
+  _driver = g_new(char, strlen(name) + 1);
+  if (sscanf(name, "%[^0-9]%i", _driver, &_unit) == 2)
+    {
+      if (driver)
+	{
+	  *driver = _driver;
+	  _driver = NULL;
+	}
+      if (unit)
+	*unit = _unit;
+      status = TRUE;
+    }
+
+  g_free(_driver);
+  return status;
+}
+
+static gboolean
+hf_devtree_cpu_can_throttle (int cpu)
+{
+  gboolean can = FALSE;
+  char *levels;
+
+  levels = hf_get_string_sysctl(NULL, "dev.cpu.%i.freq_levels", cpu);
+  if (levels)
+    {
+      char **toks;
+
+      toks = g_strsplit(levels, " ", 0);
+
+      if (g_strv_length(toks) > 1)
+        can = TRUE;
+
+      g_strfreev(toks);
+      g_free(levels);
+    }
+
+  return can;
+}
+
+static int
+hf_devtree_cpu_get_maxfreq (int cpu)
+{
+  char *levels;
+  int freq = -1;
+
+  levels = hf_get_string_sysctl(NULL, "dev.cpu.%i.freq_levels", cpu);
+  if (levels)
+    {
+      sscanf(levels, "%i/", &freq);
+      g_free(levels);
+    }
+
+  if (freq == -1)
+    {
+      int ncpu;
+
+      /* freq not found, on UP systems fallback to hw.clockrate */
+
+      if (hf_get_int_sysctl(&ncpu, NULL, "hw.ncpu") && ncpu == 1)
+	hf_get_int_sysctl(&freq, NULL, "hw.clockrate");
+    }
+
+  return freq;
+}
+
+static void
+hf_devtree_cpu_set_properties (HalDevice *device)
+{
+  int unit;
+  int freq;
+  int ncpu;
+
+  unit = hal_device_property_get_int(device, "freebsd.unit");
+
+  hal_device_property_set_string(device, "info.category", "processor");
+  hal_device_add_capability(device, "processor");
+
+  hal_device_property_set_int(device, "processor.number", unit);
+  hal_device_property_set_bool(device, "processor.can_throttle", hf_devtree_cpu_can_throttle(unit));
+
+  freq = hf_devtree_cpu_get_maxfreq(unit);
+  if (freq != -1)
+    hal_device_property_set_int(device, "processor.maximum_speed", freq);
+
+  /* on UP systems, set a better info.product */
+  if (hf_get_int_sysctl(&ncpu, NULL, "hw.ncpu") && ncpu == 1)
+    {
+      char *model;
+
+      model = hf_get_string_sysctl(NULL, "hw.model");
+      if (model)
+	{
+	  hal_device_property_set_string(device, "info.product", model);
+	  g_free(model);
+	}
+    }
+}
+
+static void
+hf_devtree_fd_set_properties (HalDevice *device)
+{
+  char *devname;
+
+  devname = hf_devtree_device_get_name(device);
+  hf_block_device_enable(device, devname);
+  g_free(devname);
+
+  hf_storage_device_enable(device);
+
+  hal_device_property_set_string(device, "storage.drive_type", "floppy");
+
+  hal_device_property_set_bool(device, "storage.removable", TRUE);
+  hal_device_property_set_bool(device, "storage.no_partitions_hint", TRUE);
+
+  hal_device_copy_property(device, "info.product", device, "storage.model");
+
+  hf_block_device_complete(device, device, FALSE);
+}
+
+static void
+hf_devtree_atkbd_set_properties (HalDevice *device)
+{
+  hf_device_set_input(device, "keyboard", NULL);
+}
+
+static void
+hf_devtree_psm_set_properties (HalDevice *device)
+{
+  char *devname;
+
+  devname = hf_devtree_device_get_name(device);
+  hf_device_set_input(device, "mouse", devname);
+  g_free(devname);
+}
+
+static void
+hf_devtree_joy_set_properties (HalDevice *device)
+{
+  char *devname;
+
+  devname = hf_devtree_device_get_name(device);
+  hf_device_set_input(device, "joystick", devname);
+  g_free(devname);
+
+  if (! hal_device_has_property(device, "info.product"))
+    hal_device_property_set_string(device, "info.product", "PC Joystick");
+}
+
+static HalDevice *
+hf_devtree_device_new (HalDevice *parent, const Handler *handler, int unit)
+{
+  HalDevice *device;
+  char *desc;
+  char *pnpinfo;
+
+  g_return_val_if_fail(handler != NULL, NULL);
+
+  device = hf_device_new(parent);
+
+  hf_device_set_udi(device, "%s_%i", handler->driver, unit);
+
+  desc = hf_get_string_sysctl(NULL, "dev.%s.%i.%%desc", handler->driver, unit);
+  if (desc && *desc)
+    hal_device_property_set_string(device, "info.product", desc);
+  g_free(desc);
+
+  hf_devtree_device_set_info(device, handler->driver, unit);
+
+  /* find PNP ID */
+  pnpinfo = hf_get_string_sysctl(NULL, "dev.%s.%i.%%pnpinfo", handler->driver, unit);
+  if (pnpinfo)
+    {
+      char **items;
+      int i;
+
+      items = g_strsplit(pnpinfo, " ", 0);
+      g_free(pnpinfo);
+
+      for (i = 0; items[i]; i++)
+	if (g_str_has_prefix(items[i], "_HID="))
+	  {
+	    if (strcmp(items[i], "_HID=none"))
+              {
+                char *pnp_description;
+
+	        hal_device_property_set_string(device, "pnp.id", items[i] + 5);
+
+		ids_find_pnp(items[i] + 5, &pnp_description);
+		if (pnp_description)
+                  {
+                    hal_device_property_set_string(device, "pnp.description", pnp_description);
+		    if (! hal_device_has_property(device, "info.product"))
+                      hal_device_property_set_string(device, "pnp.description", pnp_description);
+		  }
+	      }
+
+	    break;
+	  }
+      g_strfreev(items);
+    }
+
+  if (handler->set_properties)
+    handler->set_properties(device);
+
+  if (! hal_device_has_property(device, "info.bus"))
+    {
+      hal_device_property_set_string(device, "info.bus", "platform");
+      hf_device_property_set_string_printf(device, "platform.id", "%s.%i", handler->driver, unit);
+    }
+
+  return device;
+}
+
+static gboolean
+hf_devtree_device_is (HalDevice *device)
+{
+  g_return_val_if_fail(HAL_IS_DEVICE(device), FALSE);
+
+  return hal_device_has_property(device, "freebsd.driver");
+}
+
+static GSList *
+hf_devtree_lookup (GSList *devices, const char *driver, int unit)
+{
+  GSList *l;
+
+  g_return_val_if_fail(driver != NULL, NULL);
+
+  HF_LIST_FOREACH(l, devices)
+    {
+      DeviceInfo *info = l->data;
+
+      if (! strcmp(info->handler->driver, driver) && info->unit == unit)
+	return l;
+    }
+
+  return NULL;
+}
+
+static GSList *
+hf_devtree_get_root (GSList *devices)
+{
+  GSList *root;
+  DeviceInfo *info;
+  char *driver;
+  int unit;
+
+  g_return_val_if_fail(devices != NULL, NULL);
+
+  root = devices;
+  info = root->data;
+
+  driver = g_strdup(info->handler->driver);
+  unit = info->unit;
+
+  while (driver)
+    {
+      char *parent_name;
+
+      parent_name = hf_get_string_sysctl(NULL, "dev.%s.%i.%%parent", driver, unit);
+
+      g_free(driver);
+      driver = NULL;
+
+      if (parent_name)
+	{
+	  if (hf_devtree_parse_name(parent_name, &driver, &unit))
+	    {
+	      GSList *new_root;
+
+	      new_root = hf_devtree_lookup(devices, driver, unit);
+	      if (new_root)
+		{
+		  root = new_root;
+		  info = root->data;
+
+		  g_free(driver);
+		  driver = g_strdup(info->handler->driver);
+		  unit = info->unit;
+		}
+	    }
+	  g_free(parent_name);
+	}
+    }
+
+  return root;
+}
+
+static Handler handlers[] = {
+  { "acpi_acad",	hf_acpi_acad_set_properties		},
+  { "acpi_asus",	NULL					},
+  { "acpi_button",	hf_acpi_button_set_properties		},
+  { "acpi_fujitsu",	NULL					},
+  { "acpi_ibm",		NULL					},
+  { "acpi_lid",		hf_acpi_button_set_properties		},
+  { "acpi_panasonic",	NULL					},
+  { "acpi_sony",	NULL					},
+  { "acpi_toshiba",	NULL					},
+  { "acpi_tz",		hf_acpi_tz_set_properties		},
+  { "acpi_video",	NULL					},
+  { "ata",		hf_ata_channel_set_properties		},
+  { "atkbd",		hf_devtree_atkbd_set_properties		},
+  { "atkbdc",		NULL					},
+  { "battery",		hf_acpi_battery_set_properties		},
+  { "cardbus",		hf_pcmcia_set_properties		},
+  { "cpu",		hf_devtree_cpu_set_properties		},
+  { "fd",		hf_devtree_fd_set_properties		},
+  { "fdc",		NULL					},
+  { "joy",		hf_devtree_joy_set_properties		},
+  { "pccard",		hf_pcmcia_set_properties		},
+  { "pcm",		NULL					},
+  { "psm",		hf_devtree_psm_set_properties		},
+  { "sio",		NULL					},
+  { "speaker",		NULL					}
+};
+
+static void
+hf_devtree_probe (void)
+{
+  GSList *devices = NULL;
+  int i;
+
+  /* build a list of devices */
+
+  for (i = 0; i < (int) G_N_ELEMENTS(handlers); i++)
+    {
+      int j;
+
+      for (j = 0; hf_has_sysctl("dev.%s.%i.%%driver", handlers[i].driver, j); j++)
+	if (! hf_devtree_find_from_info(hald_get_gdl(), handlers[i].driver, j))
+	  {
+	    DeviceInfo *info;
+
+	    info = g_new(DeviceInfo, 1);
+	    info->handler = &handlers[i];
+	    info->unit = j;
+
+	    devices = g_slist_prepend(devices, info);
+	  }
+    }
+
+  /* add the devices (parents first) */
+
+  while (devices)
+    {
+      GSList *root;
+      DeviceInfo *info;
+      HalDevice *parent;
+
+      root = hf_devtree_get_root(devices);
+      g_assert(root != NULL);
+
+      info = root->data;
+
+      parent = hf_devtree_find_parent_from_info(hald_get_gdl(), info->handler->driver, info->unit);
+      if (! parent || ! hal_device_property_get_bool(parent, "info.ignore"))
+	{
+	  HalDevice *device;
+
+	  device = hf_devtree_device_new(parent, info->handler, info->unit);
+	  hf_device_preprobe_and_add(device);
+	}
+
+      devices = g_slist_delete_link(devices, root);
+      g_free(info);
+    }
+}
+
+HalDevice *
+hf_devtree_find_from_name (HalDeviceStore *store, const char *name)
+{
+  char *driver;
+  int unit;
+  HalDevice *device = NULL;
+
+  g_return_val_if_fail(HAL_IS_DEVICE_STORE(store), NULL);
+  g_return_val_if_fail(name != NULL, NULL);
+
+  if (hf_devtree_parse_name(name, &driver, &unit))
+    {
+      device = hf_devtree_find_from_info(store, driver, unit);
+      g_free(driver);
+    }
+
+  return device;
+}
+
+HalDevice *
+hf_devtree_find_from_info (HalDeviceStore *store, const char *driver, int unit)
+{
+  g_return_val_if_fail(HAL_IS_DEVICE_STORE(store), NULL);
+  g_return_val_if_fail(driver != NULL, NULL);
+
+  return hf_device_store_match(store,
+                               "freebsd.driver", HAL_PROPERTY_TYPE_STRING, driver,
+			       "freebsd.unit", HAL_PROPERTY_TYPE_INT32, unit,
+			       NULL);
+}
+
+HalDevice *
+hf_devtree_find_parent_from_name (HalDeviceStore *store, const char *name)
+{
+  char *driver;
+  int unit;
+  HalDevice *device = NULL;
+
+  g_return_val_if_fail(HAL_IS_DEVICE_STORE(store), NULL);
+  g_return_val_if_fail(name != NULL, NULL);
+
+  if (hf_devtree_parse_name(name, &driver, &unit))
+    {
+      device = hf_devtree_find_parent_from_info(store, driver, unit);
+      g_free(driver);
+    }
+
+  return device;
+}
+
+HalDevice *
+hf_devtree_find_parent_from_info (HalDeviceStore *store,
+				  const char *driver,
+				  int unit)
+{
+  HalDevice *parent = NULL;
+  char *driver_iter;
+
+  g_return_val_if_fail(HAL_IS_DEVICE_STORE(store), NULL);
+  g_return_val_if_fail(driver != NULL, NULL);
+
+  driver_iter = g_strdup(driver);
+  while (! parent && driver_iter)
+    {
+      char *parent_name;
+
+      parent_name = hf_get_string_sysctl(NULL, "dev.%s.%i.%%parent", driver_iter, unit);
+
+      g_free(driver_iter);
+      driver_iter = NULL;
+
+      if (parent_name)
+	{
+	  parent = hf_devtree_find_from_name(store, parent_name);
+	  if (! parent)
+	    hf_devtree_parse_name(parent_name, &driver_iter, &unit);
+	  g_free(parent_name);
+	}
+    }
+
+  return parent;
+}
+
+void
+hf_devtree_device_set_info (HalDevice *device, const char *driver, int unit)
+{
+  g_return_if_fail(HAL_IS_DEVICE(device));
+  g_return_if_fail(driver != NULL);
+
+  hal_device_property_set_string(device, "freebsd.driver", driver);
+  hal_device_property_set_int(device, "freebsd.unit", unit);
+}
+
+gboolean
+hf_devtree_device_get_info (HalDevice *device, const char **driver, int *unit)
+{
+  g_return_val_if_fail(HAL_IS_DEVICE(device), FALSE);
+
+  if (hf_devtree_device_is(device))
+    {
+      if (driver)
+	*driver = hal_device_property_get_string(device, "freebsd.driver");
+      if (unit)
+	*unit = hal_device_property_get_int(device, "freebsd.unit");
+
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+void
+hf_devtree_device_set_name (HalDevice *device, const char *name)
+{
+  char *driver;
+  int unit;
+
+  g_return_if_fail(HAL_IS_DEVICE(device));
+  g_return_if_fail(name != NULL);
+
+  if (hf_devtree_parse_name(name, &driver, &unit))
+    {
+      hf_devtree_device_set_info(device, driver, unit);
+      g_free(driver);
+    }
+}
+
+char *
+hf_devtree_device_get_name (HalDevice *device)
+{
+  g_return_val_if_fail(HAL_IS_DEVICE(device), NULL);
+
+  if (hf_devtree_device_is(device))
+    return g_strdup_printf("%s%i",
+			   hal_device_property_get_string(device, "freebsd.driver"),
+			   hal_device_property_get_int(device, "freebsd.unit"));
+  else
+    return NULL;
+}
+
+gboolean
+hf_devtree_is_driver (const char *name, const char *driver)
+{
+  char *unit;
+
+  g_return_val_if_fail(name != NULL, FALSE);
+  g_return_val_if_fail(driver != NULL, FALSE);
+
+  unit = strpbrk(name, "0123456789");
+  if (unit)
+    return ! strncmp(name, driver, unit - name);
+  else
+    return FALSE;
+}
+
+HFHandler hf_devtree_handler = {
+  .probe = hf_devtree_probe
+};
diff --git a/hald/freebsd/hf-devtree.h b/hald/freebsd/hf-devtree.h
new file mode 100644
index 0000000..0c6a199
--- /dev/null
+++ b/hald/freebsd/hf-devtree.h
@@ -0,0 +1,59 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-devtree.h : generic device tree support
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifndef _HF_DEVTREE_H
+#define _HF_DEVTREE_H
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "hf-osspec.h"
+
+extern HFHandler hf_devtree_handler;
+
+HalDevice *hf_devtree_find_from_name (HalDeviceStore *store,
+				      const char *name);
+HalDevice *hf_devtree_find_from_info (HalDeviceStore *store,
+				      const char *driver,
+				      int unit);
+
+HalDevice *hf_devtree_find_parent_from_name (HalDeviceStore *store,
+					     const char *name);
+HalDevice *hf_devtree_find_parent_from_info (HalDeviceStore *store,
+					     const char *driver,
+					     int unit);
+
+void hf_devtree_device_set_info (HalDevice *device,
+				 const char *driver,
+				 int unit);
+gboolean hf_devtree_device_get_info (HalDevice *device,
+				     const char **driver,
+				     int *unit);
+
+void hf_devtree_device_set_name (HalDevice *device, const char *devname);
+char *hf_devtree_device_get_name (HalDevice *device);
+
+gboolean hf_devtree_is_driver (const char *name, const char *driver);
+
+#endif /* _HF_DEVTREE_H */
diff --git a/hald/freebsd/hf-net.c b/hald/freebsd/hf-net.c
new file mode 100644
index 0000000..2a92e7f
--- /dev/null
+++ b/hald/freebsd/hf-net.c
@@ -0,0 +1,355 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-net.c : networking device support
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ * Copyright (C) 2006 Joe Marcus Clarke <marcus at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_media.h>
+
+#include "../hald_dbus.h"
+#include "../logger.h"
+#include "../util.h"
+
+#include "hf-net.h"
+#include "hf-devtree.h"
+#include "hf-util.h"
+
+static gboolean
+hf_net_get_link_up (const char *interface)
+{
+  int fd;
+  struct ifreq req;
+  gboolean is_up = FALSE;
+
+  g_return_val_if_fail(interface != NULL, FALSE);
+
+  fd = socket(AF_INET, SOCK_DGRAM, 0);
+  if (fd < 0)
+    return FALSE;
+
+  memset(&req, 0, sizeof(req));
+  strncpy(req.ifr_name, interface, sizeof(req.ifr_name));
+
+  if (ioctl(fd, SIOCGIFFLAGS, &req) != -1 && (req.ifr_flags & IFF_UP) != 0)
+    is_up = TRUE;
+
+  close(fd);
+
+  return is_up;
+}
+
+static void
+hf_net_device_set_link_up (HalDevice *device, gboolean is_up)
+{
+  g_return_if_fail(HAL_IS_DEVICE(device));
+
+  hal_device_property_set_bool(device, "net.interface_up", is_up);
+  if (hal_device_has_capability(device, "net.80203"))
+    hal_device_property_set_bool(device, "net.80203.link", is_up);
+}
+
+static HalDevice *
+hf_net_device_new (const char *interface, HalDevice *parent, GError **err)
+{
+  char *output;
+  char **lines;
+  int i;
+  GError *tmp_err = NULL;
+  const char *mac = NULL;
+  const char *media = NULL;
+  gboolean is_ethernet = FALSE;
+  gboolean is_wireless = FALSE;
+  gboolean is_tokenring = FALSE;
+  HalDevice *device = NULL;
+
+  g_return_val_if_fail(interface != NULL, NULL);
+  g_return_val_if_fail(HAL_IS_DEVICE(parent), NULL);
+
+  output = hf_run(&tmp_err, "/sbin/ifconfig %s", interface);
+  if (! output)
+    {
+      g_set_error(err, 0, 0, "ifconfig failure: %s", tmp_err->message);
+      g_error_free(tmp_err);
+      return NULL;
+    }
+
+  lines = g_strsplit(output, "\n", 0);
+  g_free(output);
+
+  for (i = 0; lines[i]; i++)
+    {
+      if (g_str_has_prefix(lines[i], "\tether "))
+	mac = lines[i] + 7;
+      if (g_str_has_prefix(lines[i], "\tmedia: "))
+	media = lines[i] + 8;
+      if (g_str_has_prefix(lines[i], "\tmedia: Ethernet"))
+	is_ethernet = TRUE;
+      else if (g_str_has_prefix(lines[i], "\tmedia: IEEE 802.11 Wireless Ethernet"))
+        {
+          is_ethernet = TRUE;
+          is_wireless = TRUE;
+        }
+      else if (g_str_has_prefix(lines[i], "\tmedia: Token ring"))
+        is_tokenring = TRUE;
+    }
+
+  device = hf_device_new(parent);
+
+  hf_device_set_udi(device, "net_%s", mac ? mac : hal_util_get_last_element(hal_device_get_udi(parent)));
+  hal_device_property_set_string(device, "info.product", "Networking Interface");
+
+  hal_device_add_capability(device, "net");
+  hal_device_property_set_string(device, "net.address", mac ? mac : "00:00:00:00:00:00");
+  hal_device_property_set_string(device, "net.interface", interface);
+  hal_device_property_set_string(device, "net.physical_device", hal_device_get_udi(parent));
+  hal_device_property_set_string(device, "net.media", media);
+  if (hf_devtree_is_driver(interface, "fwe"))
+    hal_device_property_set_int(device, "net.arp_proto_hw_id", ARPHRD_IEEE1394);
+  else if (is_ethernet)
+    hal_device_property_set_int(device, "net.arp_proto_hw_id", ARPHRD_ETHER);
+  else if (is_tokenring)
+    hal_device_property_set_int(device, "net.arp_proto_hw_id", ARPHRD_IEEE802);
+  /* FIXME Add additional net.arp_proto_hw_id support */
+
+  if (is_ethernet)
+    {
+      dbus_uint64_t numeric_mac = 0;
+      unsigned int a5, a4, a3, a2, a1, a0;
+
+      if (mac && sscanf(mac, "%x:%x:%x:%x:%x:%x", &a5, &a4, &a3, &a2, &a1, &a0) == 6)
+	numeric_mac =
+	  ((dbus_uint64_t) a5 << 40) |
+	  ((dbus_uint64_t) a4 << 32) |
+	  ((dbus_uint64_t) a3 << 24) |
+	  ((dbus_uint64_t) a2 << 16) |
+	  ((dbus_uint64_t) a1 << 8) |
+	  ((dbus_uint64_t) a0 << 0);
+
+      if (is_wireless)
+        {
+          hal_device_property_set_string(device, "info.product", "WLAN Networking Interface");
+          hal_device_add_capability(device, "net.80211");
+          hal_device_property_set_string(device, "info.category", "net.80211");
+          hal_device_property_set_uint64(device, "net.80211.mac_address", numeric_mac);
+        }
+      else
+        {
+          hal_device_add_capability(device, "net.80203");
+          hal_device_property_set_string(device, "info.category", "net.80203");
+          hal_device_property_set_uint64(device, "net.80203.mac_address", numeric_mac);
+        }
+    }
+  else
+    hal_device_property_set_string(device, "info.category", "net");
+
+  g_strfreev(lines);
+
+  hf_net_device_set_link_up(device, hf_net_get_link_up(interface));
+
+  return device;
+}
+
+static gboolean
+hf_net_update_timeout_cb (gpointer data)
+{
+  GSList *l;
+
+  if (hf_is_waiting)
+    return TRUE;
+
+  HF_LIST_FOREACH(l, hald_get_gdl()->devices)
+    {
+      HalDevice *device = l->data;
+      const char *interface;
+
+      interface = hal_device_property_get_string(device, "net.interface");
+      if (interface)
+	{
+	  device_property_atomic_update_begin();
+	  hf_net_device_set_link_up(device, hf_net_get_link_up(interface));
+	  device_property_atomic_update_end();
+	}
+    }
+
+  return TRUE;
+}
+
+static void
+hf_net_init (void)
+{
+  g_timeout_add(3000, hf_net_update_timeout_cb, NULL);
+}
+
+static void
+hf_net_probe (void)
+{
+  GError *err = NULL;
+  char *output;
+  char *terminator;
+  char **interfaces;
+  int i;
+
+  output = hf_run(&err, "/sbin/ifconfig -l");
+  if (! output)
+    {
+      HAL_WARNING(("ifconfig failure: %s", err->message));
+      g_error_free(err);
+      return;
+    }
+
+  terminator = strrchr(output, '\n');
+  if (terminator)
+    *terminator = 0;
+
+  interfaces = g_strsplit(output, " ", 0);
+  g_free(output);
+
+  for (i = 0; interfaces[i]; i++)
+    if (! hal_device_store_match_key_value_string(hald_get_gdl(), "net.interface", interfaces[i]))
+      {
+	HalDevice *parent;
+
+	parent = hf_devtree_find_from_name(hald_get_gdl(), interfaces[i]);
+	if (parent && ! hal_device_property_get_bool(parent, "info.ignore"))
+	  {
+	    HalDevice *device;
+
+	    device = hf_net_device_new(interfaces[i], parent, &err);
+	    if (device)
+	      hf_device_preprobe_and_add(device);
+	    else
+	      {
+		HAL_WARNING(("unable to handle network interface %s: %s", interfaces[i], err->message));
+		g_clear_error(&err);
+	      }
+	  }
+      }
+  g_strfreev(interfaces);
+}
+
+static gboolean
+hf_net_devd_add (const char *name,
+		 GHashTable *params,
+		 GHashTable *at,
+		 const char *parent)
+{
+  int s;
+  gboolean consumed = FALSE;
+
+  /* Adapted code from devd.cc to find out if this is a network
+   * interface. */
+
+  s = socket(PF_INET, SOCK_DGRAM, 0);
+  if (s >= 0)
+    {
+      struct ifmediareq ifmr;
+
+      memset(&ifmr, 0, sizeof(ifmr));
+      strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name));
+
+      if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) >= 0
+	  && (ifmr.ifm_status & IFM_AVALID) != 0)
+	{
+	  HAL_INFO(("found new network interface: %s", name));
+	  hf_net_probe();
+	  consumed = TRUE;
+	}
+      close(s);
+    }
+
+  return consumed;
+}
+
+static gboolean
+hf_net_devd_remove (const char *name,
+		    GHashTable *params,
+		    GHashTable *at,
+		    const char *parent)
+{
+  HalDevice *device;
+
+  /*
+   * If a network driver was detached, do not let hf-devd remove the
+   * physical device, just remove the interface device.
+   */
+  device = hal_device_store_match_key_value_string(hald_get_gdl(), "net.interface", name);
+  if (device)
+    {
+      hf_device_remove_tree(device);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+hf_net_devd_notify (const char *system,
+		    const char *subsystem,
+		    const char *type,
+		    const char *data)
+{
+  HalDevice *device;
+
+  if (strcmp(system, "IFNET"))
+    return FALSE;
+
+  device = hal_device_store_match_key_value_string(hald_get_gdl(), "net.interface", subsystem);
+  if (device)
+    {
+      gboolean is_up;
+
+      if (! strcmp(type, "LINK_UP"))
+	is_up = TRUE;
+      else if (! strcmp(type, "LINK_DOWN"))
+	is_up = FALSE;
+      else
+	return TRUE;
+
+      device_property_atomic_update_begin();
+      hf_net_device_set_link_up(device, is_up);
+      device_property_atomic_update_end();
+    }
+
+  return TRUE;
+}
+
+HFHandler hf_net_handler = {
+  .init =	hf_net_init,
+  .probe =	hf_net_probe
+};
+
+HFDevdHandler hf_net_devd_handler = {
+  .add =	hf_net_devd_add,
+  .remove =	hf_net_devd_remove,
+  .notify =	hf_net_devd_notify
+};
diff --git a/hald/freebsd/hf-net.h b/hald/freebsd/hf-net.h
new file mode 100644
index 0000000..9b094e3
--- /dev/null
+++ b/hald/freebsd/hf-net.h
@@ -0,0 +1,37 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-net.h : networking device support
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifndef _HF_NET_H
+#define _HF_NET_H
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "hf-osspec.h"
+#include "hf-devd.h"
+
+extern HFHandler hf_net_handler;
+extern HFDevdHandler hf_net_devd_handler;
+
+#endif /* _HF_NET_H */
diff --git a/hald/freebsd/hf-osspec.h b/hald/freebsd/hf-osspec.h
new file mode 100644
index 0000000..ffb2d91
--- /dev/null
+++ b/hald/freebsd/hf-osspec.h
@@ -0,0 +1,42 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-osspec.h : HAL backend for FreeBSD
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifndef _HF_OSSPEC_H
+#define _HF_OSSPEC_H
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "../hald.h"
+
+typedef struct
+{
+  void		(*privileged_init)	(void);
+  void		(*init)			(void);
+  void		(*probe)		(void);
+  gboolean	(*device_rescan)	(HalDevice *device);
+  gboolean	(*device_reprobe)	(HalDevice *device);
+} HFHandler;
+
+#endif /* _HF_OSSPEC_H */
diff --git a/hald/freebsd/hf-pci.c b/hald/freebsd/hf-pci.c
new file mode 100644
index 0000000..d721819
--- /dev/null
+++ b/hald/freebsd/hf-pci.c
@@ -0,0 +1,282 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-pci.c : enumerate PCI devices
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/pciio.h>
+#include <glib.h>
+
+#include "../hald.h"
+#include "../ids.h"
+#include "../logger.h"
+#include "../util.h"
+
+#include "hf-pci.h"
+#include "hf-devtree.h"
+#include "hf-util.h"
+
+#define HF_PCI_DEVICE			"/dev/pci"
+
+/* from sys/dev/pci/pcireg.h */
+#define PCIR_SECBUS_1			0x19
+
+typedef struct
+{
+  struct pci_conf	p;
+  int			secondary_bus;
+} DeviceInfo;
+
+static int hf_pci_fd;
+
+static int
+hf_pci_get_register (const struct pci_conf *p, int reg)
+{
+  struct pci_io io;
+
+  g_return_val_if_fail(p != NULL, 0);
+
+  memset(&io, 0, sizeof(io));
+  io.pi_sel = p->pc_sel;
+  io.pi_reg = reg;
+  io.pi_width = 1;
+
+  if (ioctl(hf_pci_fd, PCIOCREAD, &io) < 0)
+    {
+      HAL_WARNING(("unable to read register %.2x of PCI device %i:%i:%i", reg, p->pc_sel.pc_bus, p->pc_sel.pc_dev, p->pc_sel.pc_func));
+      return 0;
+    }
+
+  return io.pi_data;
+}
+
+static HalDevice *
+hf_pci_device_new (HalDevice *parent, const struct pci_conf *p, int secondary_bus)
+{
+  HalDevice *device;
+  char *vendor;
+  char *product;
+  char *subsys_vendor;
+  char *subsys_product;
+
+  g_return_val_if_fail(p != NULL, NULL);
+
+  device = hf_device_new(parent);
+
+  hf_device_set_udi(device, "pci_%.4x_%.4x", p->pc_vendor, p->pc_device);
+  hal_device_property_set_string(device, "info.bus", "pci");
+  hal_device_property_set_int(device, "pci.device_class", p->pc_class);
+  hal_device_property_set_int(device, "pci.device_subclass", p->pc_subclass);
+  hal_device_property_set_int(device, "pci.device_protocol", p->pc_progif);
+  hal_device_property_set_int(device, "pci.product_id", p->pc_device);
+  hal_device_property_set_int(device, "pci.vendor_id", p->pc_vendor);
+  hal_device_property_set_int(device, "pci.subsys_product_id", p->pc_subdevice);
+  hal_device_property_set_int(device, "pci.subsys_vendor_id", p->pc_subvendor);
+
+  if (p->pd_name && *p->pd_name)
+    hf_devtree_device_set_info(device, p->pd_name, p->pd_unit);
+
+  hal_device_property_set_int(device, "pci.freebsd.bus", p->pc_sel.pc_bus);
+  hal_device_property_set_int(device, "pci.freebsd.device", p->pc_sel.pc_dev);
+  hal_device_property_set_int(device, "pci.freebsd.function", p->pc_sel.pc_func);
+  hal_device_property_set_int(device, "pci.freebsd.secondary_bus", secondary_bus);
+
+  ids_find_pci(p->pc_vendor, p->pc_device,
+	       p->pc_subvendor, p->pc_subdevice,
+	       &vendor, &product,
+	       &subsys_vendor, &subsys_product);
+
+  if (vendor)
+    {
+      hal_device_property_set_string(device, "info.vendor", vendor);
+      hal_device_property_set_string(device, "pci.vendor", vendor);
+    }
+  if (product)
+    {
+      hal_device_property_set_string(device, "info.product", product);
+      hal_device_property_set_string(device, "pci.product", product);
+    }
+  if (subsys_vendor)
+    hal_device_property_set_string(device, "pci.subsys_vendor", subsys_vendor);
+  if (subsys_product)
+    hal_device_property_set_string(device, "pci.subsys_product", subsys_product);
+
+  return device;
+}
+
+static void
+hf_pci_privileged_init (void)
+{
+  hf_pci_fd = open(HF_PCI_DEVICE, O_RDWR);
+  if (hf_pci_fd < 0)
+    HAL_INFO(("unable to open %s: %s", HF_PCI_DEVICE, g_strerror(errno)));
+}
+
+static GSList *
+hf_pci_lookup (GSList *devices, int bus)
+{
+  if (bus != 0)
+    {
+      GSList *l;
+
+      HF_LIST_FOREACH(l, devices)
+	{
+	  DeviceInfo *info = l->data;
+
+	  if (info->secondary_bus == bus)
+	    return l;
+	}
+    }
+
+  return NULL;
+}
+
+static GSList *
+hf_pci_get_root (GSList *devices)
+{
+  GSList *root;
+  DeviceInfo *info;
+  int bus;
+
+  g_return_val_if_fail(devices != NULL, NULL);
+
+  root = devices;
+  info = root->data;
+
+  bus = info->p.pc_sel.pc_bus;
+  while (bus != -1)
+    {
+      GSList *new_root;
+
+      new_root = hf_pci_lookup(devices, bus);
+      bus = -1;
+
+      if (new_root)
+	{
+	  root = new_root;
+	  info = root->data;
+	  bus = info->p.pc_sel.pc_bus;
+	}
+    }
+
+  return root;
+}
+
+static void
+hf_pci_probe (void)
+{
+  struct pci_conf_io pc;
+  struct pci_conf conf[255];
+  struct pci_conf *p;
+  GSList *devices = NULL;
+
+  if (hf_pci_fd < 0)
+    return;
+
+ start:
+  memset(&pc, 0, sizeof(pc));
+  pc.match_buf_len = sizeof(conf);
+  pc.matches = conf;
+
+  /* build a list of PCI devices */
+
+  do
+    {
+      if (ioctl(hf_pci_fd, PCIOCGETCONF, &pc) < 0)
+	{
+	  HAL_WARNING(("ioctl PCIOCGETCONF: %s", g_strerror(errno)));
+	  break;
+	}
+
+      if (pc.status == PCI_GETCONF_LIST_CHANGED)
+	{
+	  g_slist_foreach(devices, (GFunc) g_free, NULL);
+	  g_slist_free(devices);
+	  devices = NULL;
+	  goto start;
+	}
+      else if (pc.status == PCI_GETCONF_ERROR)
+	{
+	  HAL_WARNING(("PCI_GETCONF_ERROR"));
+	  break;
+	}
+
+      for (p = conf; p < &conf[pc.num_matches]; p++)
+	  if (! hf_device_store_match(hald_get_gdl(),
+                                      "pci.freebsd.bus", HAL_PROPERTY_TYPE_INT32, p->pc_sel.pc_bus,
+				      "pci.freebsd.device", HAL_PROPERTY_TYPE_INT32, p->pc_sel.pc_dev,
+				      "pci.freebsd.function", HAL_PROPERTY_TYPE_INT32, p->pc_sel.pc_func,
+				      NULL))
+	    {
+	      DeviceInfo *info;
+
+	      info = g_new(DeviceInfo, 1);
+	      info->p = *p;
+	      info->secondary_bus = hf_pci_get_register(p, PCIR_SECBUS_1);
+
+	      devices = g_slist_prepend(devices, info);
+	    }
+    }
+  while (pc.status == PCI_GETCONF_MORE_DEVS);
+
+  /* add the devices (parents first) */
+
+  while (devices)
+    {
+      GSList *root;
+      DeviceInfo *info;
+      HalDevice *parent = NULL;
+
+      root = hf_pci_get_root(devices);
+      g_assert(root != NULL);
+
+      info = root->data;
+
+      if (info->p.pc_sel.pc_bus != 0)
+	parent = hal_device_store_match_key_value_int(hald_get_gdl(), "pci.freebsd.secondary_bus", info->p.pc_sel.pc_bus);
+
+      if (! parent || ! hal_device_property_get_bool(parent, "info.ignore"))
+	{
+	  HalDevice *device;
+
+	  device = hf_pci_device_new(parent, &info->p, info->secondary_bus);
+	  hf_device_preprobe_and_add(device);
+	}
+
+      devices = g_slist_delete_link(devices, root);
+      g_free(info);
+    }
+}
+
+HFHandler hf_pci_handler = {
+  .privileged_init	= hf_pci_privileged_init,
+  .probe		= hf_pci_probe
+};
diff --git a/hald/freebsd/hf-pci.h b/hald/freebsd/hf-pci.h
new file mode 100644
index 0000000..49c648c
--- /dev/null
+++ b/hald/freebsd/hf-pci.h
@@ -0,0 +1,35 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-pci.h : enumerate PCI devices
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifndef _HF_PCI_H
+#define _HF_PCI_H
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "hf-osspec.h"
+
+extern HFHandler hf_pci_handler;
+
+#endif /* _HF_PCI_H */
diff --git a/hald/freebsd/hf-pcmcia.c b/hald/freebsd/hf-pcmcia.c
new file mode 100644
index 0000000..c98642e
--- /dev/null
+++ b/hald/freebsd/hf-pcmcia.c
@@ -0,0 +1,210 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-pcmcia.c : PCMCIA processing functions
+ *
+ * Copyright (C) 2006 Joe Marcus Clarke <marcus at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include "../hald_dbus.h"
+#include "../logger.h"
+
+#include "hf-pcmcia.h"
+#include "hf-devtree.h"
+#include "hf-util.h"
+
+static void
+hf_pcmcia_set_oem_info (HalDevice *device,
+				const char *info,
+				const char *info_property,
+				const char *id_property)
+{
+  g_return_if_fail(HAL_IS_DEVICE(device));
+  g_return_if_fail(info_property != NULL);
+  g_return_if_fail(id_property != NULL);
+
+  if (info)
+    hal_device_property_set_string(device, info_property, info);
+  else
+    {
+      int id;
+
+      id = hal_device_property_get_int(device, id_property);
+      if (id > -1)
+	hf_device_property_set_string_printf(device, info_property, "Unknown (0x%04x)", id);
+    }
+}
+
+static void
+hf_pcmcia_update_cis (HalDevice *device)
+{
+  char *devname;
+  char *output;
+  char *vendor = NULL, *product = NULL;
+  char **lines;
+  const char *command;
+  int i;
+
+  devname = hf_devtree_device_get_name(device);
+  if (g_file_test("/usr/sbin/dumpcis", G_FILE_TEST_IS_EXECUTABLE))
+    {
+      command = "/usr/sbin/dumpcis";
+    }
+  else
+    {
+      command = "/usr/sbin/pccardc dumpcisfile";
+    }
+  output = hf_run(NULL, "%s /dev/%s.cis", command, devname);
+  g_free(devname);
+
+  if (! output)
+    /* The CIS device may not exist, or the slot might be empty. */
+    return;
+
+  lines = g_strsplit(output, "\n", 0);
+  g_free(output);
+
+  for (i = 0; lines[i]; i++)
+    {
+      if (g_str_has_prefix(lines[i], "\tPCMCIA ID ="))
+        {
+          int manuf_id, card_id;
+
+	  if (sscanf(lines[i], "\tPCMCIA ID = 0x%x, OEM ID = 0x%x", &manuf_id,
+              &card_id))
+            {
+              hal_device_property_set_int(device, "pcmcia.manf_id", manuf_id);
+	      hal_device_property_set_int(device, "pcmcia.card_id", card_id);
+	    }
+	}
+	if (strstr(lines[i], "Functional ID"))
+          {
+            if (lines[i + 1])
+              {
+                int func_id;
+
+                if (sscanf(lines[i + 1], "   000:  %x", &func_id))
+                  hal_device_property_set_int(device, "pcmcia.func_id",
+                                              func_id);
+	      }
+	  }
+	if (g_str_has_prefix(lines[i], "\tVersion ="))
+          {
+            char **toks;
+
+	    toks = g_strsplit_set(lines[i], "[]", 0);
+	    if (g_strv_length(toks) >= 2)
+	      vendor = g_strdup(toks[1]);
+	    g_strfreev(toks);
+	  }
+        if (g_str_has_prefix(lines[i], "\tAddit. info ="))
+          {
+            char **toks;
+
+            toks = g_strsplit_set(lines[i], "[]", 0);
+            if (g_strv_length(toks) >= 2)
+	      product = g_strdup(toks[1]);
+            g_strfreev(toks);
+          }
+    }
+  g_strfreev(lines);
+
+  hf_pcmcia_set_oem_info(device, vendor, "info.vendor", "pcmcia.manf_id");
+  g_free(vendor);
+
+  hf_pcmcia_set_oem_info(device, product, "info.product", "pcmcia.card_id");
+  g_free(product);
+}
+
+void
+hf_pcmcia_set_properties (HalDevice *device)
+{
+
+  hal_device_property_set_string(device, "info.bus", "pcmcia");
+  hal_device_add_capability(device, "pcmcia_socket");
+  hal_device_property_set_string(device, "info.category", "pcmcia_socket");
+  hal_device_property_set_int(device, "pcmcia_socket.number",
+    hal_device_property_get_int(device, "freebsd.unit"));
+  hf_pcmcia_update_cis(device);
+}
+
+static gboolean
+hf_pcmcia_devd_add (const char *name,
+		    GHashTable *params,
+		    GHashTable *at,
+		    const char *parent)
+{
+  HalDevice *device;
+
+  if (! parent)
+    return FALSE;
+  if (! hf_devtree_is_driver(parent, "pccard") && ! hf_devtree_is_driver(parent, "cardbus"))
+    return FALSE;
+
+  device = hf_devtree_find_from_name(hald_get_gdl(), parent);
+  if (device)
+    {
+      if (name)
+        HAL_INFO(("found new PC Card %s on %s", name, parent));
+      else
+        HAL_INFO(("updating PC Card information for %s", parent));
+
+      device_property_atomic_update_begin();
+      hf_pcmcia_update_cis(device);
+      device_property_atomic_update_end();
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+hf_pcmcia_devd_nomatch (GHashTable *at,
+                        const char *parent)
+{
+  return hf_pcmcia_devd_add(NULL, NULL, NULL, parent);
+}
+
+static gboolean
+hf_pcmcia_devd_remove (const char *name,
+                       GHashTable *params,
+		       GHashTable *at,
+		       const char *parent)
+{
+  if (! parent)
+    return FALSE;
+  if (! hf_devtree_is_driver(parent, "pccard") && ! hf_devtree_is_driver(parent, "cardbus"))
+    return FALSE;
+
+  HAL_INFO(("detected removal of PC Card %s", name));
+  return hf_pcmcia_devd_add(NULL, NULL, NULL, parent);
+}
+
+HFDevdHandler hf_pcmcia_devd_handler = {
+  .add =	hf_pcmcia_devd_add,
+  .remove =	hf_pcmcia_devd_remove,
+  .nomatch =	hf_pcmcia_devd_nomatch
+};
diff --git a/hald/freebsd/hf-pcmcia.h b/hald/freebsd/hf-pcmcia.h
new file mode 100644
index 0000000..0b5ed30
--- /dev/null
+++ b/hald/freebsd/hf-pcmcia.h
@@ -0,0 +1,39 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-pci.h : PCMCIA processing functions
+ *
+ * Copyright (C) 2006 Joe Marcus Clarke <marcus at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifndef _HF_PCMCIA_H
+#define _HF_PCMCIA_H
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "../hald.h"
+
+#include "hf-devd.h"
+
+extern HFDevdHandler hf_pcmcia_devd_handler;
+
+void hf_pcmcia_set_properties (HalDevice *device);
+
+#endif /* _HF_PCMCIA_H */
diff --git a/hald/freebsd/hf-scsi.c b/hald/freebsd/hf-scsi.c
new file mode 100644
index 0000000..e937daf
--- /dev/null
+++ b/hald/freebsd/hf-scsi.c
@@ -0,0 +1,545 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-scsi.c : SCSI support
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <stdio.h>
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/scsi/scsi_all.h>
+#include <cam/scsi/scsi_pass.h>
+
+#include "../logger.h"
+
+#include "hf-scsi.h"
+#include "hf-block.h"
+#include "hf-devtree.h"
+#include "hf-storage.h"
+#include "hf-util.h"
+
+#define HF_SCSI_DEVICE			"/dev/xpt0"
+
+#define N_RESULTS			100
+
+static int hf_scsi_fd;
+
+static gboolean
+hf_scsi_is_cdrom (int type)
+{
+  return (type == T_CDROM || type == T_WORM || type == T_CHANGER || type == T_OPTICAL);
+}
+
+static HalDevice *
+hf_scsi_bus_device_new (HalDevice *parent,
+			const struct bus_match_result *match)
+{
+  HalDevice *device;
+
+  g_return_val_if_fail(match != NULL, NULL);
+
+  device = hf_device_new(parent);
+
+  hal_device_property_set_string(device, "info.bus", "scsi_host");
+  hal_device_property_set_int(device, "scsi_host.host", match->path_id);
+  hal_device_property_set_string(device, "info.product", "SCSI Host Adapter");
+
+  /* scsi_host_compute_udi() in linux2/classdev.c */
+  hf_device_set_full_udi(device, "%s_scsi_host", hal_device_property_get_string(device, "info.parent"));
+
+  return device;
+}
+
+static HalDevice *
+hf_scsi_scsi_device_new (HalDevice *parent,
+			 const struct device_match_result *match)
+{
+  HalDevice *device;
+  /* buffer sizes from camcontrol.c */
+  char vendor[16];
+  char product[48];
+  int type;
+
+  g_return_val_if_fail(match != NULL, NULL);
+
+  device = hf_device_new(parent);
+
+  hal_device_property_set_string(device, "info.bus", "scsi");
+  hal_device_property_set_int(device, "scsi.host", match->path_id);
+  hal_device_property_set_int(device, "scsi.bus", match->path_id);
+  hal_device_property_set_int(device, "scsi.target", match->target_id);
+  hal_device_property_set_int(device, "scsi.lun", match->target_lun);
+  hal_device_property_set_string(device, "info.product", "SCSI Device");
+
+  cam_strvis(vendor, match->inq_data.vendor, sizeof(match->inq_data.vendor), sizeof(vendor));
+  cam_strvis(product, match->inq_data.product, sizeof(match->inq_data.product), sizeof(product));
+
+  if (*vendor)
+    {
+      hal_device_property_set_string(device, "info.vendor", vendor);
+      hal_device_property_set_string(device, "scsi.vendor", vendor);
+    }
+  if (*product)
+    hal_device_property_set_string(device, "scsi.model", product);
+
+  /* types from cam/scsi/scsi_all.h */
+  type = SID_TYPE(&match->inq_data);
+  switch (type)
+    {
+    case T_DIRECT:
+    case T_RBC:
+      /* From linux2/physdev.c, T_RBC is a Reduced Block Command device
+       * which is sometimes used by firewire devices */
+      hal_device_property_set_string(device, "scsi.type", "disk");
+      break;
+    case T_SEQUENTIAL:
+      hal_device_property_set_string(device, "scsi.type", "tape");
+      break;
+    case T_PRINTER:
+      hal_device_property_set_string(device, "scsi.type", "printer");
+      break;
+    case T_PROCESSOR:
+      hal_device_property_set_string(device, "scsi.type", "processor");
+      break;
+    case T_WORM:
+    case T_CHANGER:
+    case T_CDROM:
+    case T_OPTICAL:
+      hal_device_property_set_string(device, "scsi.type", "cdrom");
+      break;
+    case T_SCANNER:
+      hal_device_property_set_string(device, "scsi.type", "scanner");
+      break;
+    case T_STORARRAY:
+      hal_device_property_set_string(device, "scsi.type", "array");
+      break;
+    default:
+      hal_device_property_set_string(device, "scsi.type", "unknown");
+    }
+
+  /* scsi_compute_udi() in linux2/physdev.c */
+  hf_device_set_full_udi(device, "%s_scsi_device_lun%i",
+			 hal_device_property_get_string(device, "info.parent"),
+			 hal_device_property_get_int(device, "scsi.lun"));
+
+  return device;
+}
+
+static HalDevice *
+hf_scsi_block_device_new (HalDevice *parent,
+			  const struct device_match_result *match,
+			  const char *devname)
+{
+  HalDevice *device;
+  int type;
+  /* buffer size from camcontrol.c */
+  char revision[16];
+
+  g_return_val_if_fail(HAL_IS_DEVICE(parent), NULL);
+  g_return_val_if_fail(match != NULL, NULL);
+  g_return_val_if_fail(devname != NULL, NULL);
+
+  device = hf_device_new(parent);
+
+  hf_devtree_device_set_name(device, devname);
+  hf_block_device_enable(device, devname);
+
+  hf_storage_device_enable(device);
+
+  type = SID_TYPE(&match->inq_data);
+
+  if (type == T_SEQUENTIAL)
+    hf_storage_device_enable_tape(device);
+  else if (hf_scsi_is_cdrom(type))
+    hf_storage_device_enable_cdrom(device);
+
+  if (SID_IS_REMOVABLE(&match->inq_data))
+    {
+      hal_device_property_set_bool(device, "storage.removable", TRUE);
+      hal_device_property_set_bool(device, "storage.media_check_enabled", TRUE);
+    }
+
+  cam_strvis(revision, match->inq_data.revision, sizeof(match->inq_data.revision), sizeof(revision));
+
+  if (hal_device_has_property(parent, "scsi.vendor"))
+    {
+      hal_device_copy_property(parent, "scsi.vendor", device, "info.vendor");
+      hal_device_copy_property(parent, "scsi.vendor", device, "storage.vendor");
+    }
+  if (hal_device_has_property(parent, "scsi.model"))
+    {
+      hal_device_copy_property(parent, "scsi.model", device, "info.product");
+      hal_device_copy_property(parent, "scsi.model", device, "storage.model");
+    }
+  if (*revision)
+    hal_device_property_set_string(device, "storage.firmware_revision", revision);
+
+  /*
+   * Walk up the device chain to find the physical device (adapted
+   * from hotplug_event_begin_add_blockdev() in linux2/blockdev.c).
+   */
+  while (parent)
+    {
+      const char *bus;
+      const char *parent_udi;
+
+      bus = hal_device_property_get_string(parent, "info.bus");
+      if (bus)
+	{
+	  if (! strcmp(bus, "scsi"))
+	    {
+	      hal_device_property_set_string(device, "storage.bus", "scsi");
+	      hal_device_property_set_string(device, "storage.physical_device", hal_device_get_udi(parent));
+	      hal_device_copy_property(parent, "scsi.lun", device, "storage.lun");
+	      /* do not stop here, in case it's an umass device */
+	    }
+	  else if (! strcmp(bus, "usb"))
+	    {
+	      hal_device_property_set_string(device, "storage.bus", "usb");
+	      hal_device_property_set_string(device, "storage.physical_device", hal_device_get_udi(parent));
+	      hal_device_property_set_bool(device, "storage.hotpluggable", TRUE);
+	      break;		/* done */
+	    }
+	}
+
+      parent_udi = hal_device_property_get_string(parent, "info.parent");
+      if (parent_udi)
+	{
+	  parent = hal_device_store_find(hald_get_gdl(), parent_udi);
+	  g_assert(parent != NULL);
+	}
+      else
+	parent = NULL;
+    }
+
+  return device;
+}
+
+static void
+hf_scsi_privileged_init (void)
+{
+  hf_scsi_fd = open(HF_SCSI_DEVICE, O_RDWR);
+  if (hf_scsi_fd < 0)
+    HAL_INFO(("unable to open %s: %s", HF_SCSI_DEVICE, g_strerror(errno)));
+}
+
+static HalDevice *
+hf_scsi_get_ata_channel (HalDevice *scsi_bus)
+{
+  HalDevice *parent;
+
+  g_return_val_if_fail(HAL_IS_DEVICE(scsi_bus), NULL);
+
+  parent = hf_device_store_get_parent(hald_get_gdl(), scsi_bus);
+  if (parent)
+    {
+      const char *driver;
+
+      driver = hal_device_property_get_string(parent, "freebsd.driver");
+      if (! driver || strcmp(driver, "ata"))
+	parent = NULL;		/* parent is not an ATA channel */
+    }
+
+  return parent;
+}
+
+static HalDevice *
+hf_scsi_get_atapi_device (HalDevice *ata_channel, int lun)
+{
+  HalDevice *device = NULL;
+  GSList *children;
+  GSList *l;
+
+  g_return_val_if_fail(HAL_IS_DEVICE(ata_channel), NULL);
+  g_return_val_if_fail(lun == 0 || lun == 1, NULL); /* ATA master or slave*/
+
+  children = hf_device_store_get_children(hald_get_gdl(), ata_channel);
+  HF_LIST_FOREACH(l, children)
+    {
+      HalDevice *child = l->data;
+      const char *driver;
+
+      driver = hal_device_property_get_string(child, "freebsd.driver");
+      /* ATAPI devices: CD-ROM (acd), tape (ast) or floppy (afd) */
+      if (driver && (! strcmp(driver, "acd") || ! strcmp(driver, "ast") || ! strcmp(driver, "afd")))
+	{
+	  device = child;
+	  if (lun == 0)
+	    break;		/* we wanted the first device, done */
+	}
+    }
+  g_slist_free(children);
+
+  return device;
+}
+
+static void
+hf_scsi_handle_pending_device (struct device_match_result **match,
+			       char **devname)
+{
+  g_return_if_fail(match != NULL);
+  g_return_if_fail(devname != NULL);
+
+  if (*match && *devname)
+    {
+      HalDevice *parent;
+
+      parent = hal_device_store_match_key_value_int(hald_get_gdl(), "scsi_host.host", (*match)->path_id);
+      if (! parent || ! hal_device_property_get_bool(parent, "info.ignore"))
+	{
+	  HalDevice *scsi_device;
+	  gboolean ignore = FALSE;
+	  int type;
+
+	  /*
+	   * If the device's parent (the SCSI bus) is an atapicam(4)
+	   * bus, we shall ignore the device unless its corresponding
+	   * ATAPI device was ignored. This is to ensure that the same
+	   * physical device will only be handled once (through ATA or
+	   * SCSI).
+	   */
+	  if (parent)
+	    {
+	      HalDevice *ata_channel;
+
+	      ata_channel = hf_scsi_get_ata_channel(parent);
+	      if (ata_channel)
+		{
+		  HalDevice *atapi_device;
+
+		  atapi_device = hf_scsi_get_atapi_device(ata_channel, (*match)->target_lun);
+		  if (atapi_device)
+                    {
+		      char *cam_devname;
+		      char *scsi_path;
+
+		      cam_devname = g_strdup_printf("/dev/%s", *devname);
+		      scsi_path = g_strdup_printf("%d,%d,%d", (*match)->path_id, (*match)->target_id, (*match)->target_lun);
+		      hal_device_property_set_string(atapi_device, "block.freebsd.atapi_cam_device", cam_devname);
+		      hal_device_property_set_string(atapi_device, "block.freebsd.cam_path", scsi_path);
+		      g_free(cam_devname);
+		      g_free(scsi_path);
+
+		      if (! hal_device_property_get_bool(atapi_device, "info.ignore"))
+		        ignore = TRUE; /* ATAPI device not ignored, so ignore this one */
+		    }
+		}
+	    }
+
+	  type = SID_TYPE(&(*match)->inq_data);
+	  scsi_device = hf_scsi_scsi_device_new(parent, *match);
+	  if (hf_device_preprobe_and_add(scsi_device) && (type == T_DIRECT ||
+              type == T_SEQUENTIAL || type == T_RBC || type == T_STORARRAY ||
+	      hf_scsi_is_cdrom(type)))
+	    {
+	      HalDevice *block_device;
+
+	      block_device = hf_scsi_block_device_new(scsi_device, *match, *devname);
+	      if (ignore)
+                {
+		  hal_device_property_set_bool(block_device, "info.ignore", TRUE);
+		}
+	      else
+                {
+                  char *scsi_path;
+
+		  scsi_path = g_strdup_printf("%d,%d,%d", (*match)->path_id, (*match)->target_id, (*match)->target_lun);
+                  hal_device_property_set_string(block_device, "block.freebsd.cam_path", scsi_path);
+		  g_free(scsi_path);
+		}
+	      if (hf_device_preprobe(block_device))
+		{
+		  hf_runner_run_sync(block_device, 0, "hald-probe-scsi", NULL);
+
+		  /*
+		   * hald-probe-scsi might have set storage.serial,
+		   * which is used in the UDI computed in
+		   * hf_block_device_complete().
+		   */
+		  hf_block_device_complete(block_device, block_device, FALSE);
+
+		  hf_storage_device_probe(block_device, FALSE);
+		  hf_device_add(block_device);
+		}
+	    }
+	}
+    }
+
+  g_free(*match);
+  *match = NULL;
+
+  g_free(*devname);
+  *devname = NULL;
+}
+
+/* inspired by getdevtree() in sbin/camcontrol/camcontrol.c */
+static void
+hf_scsi_probe (void)
+{
+  union ccb ccb;
+  struct device_match_result *pending_device = NULL;
+  char *pending_devname = NULL;
+
+  if (hf_scsi_fd < 0)
+    return;			/* already warned in hf_scsi_init() */
+
+  memset(&ccb, 0, sizeof(ccb));
+
+  ccb.ccb_h.path_id = CAM_XPT_PATH_ID;
+  ccb.ccb_h.target_id = CAM_TARGET_WILDCARD;
+  ccb.ccb_h.target_lun = CAM_LUN_WILDCARD;
+
+  ccb.ccb_h.func_code = XPT_DEV_MATCH;
+  ccb.cdm.match_buf_len = sizeof(struct dev_match_result) * N_RESULTS;
+  ccb.cdm.matches = g_new(struct dev_match_result, N_RESULTS);
+  ccb.cdm.num_matches = 0;
+  ccb.cdm.num_patterns = 0;
+  ccb.cdm.pattern_buf_len = 0;
+
+  do
+    {
+      int i;
+
+      if (ioctl(hf_scsi_fd, CAMIOCOMMAND, &ccb) < 0)
+	{
+	  HAL_WARNING(("unable to get list of SCSI devices: %s", g_strerror(errno)));
+	  break;
+	}
+
+      if (ccb.ccb_h.status != CAM_REQ_CMP
+	  || (ccb.cdm.status != CAM_DEV_MATCH_LAST
+	      && ccb.cdm.status != CAM_DEV_MATCH_MORE))
+	{
+	  HAL_WARNING(("got CAM error %#x, CDM error %d", ccb.ccb_h.status, ccb.cdm.status));
+	  break;
+	}
+
+      for (i = 0; i < (int) ccb.cdm.num_matches; i++)
+	switch (ccb.cdm.matches[i].type)
+	  {
+	  case DEV_MATCH_BUS:
+	    {
+	      struct bus_match_result *match;
+	      HalDevice *device;
+	      HalDevice *parent = NULL;
+
+	      match = &ccb.cdm.matches[i].result.bus_result;
+	      if ((int) match->path_id == -1)
+		break;
+
+	      hf_scsi_handle_pending_device(&pending_device, &pending_devname);
+
+	      device = hal_device_store_match_key_value_int(hald_get_gdl(), "scsi_host.host", match->path_id);
+	      if (device)
+		break;		/* device already exists */
+
+	      if (! strcmp(match->dev_name, "umass-sim"))
+		{
+		  parent = hf_devtree_find_from_info(hald_get_gdl(), "umass", match->unit_number);
+
+		  /* make it a child of the mass storage interface */
+		  if (parent)
+		    {
+		      HalDevice *parent_if;
+
+		      parent_if = hf_device_store_match(hald_get_gdl(),
+                                                        "info.parent", HAL_PROPERTY_TYPE_STRING, hal_device_get_udi(parent),
+							"usb.interface.class", HAL_PROPERTY_TYPE_INT32, 0x08,
+							NULL);
+
+		      if (parent_if)
+			parent = parent_if;
+		    }
+		}
+	      else if (! strcmp(match->dev_name, "ata")) /* ATAPI/CAM */
+		parent = hf_devtree_find_from_info(hald_get_gdl(), "ata", match->unit_number);
+
+	      if (! parent || ! hal_device_property_get_bool(parent, "info.ignore"))
+		{
+		  device = hf_scsi_bus_device_new(parent, match);
+		  hf_device_preprobe_and_add(device);
+		}
+	    }
+	    break;
+
+	  case DEV_MATCH_DEVICE:
+	    {
+	      struct device_match_result *match;
+	      HalDevice *device;
+
+	      match = &ccb.cdm.matches[i].result.device_result;
+	      if ((int) match->path_id == -1)
+		break;
+
+	      hf_scsi_handle_pending_device(&pending_device, &pending_devname);
+
+	      device = hf_device_store_match(hald_get_gdl(),
+                                             "scsi.bus", HAL_PROPERTY_TYPE_INT32, match->path_id,
+					     "scsi.target", HAL_PROPERTY_TYPE_INT32, match->target_id,
+					     "scsi.lun", HAL_PROPERTY_TYPE_INT32, match->target_lun,
+					     NULL);
+
+	      if (device)
+		break;		/* device already exists */
+
+	      pending_device = g_new(struct device_match_result, 1);
+	      *pending_device = *match;
+	    }
+	    break;
+
+	  case DEV_MATCH_PERIPH:
+	    {
+	      struct periph_match_result *match;
+
+	      if (! pending_device)
+		break;
+
+	      if (pending_devname)
+		break;		/* only use the first peripheral */
+
+	      match = &ccb.cdm.matches[i].result.periph_result;
+	      if ((int) match->path_id == -1 || ! strcmp(match->periph_name, "pass"))
+		break;
+
+	      pending_devname = g_strdup_printf("%s%i", match->periph_name, match->unit_number);
+	    }
+	    break;
+	  }
+    }
+  while (ccb.ccb_h.status == CAM_REQ_CMP && ccb.cdm.status == CAM_DEV_MATCH_MORE);
+
+  hf_scsi_handle_pending_device(&pending_device, &pending_devname);
+
+  g_free(ccb.cdm.matches);
+}
+
+HFHandler hf_scsi_handler = {
+  .privileged_init	= hf_scsi_privileged_init,
+  .probe		= hf_scsi_probe
+};
diff --git a/hald/freebsd/hf-scsi.h b/hald/freebsd/hf-scsi.h
new file mode 100644
index 0000000..b0e6f77
--- /dev/null
+++ b/hald/freebsd/hf-scsi.h
@@ -0,0 +1,35 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-scsi.h : SCSI support
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifndef _HF_SCSI_H
+#define _HF_SCSI_H
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "hf-osspec.h"
+
+extern HFHandler hf_scsi_handler;
+
+#endif /* _HF_SCSI_H */
diff --git a/hald/freebsd/hf-serial.c b/hald/freebsd/hf-serial.c
new file mode 100644
index 0000000..325c14b
--- /dev/null
+++ b/hald/freebsd/hf-serial.c
@@ -0,0 +1,94 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-serial.c : serial device support
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "hf-serial.h"
+#include "hf-util.h"
+
+static HalDevice *
+hf_serial_device_new (HalDevice *parent)
+{
+  HalDevice *device;
+  const char *product;
+  int unit;
+
+  g_return_val_if_fail(HAL_IS_DEVICE(parent), NULL);
+
+  device = hf_device_new(parent);
+
+  product = hal_device_property_get_string(parent, "info.product");
+  if (product)
+    hal_device_property_set_string(device, "info.product", product);
+
+  hal_device_property_set_string(device, "info.category", "serial");
+  hal_device_add_capability(device, "serial");
+
+  hal_device_property_set_string(device, "serial.physical_device", hal_device_get_udi(parent));
+
+  /* callin devices: /dev/ttyd[0-9a-v] -- see sio(4) */
+  unit = hal_device_property_get_int(parent, "freebsd.unit");
+  if (unit < 10)		/* 0-9 */
+    hf_device_property_set_string_printf(device, "serial.device", "/dev/ttyd%i", unit);
+  else if (unit < 32)		/* a-v */
+    hf_device_property_set_string_printf(device, "serial.device", "/dev/ttyd%c", unit - 10 + 'a');
+  else
+    hal_device_property_set_string(device, "serial.device", NULL);
+
+  hal_device_property_set_int(device, "serial.port", unit);
+  hal_device_property_set_string(device, "serial.type", "platform");
+
+  /* UDI from serial_compute_udi() in linux2/classdev.c */
+  hf_device_set_full_udi(device, "%s_serial_%s_%i", hal_device_get_udi(parent), "platform", unit);
+
+  return device;
+}
+
+static void
+hf_serial_probe (void)
+{
+  GSList *sio_devices;
+  GSList *l;
+
+  sio_devices = hal_device_store_match_multiple_key_value_string(hald_get_gdl(), "freebsd.driver", "sio");
+  HF_LIST_FOREACH(l, sio_devices)
+    {
+      HalDevice *parent = l->data;
+
+      if (! hal_device_store_match_key_value_int(hald_get_gdl(), "serial.port", hal_device_property_get_int(parent, "freebsd.unit"))
+	  && ! hal_device_property_get_bool(parent, "info.ignore"))
+	{
+	  HalDevice *device;
+
+	  device = hf_serial_device_new(l->data);
+	  hf_device_preprobe_and_add(device);
+	}
+    }
+  g_slist_free(sio_devices);
+}
+
+HFHandler hf_serial_handler = {
+  .probe = hf_serial_probe
+};
diff --git a/hald/freebsd/hf-serial.h b/hald/freebsd/hf-serial.h
new file mode 100644
index 0000000..b382bbe
--- /dev/null
+++ b/hald/freebsd/hf-serial.h
@@ -0,0 +1,35 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-serial.h : serial device support
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifndef _HF_SERIAL_H
+#define _HF_SERIAL_H
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "hf-osspec.h"
+
+extern HFHandler hf_serial_handler;
+
+#endif /* _HF_SERIAL_H */
diff --git a/hald/freebsd/hf-sound.c b/hald/freebsd/hf-sound.c
new file mode 100644
index 0000000..738f151
--- /dev/null
+++ b/hald/freebsd/hf-sound.c
@@ -0,0 +1,224 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-sound.c : sound (OSS) device support
+ *
+ * Copyright (C) 2006 Joe Marcus Clarke <marcus at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <string.h>
+
+#include "../logger.h"
+#include "../util.h"
+
+#include "hf-sound.h"
+#include "hf-devtree.h"
+#include "hf-util.h"
+
+#define HF_SNDSTAT_DEV		"/dev/sndstat"
+
+static GHashTable *drv_hash = NULL;
+
+static const struct oss_sound_device {
+  char *type;
+  char *driver;
+} oss_sound_devices[] = {
+  { "pcm",	"dsp" },
+  { "mixer",	"mixer" }
+  /* XXX midi support has been removed which means no sequencer either
+  { "midi",	"midi" }
+  */
+};
+
+static HalDevice *
+hf_sound_oss_device_new (HalDevice *parent,
+                         const char *type,
+                         const char *driver)
+{
+  HalDevice *device;
+  char *product, *card_id, *devname, *dev_node;
+  const char *pproduct;
+  int unit;
+
+  g_return_val_if_fail(HAL_IS_DEVICE(parent), NULL);
+
+  unit = hal_device_property_get_int(parent, "freebsd.unit");
+  dev_node = g_strdup_printf("/dev/%s%i", driver, unit);
+
+  if (! g_file_test(dev_node, G_FILE_TEST_EXISTS))
+    {
+      g_free(dev_node);
+      return NULL;
+    }
+
+  device = hf_device_new(parent);
+
+  hal_device_property_set_string(device, "oss.physical_device", hal_device_get_udi(parent));
+
+  pproduct = hal_device_property_get_string(parent, "info.product");
+
+  product = g_strdup_printf("%s (%s)", (pproduct != NULL) ? pproduct : "Sound Card",
+                            type);
+  hal_device_property_set_string(device, "info.product", product);
+  hal_device_property_set_string(device, "oss.device_id", product);
+  g_free(product);
+
+  hal_device_property_set_string(device, "info.category", "oss");
+  hal_device_add_capability(device, "oss");
+
+  hal_device_property_set_string(device, "oss.type", type);
+
+  hal_device_property_set_int(device, "oss.card", unit);
+  hal_device_property_set_int(device, "oss.device", unit);
+
+  devname = hf_devtree_device_get_name(parent);
+  if (devname)
+    {
+      card_id = g_hash_table_lookup(drv_hash, (gconstpointer)devname);
+      if (card_id)
+        hal_device_property_set_string(device, "oss.card_id", card_id);
+      else
+	hal_device_copy_property(device, "oss.card_id", device, "oss.device_id");
+    }
+  else
+    hal_device_copy_property(device, "oss.card_id", device, "oss.device_id");
+  g_free(devname);
+
+  hal_device_property_set_string(device, "oss.device_file", dev_node);
+  g_free(dev_node);
+
+  hf_device_set_full_udi(device, "%s_oss_%s_%i", hal_device_get_udi(parent), type, unit);
+
+  return device;
+}
+
+static void
+hf_sound_probe (void)
+{
+  GIOChannel *channel;
+  GError *err = NULL;
+  GSList *pcm_devices, *l;
+  char *buf;
+  char **toks;
+  gsize len;
+  int i;
+
+  drv_hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free);
+
+  channel = g_io_channel_new_file(HF_SNDSTAT_DEV, "r", &err);
+  if (channel == NULL)
+    {
+      HAL_WARNING(("unable to open %s: %s", HF_SNDSTAT_DEV, err->message));
+      goto nosndstat;
+    }
+
+  if (g_io_channel_read_to_end(channel, &buf, &len, &err) !=
+      G_IO_STATUS_NORMAL)
+    {
+      HAL_WARNING(("failed to read %s: %s", HF_SNDSTAT_DEV, err->message));
+      g_io_channel_unref(channel);
+      goto nosndstat;
+    }
+  g_io_channel_unref(channel);
+
+  toks = g_strsplit(buf, "\n", 0);
+  g_free(buf);
+
+  if (g_strv_length(toks) < 3)
+    {
+      HAL_INFO(("no soundcards found"));
+      g_strfreev(toks);
+      goto nosndstat;
+    }
+
+  for (i = 2; toks[i] != NULL; i++)
+    {
+      char *drv, *descr, *ptr;
+
+      /* format:
+pcm0: <Intel ICH5 (82801EB)> at io 0xffa7f800, 0xffa7f400 irq 17 bufsz 16384 kld snd_ich (1p/1r/0v channels duplex default)
+      */
+
+      /* These next two checks handle the case where hw.snd.verbose > 1 */
+      if (g_ascii_isspace(toks[i][0]))
+        continue;
+      if (strcmp(toks[i], "File Versions:") == 0)
+        break;
+
+      ptr = strchr(toks[i], ':');
+      if (ptr == NULL)
+        continue;
+
+      drv = g_strndup(toks[i], strlen(toks[i]) - strlen(ptr));
+
+      ptr = strstr(toks[i], "snd_");
+      if (ptr == NULL)
+	{
+	  g_free(drv);
+	  continue;
+	}
+
+      descr = g_strdup(ptr);
+
+      g_hash_table_insert(drv_hash, (gpointer)drv, (gpointer)descr);
+    }
+  g_strfreev(toks);
+
+nosndstat:
+
+  pcm_devices = hal_device_store_match_multiple_key_value_string(hald_get_gdl(),
+    "freebsd.driver", "pcm");
+  HF_LIST_FOREACH(l, pcm_devices)
+    {
+      HalDevice *parent = HAL_DEVICE(l->data);
+
+      if (! hal_device_property_get_bool(parent, "info.ignore"))
+	{
+	  int unit;
+	  int j;
+
+	  unit = hal_device_property_get_int(parent, "freebsd.unit");
+
+	  for (j = 0; j < (int) G_N_ELEMENTS(oss_sound_devices); j++)
+	      if (! hf_device_store_match(hald_get_gdl(),
+                                          "oss.card", HAL_PROPERTY_TYPE_INT32, unit,
+					  "oss.type", HAL_PROPERTY_TYPE_STRING, oss_sound_devices[j].type,
+					  NULL))
+	        {
+		  HalDevice *device;
+
+		  device = hf_sound_oss_device_new(parent,
+                                                   oss_sound_devices[j].type,
+                                                   oss_sound_devices[j].driver);
+		  if (device)
+		    hf_device_preprobe_and_add(device);
+	        }
+	}
+    }
+  g_slist_free(pcm_devices);
+
+  g_hash_table_destroy(drv_hash);
+}
+
+HFHandler hf_sound_handler = {
+  .probe = hf_sound_probe
+};
diff --git a/hald/freebsd/hf-sound.h b/hald/freebsd/hf-sound.h
new file mode 100644
index 0000000..3c9ef2a
--- /dev/null
+++ b/hald/freebsd/hf-sound.h
@@ -0,0 +1,35 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-net.h : sound (OSS) device support
+ *
+ * Copyright (C) 2006 Joe Marcus Clarke <marcus at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifndef _HF_SOUND_H
+#define _HF_SOUND_H
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "hf-osspec.h"
+
+extern HFHandler hf_sound_handler;
+
+#endif /* _HF_SOUND_H */
diff --git a/hald/freebsd/hf-storage.c b/hald/freebsd/hf-storage.c
new file mode 100644
index 0000000..57a1aef
--- /dev/null
+++ b/hald/freebsd/hf-storage.c
@@ -0,0 +1,780 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-storage.c : storage device support
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *                    Joe Marcus Clarke <marcus at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <limits.h>
+#include <inttypes.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/disklabel.h>
+
+#include "../logger.h"
+#include "../osspec.h"
+
+#include "hf-storage.h"
+#include "hf-block.h"
+#include "hf-devtree.h"
+#include "hf-volume.h"
+#include "hf-util.h"
+
+typedef struct
+{
+  char *name;
+  char *conf;
+} Disk;
+
+typedef struct
+{
+  char *class;
+  char *dev;
+  char *str_type;
+  guint hash;
+  guint64 mediasize;
+  guint64 offset;
+  guint sectorsize;
+  gint type;
+  gint index;
+} Geom_Object;
+
+static GNode *hf_storage_geom_tree = NULL;
+static GHashTable *hf_storage_geom_hash = NULL;
+
+static void hf_storage_init_geom (void);
+
+static void
+hf_storage_geom_free (gpointer data)
+{
+  Geom_Object *geom_obj;
+
+  g_return_if_fail(data != NULL);
+
+  geom_obj = (Geom_Object *) data;
+  g_free(geom_obj->class);
+  g_free(geom_obj->dev);
+  g_free(geom_obj->str_type);
+
+  g_free(geom_obj);
+}
+
+/* Disk_Names() has a memory leak, hence this */
+static char **
+hf_storage_get_disk_names (GError **err)
+{
+  char *list;
+  char **names;
+
+  list = hf_get_string_sysctl(err, "kern.disks");
+  if (! list)
+    return NULL;
+
+  names = g_strsplit(list, " ", 0);
+  g_free(list);
+
+  return names;
+}
+
+static gboolean
+hf_storage_class_is_partitionable (const char *geom_class)
+{
+  return (! strcmp(geom_class, "MBR") || ! strcmp(geom_class, "GPT") ||
+          ! strcmp(geom_class, "APPLE") || ! strcmp(geom_class, "SUN"));
+}
+
+static gboolean
+hf_storage_geom_has_partitions (const Geom_Object *geom_obj, GNode *node)
+{
+  if (! node || ! geom_obj)
+    return FALSE;
+
+  if (g_node_n_children(node) > 0)
+    return TRUE;
+
+  if (hf_storage_class_is_partitionable(geom_obj->class) &&
+      g_node_next_sibling(node) != NULL)
+    {
+      GNode *sibling;
+
+      for (sibling = g_node_next_sibling(node); sibling;
+           sibling = g_node_next_sibling(sibling))
+        {
+          Geom_Object *sibling_geom;
+
+          sibling_geom = g_hash_table_lookup(hf_storage_geom_hash,
+                                             sibling->data);
+
+	  if (sibling_geom &&
+              hf_storage_class_is_partitionable(sibling_geom->class))
+            return TRUE;
+        }
+    }
+
+  return FALSE;
+}
+
+static gboolean
+hf_storage_geom_is_swap (const Geom_Object *geom_obj)
+{
+  g_return_val_if_fail(geom_obj != NULL, FALSE);
+
+  return (! strcmp(geom_obj->class, "BSD") && geom_obj->type == FS_SWAP)
+    || (! strcmp(geom_obj->class, "MBR")
+	&& (geom_obj->type == 0x18		/* AST Windows swapfile */
+	    || geom_obj->type == 0x42		/* SFS or Linux swap */
+	    || geom_obj->type == 0x82		/* Linux swap or Solaris x86 */
+	    || geom_obj->type == 0xB8));	/* BSDI BSD/386 swap */
+}
+
+static void
+hf_storage_device_probe_geom (HalDevice *parent,
+			      HalDevice *storage_device,
+			      const Geom_Object *geom_obj)
+{
+  HalDevice *device = NULL;
+  GNode *node;
+  Geom_Object *next;
+
+  g_return_if_fail(HAL_IS_DEVICE(parent));
+  g_return_if_fail(HAL_IS_DEVICE(storage_device));
+
+  if (! geom_obj)
+    return;
+
+  node = g_node_find(hf_storage_geom_tree, G_PRE_ORDER, G_TRAVERSE_ALL,
+                     GUINT_TO_POINTER(geom_obj->hash));
+
+  if (! node)
+    return;
+
+  if (geom_obj->type != FS_UNUSED)
+    {
+      char *special;
+
+      special = g_strdup_printf("/dev/%s", geom_obj->dev);
+      device = hal_device_store_match_key_value_string(hald_get_gdl(), "block.device", special);
+      g_free(special);
+
+      if (! device)
+	device = hf_volume_device_add(parent,
+				      storage_device,
+				      hf_storage_geom_has_partitions(geom_obj, node),
+				      hf_storage_geom_is_swap(geom_obj),
+				      geom_obj->dev,
+				      geom_obj->class,
+				      geom_obj->str_type,
+				      geom_obj->type,
+				      geom_obj->index,
+				      geom_obj->offset,
+				      geom_obj->mediasize);
+    }
+
+  if (! device || ! hal_device_property_get_bool(device, "info.ignore"))
+    {
+      next = (g_node_first_child(node)) ?
+              g_hash_table_lookup(hf_storage_geom_hash,
+              (g_node_first_child(node))->data) : NULL;
+      hf_storage_device_probe_geom(device ? device : parent, storage_device,
+                                   next);
+    }
+  next = (g_node_next_sibling(node)) ? g_hash_table_lookup(hf_storage_geom_hash,
+          (g_node_next_sibling(node))->data) : NULL;
+  hf_storage_device_probe_geom(parent, storage_device, next);
+}
+
+static void
+hf_storage_device_probe_partitions (HalDevice *device)
+{
+  char *diskname;
+  guint hash;
+  Geom_Object *geom_obj;
+  GNode *node;
+
+  g_return_if_fail(HAL_IS_DEVICE(device));
+
+  diskname = hf_devtree_device_get_name(device);
+  if (! diskname)
+    return;
+
+  hash = g_str_hash(diskname);
+
+  node = g_node_find(hf_storage_geom_tree, G_PRE_ORDER, G_TRAVERSE_ALL,
+                     GUINT_TO_POINTER(hash));
+  if (! node || ! g_node_first_child(node))
+    return;
+
+  geom_obj = g_hash_table_lookup(hf_storage_geom_hash,
+                                 (g_node_first_child(node))->data);
+  if (! geom_obj)
+    return;
+
+  hf_storage_device_probe_geom(device, device, geom_obj);
+
+  g_free(diskname);
+}
+
+static gboolean
+hf_storage_device_has_partitions (HalDevice *device)
+{
+  gboolean has = FALSE;
+  char *diskname;
+  Geom_Object *geom_obj;
+  guint hash;
+
+  g_return_val_if_fail(HAL_IS_DEVICE(device), FALSE);
+
+  diskname = hf_devtree_device_get_name(device);
+  if (! diskname)
+    return FALSE;
+
+  hash = g_str_hash(diskname);
+  geom_obj = g_hash_table_lookup(hf_storage_geom_hash,
+                                 GUINT_TO_POINTER(hash));
+  if (geom_obj)
+    {
+      GNode *node;
+
+      node = g_node_find(hf_storage_geom_tree, G_PRE_ORDER,
+                         G_TRAVERSE_ALL, GUINT_TO_POINTER(hash));
+      if (hf_storage_geom_has_partitions(geom_obj, node))
+        has = TRUE;
+    }
+  g_free(diskname);
+
+  return has;
+}
+
+static HalDevice *
+hf_storage_device_new (HalDevice *parent, const char *diskname)
+{
+  HalDevice *device;
+
+  g_return_val_if_fail(diskname != NULL, NULL);
+
+  device = hf_device_new(parent);
+
+  hf_devtree_device_set_name(device, diskname);
+
+  hf_block_device_enable(device, diskname);
+  hf_storage_device_enable(device);
+  hf_block_device_complete(device, device, FALSE);
+
+  return device;
+}
+
+void
+hf_storage_device_probe (HalDevice *device, gboolean only_media)
+{
+  g_return_if_fail(HAL_IS_DEVICE(device));
+
+  hf_storage_init_geom();
+
+  if (hf_runner_run_sync(device, 0, "hald-probe-storage",
+			 "HF_HAS_CHILDREN", HF_BOOL_TO_STRING(hf_storage_device_has_partitions(device)),
+			 "HF_ONLY_CHECK_FOR_MEDIA", HF_BOOL_TO_STRING(only_media),
+			 NULL) == 2)
+    {				/* add a child volume */
+      char *devname;
+
+      devname = hf_devtree_device_get_name(device);
+      g_assert(devname != NULL);
+
+      /*
+       * We do not need to check if the device already exists:
+       *
+       *   - if we're called from osspec_device_rescan() or
+       *     osspec_device_reprobe(), the child device has been removed.
+       *   - otherwise, the storage device is new and has no child.
+       */
+
+      hf_volume_device_add(device, device, FALSE, FALSE, devname, NULL, NULL, -1, 0, 0, 0);
+      g_free(devname);
+    }
+  else				/* probe partitions */
+    hf_storage_device_probe_partitions(device);
+}
+
+static void
+hf_storage_probe (void)
+{
+  GError *err = NULL;
+  char **disks;
+
+  /* add disks which have not been handled by hf-ata or hf-scsi */
+
+  disks = hf_storage_get_disk_names(&err);
+  if (disks)
+    {
+      int i;
+
+      for (i = 0; disks[i]; i++)
+	{
+	  HalDevice *device;
+
+	  device = hf_devtree_find_from_name(hald_get_gdl(), disks[i]);
+	  if (! device)
+	    {
+	      HalDevice *parent;
+
+	      /* device not found, add a generic storage device */
+
+	      parent = hf_devtree_find_parent_from_name(hald_get_gdl(), disks[i]);
+	      if (! parent || ! hal_device_property_get_bool(parent, "info.ignore"))
+		{
+		  device = hf_storage_device_new(parent, disks[i]);
+		  hf_storage_device_add(device);
+		}
+	    }
+	}
+
+      g_strfreev(disks);
+    }
+  else
+    {
+      HAL_WARNING(("unable to get disk list: %s", err->message));
+      g_error_free(err);
+    }
+}
+
+static GSList *
+hf_storage_parse_conftxt (const char *conftxt)
+{
+  GSList *disks = NULL;
+  char **lines;
+  Disk *disk = NULL;
+  GString *disk_conf = NULL;
+  GNode *root, *parent;
+  GNode *child = NULL;
+  GHashTable *table;
+  int curr_depth = 0;
+  int i;
+
+  if (! conftxt)
+    return NULL;
+
+  root = g_node_new (NULL);
+  parent = root;
+
+  table = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL,
+                                (GDestroyNotify) hf_storage_geom_free);
+
+  lines = g_strsplit(conftxt, "\n", 0);
+  for (i = 0; lines[i]; i++)
+    {
+      Geom_Object *geom_obj;
+      char **fields;
+      int depth;
+      guint hash;
+
+      if (! *lines[i])
+        continue;
+
+      fields = g_strsplit(lines[i], " ", 0);
+      if (g_strv_length(fields) < 3)
+        {
+          g_strfreev(fields);
+          continue;
+	}
+
+      geom_obj = g_new0(Geom_Object, 1);
+
+      depth = atoi(fields[0]);
+      geom_obj->class = g_strdup(fields[1]);
+      geom_obj->dev = g_strdup(fields[2]);
+      geom_obj->type = -1;	/* We use -1 here to denote a missing type. */
+      hash = g_str_hash(geom_obj->dev);
+      geom_obj->hash = hash;
+
+      if (g_strv_length(fields) >= 5)
+        {
+          geom_obj->mediasize = strtoumax(fields[3], NULL, 10);
+	  geom_obj->sectorsize = (guint) strtoul(fields[4], NULL, 10);
+	}
+
+      if (g_strv_length(fields) >= 7)
+        if (! strcmp(fields[5], "i"))
+          geom_obj->index = atoi(fields[6]) + 1;
+
+      if (g_strv_length(fields) >= 9)
+        if (! strcmp(fields[7], "o"))
+            geom_obj->offset = strtoumax(fields[8], NULL, 10);
+
+      if (g_strv_length(fields) >= 11)
+        {
+          if (! strcmp (fields[9], "ty"))
+            {
+              if (! strcmp (geom_obj->class, "GPT") ||
+                  ! strcmp (geom_obj->class, "APPLE"))
+                geom_obj->str_type = g_strdup(fields[10]);
+	      else
+                geom_obj->type = atoi(fields[10]);
+	    }
+	}
+
+      g_hash_table_insert (table, GUINT_TO_POINTER(hash), geom_obj);
+
+      if (depth > curr_depth)
+        {
+          g_assert(child != NULL);
+          parent = child;
+        }
+      else if (depth < curr_depth)
+        {
+          GNode *cur;
+          int levels;
+
+          levels = curr_depth - depth;
+          for (cur = child; cur && levels > 0; cur = cur->parent, levels--)
+            ;
+
+          if (cur == NULL)
+            parent = root;
+          else
+            parent = cur->parent;
+        }
+
+      child = g_node_append_data(parent, GUINT_TO_POINTER(hash));
+      curr_depth = depth;
+
+      if (! strcmp(geom_obj->class, "DISK"))
+	{
+	  if (disk)
+	    disk->conf = g_string_free(disk_conf, FALSE);
+
+	  disk = g_new(Disk, 1);
+	  disk->name = g_strdup(geom_obj->dev);
+	  disk_conf = g_string_new(NULL);
+	  disks = g_slist_prepend(disks, disk);
+	}
+      g_strfreev(fields);
+
+      if (disk)
+	{
+	  if (*disk_conf->str)
+	    g_string_append_c(disk_conf, '\n');
+	  g_string_append(disk_conf, lines[i]);
+	}
+    }
+  g_strfreev(lines);
+
+  if (disk)
+    disk->conf = g_string_free(disk_conf, FALSE);
+
+  if (hf_storage_geom_hash)
+    g_hash_table_destroy(hf_storage_geom_hash);
+
+  hf_storage_geom_hash = table;
+
+  if (hf_storage_geom_tree)
+    g_node_destroy(hf_storage_geom_tree);
+
+  hf_storage_geom_tree = root;
+
+  return disks;
+}
+
+static Disk *
+hf_storage_find_disk (const GSList *disks, const char *name)
+{
+  const GSList *l;
+
+  HF_LIST_FOREACH(l, disks)
+    {
+      Disk *disk = l->data;
+
+      if (! strcmp(disk->name, name))
+	return disk;
+    }
+
+  return NULL;
+}
+
+static void
+hf_storage_disk_free (Disk *disk)
+{
+  g_return_if_fail(disk != NULL);
+
+  g_free(disk->name);
+  g_free(disk->conf);
+  g_free(disk);
+}
+
+static void
+hf_storage_device_rescan_real (HalDevice *device)
+{
+  g_return_if_fail(HAL_IS_DEVICE(device));
+
+  /* remove all children */
+
+  hf_device_remove_children(device);
+
+  /* rescan */
+
+  hf_storage_device_probe(device, TRUE);
+}
+
+static gboolean
+hf_storage_conftxt_timeout_cb (gpointer data)
+{
+  static GSList *disks = NULL;
+  static gboolean first = TRUE;
+  char *conftxt;
+  GSList *new_disks;
+
+  if (hf_is_waiting)
+    return TRUE;
+
+  conftxt = hf_get_string_sysctl(NULL, "kern.geom.conftxt");
+  new_disks = hf_storage_parse_conftxt(conftxt);
+  g_free(conftxt);
+
+  /* if this is not the initial check, handle changes */
+
+  if (first)
+    first = FALSE;
+  else
+    {
+      GSList *l;
+
+      /* check for new disks */
+
+      HF_LIST_FOREACH(l, new_disks)
+	{
+	  Disk *disk = l->data;
+
+	  if (! hf_storage_find_disk(disks, disk->name))
+	    {
+	      osspec_probe();	/* catch new disk(s) */
+	      break;
+	    }
+	}
+
+      /* check for disks which have changed or have been removed */
+
+      HF_LIST_FOREACH(l, disks)
+	{
+	  Disk *disk = l->data;
+	  Disk *new_disk;
+	  HalDevice *device;
+
+	  new_disk = hf_storage_find_disk(new_disks, disk->name);
+	  if (new_disk)
+	    {
+	      if (strcmp(disk->conf, new_disk->conf))
+		{
+		  /* disk changed */
+		  device = hf_devtree_find_from_name(hald_get_gdl(), disk->name);
+		  if (device)
+		    {
+		      g_assert(hal_device_has_capability(device, "storage"));
+		      hf_storage_device_rescan_real(device);
+		    }
+		}
+	    }
+	  else
+	    {
+	      /* disk removed */
+	      device = hf_devtree_find_from_name(hald_get_gdl(), disk->name);
+	      if (device)
+		{
+		  g_assert(hal_device_has_capability(device, "storage"));
+		  hf_device_remove_tree(device);
+		}
+	    }
+	}
+    }
+
+  g_slist_foreach(disks, (GFunc) hf_storage_disk_free, NULL);
+  g_slist_free(disks);
+  disks = new_disks;
+
+  return TRUE;
+}
+
+static void
+hf_storage_init_geom (void)
+{
+  char *conftxt;
+  static gboolean inited = FALSE;
+  GSList *disks;
+
+  if (inited)
+    return;
+
+  conftxt = hf_get_string_sysctl(NULL, "kern.geom.conftxt");
+  disks = hf_storage_parse_conftxt(conftxt);
+  g_free(conftxt);
+
+  g_slist_foreach(disks, (GFunc) hf_storage_disk_free, NULL);
+  g_slist_free(disks);
+
+  inited = TRUE;
+}
+
+static void
+hf_storage_init (void)
+{
+  hf_storage_init_geom();
+  g_timeout_add(3000, hf_storage_conftxt_timeout_cb, NULL);
+}
+
+void
+hf_storage_device_enable (HalDevice *device)
+{
+  g_return_if_fail(HAL_IS_DEVICE(device));
+  g_return_if_fail(devname != NULL);
+
+  hal_device_property_set_string(device, "storage.bus", "platform");
+  hal_device_property_set_string(device, "storage.drive_type", "disk");
+
+  hal_device_property_set_bool(device, "storage.removable", FALSE);
+  hal_device_property_set_bool(device, "storage.requires_eject", FALSE);
+  hal_device_property_set_bool(device, "storage.hotpluggable", FALSE);
+  hal_device_property_set_bool(device, "storage.media_check_enabled", FALSE);
+  hal_device_property_set_bool(device, "storage.automount_enabled_hint", TRUE);
+  hal_device_property_set_bool(device, "storage.no_partitions_hint", FALSE);
+
+  hal_device_property_set_string(device, "storage.physical_device", NULL);
+  hal_device_property_set_string(device, "storage.model", NULL);
+  hal_device_property_set_string(device, "storage.vendor", NULL);
+
+  hal_device_add_capability(device, "storage");
+  hal_device_property_set_string(device, "info.category", "storage");
+}
+
+void
+hf_storage_device_enable_tape (HalDevice *device)
+{
+  g_return_if_fail(HAL_IS_DEVICE(device));
+
+  hal_device_property_set_string(device, "storage.drive_type", "tape");
+  hal_device_property_set_bool(device, "storage.removable", TRUE);
+}
+
+void
+hf_storage_device_enable_cdrom (HalDevice *device)
+{
+  g_return_if_fail(HAL_IS_DEVICE(device));
+
+  hal_device_add_capability(device, "storage.cdrom");
+  hal_device_property_set_string(device, "info.category", "storage.cdrom");
+  hal_device_property_set_string(device, "storage.drive_type", "cdrom");
+  hal_device_property_set_bool(device, "storage.removable", TRUE);
+  /* enable media checks */
+  hal_device_property_set_bool(device, "storage.media_check_enabled", TRUE);
+  /* CD-ROM discs most likely don't have a partition table */
+  hal_device_property_set_bool(device, "storage.no_partitions_hint", TRUE);
+  /* the linux backend sets this one */
+  hal_device_property_set_bool(device, "storage.requires_eject", TRUE);
+
+  /* some of these will be set by probe-storage */
+  hal_device_property_set_bool(device, "storage.cdrom.cdr", FALSE);
+  hal_device_property_set_bool(device, "storage.cdrom.cdrw", FALSE);
+  hal_device_property_set_bool(device, "storage.cdrom.dvd", FALSE);
+  hal_device_property_set_bool(device, "storage.cdrom.dvdr", FALSE);
+  hal_device_property_set_bool(device, "storage.cdrom.dvdrw", FALSE);
+  hal_device_property_set_bool(device, "storage.cdrom.dvdram", FALSE);
+  hal_device_property_set_bool(device, "storage.cdrom.dvdplusr", FALSE);
+  hal_device_property_set_bool(device, "storage.cdrom.dvdplusrw", FALSE);
+  hal_device_property_set_bool(device, "storage.cdrom.dvdplusrdl", FALSE);
+  hal_device_property_set_bool(device, "storage.cdrom.dvdplusrwdl", FALSE);
+  hal_device_property_set_bool(device, "storage.cdrom.bd", FALSE);
+  hal_device_property_set_bool(device, "storage.cdrom.bdr", FALSE);
+  hal_device_property_set_bool(device, "storage.cdrom.bdre", FALSE);
+  hal_device_property_set_bool(device, "storage.cdrom.hddvd", FALSE);
+  hal_device_property_set_bool(device, "storage.cdrom.hddvdr", FALSE);
+  hal_device_property_set_bool(device, "storage.cdrom.hddvdrw", FALSE);
+  hal_device_property_set_bool(device, "storage.cdrom.support_media_changed", FALSE);
+  hal_device_property_set_int(device, "storage.cdrom.read_speed", 0);
+  hal_device_property_set_int(device, "storage.cdrom.write_speed", 0);
+}
+
+/* preprobe, probe and add */
+void
+hf_storage_device_add (HalDevice *device)
+{
+  g_return_if_fail(HAL_IS_DEVICE(device));
+
+  hf_storage_init_geom();
+
+  if (hf_device_preprobe(device))
+    {
+      hf_storage_device_probe(device, FALSE);
+      hf_device_add(device);
+    }
+}
+
+GSList *
+hf_storage_get_geoms (const char *devname)
+{
+  GNode *node;
+  GSList *geom_list = NULL;
+  int n_children, i;
+  guint hash;
+
+  g_return_val_if_fail(devname != NULL, NULL);
+
+  hf_storage_init_geom();
+
+  hash = g_str_hash(devname);
+  node = g_node_find(hf_storage_geom_tree, G_PRE_ORDER, G_TRAVERSE_ALL,
+                     GUINT_TO_POINTER(hash));
+  if (! node)
+    return NULL;
+
+  n_children = g_node_n_children(node);
+  for (i = 0; i < n_children; i++)
+    {
+      GNode *child;
+      Geom_Object *geom_obj;
+
+      child = g_node_nth_child(node, i);
+      geom_obj = (Geom_Object *) g_hash_table_lookup(hf_storage_geom_hash,
+                                                     child->data);
+      if (geom_obj)
+        geom_list = g_slist_prepend(geom_list, g_strdup(geom_obj->dev));
+    }
+
+  return geom_list;
+}
+
+static gboolean
+hf_storage_device_rescan (HalDevice *device)
+{
+  if (hal_device_has_capability(device, "storage"))
+    {
+      hf_storage_device_rescan_real(device);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+HFHandler hf_storage_handler = {
+  .init =		hf_storage_init,
+  .probe =		hf_storage_probe,
+  .device_rescan =	hf_storage_device_rescan
+};
diff --git a/hald/freebsd/hf-storage.h b/hald/freebsd/hf-storage.h
new file mode 100644
index 0000000..d8b2ff0
--- /dev/null
+++ b/hald/freebsd/hf-storage.h
@@ -0,0 +1,42 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-storage.h : storage device support
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifndef _HF_STORAGE_H
+#define _HF_STORAGE_H
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "hf-osspec.h"
+
+extern HFHandler hf_storage_handler;
+
+void hf_storage_device_enable (HalDevice *device);
+void hf_storage_device_enable_tape (HalDevice *device);
+void hf_storage_device_enable_cdrom (HalDevice *device);
+void hf_storage_device_probe (HalDevice *device, gboolean only_media);
+void hf_storage_device_add (HalDevice *device);
+GSList *hf_storage_get_geoms (const char *devname);
+
+#endif /* _HF_STORAGE_H */
diff --git a/hald/freebsd/hf-usb.c b/hald/freebsd/hf-usb.c
new file mode 100644
index 0000000..c287f92
--- /dev/null
+++ b/hald/freebsd/hf-usb.c
@@ -0,0 +1,827 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-usb.c : USB support
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <dev/usb/usb.h>
+
+#include "../logger.h"
+#include "../osspec.h"
+
+#include "hf-usb.h"
+#include "hf-devtree.h"
+#include "hf-util.h"
+
+#define HF_USB_DEVICE			"/dev/usb"
+
+typedef struct
+{
+  int		fd;
+  int		index;
+} Controller;
+
+static GSList *controllers = NULL;
+
+static int hf_usb_fd;
+
+static gboolean hf_usb_probe_device (HalDevice *parent,
+				     Controller *controller,
+				     const struct usb_device_info *device_info);
+
+static Controller *
+hf_usb_find_controller (int index)
+{
+  GSList *l;
+
+  HF_LIST_FOREACH(l, controllers)
+    {
+      Controller *controller = l->data;
+
+      if (controller->index == index)
+	return controller;
+    }
+
+  return NULL;
+}
+
+static HalDevice *
+hf_usb_find_hub (const struct usb_device_info *device_info)
+{
+  GSList *a;
+
+  g_return_val_if_fail(device_info != NULL, FALSE);
+
+  HF_LIST_FOREACH(a, hald_get_gdl()->devices)
+    {
+      HalDevice *device = a->data;
+
+      if (hal_device_property_get_int(device, "usb_device.bus_number") == device_info->udi_bus)
+	{
+          HalDeviceStrListIter iter;
+
+	  for (hal_device_property_strlist_iter_init(device, "usb_device.freebsd.ports", &iter);
+               hal_device_property_strlist_iter_is_valid(&iter);
+	       hal_device_property_strlist_iter_next(&iter))
+	    {
+	      const char *port;
+
+	      port = hal_device_property_strlist_iter_get_value(&iter);
+
+	      if (atoi(port) == device_info->udi_addr)
+		return device;
+	    }
+	}
+    }
+
+  return NULL;
+}
+
+static gboolean
+hf_usb_get_descriptor (int fd,
+		       int addr,
+		       int type,
+		       int index,
+		       gpointer desc,
+		       int len,
+		       GError **err)
+{
+  struct usb_ctl_request req;
+  char buf[len];
+
+  g_return_val_if_fail(desc != NULL, FALSE);
+
+  memset(&req, 0, sizeof(req));
+
+  req.ucr_addr = addr;
+  req.ucr_request.bmRequestType = UT_READ_DEVICE;
+  req.ucr_request.bRequest = UR_GET_DESCRIPTOR;
+  USETW2(req.ucr_request.wValue, type, index);
+  USETW(req.ucr_request.wIndex, 0);
+  USETW(req.ucr_request.wLength, len);
+  req.ucr_data = buf;
+
+  if (ioctl(fd, USB_REQUEST, &req) < 0)
+    {
+      g_set_error(err, 0, 0, "%s", g_strerror(errno));
+      return FALSE;
+    }
+  if (req.ucr_actlen != len)
+    {
+      g_set_error(err, 0, 0, "bad length");
+      return FALSE;
+    }
+
+  memcpy(desc, buf, len);
+  return TRUE;
+}
+
+static char *
+hf_usb_get_string_descriptor (int fd,
+			      int addr,
+			      int index,
+			      GError **err)
+{
+  usb_string_descriptor_t string_desc;
+  gunichar2 unicode_str[G_N_ELEMENTS(string_desc.bString)];
+  int len;			/* in words */
+  int i;
+  char *str;
+  GError *tmp_err = NULL;
+
+  /* 1. get bLength */
+
+  if (! hf_usb_get_descriptor(fd, addr, UDESC_STRING, index, &string_desc, 2, err))
+    return NULL;
+
+  if (string_desc.bLength < 2)
+    {
+      g_set_error(err, 0, 0, "bad length");
+      return NULL;
+    }
+
+  /* 2. get the whole descriptor */
+
+  if (! hf_usb_get_descriptor(fd, addr, UDESC_STRING, index, &string_desc, string_desc.bLength, err))
+    return NULL;
+
+  len = (string_desc.bLength - 2) / sizeof(uWord);
+  for (i = 0; i < len; i++)
+    unicode_str[i] = UGETW(string_desc.bString[i]);
+
+  str = g_utf16_to_utf8(unicode_str, len, NULL, NULL, &tmp_err);
+  if (! str)
+    {
+      g_set_error(err, 0, 0, "unable to convert string to UTF-8: %s", tmp_err->message);
+      g_error_free(tmp_err);
+    }
+
+  return str;
+}
+
+static gpointer
+hf_usb_get_full_config_descriptor (int fd,
+				   int addr,
+				   int index,
+				   int *len,
+				   GError **err)
+{
+  usb_config_descriptor_t config_desc;
+  int _len;
+  char *buf;
+
+  /*
+   * The USB configuration descriptor is padded with interface
+   * descriptors and endpoint descriptors. See the USB specifications
+   * for details.
+   */
+
+  /* 1. get the config descriptor alone, to obtain wTotalLength */
+
+  if (! hf_usb_get_descriptor(fd, addr, UDESC_CONFIG, index, &config_desc, USB_CONFIG_DESCRIPTOR_SIZE, err))
+    return NULL;
+
+  /* 2. get the config descriptor and the padded if/endpoint descriptors */
+
+  _len = UGETW(config_desc.wTotalLength);
+  buf = g_new(char, _len);
+
+  if (hf_usb_get_descriptor(fd, addr, UDESC_CONFIG, index, buf, _len, err))
+    {
+      if (len)
+	*len = _len;
+
+      return buf;
+    }
+
+  /* failure */
+
+  g_free(buf);
+  return NULL;
+}
+
+/*
+ * Adapted from usb_compute_udi() in linux2/physdev.c and
+ * usbclass_compute_udi() in linux2/classdev.c.
+ */
+static void
+hf_usb_device_compute_udi (HalDevice *device)
+{
+  g_return_if_fail(HAL_IS_DEVICE(device));
+
+  if (hal_device_has_capability(device, "hiddev"))
+    hf_device_set_full_udi(device, "%s_hiddev",
+			   hal_device_property_get_string(device, "info.parent"));
+  else if (hal_device_has_property(device, "usb.interface.number"))
+    hf_device_set_full_udi(device, "%s_if%i",
+			   hal_device_property_get_string(device, "info.parent"),
+			   hal_device_property_get_int(device, "usb.interface.number"));
+  else
+    hf_device_set_udi(device, "usb_device_%x_%x_%s",
+		      hal_device_property_get_int(device, "usb_device.vendor_id"),
+		      hal_device_property_get_int(device, "usb_device.product_id"),
+		      hal_device_has_property(device, "usb_device.serial")
+		      ? hal_device_property_get_string(device, "usb_device.serial")
+		      : "noserial");
+}
+
+/* adapted from usbif_set_name() in linux2/physdev.c */
+static const char *
+hf_usb_get_interface_name (const usb_interface_descriptor_t *desc)
+{
+  switch (desc->bInterfaceClass)
+    {
+    default:
+    case 0x00: return "USB Interface";
+    case 0x01: return "USB Audio Interface";
+    case 0x02: return "USB Communications Interface";
+    case 0x03: return "USB HID Interface";
+    case 0x06: return "USB Imaging Interface";
+    case 0x07: return "USB Printer Interface";
+    case 0x08: return "USB Mass Storage Interface";
+    case 0x09: return "USB Hub Interface";
+    case 0x0a: return "USB Data Interface";
+    case 0x0b: return "USB Chip/Smartcard Interface";
+    case 0x0d: return "USB Content Security Interface";
+    case 0x0e: return "USB Video Interface";
+    case 0xdc: return "USB Diagnostic Interface";
+    case 0xe0: return "USB Wireless Interface";
+    case 0xef: return "USB Miscelleneous Interface";
+    case 0xfe: return "USB Application Specific Interface";
+    case 0xff: return "USB Vendor Specific Interface";
+    }
+}
+
+static const char *
+hf_usb_get_devname (const struct usb_device_info *di, const char *driver)
+{
+  int i;
+
+  g_return_val_if_fail(di != NULL, NULL);
+  g_return_val_if_fail(driver != NULL, NULL);
+
+  for (i = 0; i < USB_MAX_DEVNAMES; i++)
+    if (hf_devtree_is_driver(di->udi_devnames[i], driver))
+      return di->udi_devnames[i];
+
+  return NULL;
+}
+
+static HalDevice *
+hf_usb_device_new (HalDevice *parent,
+		   Controller *controller,
+		   const struct usb_device_info *di,
+		   const usb_device_descriptor_t *device_desc)
+{
+  HalDevice *device;
+  int speed;
+  usb_config_descriptor_t config_desc;
+  gboolean can_wake_up = FALSE;
+  int num_interfaces = 0;
+  int i;
+  const char *devname;
+
+  g_return_val_if_fail(controller != NULL, NULL);
+  g_return_val_if_fail(di != NULL, NULL);
+  g_return_val_if_fail(device_desc != NULL, NULL);
+
+  device = hf_device_new(parent);
+
+  hal_device_property_set_string(device, "info.bus", "usb_device");
+
+  hal_device_property_set_string(device, "info.product", di->udi_product);
+  hal_device_property_set_string(device, "info.vendor", di->udi_vendor);
+
+  hal_device_property_set_int(device, "usb_device.bus_number", di->udi_bus);
+  hal_device_property_set_int(device, "usb_device.configuration_value", di->udi_config);
+  hal_device_property_set_int(device, "usb_device.num_configurations", device_desc->bNumConfigurations);
+  hal_device_property_set_int(device, "usb_device.device_class", di->udi_class);
+  hal_device_property_set_int(device, "usb_device.device_subclass", di->udi_subclass);
+  hal_device_property_set_int(device, "usb_device.device_protocol", di->udi_protocol);
+  hal_device_property_set_bool(device, "usb_device.is_self_powered", di->udi_power == 0);
+  hal_device_property_set_int(device, "usb_device.max_power", di->udi_power);
+  hal_device_property_set_int(device, "usb_device.num_ports", di->udi_nports);
+  hal_device_property_set_int(device, "usb_device.port_number", di->udi_addr);
+
+  switch (di->udi_speed)
+    {
+    case USB_SPEED_LOW:		speed = 0x00150; break;
+    case USB_SPEED_FULL:	speed = 0x01200; break;
+    case USB_SPEED_HIGH:	speed = 0x48000; break;
+    default:			speed = 0; break;
+    }
+  hal_device_property_set_int(device, "usb_device.speed_bcd", speed);
+
+  hal_device_property_set_int(device, "usb_device.version_bcd", UGETW(device_desc->bcdUSB));
+  /* FIXME usb_device.level_number */
+  hal_device_property_set_int(device, "usb_device.product_id", di->udi_productNo);
+  hal_device_property_set_int(device, "usb_device.vendor_id", di->udi_vendorNo);
+  hal_device_property_set_int(device, "usb_device.device_revision_bcd", UGETW(device_desc->bcdDevice));
+
+  if (device_desc->iSerialNumber != 0)
+    {
+      char *serial;
+
+      serial = hf_usb_get_string_descriptor(controller->fd, di->udi_addr, device_desc->iSerialNumber, NULL);
+      if (serial)
+	{
+	  hal_device_property_set_string(device, "usb_device.serial", serial);
+	  g_free(serial);
+	}
+    }
+
+  hal_device_property_set_string(device, "usb_device.product", di->udi_product);
+  hal_device_property_set_string(device, "usb_device.vendor", di->udi_vendor);
+
+  if (di->udi_config != 0 && hf_usb_get_descriptor(controller->fd,
+						   di->udi_addr,
+						   UDESC_CONFIG,
+						   di->udi_config - 1,
+						   &config_desc,
+						   USB_CONFIG_DESCRIPTOR_SIZE,
+						   NULL))
+    {
+      can_wake_up = (config_desc.bmAttributes & UC_REMOTE_WAKEUP) != 0;
+      num_interfaces = config_desc.bNumInterface;
+    }
+
+  hal_device_property_set_bool(device, "usb_device.can_wake_up", can_wake_up);
+  hal_device_property_set_int(device, "usb_device.num_interfaces", num_interfaces);
+
+  for (i = 0; i < di->udi_nports; i++)
+    if (di->udi_ports[i] > 0 && di->udi_ports[i] < USB_MAX_DEVICES)
+      {
+	char *port;
+
+	port = g_strdup_printf("%i", di->udi_ports[i]);
+	hal_device_property_strlist_append(device, "usb_device.freebsd.ports", port);
+	g_free(port);
+      }
+
+  if ((devname = hf_usb_get_devname(di, "ukbd")))	/* USB keyboard */
+    hf_device_set_input(device, "keyboard", devname);
+  else if ((devname = hf_usb_get_devname(di, "ums")))	/* USB mouse */
+    hf_device_set_input(device, "mouse", devname);
+  else if ((devname = hf_usb_get_devname(di, "uhid")))	/* UHID device */
+    {
+      hal_device_property_set_string(device, "info.category", "hiddev");
+      hal_device_add_capability(device, "hiddev");
+      hf_device_property_set_string_printf(device, "hiddev.device", "/dev/%s", devname);
+      hal_device_copy_property(device, "info.product", device, "hiddev.product");
+    }
+
+  /*
+   * Register the first attached driver (if any) with devtree (mostly
+   * useful for allowing hf-scsi to find umass devices).
+   */
+  if (*di->udi_devnames[0])
+    hf_devtree_device_set_name(device, di->udi_devnames[0]);
+
+  hf_usb_device_compute_udi(device);
+
+  return device;
+}
+
+static HalDevice *
+hf_usb_interface_device_new (HalDevice *parent,
+			     const usb_interface_descriptor_t *desc)
+{
+  HalDevice *device;
+  const char *name;
+
+  g_return_val_if_fail(HAL_IS_DEVICE(parent), NULL);
+  g_return_val_if_fail(desc != NULL, NULL);
+
+  device = hf_device_new(parent);
+
+  hal_device_property_set_string(device, "info.bus", "usb");
+
+  hal_device_merge_with_rewrite(device, parent, "usb.", "usb_device.");
+
+  name = hf_usb_get_interface_name(desc);
+  hal_device_property_set_string(device, "info.product", name);
+  hal_device_property_set_string(device, "usb.product", name);
+
+  hal_device_property_set_int(device, "usb.interface.class", desc->bInterfaceClass);
+  hal_device_property_set_int(device, "usb.interface.subclass", desc->bInterfaceSubClass);
+  hal_device_property_set_int(device, "usb.interface.protocol", desc->bInterfaceProtocol);
+  hal_device_property_set_int(device, "usb.interface.number", desc->bInterfaceNumber);
+
+  hf_usb_device_compute_udi(device);
+
+  return device;
+}
+
+static void
+hf_usb_probe_address (HalDevice *parent,
+		      Controller *controller,
+		      int addr)
+{
+  struct usb_device_info device_info;
+
+  g_return_if_fail(controller != NULL);
+
+  memset(&device_info, 0, sizeof(device_info));
+  device_info.udi_addr = addr;
+
+  if (ioctl(controller->fd, USB_DEVICEINFO, &device_info) != -1)
+    {
+      HalDevice *real_parent;
+
+      /*
+       * If we're called from hf_usb_probe_device() the passed parent
+       * will be our actual one. However, if we're called from
+       * hf_usb_probe() during a reprobe, the device may be located
+       * down the bus while the passed parent is the root USB
+       * controller. See if we can find our real parent (our hub).
+       */
+      real_parent = hf_usb_find_hub(&device_info);
+      if (real_parent)
+	{
+	  parent = real_parent;
+	  if (hal_device_property_get_bool(parent, "info.ignore"))
+	    return;
+	}
+
+      hf_usb_probe_device(parent, controller, &device_info);
+    }
+}
+
+static gboolean
+hf_usb_probe_device (HalDevice *parent,
+		     Controller *controller,
+		     const struct usb_device_info *device_info)
+{
+  usb_device_descriptor_t device_desc;
+  GError *err = NULL;
+  HalDevice *device;
+  int i;
+
+  g_return_val_if_fail(controller != NULL, FALSE);
+  g_return_val_if_fail(device_info != NULL, FALSE);
+
+  device = hf_device_store_match(hald_get_gdl(),
+                                 "usb_device.bus_number", HAL_PROPERTY_TYPE_INT32, device_info->udi_bus,
+				 "usb_device.port_number", HAL_PROPERTY_TYPE_INT32, device_info->udi_addr,
+				 NULL);
+
+  if (device)
+    return FALSE;		/* device already exists */
+
+  if (! hf_usb_get_descriptor(controller->fd,
+			      device_info->udi_addr,
+			      UDESC_DEVICE,
+			      0,
+			      &device_desc,
+			      USB_DEVICE_DESCRIPTOR_SIZE,
+			      &err))
+    {
+      HAL_WARNING(("unable to get device descriptor of USB device %i.%i: %s", controller->index, device_info->udi_addr, err->message));
+      g_error_free(err);
+      return FALSE;
+    }
+
+  /* add device */
+
+  device = hf_usb_device_new(parent, controller, device_info, &device_desc);
+  if (hf_device_preprobe(device))
+    {
+      if (hal_device_has_capability(device, "hiddev"))
+	hf_runner_run_sync(device, 0, "hald-probe-hiddev", NULL);
+
+      hf_device_add(device);
+    }
+  else
+    return FALSE;		/* device ignored */
+
+  /* add interfaces */
+
+  for (i = 0; i < device_desc.bNumConfigurations; i++)
+    {
+      usb_config_descriptor_t *config_desc;
+      int len;
+      char *p;
+      int j;
+
+      config_desc = hf_usb_get_full_config_descriptor(controller->fd, device_info->udi_addr, i, &len, &err);
+      if (! config_desc)
+	{
+	  HAL_WARNING(("unable to get configuration descriptor %i of USB device %i.%i: %s", i, controller->index, device_info->udi_addr, err->message));
+	  g_clear_error(&err);
+	  continue;
+	}
+
+      p = (char *) config_desc + USB_CONFIG_DESCRIPTOR_SIZE;
+      for (j = 0; j < config_desc->bNumInterface; j++)
+	{
+	  usb_interface_descriptor_t *if_desc = (usb_interface_descriptor_t *) p;
+	  HalDevice *if_device;
+
+	  if (p + USB_INTERFACE_DESCRIPTOR_SIZE > (char *) config_desc + len)
+	    {
+	      HAL_WARNING(("USB device %i.%i: short configuration descriptor %i", controller->index, device_info->udi_addr, i));
+	      break;
+	    }
+
+	  if_device = hf_usb_interface_device_new(device, if_desc);
+	  hf_device_preprobe_and_add(if_device);
+
+	  p += USB_INTERFACE_DESCRIPTOR_SIZE + if_desc->bNumEndpoints * USB_ENDPOINT_DESCRIPTOR_SIZE;
+	}
+
+      g_free(config_desc);
+    }
+
+  /* add child devices if this is a hub */
+
+  for (i = 0; i < device_info->udi_nports; i++)
+    if (device_info->udi_ports[i] > 0 && device_info->udi_ports[i] < USB_MAX_DEVICES)
+      hf_usb_probe_address(device, controller, device_info->udi_ports[i]);
+
+  return TRUE;
+}
+
+static void
+hf_usb_privileged_init (void)
+{
+  int i;
+
+  hf_usb_fd = open(HF_USB_DEVICE, O_RDONLY);
+  if (hf_usb_fd < 0)
+    HAL_INFO(("unable to open %s: %s", HF_USB_DEVICE, g_strerror(errno)));
+
+  for (i = 0; i < 16; i++)
+    {
+      char *filename;
+      int fd;
+
+      filename = g_strdup_printf("/dev/usb%i", i);
+      fd = open(filename, O_RDONLY);
+      g_free(filename);
+
+      if (fd >= 0)
+	{
+	  Controller *controller;
+
+	  controller = g_new0(Controller, 1);
+	  controller->fd = fd;
+	  controller->index = i;
+
+	  controllers = g_slist_append(controllers, controller);
+	}
+    }
+}
+
+static void
+hf_usb_process_event (const struct usb_event *event)
+{
+  g_return_if_fail(event != NULL);
+
+  switch (event->ue_type)
+    {
+    case USB_EVENT_CTRLR_ATTACH:
+      HAL_INFO(("received USB_EVENT_CTRLR_ATTACH event, bus %i",
+		event->u.ue_ctrlr.ue_bus));
+      /* FIXME */
+      break;
+
+    case USB_EVENT_CTRLR_DETACH:
+      HAL_INFO(("received USB_EVENT_CTRLR_DETACH event, bus %i",
+		event->u.ue_ctrlr.ue_bus));
+      /* FIXME */
+      break;
+
+    case USB_EVENT_DEVICE_ATTACH:
+      {
+	Controller *controller;
+
+	HAL_INFO(("received USB_EVENT_DEVICE_ATTACH event, device %i.%i",
+		  event->u.ue_device.udi_bus, event->u.ue_device.udi_addr));
+
+	controller = hf_usb_find_controller(event->u.ue_device.udi_bus);
+	if (controller)
+	  {
+	    HalDevice *parent;
+
+	    parent = hf_usb_find_hub(&event->u.ue_device);
+	    if (! parent || ! hal_device_property_get_bool(parent, "info.ignore"))
+	      hf_usb_probe_device(parent, controller, &event->u.ue_device);
+	  }
+      }
+      break;
+
+    case USB_EVENT_DEVICE_DETACH:
+      {
+	HalDevice *device;
+
+	HAL_INFO(("received USB_EVENT_DEVICE_DETACH event, device %i.%i",
+		  event->u.ue_device.udi_bus, event->u.ue_device.udi_addr));
+
+	device = hf_device_store_match(hald_get_gdl(),
+                                       "usb_device.bus_number", HAL_PROPERTY_TYPE_INT32, event->u.ue_device.udi_bus,
+				       "usb_device.port_number", HAL_PROPERTY_TYPE_INT32, event->u.ue_device.udi_addr,
+				       NULL);
+
+	if (device)		/* device gone, remove it and all its children */
+	  hf_device_remove_tree(device);
+      }
+      break;
+
+      /*
+       * For some reason the kernel does not actually emit
+       * USB_EVENT_DRIVER_ATTACH/USB_EVENT_DRIVER_DETACH events. We
+       * handle driver events in hf_usb_devd_add() and
+       * hf_usb_devd_remove(), anyway.
+       */
+
+    case USB_EVENT_DRIVER_ATTACH:
+      HAL_INFO(("received USB_EVENT_DRIVER_ATTACH event"));
+      break;
+
+    case USB_EVENT_DRIVER_DETACH:
+      HAL_INFO(("received USB_EVENT_DRIVER_DETACH event"));
+      break;
+
+    default:
+      HAL_WARNING(("received unknown USB event %i", event->ue_type));
+      break;
+    }
+}
+
+static gboolean
+hf_usb_event_cb (GIOChannel *source, GIOCondition condition, gpointer user_data)
+{
+  struct usb_event event;
+
+  if (hf_is_waiting)
+    return TRUE;
+
+  if (read(hf_usb_fd, &event, sizeof(event)) == sizeof(event))
+    hf_usb_process_event(&event);
+
+  return TRUE;
+}
+
+static void
+hf_usb_init (void)
+{
+  GIOChannel *channel;
+
+  if (hf_usb_fd < 0)
+    return;
+
+  channel = g_io_channel_unix_new(hf_usb_fd);
+  g_io_add_watch(channel, G_IO_IN, hf_usb_event_cb, NULL);
+}
+
+static void
+hf_usb_probe (void)
+{
+  GSList *l;
+
+  /* probe all devices */
+
+  HF_LIST_FOREACH(l, controllers)
+    {
+      Controller *controller = l->data;
+      HalDevice *parent;
+
+      parent = hf_devtree_find_parent_from_info(hald_get_gdl(), "usb", controller->index);
+      if (! parent || ! hal_device_property_get_bool(parent, "info.ignore"))
+	{
+	  int i;
+
+	  for (i = 1; i < USB_MAX_DEVICES; i++)
+	    hf_usb_probe_address(parent, controller, i);
+	}
+    }
+}
+
+static gboolean
+hf_usb_devd_add (const char *name,
+		 GHashTable *params,
+		 GHashTable *at,
+		 const char *parent)
+{
+  HalDevice *parent_device;
+  const char *port_index;
+  const char *port;
+  HalDevice *device;
+
+  /*
+   * A driver was attached; if it was attached to an USB device, we
+   * should reprobe the device to catch the new driver.
+   */
+
+  if (! parent)
+    return FALSE;
+
+  port_index = g_hash_table_lookup(at, "port");
+  if (! port_index)
+    return FALSE;		/* no port information: not an USB driver */
+
+  /*
+   * If the device already exists, it's either not an USB device or we
+   * had already picked up the driver; eitherway, we have nothing to
+   * do.
+   */
+  if (hf_devtree_find_from_name(hald_get_gdl(), name))
+    return FALSE;
+
+  /*
+   * Try to find the USB device to which the driver was attached. We
+   * do that by using the port key reported by devd. That key is not
+   * the USB port number, but the index of the port in the parent
+   * hub's port list (udi_ports).
+   */
+
+  parent_device = hf_devtree_find_from_name(hald_get_gdl(), parent);
+  if (! parent_device)
+    return FALSE;
+
+  port = hal_device_property_get_strlist_elem(parent_device, "usb_device.freebsd.ports", atoi(port_index));
+  if (! port)
+    return FALSE;
+
+  device = hf_device_store_match(hald_get_gdl(),
+                                 "info.parent", HAL_PROPERTY_TYPE_STRING, hal_device_get_udi(parent_device),
+				 "usb_device.port_number", HAL_PROPERTY_TYPE_INT32, atoi(port),
+				 NULL);
+
+  if (device)
+    {
+      osspec_device_reprobe(device); /* found, reprobe it to catch driver */
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static gboolean
+hf_usb_devd_remove (const char *name,
+		    GHashTable *params,
+		    GHashTable *at,
+		    const char *parent)
+{
+  HalDevice *device;
+
+  /*
+   * A driver was detached. If its device is an USB device, reprobe it
+   * to catch the detachment.
+   *
+   * Note that devd also reports driver detachment when an USB device
+   * is physically detached. In that case we will probably have caught
+   * the device detachment first (in hf_usb_process_event()), since
+   * the USB event watch was installed before the devd event watch
+   * (the USB handler is listed before the devd handler in osspec). If
+   * devd actually caught the event before USB, reprobing the device
+   * will not hurt anyway.
+   */
+
+  device = hf_devtree_find_from_name(hald_get_gdl(), name);
+  if (device && hal_device_has_property(device, "usb_device.port_number"))
+    {
+      osspec_device_reprobe(device);
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+HFHandler hf_usb_handler = {
+  .privileged_init	= hf_usb_privileged_init,
+  .init			= hf_usb_init,
+  .probe		= hf_usb_probe
+};
+
+HFDevdHandler hf_usb_devd_handler = {
+  .add =	hf_usb_devd_add,
+  .remove =	hf_usb_devd_remove
+};
diff --git a/hald/freebsd/hf-usb.h b/hald/freebsd/hf-usb.h
new file mode 100644
index 0000000..edc9c6c
--- /dev/null
+++ b/hald/freebsd/hf-usb.h
@@ -0,0 +1,37 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-usb.h : USB support
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifndef _HF_USB_H
+#define _HF_USB_H
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "hf-osspec.h"
+#include "hf-devd.h"
+
+extern HFHandler hf_usb_handler;
+extern HFDevdHandler hf_usb_devd_handler;
+
+#endif /* _HF_USB_H */
diff --git a/hald/freebsd/hf-util.c b/hald/freebsd/hf-util.c
new file mode 100644
index 0000000..ae9e149
--- /dev/null
+++ b/hald/freebsd/hf-util.c
@@ -0,0 +1,621 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-util.c : utilities
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <glib.h>
+
+#include "../hald_runner.h"
+#include "../osspec.h"
+#include "../util.h"
+#include "../device_info.h"
+
+#include "hf-util.h"
+
+typedef struct
+{
+  gboolean done;
+} AsyncInfo;
+
+typedef struct
+{
+  int exit_type;
+  int return_code;
+} RunnerInfo;
+
+typedef struct
+{
+  char *key;
+  char *strval;
+  int type;
+  int intval;
+} PropertyBag;
+
+gboolean hf_is_waiting = FALSE;
+
+static void
+hf_async_init (AsyncInfo *info)
+{
+  g_return_if_fail(info != NULL);
+
+  info->done = FALSE;
+}
+
+static void
+hf_async_wait (AsyncInfo *info)
+{
+  g_return_if_fail(info != NULL);
+  g_return_if_fail(hf_is_waiting == FALSE);
+
+  while (! info->done)
+    {
+      hf_is_waiting = TRUE;
+      g_main_context_iteration(NULL, TRUE);
+      hf_is_waiting = FALSE;
+    }
+
+  info->done = FALSE;		/* reset in case they want to call us again */
+}
+
+HalDevice *
+hf_device_new (HalDevice *parent)
+{
+  HalDevice *device;
+
+  device = hal_device_new();
+
+  hal_device_property_set_string(device, "info.parent", parent ? hal_device_get_udi(parent) : HF_COMPUTER);
+
+  return device;
+}
+
+static void
+hf_device_callout_done_cb (HalDevice *device, gpointer data1, gpointer data2)
+{
+  AsyncInfo *async = data1;
+
+  async->done = TRUE;
+}
+
+gboolean
+hf_device_preprobe (HalDevice *device)
+{
+  AsyncInfo async;
+  gboolean ignore;
+
+  g_return_val_if_fail(HAL_IS_DEVICE(device), FALSE);
+
+  /* add to temporary device store */
+  hal_device_store_add(hald_get_tdl(), device);
+
+  /* process preprobe fdi files */
+  di_search_and_merge(device, DEVICE_INFO_TYPE_PREPROBE);
+
+  /* run preprobe callouts */
+  hf_async_init(&async);
+  hal_util_callout_device_preprobe(device, hf_device_callout_done_cb, &async, NULL);
+  hf_async_wait(&async);
+
+  ignore = hal_device_property_get_bool(device, "info.ignore");
+  if (ignore)
+    {
+      hal_device_property_remove(device, "info.category");
+      hal_device_property_remove(device, "info.capabilities");
+      hal_device_property_set_string(device, "info.udi", "/org/freedesktop/Hal/devices/ignored-device");
+      hal_device_property_set_string(device, "info.product", "Ignored Device");
+
+      /* move from temporary to global device store */
+      hal_device_store_remove(hald_get_tdl(), device);
+      hal_device_store_add(hald_get_gdl(), device);
+    }
+
+  return ! ignore;
+}
+
+void
+hf_device_add (HalDevice *device)
+{
+  AsyncInfo async;
+
+  g_return_if_fail(HAL_IS_DEVICE(device));
+
+  /* process information and policy fdi files */
+  di_search_and_merge(device, DEVICE_INFO_TYPE_INFORMATION);
+  di_search_and_merge(device, DEVICE_INFO_TYPE_POLICY);
+
+  /* run add callouts */
+  hf_async_init(&async);
+  hal_util_callout_device_add(device, hf_device_callout_done_cb, &async, NULL);
+  hf_async_wait(&async);
+
+  /* move from temporary to global device store */
+  hal_device_store_remove(hald_get_tdl(), device);
+  hal_device_store_add(hald_get_gdl(), device);
+}
+
+gboolean
+hf_device_preprobe_and_add (HalDevice *device)
+{
+  g_return_val_if_fail(HAL_IS_DEVICE(device), FALSE);
+
+  if (hf_device_preprobe(device))
+    {
+      hf_device_add(device);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+void
+hf_device_remove (HalDevice *device)
+{
+  AsyncInfo async;
+
+  g_return_if_fail(HAL_IS_DEVICE(device));
+
+  hf_async_init(&async);
+  hal_util_callout_device_remove(device, hf_device_callout_done_cb, &async, NULL);
+  hf_async_wait(&async);
+
+  hal_device_store_remove(hald_get_gdl(), device);
+  g_object_unref(device);
+}
+
+void
+hf_device_remove_children (HalDevice *device)
+{
+  GSList *children;
+
+  g_return_if_fail(HAL_IS_DEVICE(device));
+
+  children = hf_device_store_get_children(hald_get_gdl(), device);
+  children = g_slist_reverse(children);	/* remove leaves first */
+  g_slist_foreach(children, (GFunc) hf_device_remove, NULL);
+  g_slist_free(children);
+}
+
+/* remove the device and all its children */
+void
+hf_device_remove_tree (HalDevice *device)
+{
+  g_return_if_fail(HAL_IS_DEVICE(device));
+
+  hf_device_remove_children(device);
+  hf_device_remove(device);
+}
+
+void
+hf_device_set_udi (HalDevice *device, const char *format, ...)
+{
+  va_list args;
+  char *udi;
+
+  g_return_if_fail(HAL_IS_DEVICE(device));
+  g_return_if_fail(format != NULL);
+
+  va_start(args, format);
+  udi = g_strdup_vprintf(format, args);
+  va_end(args);
+
+  hf_device_set_full_udi(device, "/org/freedesktop/Hal/devices/%s", udi);
+  g_free(udi);
+}
+
+void
+hf_device_set_full_udi (HalDevice *device, const char *format, ...)
+{
+  va_list args;
+  char *requested_udi;
+  char actual_udi[256];
+
+  g_return_if_fail(HAL_IS_DEVICE(device));
+  g_return_if_fail(format != NULL);
+
+  va_start(args, format);
+  requested_udi = g_strdup_vprintf(format, args);
+  va_end(args);
+
+  hal_util_compute_udi(hald_get_gdl(), actual_udi, sizeof(actual_udi), "%s", requested_udi);
+  g_free(requested_udi);
+
+  hal_device_set_udi(device, actual_udi);
+  hal_device_property_set_string(device, "info.udi", actual_udi);
+}
+
+void
+hf_device_property_set_string_printf (HalDevice *device,
+				      const char *key,
+				      const char *format,
+				      ...)
+{
+  char *value = NULL;
+
+  g_return_if_fail(HAL_IS_DEVICE(device));
+  g_return_if_fail(key != NULL);
+
+  if (format)
+    {
+      va_list args;
+
+      va_start(args, format);
+      value = g_strdup_vprintf(format, args);
+      va_end(args);
+    }
+
+  hal_device_property_set_string(device, key, value);
+  g_free(value);
+}
+
+void
+hf_device_set_input (HalDevice *device,
+		     const char *class,
+		     const char *devname)
+{
+  g_return_if_fail(HAL_IS_DEVICE(device));
+
+  hal_device_add_capability(device, "input");
+
+  if (class)
+    {
+      char *category;
+
+      category = g_strdup_printf("input.%s", class);
+      hal_device_property_set_string(device, "info.category", category);
+      hal_device_add_capability(device, category);
+      g_free(category);
+    }
+  else
+    hal_device_property_set_string(device, "info.category", "input");
+
+  if (devname)
+    hf_device_property_set_string_printf(device, "input.device", "/dev/%s", devname);
+  else
+    hal_device_property_set_string(device, "input.device", NULL);
+}
+
+HalDevice *
+hf_device_store_get_parent (HalDeviceStore *store, HalDevice *device)
+{
+  const char *parent_udi;
+
+  g_return_val_if_fail(HAL_IS_DEVICE_STORE(store), NULL);
+  g_return_val_if_fail(HAL_IS_DEVICE(device), NULL);
+
+  parent_udi = hal_device_property_get_string(device, "info.parent");
+  if (parent_udi)
+    return hal_device_store_find(store, parent_udi);
+  else
+    return NULL;
+}
+
+GSList *
+hf_device_store_get_children (HalDeviceStore *store, HalDevice *device)
+{
+  GSList *children;
+  GSList *tmp;
+  GSList *l;
+
+  g_return_val_if_fail(HAL_IS_DEVICE_STORE(store), NULL);
+  g_return_val_if_fail(HAL_IS_DEVICE(device), NULL);
+
+  children = hal_device_store_match_multiple_key_value_string(store, "info.parent", hal_device_get_udi(device));
+
+  /* recurse down the tree */
+  tmp = g_slist_copy(children);
+  HF_LIST_FOREACH(l, tmp)
+    children = g_slist_concat(children, hf_device_store_get_children(store, l->data));
+  g_slist_free(tmp);
+
+  return children;
+}
+
+gboolean
+hf_has_sysctl (const char *format, ...)
+{
+  va_list args;
+  char *name;
+  size_t value_len;
+  gboolean status;
+
+  g_return_val_if_fail(format != NULL, FALSE);
+
+  va_start(args, format);
+  name = g_strdup_vprintf(format, args);
+  va_end(args);
+
+  status = sysctlbyname(name, NULL, &value_len, NULL, 0) == 0;
+
+  g_free(name);
+  return status;
+}
+
+gboolean
+hf_get_int_sysctl (int *value, GError **err, const char *format, ...)
+{
+  va_list args;
+  char *name;
+  size_t value_len = sizeof(int);
+  gboolean status;
+
+  g_return_val_if_fail(value != NULL, FALSE);
+  g_return_val_if_fail(format != NULL, FALSE);
+
+  va_start(args, format);
+  name = g_strdup_vprintf(format, args);
+  va_end(args);
+
+  status = sysctlbyname(name, value, &value_len, NULL, 0) == 0;
+  if (! status)
+    g_set_error(err, 0, 0, "%s", g_strerror(errno));
+
+  g_free(name);
+  return status;
+}
+
+char *
+hf_get_string_sysctl (GError **err, const char *format, ...)
+{
+  va_list args;
+  char *name;
+  size_t value_len;
+  char *str = NULL;
+
+  g_return_val_if_fail(format != NULL, FALSE);
+
+  va_start(args, format);
+  name = g_strdup_vprintf(format, args);
+  va_end(args);
+
+  if (sysctlbyname(name, NULL, &value_len, NULL, 0) == 0)
+    {
+      str = g_new(char, value_len + 1);
+      if (sysctlbyname(name, str, &value_len, NULL, 0) == 0)
+	str[value_len] = 0;
+      else
+	{
+	  g_free(str);
+	  str = NULL;
+	}
+    }
+
+  if (! str)
+    g_set_error(err, 0, 0, "%s", g_strerror(errno));
+
+  g_free(name);
+  return str;
+}
+
+char *
+hf_run (GError **err, const char *format, ...)
+{
+  va_list args;
+  char *command;
+  int exit_status;
+  char *output = NULL;
+
+  g_return_val_if_fail(format != NULL, NULL);
+
+  va_start(args, format);
+  command = g_strdup_vprintf(format, args);
+  va_end(args);
+
+  if (g_spawn_command_line_sync(command, &output, NULL, &exit_status, err))
+    {
+      if (exit_status != 0)
+	{
+	  g_set_error(err, 0, 0, "command returned status %i", exit_status);
+	  g_free(output);
+	  output = NULL;
+	}
+    }
+
+  g_free(command);
+  return output;
+}
+
+static void
+hf_runner_terminated_cb (HalDevice *device,
+			 guint32 exit_type,
+			 int return_code,
+			 char **error,
+			 gpointer data1,
+			 gpointer data2)
+{
+  AsyncInfo *async = data1;
+  RunnerInfo *info = data2;
+
+  info->exit_type = exit_type;
+  info->return_code = return_code;
+
+  async->done = TRUE;
+}
+
+int
+hf_runner_run_sync (HalDevice *device,
+                    int timeout,
+                    const char *command_line, ...)
+{
+  AsyncInfo async;
+  RunnerInfo info;
+  GPtrArray *extra_env;
+  const char *variable;
+  va_list args;
+
+  g_return_val_if_fail(HAL_IS_DEVICE(device), 0);
+  g_return_val_if_fail(command_line != NULL, 0);
+
+  if (timeout <= 0)
+    timeout = HAL_HELPER_TIMEOUT;
+
+  extra_env = g_ptr_array_new();
+
+  va_start(args, command_line);
+  while ((variable = va_arg(args, const char *)))
+    {
+      const char *value;
+
+      value = va_arg(args, const char *);
+      g_assert(value != NULL);
+
+      g_ptr_array_add(extra_env, g_strdup_printf("%s=%s", variable, value));
+    }
+  va_end(args);
+
+  g_ptr_array_add(extra_env, NULL);
+
+  hf_async_init(&async);
+  hald_runner_run(device, command_line, (char **) extra_env->pdata, timeout, hf_runner_terminated_cb, &async, &info);
+  hf_async_wait(&async);
+
+  g_ptr_array_foreach(extra_env, (GFunc) g_free, NULL);
+  g_ptr_array_free(extra_env, TRUE);
+
+  return info.exit_type == HALD_RUN_SUCCESS ? info.return_code : -1;
+}
+
+int
+hf_strv_find (char **strv, const char *elem)
+{
+  int i;
+
+  g_return_val_if_fail(strv != NULL, -1);
+  g_return_val_if_fail(elem != NULL, -1);
+
+  for (i = 0; strv[i]; i++)
+    if (! strcmp(strv[i], elem))
+      return i;
+
+  return -1;
+}
+
+static void
+hf_property_bag_free (PropertyBag *bag)
+{
+  g_free(bag->key);
+  g_free(bag->strval);
+
+  g_free(bag);
+}
+
+HalDevice *
+hf_device_store_match (HalDeviceStore *store, ...)
+{
+  GSList *props = NULL;
+  va_list args;
+  GSList *a;
+  HalDevice *device = NULL;
+
+  g_return_val_if_fail(HAL_IS_DEVICE_STORE(store), NULL);
+
+  va_start(args, store);
+
+  while (TRUE)
+    {
+      char *key;
+      PropertyBag *bag;
+
+      key = va_arg(args, char *);
+      if (! key)
+        break;
+
+      bag = g_new0(PropertyBag, 1);
+      bag->key = g_strdup(key);
+
+      bag->type = va_arg(args, int);
+      switch (bag->type)
+        {
+          case HAL_PROPERTY_TYPE_STRING:
+            {
+              char *val;
+
+	      val = va_arg(args, char *);
+              bag->strval = g_strdup(val ? val : "");
+	      break;
+	    }
+	  case HAL_PROPERTY_TYPE_INT32:
+            bag->intval = va_arg(args, int);
+	    break;
+	  default:
+            g_slist_foreach(props, (GFunc) hf_property_bag_free, NULL);
+            g_slist_free(props);
+	    hf_property_bag_free(bag);
+
+	    va_end(args);
+
+	    g_return_val_if_reached(NULL);
+	}
+
+      props = g_slist_prepend(props, bag);
+    }
+
+  va_end(args);
+
+  HF_LIST_FOREACH(a, store->devices)
+    {
+      GSList *b;
+
+      device = (HalDevice *) a->data;
+
+      HF_LIST_FOREACH(b, props)
+        {
+          PropertyBag *bag;
+
+          if (! device)
+            break;
+
+          bag = (PropertyBag *) b->data;
+
+          switch (bag->type)
+            {
+              case HAL_PROPERTY_TYPE_STRING:
+                if (strcmp(hal_device_property_get_string(device, bag->key),
+                           bag->strval))
+                  device = NULL;
+                break;
+              case HAL_PROPERTY_TYPE_INT32:
+                if (hal_device_property_get_int(device, bag->key) !=
+                    bag->intval)
+                  device = NULL;
+                break;
+              default:
+                g_assert_not_reached();
+            }
+        }
+
+      if (device)
+        break;
+    }
+
+  g_slist_foreach(props, (GFunc) hf_property_bag_free, NULL);
+  g_slist_free(props);
+
+  return device;
+}
diff --git a/hald/freebsd/hf-util.h b/hald/freebsd/hf-util.h
new file mode 100644
index 0000000..8648518
--- /dev/null
+++ b/hald/freebsd/hf-util.h
@@ -0,0 +1,115 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-util.h : utilities
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifndef _HF_UTIL_H
+#define _HF_UTIL_H
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdarg.h>
+#include <glib.h>
+
+#include "../hald.h"
+
+#define HF_COMPUTER		"/org/freedesktop/Hal/devices/computer"
+
+#define HF_BOOL_TO_STRING(val)	((val) ? "true" : "false")
+
+#define HF_LIST_FOREACH(var, head)	\
+  for ((var) = (head);			\
+       (var);				\
+       (var) = (var)->next)
+
+/*
+ * The hf_is_waiting variable is set to true when hald is waiting in
+ * one of the following functions:
+ *
+ *	hf_device_preprobe()
+ *	hf_device_add()
+ *	hf_device_preprobe_and_add()
+ *	hf_device_remove()
+ *	hf_device_remove_children()
+ *	hf_device_remove_tree()
+ *	hf_runner_run_sync()
+ *
+ * Since these functions wait by recursing into the GLib main loop,
+ * main loop callbacks can be executed during the wait and cause
+ * undesirable side effects. For instance, an USB attach event for a
+ * child device could be received while the parent device is still
+ * being processed in hf_usb_probe_device().
+ *
+ * Main loop callbacks must therefore do nothing and return if
+ * hf_is_waiting is true.
+ */
+extern int hf_is_waiting;
+
+HalDevice *hf_device_new (HalDevice *parent);
+
+gboolean hf_device_preprobe (HalDevice *device);
+void hf_device_add (HalDevice *device);
+gboolean hf_device_preprobe_and_add (HalDevice *device);
+void hf_device_remove (HalDevice *device);
+void hf_device_remove_children (HalDevice *device);
+void hf_device_remove_tree (HalDevice *device);
+
+void hf_device_set_udi (HalDevice *device,
+			const char *format,
+			...) G_GNUC_PRINTF(2, 3);
+void hf_device_set_full_udi (HalDevice *device,
+			     const char *format,
+			     ...) G_GNUC_PRINTF(2, 3);
+
+void hf_device_property_set_string_printf (HalDevice *device,
+					   const char *key,
+					   const char *format,
+					   ...) G_GNUC_PRINTF(3, 4);
+
+void hf_device_set_input (HalDevice *device,
+			  const char *class,
+			  const char *devname);
+
+HalDevice *hf_device_store_get_parent (HalDeviceStore *store,
+				       HalDevice *device);
+GSList *hf_device_store_get_children (HalDeviceStore *store,
+				      HalDevice *device);
+
+gboolean hf_has_sysctl (const char *format, ...) G_GNUC_PRINTF(1, 2);
+gboolean hf_get_int_sysctl (int *value,
+			    GError **err,
+			    const char *format,
+			    ...) G_GNUC_PRINTF(3, 4);
+char *hf_get_string_sysctl (GError **err,
+			    const char *format,
+			    ...) G_GNUC_PRINTF(2, 3);
+
+char *hf_run (GError **err, const char *format, ...) G_GNUC_PRINTF(2, 3);
+
+int hf_runner_run_sync (HalDevice *device, int timeout, const char *command_line, ...);
+
+int hf_strv_find (char **strv, const char *elem);
+
+HalDevice *hf_device_store_match (HalDeviceStore *store, ...);
+
+#endif /* _HF_UTIL_H */
diff --git a/hald/freebsd/hf-volume.c b/hald/freebsd/hf-volume.c
new file mode 100644
index 0000000..0ff1c31
--- /dev/null
+++ b/hald/freebsd/hf-volume.c
@@ -0,0 +1,283 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-volume.c : volume device support
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
+
+#include "../hald.h"
+#include "../hald_dbus.h"
+#include "../hald_runner.h"
+#include "../logger.h"
+#include "../util.h"
+#include "../device_info.h"
+#include "../osspec.h"
+
+#include "hf-volume.h"
+#include "hf-block.h"
+#include "hf-storage.h"
+#include "hf-util.h"
+
+#define PROBE_VOLUME_TIMEOUT		(HAL_HELPER_TIMEOUT * 6)
+
+static void
+hf_volume_get_mounts (struct statfs **mounts, int *n_mounts)
+{
+  g_return_if_fail(mounts != NULL);
+  g_return_if_fail(n_mounts != NULL);
+
+  *n_mounts = getmntinfo(mounts, MNT_NOWAIT);
+  if (*n_mounts == 0)
+    {
+      HAL_WARNING(("unable to get list of mounted filesystems: %s", g_strerror(errno)));
+      *mounts = NULL;
+    }
+}
+
+static const struct statfs *
+hf_volume_mounts_find (const struct statfs *mounts,
+		       int n_mounts,
+		       const char *special)
+{
+  int i;
+
+  g_return_val_if_fail(mounts != NULL, NULL);
+  g_return_val_if_fail(special != NULL, NULL);
+
+  for (i = 0; i < n_mounts; i++)
+    if (! strcmp(mounts[i].f_mntfromname, special))
+      return &mounts[i];
+
+  return NULL;
+}
+
+static void
+hf_volume_device_update_mount_properties (HalDevice *device,
+					  const struct statfs *mounts,
+					  int n_mounts)
+{
+  const struct statfs *mount = NULL;
+
+  g_return_if_fail(HAL_IS_DEVICE(device));
+
+  if (mounts)
+    {
+      const char *special;
+
+      special = hal_device_property_get_string(device, "block.device");
+      if (special)
+	mount = hf_volume_mounts_find(mounts, n_mounts, special);
+    }
+
+  hal_device_property_set_bool(device, "volume.is_mounted", mount != NULL);
+  hal_device_property_set_bool(device, "volume.is_mounted_read_only", mount && (mount->f_flags & MNT_RDONLY) != 0);
+  if (mount)
+    {
+      const char *vlabel;
+
+      vlabel = hal_device_property_get_string(device, "volume.label");
+      hal_device_property_set_string(device, "volume.mount_point", mount->f_mntonname);
+      if (! vlabel || ! strcmp(vlabel, ""))
+        {
+          char *last_part;
+
+	  last_part = strrchr(mount->f_mntonname, '/');
+	  if (! last_part)
+            vlabel = mount->f_mntonname;
+	  else if (*(last_part + 1) == '\0')
+            vlabel = "Root";
+	  else
+            vlabel = last_part + 1;
+
+          hal_device_property_set_string(device, "volume.label", vlabel);
+	}
+    }
+  else
+    hal_device_property_set_string(device, "volume.mount_point", NULL);
+}
+
+HalDevice *
+hf_volume_device_add (HalDevice *parent,
+		      HalDevice *storage_device,
+		      gboolean has_children,
+		      gboolean is_swap,
+		      const char *devname,
+		      const char *gclass,
+		      const char *gstr_type,
+		      int gtype,
+		      int gindex,
+		      dbus_int64_t partition_offset,
+		      dbus_int64_t partition_size)
+{
+  HalDevice *device;
+  char *partition_offset_str;
+  char *partition_size_str;
+  char *type_str;
+  char *geom_class;
+  char *index_str;
+  gboolean is_volume;
+
+  g_return_val_if_fail(HAL_IS_DEVICE(parent), NULL);
+  g_return_val_if_fail(HAL_IS_DEVICE(storage_device), NULL);
+  g_return_val_if_fail(devname != NULL, NULL);
+
+  device = hf_device_new(parent);
+
+  hf_block_device_enable(device, devname);
+
+  if (! hf_device_preprobe(device))
+    goto end;
+
+  if (! gclass)
+    geom_class = g_strdup("");
+  else
+    geom_class = g_strdup(gclass);
+
+  if (gstr_type)
+    type_str = g_strdup(gstr_type);
+  else
+    type_str = g_strdup_printf("%d", gtype);
+
+  index_str = g_strdup_printf("%d", gindex);
+
+  partition_offset_str = g_strdup_printf("%ju", partition_offset);
+  partition_size_str = g_strdup_printf("%ju", partition_size);
+
+  is_volume = hf_runner_run_sync(device, PROBE_VOLUME_TIMEOUT,
+                                 "hald-probe-volume",
+				 "HF_HAS_CHILDREN", HF_BOOL_TO_STRING(has_children),
+				 "HF_IS_SWAP", HF_BOOL_TO_STRING(is_swap),
+				 "HF_VOLUME_GEOM_CLASS", geom_class,
+				 "HF_VOLUME_PART_TYPE", type_str,
+				 "HF_VOLUME_PART_INDEX", index_str,
+				 "HF_VOLUME_OFFSET", partition_offset_str,
+				 "HF_VOLUME_SIZE", partition_size_str,
+				 NULL) == 0;
+  if (is_volume)
+    {
+      struct statfs *mounts;
+      int n_mounts;
+
+      hf_volume_get_mounts(&mounts, &n_mounts);
+      hf_volume_device_update_mount_properties(device, mounts, n_mounts);
+    }
+
+  g_free(partition_offset_str);
+  g_free(partition_size_str);
+  g_free(index_str);
+  g_free(type_str);
+  g_free(geom_class);
+
+  hf_block_device_complete(device, storage_device, is_volume);
+
+  hf_device_add(device);
+
+ end:
+  return device;
+}
+
+static void
+hf_volume_update_mounts (void)
+{
+  GSList *l;
+  struct statfs *mounts;
+  int n_mounts;
+
+  hf_volume_get_mounts(&mounts, &n_mounts);
+
+  HF_LIST_FOREACH(l, hald_get_gdl()->devices)
+    {
+      HalDevice *device = l->data;
+
+      if (hal_device_property_get_bool(device, "block.is_volume"))
+	{
+	  device_property_atomic_update_begin();
+	  hf_volume_device_update_mount_properties(device, mounts, n_mounts);
+	  device_property_atomic_update_end();
+	}
+    }
+}
+
+void
+hf_volume_update_mount (HalDevice *device)
+{
+  struct statfs *mounts;
+  int n_mounts;
+
+  g_return_if_fail(device != NULL);
+
+  hf_volume_get_mounts(&mounts, &n_mounts);
+
+  device_property_atomic_update_begin();
+  hf_volume_device_update_mount_properties(device, mounts, n_mounts);
+  device_property_atomic_update_end();
+}
+
+static gboolean
+hf_volume_update_mounts_timeout_cb (gpointer data)
+{
+  if (hf_is_waiting)
+    return TRUE;
+
+  hf_volume_update_mounts();
+
+  return TRUE;
+}
+
+static void
+hf_volume_init (void)
+{
+  g_timeout_add(3000, hf_volume_update_mounts_timeout_cb, NULL);
+}
+
+static gboolean
+hf_volume_device_reprobe (HalDevice *device)
+{
+  const char *storage_device_udi;
+  HalDevice *storage_device;
+
+  storage_device_udi = hal_device_property_get_string(device, "block.storage_device");
+  if (! storage_device_udi || ! strcmp(storage_device_udi, hal_device_get_udi(device)))
+    return FALSE;		/* not a child of a storage device */
+
+  storage_device = hal_device_store_find(hald_get_gdl(), storage_device_udi);
+  g_assert(storage_device != NULL);
+
+  hf_device_remove_tree(device);
+  osspec_probe();
+
+  hf_storage_device_probe(storage_device, TRUE);
+
+  return TRUE;
+}
+
+HFHandler hf_volume_handler = {
+  .init =		hf_volume_init,
+  .device_reprobe =	hf_volume_device_reprobe
+};
diff --git a/hald/freebsd/hf-volume.h b/hald/freebsd/hf-volume.h
new file mode 100644
index 0000000..b20f271
--- /dev/null
+++ b/hald/freebsd/hf-volume.h
@@ -0,0 +1,49 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hf-volume.h : volume device support
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifndef _HF_VOLUME_H
+#define _HF_VOLUME_H
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include "hf-osspec.h"
+
+extern HFHandler hf_volume_handler;
+
+void hf_volume_update_mount (HalDevice *device);
+
+HalDevice *hf_volume_device_add (HalDevice *parent,
+				 HalDevice *storage_device,
+				 gboolean has_children,
+				 gboolean is_swap,
+				 const char *devname,
+				 const char *gclass,
+				 const char *gstr_type,
+				 int gtype,
+				 int gindex,
+				 dbus_int64_t partition_offset,
+				 dbus_int64_t partition_size);
+
+#endif /* _HF_VOLUME_H */
diff --git a/hald/freebsd/libprobe/Makefile.am b/hald/freebsd/libprobe/Makefile.am
new file mode 100644
index 0000000..4174bcd
--- /dev/null
+++ b/hald/freebsd/libprobe/Makefile.am
@@ -0,0 +1,19 @@
+INCLUDES = \
+	-DPACKAGE_SYSCONF_DIR=\""$(sysconfdir)"\" \
+	-DPACKAGE_DATA_DIR=\""$(datadir)"\" \
+	-DPACKAGE_BIN_DIR=\""$(bindir)"\" \
+	-DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \
+	-DPACKAGE_LOCALSTATEDIR=\""$(localstatedir)"\" \
+	-I$(top_srcdir) -I.. \
+	@DBUS_CFLAGS@
+
+if HALD_COMPILE_FREEBSD
+lib_LTLIBRARIES = libhald_freebsd_probe.la
+endif
+
+libhald_freebsd_probe_la_SOURCES = \
+	hfp.c		\
+	hfp.h		\
+	hfp-cdrom.c	\
+	hfp-cdrom.h
+libhald_freebsd_probe_la_LDFLAGS = $(top_builddir)/libhal/libhal.la -lcam
diff --git a/hald/freebsd/libprobe/hfp-cdrom.c b/hald/freebsd/libprobe/hfp-cdrom.c
new file mode 100644
index 0000000..286dd29
--- /dev/null
+++ b/hald/freebsd/libprobe/hfp-cdrom.c
@@ -0,0 +1,238 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hfp-cdrom.c : ATAPI/SCSI CD-ROM abstraction layer
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/ata.h>
+#include <stdio.h>
+#include <camlib.h>
+#include <cam/scsi/scsi_message.h>
+
+#include "hfp.h"
+#include "hfp-cdrom.h"
+
+struct _HFPCDROM
+{
+  struct cam_device	*cam;	/* for SCSI drives */
+  int			fd;	/* for ATAPI drives */
+  boolean		fd_owned;
+};
+
+static HFPCDROM *
+hfp_cdrom_new_real (boolean has_fd, int fd, const char *path)
+{
+  HFPCDROM *cdrom = NULL;
+  struct cam_device *cam;
+
+  assert(path != NULL);
+
+  /* cam_open_device() fails unless we use O_RDWR */
+  cam = cam_open_device(path, O_RDWR);
+  if (cam)
+    {
+      cdrom = hfp_new0(HFPCDROM, 1);
+      cdrom->cam = cam;
+      cdrom->fd = -1;
+    }
+  else
+    {
+      if (! has_fd)
+	fd = open(path, O_RDONLY);
+      if (fd >= 0)
+	{
+	  cdrom = hfp_new0(HFPCDROM, 1);
+	  cdrom->fd = fd;
+	  cdrom->fd_owned = ! has_fd;
+	}
+    }
+
+  return cdrom;
+}
+
+HFPCDROM *
+hfp_cdrom_new (const char *path)
+{
+  assert(path != NULL);
+
+  return hfp_cdrom_new_real(FALSE, -1, path);
+}
+
+HFPCDROM *
+hfp_cdrom_new_from_fd (int fd, const char *path)
+{
+  assert(path != NULL);
+
+  return hfp_cdrom_new_real(TRUE, fd, path);
+}
+
+boolean
+hfp_cdrom_send_ccb (HFPCDROM *cdrom,
+		    char ccb[16],
+		    int timeout,
+		    HFPCDROMDirection direction,
+		    void *data,
+		    int len,
+		    char **err)
+{
+  assert(cdrom != NULL);
+  assert(ccb != NULL);
+  assert(direction == HFP_CDROM_DIRECTION_NONE
+	 || direction == HFP_CDROM_DIRECTION_IN
+	 || direction == HFP_CDROM_DIRECTION_OUT);
+  assert(direction == HFP_CDROM_DIRECTION_NONE || data != NULL);
+
+  if (cdrom->fd >= 0)		/* ATAPI transport */
+    {
+#ifdef IOCATAREQUEST
+      struct ata_ioc_request req;
+
+      memset(&req, 0, sizeof(req));
+      req.flags = ATA_CMD_ATAPI;
+      req.timeout = timeout;
+      memcpy(req.u.atapi.ccb, ccb, 16);
+
+      if (data)
+	{
+	  static int atapi_direction[] = { 0, ATA_CMD_READ, ATA_CMD_WRITE };
+
+	  req.flags |= atapi_direction[direction];
+	  req.data = data;
+	  req.count = len;
+	}
+
+      if (ioctl(cdrom->fd, IOCATAREQUEST, &req) < 0)
+	{
+	  if (err)
+	    *err = hfp_strdup_printf("IOCATAREQUEST failure: %s", strerror(errno));
+	  return FALSE;
+	}
+      if (req.error != 0)
+	{
+	  if (err)
+	    *err = hfp_strdup_printf("ATAPI error %i", req.error);
+	  return FALSE;
+	}
+#else
+      struct ata_cmd iocmd;
+
+      memset(&iocmd, 0, sizeof(iocmd));
+      iocmd.u.request.flags = ATA_CMD_ATAPI;
+      iocmd.u.request.timeout = timeout;
+      iocmd.cmd = ATAREQUEST;
+      iocmd.device = -1;
+      memcpy(iocmd.u.request.u.atapi.ccb, ccb, 16);
+
+      if (data)
+        {
+	  static int atapi_direction[] = { 0, ATA_CMD_READ, ATA_CMD_WRITE };
+
+	  iocmd.u.request.flags |= atapi_direction[direction];
+	  iocmd.u.request.data = data;
+	  iocmd.u.request.count = len;
+	}
+
+      if (ioctl(cdrom->fd, IOCATA, &iocmd) < 0)
+        {
+	  if (err)
+	    *err = hfp_strdup_printf("IOCATA failure: %s", strerror(errno));
+	  return FALSE;
+	}
+      if (iocmd.u.request.error != 0)
+        {
+	  if (err)
+	    *err = hfp_strdup_printf("ATAPI error %i", iocmd.u.request.error);
+	  return FALSE;
+	}
+#endif
+    }
+  else				/* SCSI transport */
+    {
+      union ccb cam_ccb;
+      static int scsi_direction[] = { CAM_DIR_NONE, CAM_DIR_IN, CAM_DIR_OUT };
+
+      memset(&cam_ccb, 0, sizeof(cam_ccb));
+
+      cam_ccb.ccb_h.path_id = cdrom->cam->path_id;
+      cam_ccb.ccb_h.target_id = cdrom->cam->target_id;
+      cam_ccb.ccb_h.target_lun = cdrom->cam->target_lun;
+
+      cam_fill_csio(&cam_ccb.csio,
+		    1,
+		    NULL,
+		    scsi_direction[direction],
+		    MSG_SIMPLE_Q_TAG,
+		    data,
+		    len,
+		    sizeof(cam_ccb.csio.sense_data),
+		    16,
+		    timeout * 1000);
+
+      memcpy(cam_ccb.csio.cdb_io.cdb_bytes, ccb, 16);
+
+      if (cam_send_ccb(cdrom->cam, &cam_ccb) == -1)
+	{
+	  if (err)
+	    *err = hfp_strdup_printf("cam_send_ccb() failure: %s", strerror(errno));
+	}
+      if ((cam_ccb.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)
+	{
+	  if (err)
+	    *err = hfp_strdup_printf("CCB request failed with status %i", cam_ccb.ccb_h.status & CAM_STATUS_MASK);
+	  return FALSE;
+	}
+    }
+
+  return TRUE;
+}
+
+boolean
+hfp_cdrom_test_unit_ready (HFPCDROM *cdrom)
+{
+  static char ccb[16] = { HFP_CDROM_TEST_UNIT_READY };
+
+  assert(cdrom != NULL);
+
+  return hfp_cdrom_send_ccb(cdrom, ccb, 10, HFP_CDROM_DIRECTION_NONE, NULL, 0, NULL);
+}
+
+void
+hfp_cdrom_free (HFPCDROM *cdrom)
+{
+  assert(cdrom != NULL);
+
+  if (cdrom->cam)
+    cam_close_device(cdrom->cam);
+  if (cdrom->fd_owned && cdrom->fd >= 0)
+    close(cdrom->fd);
+
+  hfp_free(cdrom);
+}
diff --git a/hald/freebsd/libprobe/hfp-cdrom.h b/hald/freebsd/libprobe/hfp-cdrom.h
new file mode 100644
index 0000000..6b69f0f
--- /dev/null
+++ b/hald/freebsd/libprobe/hfp-cdrom.h
@@ -0,0 +1,163 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hfp-cdrom.h : ATAPI/SCSI CD-ROM abstraction layer
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifndef _HFP_CDROM_H
+#define _HFP_CDROM_H
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <sys/types.h>
+
+#include "hfp.h"
+
+typedef struct _HFPCDROM HFPCDROM;
+
+typedef enum
+{
+  HFP_CDROM_DIRECTION_NONE,
+  HFP_CDROM_DIRECTION_IN,
+  HFP_CDROM_DIRECTION_OUT
+} HFPCDROMDirection;
+
+/* ATAPI/SCSI commands */
+enum
+{
+  HFP_CDROM_TEST_UNIT_READY			= 0x00,
+  HFP_CDROM_GET_EVENT_STATUS_NOTIFICATION	= 0x4a,
+  HFP_CDROM_MODE_SENSE_BIG			= 0x5a
+};
+
+/* structure from sys/dev/ata/atapi-cd.h */
+typedef struct
+{
+  /* mode page data header */
+  u_int16_t   data_length;
+  u_int8_t    medium_type;
+#define HFP_CDROM_MST_TYPE_MASK_LOW	0x0f
+#define HFP_CDROM_MST_FMT_NONE		0x00
+#define HFP_CDROM_MST_DATA_120		0x01
+#define HFP_CDROM_MST_AUDIO_120		0x02
+#define HFP_CDROM_MST_COMB_120		0x03
+#define HFP_CDROM_MST_PHOTO_120		0x04
+#define HFP_CDROM_MST_DATA_80		0x05
+#define HFP_CDROM_MST_AUDIO_80		0x06
+#define HFP_CDROM_MST_COMB_80		0x07
+#define HFP_CDROM_MST_PHOTO_80		0x08
+
+#define HFP_CDROM_MST_TYPE_MASK_HIGH	0x70
+#define HFP_CDROM_MST_CDROM		0x00
+#define HFP_CDROM_MST_CDR		0x10
+#define HFP_CDROM_MST_CDRW		0x20
+
+#define HFP_CDROM_MST_NO_DISC		0x70
+#define HFP_CDROM_MST_DOOR_OPEN		0x71
+#define HFP_CDROM_MST_FMT_ERROR		0x72
+
+  u_int8_t    dev_spec;
+  u_int16_t   unused;
+  u_int16_t   blk_desc_len;
+
+  /* capabilities page */
+  u_int8_t    page_code;
+#define HFP_CDROM_CAP_PAGE		0x2a
+
+  u_int8_t    param_len;
+
+  u_int16_t   media;
+#define HFP_CDROM_MST_READ_CDR		0x0001
+#define HFP_CDROM_MST_READ_CDRW		0x0002
+#define HFP_CDROM_MST_READ_PACKET	0x0004
+#define HFP_CDROM_MST_READ_DVDROM	0x0008
+#define HFP_CDROM_MST_READ_DVDR		0x0010
+#define HFP_CDROM_MST_READ_DVDRAM	0x0020
+#define HFP_CDROM_MST_WRITE_CDR		0x0100
+#define HFP_CDROM_MST_WRITE_CDRW	0x0200
+#define HFP_CDROM_MST_WRITE_TEST	0x0400
+#define HFP_CDROM_MST_WRITE_DVDR	0x1000
+#define HFP_CDROM_MST_WRITE_DVDRAM	0x2000
+
+  u_int16_t   capabilities;
+#define HFP_CDROM_MSTAUDIO_PLAY		0x0001
+#define HFP_CDROM_MST_COMPOSITE		0x0002
+#define HFP_CDROM_MST_AUDIO_P1		0x0004
+#define HFP_CDROM_MST_AUDIO_P2		0x0008
+#define HFP_CDROM_MST_MODE2_f1		0x0010
+#define HFP_CDROM_MST_MODE2_f2		0x0020
+#define HFP_CDROM_MST_MULTISESSION	0x0040
+#define HFP_CDROM_MST_BURNPROOF		0x0080
+#define HFP_CDROM_MST_READ_CDDA		0x0100
+#define HFP_CDROM_MST_CDDA_STREAM	0x0200
+#define HFP_CDROM_MST_COMBINED_RW	0x0400
+#define HFP_CDROM_MST_CORRECTED_RW	0x0800
+#define HFP_CDROM_MST_SUPPORT_C2	0x1000
+#define HFP_CDROM_MST_ISRC		0x2000
+#define HFP_CDROM_MST_UPC		0x4000
+
+  u_int8_t    mechanism;
+#define HFP_CDROM_MST_LOCKABLE		0x01
+#define HFP_CDROM_MST_LOCKED		0x02
+#define HFP_CDROM_MST_PREVENT		0x04
+#define HFP_CDROM_MST_EJECT		0x08
+#define HFP_CDROM_MST_MECH_MASK		0xe0
+#define HFP_CDROM_MST_MECH_CADDY	0x00
+#define HFP_CDROM_MST_MECH_TRAY		0x20
+#define HFP_CDROM_MST_MECH_POPUP	0x40
+#define HFP_CDROM_MST_MECH_CHANGER	0x80
+#define HFP_CDROM_MST_MECH_CARTRIDGE	0xa0
+
+  uint8_t     audio;
+#define HFP_CDROM_MST_SEP_VOL		0x01
+#define HFP_CDROM_MST_SEP_MUTE		0x02
+
+  u_int16_t   max_read_speed;		/* max raw data rate in bytes/1000 */
+  u_int16_t   max_vol_levels;		/* number of discrete volume levels */
+  u_int16_t   buf_size;			/* internal buffer size in bytes/1024 */
+  u_int16_t   cur_read_speed;		/* current data rate in bytes/1000  */
+
+  u_int8_t    reserved3;
+  u_int8_t    misc;
+
+  u_int16_t   max_write_speed;		/* max raw data rate in bytes/1000 */
+  u_int16_t   cur_write_speed;		/* current data rate in bytes/1000  */
+  u_int16_t   copy_protect_rev;
+  u_int16_t   reserved4;
+} HFPCDROMCapabilities;
+
+HFPCDROM *hfp_cdrom_new (const char *path);
+HFPCDROM *hfp_cdrom_new_from_fd (int fd, const char *path);
+
+boolean hfp_cdrom_send_ccb (HFPCDROM *cdrom,
+			    char ccb[16],
+			    int timeout,
+			    HFPCDROMDirection direction,
+			    void *data,
+			    int len,
+			    char **err);
+
+boolean hfp_cdrom_test_unit_ready (HFPCDROM *cdrom);
+
+void hfp_cdrom_free (HFPCDROM *cdrom);
+
+#endif /* _HFP_CDROM_H */
diff --git a/hald/freebsd/libprobe/hfp.c b/hald/freebsd/libprobe/hfp.c
new file mode 100644
index 0000000..d51ca61
--- /dev/null
+++ b/hald/freebsd/libprobe/hfp.c
@@ -0,0 +1,270 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hfp.c : utility library for HAL probers
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <syslog.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <libgen.h>
+
+#include "hfp.h"
+
+LibHalContext *hfp_ctx = NULL;
+char *hfp_udi = NULL;
+DBusError hfp_error;
+
+static char *log_domain;
+static boolean verbose = FALSE;
+static boolean use_syslog = FALSE;
+
+boolean
+hfp_init (int argc, char **argv)
+{
+  char *psname = NULL;
+
+  assert(hfp_ctx == NULL);
+
+  if (argc > 0)
+    psname = basename(argv[0]);
+
+  if (psname)
+    log_domain = hfp_strdup(psname);
+  else
+    log_domain = "hfp";
+
+  if (getenv("HALD_VERBOSE"))
+    verbose = TRUE;
+  if (getenv("HALD_USE_SYSLOG"))
+    use_syslog = TRUE;
+
+  hfp_udi = getenv("UDI");
+  if (! hfp_udi)
+    return FALSE;
+
+  dbus_error_init(&hfp_error);
+  hfp_ctx = libhal_ctx_init_direct(&hfp_error);
+  dbus_error_free(&hfp_error);
+
+  return TRUE;
+}
+
+static void
+hfp_no_memory (void)
+{
+  hfp_critical("memory allocation failure");
+  abort();
+}
+
+void *
+hfp_malloc (size_t size)
+{
+  void *mem;
+
+  mem = malloc(size);
+  if (! mem)
+    hfp_no_memory();
+
+  return mem;
+}
+
+void *
+hfp_malloc0 (size_t size)
+{
+  void *mem;
+
+  mem = calloc(1, size);
+  if (! mem)
+    hfp_no_memory();
+
+  return mem;
+}
+
+char *
+hfp_strdup (const char *str)
+{
+  char *copy;
+
+  if (str)
+    {
+      copy = strdup(str);
+      if (! copy)
+	hfp_no_memory();
+    }
+  else
+    copy = NULL;
+
+  return copy;
+}
+
+char *
+hfp_strndup (const char *str, size_t size)
+{
+  char *copy;
+
+  if (str)
+    {
+      copy = hfp_new(char, size + 1);
+      strncpy(copy, str, size);
+      copy[size] = 0;
+    }
+  else
+    copy = NULL;
+
+  return copy;
+}
+
+char *
+hfp_strdup_printf (const char *format, ...)
+{
+  va_list args;
+  char *str;
+
+  assert(format != NULL);
+
+  va_start(args, format);
+  if (vasprintf(&str, format, args) == -1)
+    hfp_no_memory();
+  va_end(args);
+
+  return str;
+}
+
+void
+hfp_free (void *mem)
+{
+  if (mem)
+    free(mem);
+}
+
+void
+hfp_logv (HFPLogLevel log_level, const char *format, va_list args)
+{
+  assert(log_level == HFP_LOG_LEVEL_DEBUG
+	 || log_level == HFP_LOG_LEVEL_INFO
+	 || log_level == HFP_LOG_LEVEL_WARNING
+	 || log_level == HFP_LOG_LEVEL_CRITICAL);
+  assert(format != NULL);
+
+  if (verbose)
+    {
+      static const char *log_levels[] = { "debug", "info", "WARNING", "CRITICAL" };
+      static const int syslog_levels[] = { LOG_DEBUG, LOG_INFO, LOG_WARNING, LOG_CRIT };
+      if (use_syslog)
+        vsyslog(syslog_levels[log_level], format, args);
+      else
+        {
+          fprintf(stderr, "%s %s: ", log_domain, log_levels[log_level]);
+	  vfprintf(stderr, format, args);
+	  fprintf(stderr, "\n");
+	}
+    }
+}
+
+#define LOG_FUNCTION(name, level) \
+  void name (const char *format, ...)			\
+  {							\
+    va_list args;					\
+							\
+    assert(format != NULL);				\
+							\
+    va_start(args, format);				\
+    hfp_logv(HFP_LOG_LEVEL_ ## level, format, args);	\
+    va_end(args);					\
+  }
+
+LOG_FUNCTION(hfp_info, INFO)
+LOG_FUNCTION(hfp_warning, WARNING)
+LOG_FUNCTION(hfp_critical, CRITICAL)
+LOG_FUNCTION(volume_id_log, DEBUG) /* used by volume_id */
+
+boolean
+hfp_getenv_bool (const char *variable)
+{
+  char *value;
+
+  assert(variable != NULL);
+
+  value = getenv(variable);
+
+  return value && ! strcmp(value, "true");
+}
+
+void
+hfp_gettimeofday (struct timeval *t)
+{
+  int status;
+
+  assert(t != NULL);
+
+  status = gettimeofday(t, NULL);
+  assert(status == 0);
+}
+
+/* timeval functions from sys/kern/kern_time.c */
+
+static void
+hfp_timevalfix (struct timeval *t)
+{
+  assert(t != NULL);
+
+  if (t->tv_usec < 0)
+    {
+      t->tv_sec--;
+      t->tv_usec += 1000000;
+    }
+  if (t->tv_usec >= 1000000)
+    {
+      t->tv_sec++;
+      t->tv_usec -= 1000000;
+    }
+}
+
+void
+hfp_timevaladd (struct timeval *t1, const struct timeval *t2)
+{
+  assert(t1 != NULL);
+  assert(t2 != NULL);
+
+  t1->tv_sec += t2->tv_sec;
+  t1->tv_usec += t2->tv_usec;
+
+  hfp_timevalfix(t1);
+}
+
+void
+hfp_timevalsub (struct timeval *t1, const struct timeval *t2)
+{
+  assert(t1 != NULL);
+  assert(t2 != NULL);
+
+  t1->tv_sec -= t2->tv_sec;
+  t1->tv_usec -= t2->tv_usec;
+
+  hfp_timevalfix(t1);
+}
diff --git a/hald/freebsd/libprobe/hfp.h b/hald/freebsd/libprobe/hfp.h
new file mode 100644
index 0000000..1d95811
--- /dev/null
+++ b/hald/freebsd/libprobe/hfp.h
@@ -0,0 +1,97 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * hfp.h : utility library for HAL probers
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifndef _HFP_H
+#define _HFP_H
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "libhal/libhal.h"
+
+/* from GLib */
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ > 4)
+#define HFP_GNUC_PRINTF(format_idx, arg_idx) \
+  __attribute__((__format__(__printf__, format_idx, arg_idx)))
+#else
+#define HFP_GNUC_PRINTF(format_idx, arg_idx)
+#endif
+
+/* from GLib */
+#define HFP_N_ELEMENTS(arr)		(sizeof(arr) / sizeof((arr)[0]))
+
+typedef int boolean;
+
+typedef enum
+{
+  HFP_LOG_LEVEL_DEBUG,
+  HFP_LOG_LEVEL_INFO,
+  HFP_LOG_LEVEL_WARNING,
+  HFP_LOG_LEVEL_CRITICAL
+} HFPLogLevel;
+
+extern LibHalContext *hfp_ctx;
+extern char *hfp_udi;
+extern DBusError hfp_error;
+
+boolean hfp_init (int argc, char **argv);
+
+void *hfp_malloc (size_t size);
+void *hfp_malloc0 (size_t size);
+
+#define hfp_new(type, number)	((type *) hfp_malloc(sizeof(type) * number))
+#define hfp_new0(type, number)	((type *) hfp_malloc0(sizeof(type) * number))
+
+char *hfp_strdup (const char *str);
+char *hfp_strndup (const char *str, size_t size);
+char *hfp_strdup_printf (const char *format, ...) HFP_GNUC_PRINTF(1, 2);
+
+void hfp_free (void *mem);
+
+void hfp_logv (HFPLogLevel log_level, const char *format, va_list args);
+
+void hfp_info (const char *format, ...) HFP_GNUC_PRINTF(1, 2);
+void hfp_warning (const char *format, ...) HFP_GNUC_PRINTF(1, 2);
+void hfp_critical (const char *format, ...) HFP_GNUC_PRINTF(1, 2);
+
+/* this is used by volume_id */
+void volume_id_log (const char *format, ...) HFP_GNUC_PRINTF(1, 2);
+
+boolean hfp_getenv_bool (const char *variable);
+
+void hfp_gettimeofday (struct timeval *t);
+void hfp_timevaladd (struct timeval *t1, const struct timeval *t2);
+void hfp_timevalsub (struct timeval *t1, const struct timeval *t2);
+
+/* from sys/time.h (_KERNEL) */
+#define hfp_timevalcmp(t1, t2, cmp) \
+  (((t1)->tv_sec == (t2)->tv_sec	\
+    ? ((t1)->tv_usec cmp (t2)->tv_usec)	\
+    : ((t1)->tv_sec cmp (t2)->tv_sec)))
+
+#endif /* _HFP_H */
diff --git a/hald/freebsd/osspec.c b/hald/freebsd/osspec.c
new file mode 100644
index 0000000..8959019
--- /dev/null
+++ b/hald/freebsd/osspec.c
@@ -0,0 +1,143 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * osspec.c : HAL backend for FreeBSD
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <string.h>
+
+#include "../ids.h"
+#include "../osspec.h"
+
+#include "hf-util.h"
+#include "hf-osspec.h"
+#include "hf-acpi.h"
+#include "hf-ata.h"
+#include "hf-computer.h"
+#include "hf-devd.h"
+#include "hf-devtree.h"
+#include "hf-net.h"
+#include "hf-pci.h"
+#include "hf-scsi.h"
+#include "hf-serial.h"
+#include "hf-sound.h"
+#include "hf-storage.h"
+#include "hf-usb.h"
+#include "hf-volume.h"
+
+/* the order matters: PCI devices must be created before their children, etc */
+static HFHandler *handlers[] = {
+  &hf_pci_handler,
+  &hf_devtree_handler,
+  &hf_usb_handler,
+  &hf_ata_handler,
+  &hf_scsi_handler,
+  &hf_storage_handler,
+  &hf_volume_handler,
+  &hf_net_handler,
+  &hf_serial_handler,
+  &hf_acpi_handler,
+  &hf_sound_handler,
+  &hf_devd_handler
+};
+
+void
+osspec_privileged_init (void)
+{
+  int i;
+
+  for (i = 0; i < (int) G_N_ELEMENTS(handlers); i++)
+    if (handlers[i]->privileged_init)
+      handlers[i]->privileged_init();
+}
+
+void
+osspec_init (void)
+{
+  int i;
+
+  pci_ids_init();
+
+  for (i = 0; i < (int) G_N_ELEMENTS(handlers); i++)
+    if (handlers[i]->init)
+      handlers[i]->init();
+}
+
+void
+osspec_probe (void)
+{
+  if (hf_computer_device_add())
+    {
+      int i;
+
+      for (i = 0; i < (int) G_N_ELEMENTS(handlers); i++)
+	if (handlers[i]->probe)
+	  handlers[i]->probe();
+    }
+
+  if (hald_is_initialising)
+    osspec_probe_done();
+}
+
+gboolean
+osspec_device_rescan (HalDevice *d)
+{
+  int i;
+
+  for (i = 0; i < (int) G_N_ELEMENTS(handlers); i++)
+    if (handlers[i]->device_rescan && handlers[i]->device_rescan(d))
+      return TRUE;
+
+  return FALSE;
+}
+
+gboolean
+osspec_device_reprobe (HalDevice *d)
+{
+  int i;
+
+  for (i = 0; i < (int) G_N_ELEMENTS(handlers); i++)
+    if (handlers[i]->device_reprobe && handlers[i]->device_reprobe(d))
+      goto end;
+
+  /* no match, default action */
+
+  hf_device_remove_tree(d);
+  osspec_probe();
+
+ end:
+  return FALSE;			/* this is what linux2 returns */
+}
+
+void
+osspec_refresh_mount_state_for_block_device (HalDevice *d)
+{
+  hf_volume_update_mount(d);
+}
+
+DBusHandlerResult
+osspec_filter_function (DBusConnection *connection, DBusMessage *message, void *user_data)
+{
+  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
diff --git a/hald/freebsd/probing/Makefile.am b/hald/freebsd/probing/Makefile.am
new file mode 100644
index 0000000..f98c620
--- /dev/null
+++ b/hald/freebsd/probing/Makefile.am
@@ -0,0 +1,44 @@
+AM_CPPFLAGS = \
+	-DPACKAGE_SYSCONF_DIR=\""$(sysconfdir)"\" \
+	-DPACKAGE_DATA_DIR=\""$(datadir)"\" \
+	-DPACKAGE_BIN_DIR=\""$(bindir)"\" \
+	-DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\" \
+	-DPACKAGE_LOCALSTATEDIR=\""$(localstatedir)"\" \
+	-I$(top_srcdir) \
+	@DBUS_CFLAGS@
+
+if HALD_COMPILE_FREEBSD
+libexec_PROGRAMS = \
+	hald-probe-hiddev	\
+	hald-probe-scsi		\
+	hald-probe-smbios	\
+	hald-probe-storage	\
+	hald-probe-volume
+endif
+
+hald_probe_hiddev_SOURCES = probe-hiddev.c
+hald_probe_hiddev_LDADD = \
+	$(top_builddir)/hald/freebsd/libprobe/libhald_freebsd_probe.la	\
+	-lusbhid
+
+hald_probe_smbios_SOURCES = probe-smbios.c
+hald_probe_smbios_LDADD = \
+	$(top_builddir)/hald/freebsd/libprobe/libhald_freebsd_probe.la
+
+hald_probe_scsi_SOURCES = probe-scsi.c
+hald_probe_scsi_LDADD = \
+	$(top_builddir)/hald/freebsd/libprobe/libhald_freebsd_probe.la
+
+hald_probe_storage_SOURCES = freebsd_dvd_rw_utils.c freebsd_dvd_rw_utils.h probe-storage.c
+hald_probe_storage_CPPFLAGS = $(AM_CPPFLAGS) @GLIB_CFLAGS@ @VOLUME_ID_CFLAGS@
+hald_probe_storage_LDADD = \
+	@GLIB_LIBS@							\
+	@VOLUME_ID_LIBS@						\
+	$(top_builddir)/hald/freebsd/libprobe/libhald_freebsd_probe.la
+
+hald_probe_volume_SOURCES = freebsd_dvd_rw_utils.c freebsd_dvd_rw_utils.h probe-volume.c
+hald_probe_volume_CPPFLAGS = $(AM_CPPFLAGS) @GLIB_CFLAGS@ @VOLUME_ID_CFLAGS@
+hald_probe_volume_LDADD = \
+	@GLIB_LIBS@							\
+	@VOLUME_ID_LIBS@						\
+	$(top_builddir)/hald/freebsd/libprobe/libhald_freebsd_probe.la
diff --git a/hald/freebsd/probing/freebsd_dvd_rw_utils.c b/hald/freebsd/probing/freebsd_dvd_rw_utils.c
new file mode 100644
index 0000000..351e50d
--- /dev/null
+++ b/hald/freebsd/probing/freebsd_dvd_rw_utils.c
@@ -0,0 +1,717 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*-
+ *
+ * This is part of dvd+rw-tools by Andy Polyakov <appro at fy.chalmers.se>
+ *
+ * Use-it-on-your-own-risk, GPL bless...
+ *
+ * For further details see http://fy.chalmers.se/~appro/linux/DVD+RW/
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ata.h>
+
+#include <glib.h>
+
+#include "freebsd_dvd_rw_utils.h"
+
+typedef enum {
+	NONE = HFP_CDROM_DIRECTION_NONE,
+	READ = HFP_CDROM_DIRECTION_IN,
+	WRITE = HFP_CDROM_DIRECTION_OUT
+} Direction;
+
+typedef struct ScsiCommand ScsiCommand;
+
+struct ScsiCommand {
+	HFPCDROM		*cdrom;
+	char			ccb[16];
+};
+
+static ScsiCommand *
+scsi_command_new_from_cdrom (HFPCDROM *cdrom)
+{
+	ScsiCommand *cmd;
+
+	cmd = g_new0 (ScsiCommand, 1);
+	cmd->cdrom = cdrom;
+
+	return cmd;
+}
+
+static void
+scsi_command_free (ScsiCommand * cmd)
+{
+	free (cmd);
+}
+
+static void
+scsi_command_init (ScsiCommand * cmd, size_t i, int arg)
+{
+	cmd->ccb[i] = arg;
+}
+
+static int
+scsi_command_transport (ScsiCommand * cmd, Direction dir, void *buf,
+			size_t sz)
+{
+	if (hfp_cdrom_send_ccb(cmd->cdrom, cmd->ccb, 10, dir, buf, sz, NULL))
+		return 0;
+	else
+		return -1;
+}
+
+int
+get_dvd_r_rw_profile (HFPCDROM *cdrom)
+{
+	ScsiCommand *cmd;
+	int retval = 0;
+	unsigned char page[20];
+	unsigned char *list;
+	int i, len;
+
+	g_return_val_if_fail (cdrom != NULL, -1);
+
+	cmd = scsi_command_new_from_cdrom (cdrom);
+
+	scsi_command_init (cmd, 0, 0x46);
+	scsi_command_init (cmd, 1, 2);
+	scsi_command_init (cmd, 8, 8);
+	scsi_command_init (cmd, 9, 0);
+	if (scsi_command_transport (cmd, READ, page, 8)) {
+		/* GET CONFIGURATION failed */
+		scsi_command_free (cmd);
+		return -1;
+	}
+
+	/* See if it's 2 gen drive by checking if DVD+R profile is an option */
+	len = 4 + (page[0] << 24 | page[1] << 16 | page[2] << 8 | page[3]);
+	if (len > 264) {
+		scsi_command_free (cmd);
+		/* insane profile list length */
+		return -1;
+	}
+
+	list = g_new (unsigned char, len);
+
+	scsi_command_init (cmd, 0, 0x46);
+	scsi_command_init (cmd, 1, 2);
+	scsi_command_init (cmd, 7, len >> 8);
+	scsi_command_init (cmd, 8, len);
+	scsi_command_init (cmd, 9, 0);
+	if (scsi_command_transport (cmd, READ, list, len)) {
+		/* GET CONFIGURATION failed */
+		scsi_command_free (cmd);
+		free (list);
+		return -1;
+	}
+
+	for (i = 12; i < list[11]; i += 4) {
+		int profile = (list[i] << 8 | list[i + 1]);
+		/* 0x13: DVD-RW Restricted Overwrite
+		 * 0x14: DVD-RW Sequential
+		 * 0x1B: DVD+R
+		 * 0x1A: DVD+RW
+		 * 0x2A: DVD+RW DL
+		 * 0x2B: DVD+R DL
+		 */
+
+		switch (profile) {
+			case 0x13:
+			case 0x14:
+				retval |= DRIVE_CDROM_CAPS_DVDRW;
+				break;
+			case 0x1B:
+				retval |= DRIVE_CDROM_CAPS_DVDPLUSR;
+				break;
+			case 0x1A:
+				retval |= DRIVE_CDROM_CAPS_DVDPLUSRW;
+				break;
+			case 0x2A:
+				retval |= DRIVE_CDROM_CAPS_DVDPLUSRWDL;
+				break;
+			case 0x2B:
+				retval |= DRIVE_CDROM_CAPS_DVDPLUSRDL;
+				break;
+			default:
+				break;
+		}
+	}
+
+	scsi_command_free (cmd);
+	free (list);
+
+	return retval;
+
+}
+
+static unsigned char *
+pull_page2a_from_cdrom (HFPCDROM *cdrom)
+{
+	ScsiCommand *cmd;
+	unsigned char header[12], *page2A;
+	unsigned int len, bdlen;
+
+	g_return_val_if_fail (cdrom != NULL, NULL);
+
+	cmd = scsi_command_new_from_cdrom (cdrom);
+
+	scsi_command_init (cmd, 0, 0x5A);	/* MODE SENSE */
+	scsi_command_init (cmd, 1, 0x08);	/* Disable Block Descriptors */
+	scsi_command_init (cmd, 2, 0x2A);	/* Capabilities and Mechanical Status */
+	scsi_command_init (cmd, 8, sizeof (header));	/* header only to start with */
+	scsi_command_init (cmd, 9, 0);
+
+	if (scsi_command_transport (cmd, READ, header, sizeof (header))) {
+		/* MODE SENSE failed */
+		scsi_command_free (cmd);
+		return NULL;
+	}
+
+	len = (header[0] << 8 | header[1]) + 2;
+	bdlen = header[6] << 8 | header[7];
+
+	/* should never happen as we set "DBD" above */
+	if (bdlen) {
+		if (len < (8 + bdlen + 30)) {
+			/* LUN impossible to bear with */
+			scsi_command_free (cmd);
+			return NULL;
+		}
+	} else if (len < (8 + 2 + (unsigned int) header[9])) {
+		/* SANYO does this. */
+		len = 8 + 2 + header[9];
+	}
+
+	page2A = g_new (unsigned char, len);
+	if (page2A == NULL) {
+		/* ENOMEM */
+		scsi_command_free (cmd);
+		return NULL;
+	}
+
+	scsi_command_init (cmd, 0, 0x5A);	/* MODE SENSE */
+	scsi_command_init (cmd, 1, 0x08);	/* Disable Block Descriptors */
+	scsi_command_init (cmd, 2, 0x2A);	/* Capabilities and Mechanical Status */
+	scsi_command_init (cmd, 7, len >> 8);
+	scsi_command_init (cmd, 8, len);	/* Real length */
+	scsi_command_init (cmd, 9, 0);
+	if (scsi_command_transport (cmd, READ, page2A, len)) {
+		/* MODE SENSE failed */
+		scsi_command_free (cmd);
+		free (page2A);
+		return NULL;
+	}
+
+	scsi_command_free (cmd);
+
+	len -= 2;
+	/* paranoia */
+	if (len < ((unsigned int) page2A[0] << 8 | page2A[1])) {
+		page2A[0] = len >> 8;
+		page2A[1] = len;
+	}
+
+	return page2A;
+}
+
+static int
+int_compare (const void *a, const void *b)
+{
+	/* descending order */
+	return *((int *) b) - *((int *) a);
+}
+
+/* gets the list of supported write speeds.  in the event
+ * that anything goes wrong, returns NULL.
+ */
+static char *
+get_write_speeds (const unsigned char *p, int length, int max_speed)
+{
+	char *result, *str;
+	int nr_records;
+	int *tmpspeeds;
+	int i, j;
+
+	result = NULL;
+
+	/* paranoia */
+	if (length < 32)
+		return NULL;
+
+	nr_records = p[30] << 8 | p[31];
+
+	/* paranoia */
+	if (length < 32 + 4 * nr_records)
+		return NULL;
+
+	tmpspeeds = g_new (int, nr_records);
+
+	for (i = 0; i < nr_records; i++)
+	{
+		tmpspeeds[i] = p[4*i + 34] << 8 | p[4*i + 35];
+
+		/* i'm not sure how likely this is to show up, but it's
+		 * definitely wrong.  if we see it, abort.
+		 */
+		if (tmpspeeds[i] == 0)
+			goto free_tmpspeeds;
+	}
+
+	/* sort */
+	qsort (tmpspeeds, nr_records, sizeof (int), int_compare);
+
+	/* uniq */
+	for (i = j = 0; i < nr_records; i++)
+	{
+		tmpspeeds[j] = tmpspeeds[i];
+
+		/* make sure we don't look past the end of the array */
+		if (i >= (nr_records - 1) || tmpspeeds[i+1] != tmpspeeds[i])
+			j++;
+	}
+
+	/* j is now the number of unique entries in the array */
+	if (j == 0)
+		/* no entries?  this isn't right. */
+		goto free_tmpspeeds;
+
+	/* sanity check: the first item in the descending order
+	 * list ought to be the highest speed as detected through
+	 * other means
+	 */
+	if (tmpspeeds[0] != max_speed)
+		/* sanity check failed. */
+		goto free_tmpspeeds;
+
+	/* our values are 16-bit.  8 bytes per value
+	 * is more than enough including space for
+	 * ',' and '\0'.  we know j is not zero.
+	 */
+	result = str = g_new (char, 8 * j);
+
+	for (i = 0; i < j; i++)
+	{
+		if (i > 0)
+			*(str++) = ',';
+
+		str += sprintf (str, "%d", tmpspeeds[i]);
+	}
+
+free_tmpspeeds:
+	free (tmpspeeds);
+
+	return result;
+}
+
+int
+get_read_write_speed (HFPCDROM *cdrom, int *read_speed, int *write_speed, char **write_speeds)
+{
+	unsigned char *page2A;
+	int len, hlen;
+	unsigned char *p;
+
+	g_return_val_if_fail (cdrom != NULL, -1);
+
+	*read_speed = 0;
+	*write_speed = 0;
+	*write_speeds = NULL;
+
+	page2A = pull_page2a_from_cdrom (cdrom);
+	if (page2A == NULL) {
+		printf ("Failed to get Page 2A\n");
+		/* Failed to get Page 2A */
+		return -1;
+	}
+
+	len = (page2A[0] << 8 | page2A[1]) + 2;
+	hlen = 8 + (page2A[6] << 8 | page2A[7]);
+	p = page2A + hlen;
+
+	/* Values guessed from the cd_mode_page_2A struct
+	 * in cdrecord's libscg/scg/scsireg.h */
+	if (len < (hlen + 30) || p[1] < (30 - 2)) {
+		/* no MMC-3 "Current Write Speed" present,
+		 * try to use the MMC-2 one */
+		if (len < (hlen + 20) || p[1] < (20 - 2))
+			*write_speed = 0;
+		else
+			*write_speed = p[18] << 8 | p[19];
+	} else {
+		*write_speed = p[28] << 8 | p[29];
+	}
+
+	if (len >= hlen+9)
+	    *read_speed = p[8] << 8 | p[9];
+	else
+	    *read_speed = 0;
+
+	*write_speeds = get_write_speeds (p, len, *write_speed);
+
+	free (page2A);
+
+	return 0;
+}
+
+
+static int
+get_disc_capacity_cd (HFPCDROM *cdrom, guint64 *size)
+{
+	ScsiCommand *cmd;
+	int retval;
+	guint64 block_size;
+	guint64 num_blocks;
+	unsigned char header [8];
+
+	g_return_val_if_fail (cdrom != NULL, -1);
+
+	retval = -1;
+
+	cmd = scsi_command_new_from_cdrom (cdrom);
+	scsi_command_init (cmd, 0, 0x25);
+	scsi_command_init (cmd, 9, 0);
+	if (scsi_command_transport (cmd, READ, header, 8)) {
+		/* READ CDROM CAPACITY failed */
+		goto done;
+	}
+
+	num_blocks = (header [0] << 24) | (header [1] << 16) | (header [2] << 8) | header [3];
+	num_blocks++;
+	block_size = header [4] << 24 | header [5] << 16 | header [6] << 8 | header [7];
+
+	if (size) {
+		*size = num_blocks * block_size;
+	}
+	retval = 0;
+
+ done:
+	scsi_command_free (cmd);
+
+	return retval;
+}
+
+static int
+get_disc_capacity_cdr (HFPCDROM *cdrom, guint64 *size)
+{
+	ScsiCommand *cmd;
+	int retval;
+	guint64 secs;
+	unsigned char toc [8];
+	unsigned char *atip;
+	int len;
+
+	g_return_val_if_fail (cdrom != NULL, -1);
+
+	retval = -1;
+
+	cmd = scsi_command_new_from_cdrom (cdrom);
+	/* READ_TOC */
+	scsi_command_init (cmd, 0, 0x43);
+	/* FMT_ATIP */
+	scsi_command_init (cmd, 2, 4 & 0x0F);
+	scsi_command_init (cmd, 6, 0);
+	scsi_command_init (cmd, 8, 4);
+	scsi_command_init (cmd, 9, 0);
+
+	if (scsi_command_transport (cmd, READ, toc, 4)) {
+		/* READ TOC failed */
+		goto done;
+	}
+
+	len = 2 + (toc [0] << 8 | toc [1]);
+
+	atip = g_new (unsigned char, len);
+
+	scsi_command_init (cmd, 0, 0x43);
+	scsi_command_init (cmd, 2, 4 & 0x0F);
+	scsi_command_init (cmd, 6, 0);
+	scsi_command_init (cmd, 7, len >> 8);
+	scsi_command_init (cmd, 8, len);
+	scsi_command_init (cmd, 9, 0);
+
+	if (scsi_command_transport (cmd, READ, atip, len)) {
+		/* READ TOC failed */
+		free (atip);
+		goto done;
+	}
+
+	secs = atip [12] * 60 + atip [13] + (atip [14] / 75 + 1);
+
+	if (size) {
+		*size = (1 + secs * 7 / 48) * 1024 * 1024;
+	}
+	retval = 0;
+
+	free (atip);
+ done:
+	scsi_command_free (cmd);
+
+	return retval;
+}
+
+static int
+get_disc_capacity_dvdr_from_type (HFPCDROM *cdrom, int type, guint64 *size)
+{
+	ScsiCommand *cmd;
+	unsigned char formats [260];
+	unsigned char buf [32];
+	guint64 blocks;
+	guint64 nwa;
+	int i;
+	int len;
+	int obligatory;
+	int retval;
+	int next_track;
+
+	g_return_val_if_fail (cdrom != NULL, -1);
+
+	retval = -1;
+	blocks = 0;
+	next_track = 1;
+
+	cmd = scsi_command_new_from_cdrom (cdrom);
+
+ retry:
+	if (type == 0x1A || type == 0x14 || type == 0x13 || type == 0x12) {
+
+		/* READ FORMAT CAPACITIES */
+		scsi_command_init (cmd, 0, 0x23);
+		scsi_command_init (cmd, 8, 12);
+		scsi_command_init (cmd, 9, 0);
+		if (scsi_command_transport (cmd, READ, formats, 12)) {
+			/* READ FORMAT CAPACITIES failed */
+			goto done;
+		}
+
+		len = formats [3];
+		if (len & 7 || len < 16) {
+			/* Length isn't sane */
+			goto done;
+		}
+
+		scsi_command_init (cmd, 0, 0x23);
+		scsi_command_init (cmd, 7, (4 + len) >> 8);
+		scsi_command_init (cmd, 8, (4 + len) & 0xFF);
+		scsi_command_init (cmd, 9, 0);
+		if (scsi_command_transport (cmd, READ, formats, 4 + len)) {
+			/* READ FORMAT CAPACITIES failed */
+			goto done;
+		}
+
+		if (len != formats [3]) {
+			/* Parameter length inconsistency */
+			goto done;
+		}
+	}
+
+	obligatory = 0x00;
+
+	switch (type) {
+    	case 0x1A:		/* DVD+RW */
+		obligatory = 0x26;
+	case 0x13:		/* DVD-RW Restricted Overwrite */
+	case 0x14:		/* DVD-RW Sequential */
+		for (i = 8, len = formats [3]; i < len; i += 8) {
+			if ((formats [4 + i + 4] >> 2) == obligatory) {
+				break;
+			}
+		}
+
+		if (i == len) {
+			/* Can't find obligatory format descriptor */
+			goto done;
+		}
+
+		blocks  = formats [4 + i + 0] << 24;
+		blocks |= formats [4 + i + 1] << 16;
+		blocks |= formats [4 + i + 2] << 8;
+		blocks |= formats [4 + i + 3];
+		nwa = formats [4 + 5] << 16 | formats [4 + 6] << 8 | formats [4 + 7];
+		if (nwa > 2048) {
+			blocks *= nwa / 2048;
+		} else if (nwa < 2048) {
+			blocks /= 2048 / nwa;
+		}
+
+		retval = 0;
+		break;
+
+	case 0x12:		/* DVD-RAM */
+
+		blocks  = formats [4 + 0] << 24;
+		blocks |= formats [4 + 1] << 16;
+		blocks |= formats [4 + 2] << 8;
+		blocks |= formats [4 + 3];
+		nwa = formats [4 + 5] << 16 | formats [4 + 6] << 8 | formats [4 + 7];
+		if (nwa > 2048) {
+			blocks *= nwa / 2048;
+		} else if (nwa < 2048) {
+			blocks /= 2048 / nwa;
+		}
+
+		retval = 0;
+		break;
+
+	case 0x11:		/* DVD-R */
+	case 0x1B:		/* DVD+R */
+	case 0x2B:		/* DVD+R Double Layer */
+
+		/* READ TRACK INFORMATION */
+		scsi_command_init (cmd, 0, 0x52);
+		scsi_command_init (cmd, 1, 1);
+		scsi_command_init (cmd, 4, next_track >> 8);
+		scsi_command_init (cmd, 5, next_track & 0xFF);
+		scsi_command_init (cmd, 8, sizeof (buf));
+		scsi_command_init (cmd, 9, 0);
+		if (scsi_command_transport (cmd, READ, buf, sizeof (buf))) {
+			/* READ TRACK INFORMATION failed */
+			if (next_track > 0) {
+				goto done;
+			} else {
+				next_track = 1;
+				goto retry;
+			}
+		}
+
+		blocks = buf [24] << 24;
+		blocks |= buf [25] << 16;
+		blocks |= buf [26] << 8;
+		blocks |= buf [27];
+
+		retval = 0;
+		break;
+	default:
+		blocks = 0;
+		break;
+	}
+
+ done:
+	scsi_command_free (cmd);
+
+	if (size) {
+		*size = blocks * 2048;
+	}
+
+	return retval;
+}
+
+int
+get_disc_capacity_for_type (HFPCDROM *cdrom, int type, guint64 *size)
+{
+	int retval;
+
+	g_return_val_if_fail (cdrom != NULL, -1);
+
+	retval = -1;
+
+	switch (type) {
+	case 0x8:
+		retval = get_disc_capacity_cd (cdrom, size);
+		break;
+	case 0x9:
+	case 0xa:
+		retval = get_disc_capacity_cdr (cdrom, size);
+		break;
+	case 0x10:
+		retval = get_disc_capacity_cd (cdrom, size);
+		break;
+	case 0x11:
+	case 0x13:
+	case 0x14:
+	case 0x1B:
+	case 0x2B:
+	case 0x1A:
+	case 0x12:
+		retval = get_disc_capacity_dvdr_from_type (cdrom, type, size);
+		break;
+	default:
+		retval = -1;
+	}
+
+	return retval;
+}
+
+int
+get_disc_type (HFPCDROM *cdrom)
+{
+	ScsiCommand *cmd;
+	int retval = -1;
+	unsigned char header[8];
+
+	g_return_val_if_fail (cdrom != NULL, -1);
+
+	cmd = scsi_command_new_from_cdrom (cdrom);
+
+	scsi_command_init (cmd, 0, 0x46);
+	scsi_command_init (cmd, 1, 1);
+	scsi_command_init (cmd, 8, 8);
+	scsi_command_init (cmd, 9, 0);
+	if (scsi_command_transport (cmd, READ, header, 8)) {
+		/* GET CONFIGURATION failed */
+		scsi_command_free (cmd);
+		return -1;
+	}
+
+	retval = (header[6]<<8)|(header[7]);
+
+
+	scsi_command_free (cmd);
+	return retval;
+}
+
+
+int
+disc_is_appendable (HFPCDROM *cdrom)
+{
+	ScsiCommand *cmd;
+	int retval = -1;
+	unsigned char header[32];
+
+	g_return_val_if_fail (cdrom != NULL, -1);
+
+	cmd = scsi_command_new_from_cdrom (cdrom);
+
+	/* see section 5.19 of MMC-3 from http://www.t10.org/drafts.htm#mmc3 */
+	scsi_command_init (cmd, 0, 0x51); /* READ_DISC_INFORMATION */
+	scsi_command_init (cmd, 8, 32);
+	scsi_command_init (cmd, 9, 0);
+	if (scsi_command_transport (cmd, READ, header, 32)) {
+		/* READ_DISC_INFORMATION failed */
+		scsi_command_free (cmd);
+		return 0;
+	}
+
+	retval = ((header[2]&0x03) == 0x01);
+
+	scsi_command_free (cmd);
+	return retval;
+}
+
+int
+disc_is_rewritable (HFPCDROM *cdrom)
+{
+	ScsiCommand *cmd;
+	int retval = -1;
+	unsigned char header[32];
+
+	g_return_val_if_fail (cdrom != NULL, -1);
+
+	cmd = scsi_command_new_from_cdrom (cdrom);
+
+	/* see section 5.19 of MMC-3 from http://www.t10.org/drafts.htm#mmc3 */
+	scsi_command_init (cmd, 0, 0x51); /* READ_DISC_INFORMATION */
+	scsi_command_init (cmd, 8, 32);
+	scsi_command_init (cmd, 9, 0);
+	if (scsi_command_transport (cmd, READ, header, 32)) {
+		/* READ_DISC_INFORMATION failed */
+		scsi_command_free (cmd);
+		return 0;
+	}
+
+	retval = ((header[2]&0x10) != 0);
+
+	scsi_command_free (cmd);
+	return retval;
+}
diff --git a/hald/freebsd/probing/freebsd_dvd_rw_utils.h b/hald/freebsd/probing/freebsd_dvd_rw_utils.h
new file mode 100644
index 0000000..f84c59e
--- /dev/null
+++ b/hald/freebsd/probing/freebsd_dvd_rw_utils.h
@@ -0,0 +1,33 @@
+//
+// This is part of dvd+rw-tools by Andy Polyakov <appro at fy.chalmers.se>
+//
+// Use-it-on-your-own-risk, GPL bless...
+//
+// For further details see http://fy.chalmers.se/~appro/linux/DVD+RW/
+//
+
+#ifndef FREEBSD_DVD_RW_UTILS_H
+#define FREEBSD_DVD_RW_UTILS_H
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <glib.h>
+
+#include "../libprobe/hfp-cdrom.h"
+
+#define DRIVE_CDROM_CAPS_DVDRW		1
+#define DRIVE_CDROM_CAPS_DVDPLUSR	2
+#define DRIVE_CDROM_CAPS_DVDPLUSRW	4
+#define DRIVE_CDROM_CAPS_DVDPLUSRWDL	8
+#define DRIVE_CDROM_CAPS_DVDPLUSRDL	16
+
+int get_dvd_r_rw_profile (HFPCDROM *cdrom);
+int get_read_write_speed (HFPCDROM *cdrom, int *read_speed, int *write_speed, char **write_speeds);
+int get_disc_capacity_for_type (HFPCDROM *cdrom, int type, guint64 *capacity);
+int get_disc_type (HFPCDROM *cdrom);
+int disc_is_appendable (HFPCDROM *cdrom);
+int disc_is_rewritable (HFPCDROM *cdrom);
+
+#endif				/* FREEBSD_DVD_RW_UTILS_H */
diff --git a/hald/freebsd/probing/probe-hiddev.c b/hald/freebsd/probing/probe-hiddev.c
new file mode 100644
index 0000000..049a17d
--- /dev/null
+++ b/hald/freebsd/probing/probe-hiddev.c
@@ -0,0 +1,140 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * probe-hiddev.c : USB HID prober
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+#include <usbhid.h>
+
+#include "../libprobe/hfp.h"
+
+#define HID_COLLECTION_APPLICATION	1
+
+int
+main (int argc, char **argv)
+{
+  char *device_file;
+  int fd = -1;
+  int report_id;
+  report_desc_t report_desc;
+  struct hid_data *data;
+  hid_item_t item;
+  boolean is_keyboard = FALSE;
+  boolean is_mouse = FALSE;
+  boolean is_joystick = FALSE;
+
+  if (! hfp_init(argc, argv))
+    goto end;
+
+  device_file = getenv("HAL_PROP_HIDDEV_DEVICE");
+  if (! device_file)
+    goto end;
+
+  fd = open(device_file, O_RDONLY);
+  if (fd < 0)
+    goto end;
+
+  /* give a meaningful process title for ps(1) */
+  setproctitle("%s", device_file);
+
+  if (ioctl(fd, USB_GET_REPORT_ID, &report_id) < 0)
+    goto end;
+
+  hid_init(NULL);
+
+  report_desc = hid_get_report_desc(fd);
+  if (! report_desc)
+    goto end;
+
+  for (data = hid_start_parse(report_desc, ~0, report_id); hid_get_item(data, &item);)
+    if (item.kind == hid_collection)
+      {
+	if (item.collection == HID_COLLECTION_APPLICATION)
+	  {
+	    const char *page;
+	    char *full_page;
+	    int i;
+
+	    page = hid_usage_page(HID_PAGE(item.usage));
+
+	    full_page = hfp_strdup_printf("%s Page", page);
+	    for (i = 0; full_page[i] != 0; i++)
+	      if (full_page[i] == '_')
+		full_page[i] = ' ';
+
+	    libhal_device_property_strlist_append(hfp_ctx, hfp_udi, "hiddev.application_pages", full_page, &hfp_error);
+	    hfp_free(full_page);
+	  }
+
+	switch (item.usage)
+	  {
+	  case HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_MOUSE):
+	    is_mouse = TRUE;
+	    break;
+
+	  case HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_JOYSTICK):
+	  case HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_GAME_PAD):
+	    is_joystick = TRUE;
+	    break;
+
+	  case HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYBOARD):
+	  case HID_USAGE2(HUP_GENERIC_DESKTOP, HUG_KEYPAD):
+	    is_keyboard = TRUE;
+	    break;
+	  }
+      }
+  hid_end_parse(data);
+
+  hid_dispose_report_desc(report_desc);
+
+  if (is_keyboard || is_mouse || is_joystick)
+    {
+      libhal_device_add_capability(hfp_ctx, hfp_udi, "input", &hfp_error);
+      libhal_device_set_property_string(hfp_ctx, hfp_udi, "input.device", device_file, &hfp_error);
+    }
+  if (is_keyboard)
+    {
+      libhal_device_add_capability(hfp_ctx, hfp_udi, "input.keyboard", &hfp_error);
+      libhal_device_set_property_string(hfp_ctx, hfp_udi, "info.category", "input.keyboard", &hfp_error);
+    }
+  if (is_mouse)
+    {
+      libhal_device_add_capability(hfp_ctx, hfp_udi, "input.mouse", &hfp_error);
+      libhal_device_set_property_string(hfp_ctx, hfp_udi, "info.category", "input.mouse", &hfp_error);
+    }
+  if (is_joystick)
+    {
+      libhal_device_add_capability(hfp_ctx, hfp_udi, "input.joystick", &hfp_error);
+      libhal_device_set_property_string(hfp_ctx, hfp_udi, "info.category", "input.joystick", &hfp_error);
+    }
+
+ end:
+  return 0;
+}
diff --git a/hald/freebsd/probing/probe-scsi.c b/hald/freebsd/probing/probe-scsi.c
new file mode 100644
index 0000000..2de75e2
--- /dev/null
+++ b/hald/freebsd/probing/probe-scsi.c
@@ -0,0 +1,71 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * probe-scsi.c : SCSI prober
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <camlib.h>
+
+#include "../libprobe/hfp.h"
+
+int
+main (int argc, char **argv)
+{
+  struct cam_device *cam;
+  char *device_file;
+
+  if (! hfp_init(argc, argv))
+    goto end;
+
+  device_file = getenv("HAL_PROP_BLOCK_DEVICE");
+  if (! device_file)
+    goto end;
+
+  /* give a meaningful process title for ps(1) */
+  setproctitle("%s", device_file);
+
+  /* cam_open_device() fails unless we use O_RDWR */
+  cam = cam_open_device(device_file, O_RDWR);
+  if (cam)
+    {
+      if (cam->serial_num_len > 0)
+	{
+	  char *serial;
+
+	  serial = hfp_strndup(cam->serial_num, cam->serial_num_len);
+	  if (*serial)
+	    libhal_device_set_property_string(hfp_ctx, hfp_udi, "storage.serial", serial, &hfp_error);
+	  hfp_free(serial);
+	}
+      cam_close_device(cam);
+    }
+
+ end:
+  return 0;
+}
diff --git a/hald/freebsd/probing/probe-smbios.c b/hald/freebsd/probing/probe-smbios.c
new file mode 100644
index 0000000..1c7647e
--- /dev/null
+++ b/hald/freebsd/probing/probe-smbios.c
@@ -0,0 +1,225 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * probe-smbios.c : Probe system BIOS according to the SMBIOS/DMI standard
+ *
+ * Copyright (C) 2005 David Zeuthen, <david at fubar.dk>
+ * Copyright (C) 2005 Richard Hughes, <richard at hughsie.com>
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "../libprobe/hfp.h"
+
+#define DMIDECODE			"/usr/local/sbin/dmidecode"
+
+#define DMIPARSER_STATE_IGNORE		0
+#define DMIPARSER_STATE_BIOS		1
+#define DMIPARSER_STATE_SYSTEM		2
+#define DMIPARSER_STATE_CHASSIS		3
+
+#define strbegin(buf, str) (strncmp (buf, str, strlen (str)) == 0)
+
+/** Finds the start of a null terminated string and sets HAL
+ *  property if valid.
+ *
+ *  @param	buf		The non tabbed prefixed, null terminated string
+ *  @param	str		The strings to compare with e.g. "Vendor:"
+ *  @param	prop		The HAL property to set
+ *  @return			TRUE is found, FALSE otherwise.
+ */
+static int
+setstr (char *buf, char *str, char *prop)
+{
+	DBusError error;
+	char *value;
+
+	if (strbegin (buf, str)) {
+		dbus_error_init (&error);
+		value = buf + strlen (str) + 1;
+		libhal_device_set_property_string (hfp_ctx, hfp_udi, prop, value, &hfp_error);
+		hfp_info ("Setting %s='%s'", prop, value);
+		return TRUE;
+	}
+	return FALSE;
+}
+
+/** Main entry point
+ *
+ *  @param	argc		Number of arguments given to program
+ *  @param	argv		Arguments given to program
+ *  @return			Return code
+ */
+int
+main (int argc, char *argv[])
+{
+	int ret;
+	DBusError error;
+	char buf[512];
+	char *nbuf;
+	int dmipipe[2];
+	int nullfd;
+	int tmp_ret;
+	FILE *f;
+	int dmiparser_state = DMIPARSER_STATE_IGNORE;
+
+	/* on some system chassis pops up several times,
+	 * so only take the first entry for each
+	 */
+	int dmiparser_done_bios = FALSE;
+	int dmiparser_done_system = FALSE;
+	int dmiparser_done_chassis = FALSE;
+
+	/* assume failure */
+	ret = 1;
+
+	if (! hfp_init (argc, argv))
+	  goto out;
+
+	dbus_error_init (&error);
+
+	tmp_ret = pipe (dmipipe);
+	f = fdopen (dmipipe[0], "r");
+	nullfd = open ("/dev/null", O_RDONLY);
+
+	/* fork the child process */
+	switch (fork ()) {
+	case 0:
+		/* child */
+
+		dup2 (nullfd, STDIN_FILENO);
+		dup2 (dmipipe[1], STDOUT_FILENO);
+		close (dmipipe[0]);
+		close (dmipipe[1]);
+
+		/* execute the child */
+		execl (DMIDECODE, DMIDECODE, NULL);
+
+		/* throw an error if we ever reach this point */
+		hfp_warning("failed to execute " DMIDECODE);
+		exit (1);
+		break;
+	case -1:
+		hfp_warning("cannot fork");
+		break;
+	}
+
+	/* parent continues from here */
+
+	/* close unused descriptor */
+	close (dmipipe[1]);
+
+	/* read the output of the child */
+	while(fgets (buf, sizeof(buf), f) != NULL)
+	{
+		unsigned int i;
+		unsigned int len;
+		unsigned int tabs = 0;
+
+		/* trim whitespace */
+		len = strlen (buf);
+
+		/* check that will fit in buffer */
+		if (len >= sizeof (buf))
+			continue;
+
+		/* not big enough for data, and protects us from underflow */
+		if (len < 3) {
+			dmiparser_state = DMIPARSER_STATE_IGNORE;
+			continue;
+		}
+
+		/* find out number of leading tabs */
+		if (buf[0] == '\t' && buf[1] == '\t')
+			tabs = 2; /* this is list data */
+		else if (buf[0] == '\t')
+			tabs = 1; /* this is data, 0 is section type */
+
+		if (tabs == 2)
+			/* we do not proccess data at depth 2 */
+			continue;
+
+		/* set the section type */
+		if (tabs == 0) {
+			if (!dmiparser_done_bios && strbegin (buf, "BIOS Information"))
+				dmiparser_state = DMIPARSER_STATE_BIOS;
+			else if (!dmiparser_done_system && strbegin (buf, "System Information"))
+				dmiparser_state = DMIPARSER_STATE_SYSTEM;
+			else if (!dmiparser_done_chassis && strbegin (buf, "Chassis Information"))
+				dmiparser_state = DMIPARSER_STATE_CHASSIS;
+			else
+				/*
+				 * We do not match the other sections,
+				 * or sections we have processed before
+				 */
+				dmiparser_state = DMIPARSER_STATE_IGNORE;
+			continue; /* next line */
+		}
+
+		/* we are not in a section we know, no point continueing */
+		if (dmiparser_state == DMIPARSER_STATE_IGNORE)
+			continue;
+
+		/* removes the leading tab */
+		nbuf = &buf[1];
+
+		/* removes the trailing spaces */
+		for (i = len - 2; isspace (nbuf[i]) && i >= 0; --i)
+			nbuf[i] = '\0';
+
+		if (dmiparser_state == DMIPARSER_STATE_BIOS) {
+			setstr (nbuf, "Vendor:", "smbios.bios.vendor");
+			setstr (nbuf, "Version:", "smbios.bios.version");
+			setstr (nbuf, "Release Date:", "smbios.bios.release_date");
+			dmiparser_done_bios = TRUE;
+		} else if (dmiparser_state == DMIPARSER_STATE_SYSTEM) {
+			setstr (nbuf, "Manufacturer:", "smbios.system.manufacturer");
+			setstr (nbuf, "Product Name:", "smbios.system.product");
+			setstr (nbuf, "Version:", "smbios.system.version");
+			setstr (nbuf, "Serial Number:", "smbios.system.serial");
+			setstr (nbuf, "UUID:", "smbios.system.uuid");
+			dmiparser_done_system = TRUE;
+		} else if (dmiparser_state == DMIPARSER_STATE_CHASSIS) {
+			setstr (nbuf, "Manufacturer:", "smbios.chassis.manufacturer");
+			setstr (nbuf, "Type:", "smbios.chassis.type");
+			dmiparser_done_chassis = TRUE;
+		}
+	}
+
+	/* as read to EOF, close */
+	fclose (f);
+
+	/* return success */
+	ret = 0;
+
+out:
+	return ret;
+}
diff --git a/hald/freebsd/probing/probe-storage.c b/hald/freebsd/probing/probe-storage.c
new file mode 100644
index 0000000..c875542
--- /dev/null
+++ b/hald/freebsd/probing/probe-storage.c
@@ -0,0 +1,209 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * probe-storage.c : storage prober
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <glib.h>
+#include <libvolume_id.h>
+
+#include "libhal/libhal.h"
+
+#include "../libprobe/hfp.h"
+#include "../libprobe/hfp-cdrom.h"
+
+#include "freebsd_dvd_rw_utils.h"
+
+static void
+hf_probe_storage_get_cdrom_capabilities (const char *device_file)
+{
+  HFPCDROM *cdrom;
+  HFPCDROMCapabilities caps;
+  gboolean status = FALSE;
+  int i;
+  int read_speed;
+  int write_speed;
+  char *write_speeds;
+
+  g_return_if_fail(device_file != NULL);
+
+  cdrom = hfp_cdrom_new(device_file);
+  if (! cdrom)
+    {
+      hfp_warning("unable to open CD-ROM device %s", device_file);
+      return;
+    }
+
+  /* according to sys/dev/ata/atapi-cd.c some buggy drives need this loop */
+  for (i = 0; i < 5; i++)
+    {
+      static char ccb[16] = { HFP_CDROM_MODE_SENSE_BIG, 0, HFP_CDROM_CAP_PAGE, 0, 0, 0, 0, sizeof(caps) >> 8, sizeof(caps) };
+      char *err = NULL;
+
+      if (! hfp_cdrom_send_ccb(cdrom, ccb, 10, HFP_CDROM_DIRECTION_IN, &caps, sizeof(caps), &err))
+	{
+	  hfp_warning("%s: unable to get capabilities: %s", device_file, err);
+	  hfp_free(err);
+	  continue;
+	}
+      if (caps.page_code != HFP_CDROM_CAP_PAGE)
+	{
+	  hfp_warning("%s: bad page code %i", device_file, caps.page_code);
+	  continue;
+	}
+
+      status = TRUE;
+    }
+
+  if (! status)
+    goto end;
+
+  if ((caps.media & HFP_CDROM_MST_WRITE_CDR) != 0)
+    libhal_device_set_property_bool(hfp_ctx, hfp_udi, "storage.cdrom.cdr", TRUE, &hfp_error);
+  if ((caps.media & HFP_CDROM_MST_WRITE_CDRW) != 0)
+    libhal_device_set_property_bool(hfp_ctx, hfp_udi, "storage.cdrom.cdrw", TRUE, &hfp_error);
+  if ((caps.media & HFP_CDROM_MST_READ_DVDROM) != 0)
+    {
+      int profile;
+      gboolean r;
+      gboolean rw;
+      gboolean rdl;
+      gboolean rwdl;
+
+      libhal_device_set_property_bool(hfp_ctx, hfp_udi, "storage.cdrom.dvd", TRUE, &hfp_error);
+
+      profile = get_dvd_r_rw_profile(cdrom);
+      r = (profile & DRIVE_CDROM_CAPS_DVDPLUSR) != 0;
+      rw = (profile & DRIVE_CDROM_CAPS_DVDRW) != 0;
+      rdl = (profile & DRIVE_CDROM_CAPS_DVDPLUSRDL) != 0;
+      rwdl = (profile & DRIVE_CDROM_CAPS_DVDPLUSRWDL) != 0;
+
+      libhal_device_set_property_bool(hfp_ctx, hfp_udi, "storage.cdrom.dvdplusr", r, &hfp_error);
+      libhal_device_set_property_bool(hfp_ctx, hfp_udi, "storage.cdrom.dvdplusrw", rw, &hfp_error);
+      libhal_device_set_property_bool(hfp_ctx, hfp_udi, "storage.cdrom.dvdplusrdl", rdl, &hfp_error);
+      libhal_device_set_property_bool(hfp_ctx, hfp_udi, "storage.cdrom.dvdplusrwdl", rwdl, &hfp_error);
+    }
+  if ((caps.media & HFP_CDROM_MST_WRITE_DVDR) != 0)
+    libhal_device_set_property_bool(hfp_ctx, hfp_udi, "storage.cdrom.dvdr", TRUE, &hfp_error);
+  if ((caps.media & HFP_CDROM_MST_WRITE_DVDRAM) != 0)
+    libhal_device_set_property_bool(hfp_ctx, hfp_udi, "storage.cdrom.dvdram", TRUE, &hfp_error);
+
+  if (get_read_write_speed(cdrom, &read_speed, &write_speed, &write_speeds) >= 0)
+    {
+      libhal_device_set_property_int(hfp_ctx, hfp_udi, "storage.cdrom.read_speed", read_speed, &hfp_error);
+      if (write_speed > 0)
+	{
+	  libhal_device_set_property_int(hfp_ctx, hfp_udi, "storage.cdrom.write_speed", write_speed, &hfp_error);
+
+	  if (write_speeds != NULL)
+	    {
+	      char **speedv;
+	      int i;
+
+	      speedv = g_strsplit_set(write_speeds, ",", 0);
+	      free(write_speeds);
+
+	      for (i = 0; speedv[i] != NULL; i++)
+		if (*(speedv[i]))
+		  libhal_device_property_strlist_append(hfp_ctx, hfp_udi, "storage.cdrom.write_speeds", speedv[i], &hfp_error);
+
+	      g_strfreev(speedv);
+	    }
+	}
+    }
+
+ end:
+  if (cdrom)
+    hfp_cdrom_free(cdrom);
+}
+
+int
+main (int argc, char **argv)
+{
+  char *device_file;
+  char *drive_type;
+  int ret = 0;			/* no media/filesystem */
+  gboolean has_children;
+  gboolean only_check_for_media;
+  gboolean is_cdrom;
+
+  if (! hfp_init(argc, argv))
+    goto end;
+
+  device_file = getenv("HAL_PROP_BLOCK_DEVICE");
+  if (! device_file)
+    goto end;
+
+  drive_type = getenv("HAL_PROP_STORAGE_DRIVE_TYPE");
+  if (! drive_type)
+    goto end;
+
+  /* give a meaningful process title for ps(1) */
+  setproctitle("%s", device_file);
+
+  has_children = hfp_getenv_bool("HF_HAS_CHILDREN");
+  only_check_for_media = hfp_getenv_bool("HF_ONLY_CHECK_FOR_MEDIA");
+
+  is_cdrom = ! strcmp(drive_type, "cdrom");
+
+  if (! only_check_for_media && is_cdrom)
+    hf_probe_storage_get_cdrom_capabilities(device_file);
+
+  if (is_cdrom)
+    {
+      HFPCDROM *cdrom;
+
+      cdrom = hfp_cdrom_new(device_file);
+      if (! cdrom)
+	goto end;
+
+      if (hfp_cdrom_test_unit_ready(cdrom))
+	ret = 2;		/* has media */
+
+      hfp_cdrom_free(cdrom);
+    }
+  else if (! has_children) /* by definition, if it has children it has no fs */
+    {
+      struct volume_id *vid;
+
+      vid = volume_id_open_node(device_file);
+      if (! vid)
+	goto end;
+
+      if (volume_id_probe_all(vid, 0, 0) == 0 && vid->usage_id == VOLUME_ID_FILESYSTEM)
+	ret = 2;		/* has a filesystem */
+
+      volume_id_close(vid);
+    }
+
+ end:
+  return ret;
+}
diff --git a/hald/freebsd/probing/probe-volume.c b/hald/freebsd/probing/probe-volume.c
new file mode 100644
index 0000000..b8f0a96
--- /dev/null
+++ b/hald/freebsd/probing/probe-volume.c
@@ -0,0 +1,545 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * probe-volume.c : volume prober
+ *
+ * Copyright (C) 2006 Jean-Yves Lefort <jylefort at FreeBSD.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <inttypes.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/disk.h>
+#include <sys/cdio.h>
+#include <glib.h>
+#include <libvolume_id.h>
+
+#include "libhal/libhal.h"
+
+#include "../libprobe/hfp.h"
+
+#include "freebsd_dvd_rw_utils.h"
+
+static uintmax_t
+hf_probe_volume_getenv_uintmax (const char *name)
+{
+  char *str;
+
+  g_return_val_if_fail(name != NULL, 0);
+
+  str = getenv(name);
+
+  return str ? strtoumax(str, NULL, 10) : 0;
+}
+
+static int
+hf_probe_volume_getenv_int (const char *name)
+{
+  char *str;
+
+  g_return_val_if_fail(name != NULL, 0);
+
+  str = getenv(name);
+
+  return str ? atoi(str) : 0;
+}
+
+static char *
+hf_probe_volume_get_label (const struct volume_id *vid)
+{
+  char *label = NULL;
+
+  if (vid && *vid->label)
+    {
+      if (g_utf8_validate(vid->label, -1, NULL))
+	label = g_strdup(vid->label);
+      else				/* assume ISO8859-1 */
+	label = g_convert(vid->label, -1, "UTF-8", "ISO8859-1", NULL, NULL, NULL);
+    }
+
+  return label;
+}
+
+static void
+hf_probe_volume_get_disc_info (int fd,
+			       gboolean *has_audio,
+			       gboolean *has_data)
+{
+  struct ioc_toc_header toc_header;
+  int n_tracks;
+  struct cd_toc_entry *buffer = NULL;
+  struct ioc_read_toc_entry read_toc_entry;
+  int i;
+
+  g_return_if_fail(has_audio != NULL);
+  g_return_if_fail(has_data != NULL);
+
+  *has_audio = FALSE;
+  *has_data = FALSE;
+
+  if (ioctl(fd, CDIOREADTOCHEADER, &toc_header) < 0)
+    return;
+
+  n_tracks = toc_header.ending_track - toc_header.starting_track + 1;
+
+  buffer = g_new(struct cd_toc_entry, n_tracks + 1);
+
+  read_toc_entry.address_format = CD_MSF_FORMAT;
+  read_toc_entry.starting_track = 0;
+  read_toc_entry.data_len = (n_tracks + 1) * sizeof(struct cd_toc_entry);
+  read_toc_entry.data = buffer;
+
+  if (ioctl(fd, CDIOREADTOCENTRYS, &read_toc_entry) < 0)
+    goto end;
+
+  for (i = 0; i < n_tracks; i++)
+    {
+      if ((buffer[i].control & 4) != 0)
+	*has_data = TRUE;
+      else
+	*has_audio = TRUE;
+    }
+
+ end:
+  g_free(buffer);
+}
+
+static void
+hf_probe_volume_advanced_disc_detect (const char *device_file)
+{
+  GError *err = NULL;
+  char *command;
+  int exit_status;
+  char *output;
+  char **lines;
+  int i;
+
+  g_return_if_fail(device_file != NULL);
+
+  command = g_strdup_printf("isoinfo -p -i %s", device_file);
+  if (! g_spawn_command_line_sync(command, &output, NULL, &exit_status, &err))
+    {
+      hfp_warning("unable to run \"%s\": %s", command, err->message);
+      g_error_free(err);
+      goto end;
+    }
+  if (exit_status != 0)
+    {
+      hfp_warning("\"%s\" returned with status %i", command, exit_status);
+      goto end;
+    }
+
+  lines = g_strsplit(output, "\n", 0);
+  g_free(output);
+
+  for (i = 0; lines[i]; i++)
+    {
+      int index;
+      int pindex;
+      int extent;
+      char dirname[strlen(lines[i]) + 1];
+
+      if (sscanf(lines[i], "%i: %i %x %s", &index, &pindex, &extent, dirname) == 4)
+	{
+	  if (! g_ascii_strcasecmp(dirname, "VIDEO_TS"))
+	    {
+	      libhal_device_set_property_bool(hfp_ctx, hfp_udi, "volume.disc.is_videodvd", TRUE, &hfp_error);
+	      break;
+	    }
+	  else if (! g_ascii_strcasecmp(dirname, "VCD"))
+	    {
+	      libhal_device_set_property_bool(hfp_ctx, hfp_udi, "volume.disc.is_vcd", TRUE, &hfp_error);
+	      break;
+	    }
+	  else if (! g_ascii_strcasecmp(dirname, "SVCD"))
+	    {
+	      libhal_device_set_property_bool(hfp_ctx, hfp_udi, "volume.disc.is_svcd", TRUE, &hfp_error);
+	      break;
+	    }
+	}
+    }
+  g_strfreev(lines);
+
+ end:
+  g_free(command);
+}
+
+static gboolean
+hf_probe_volume_get_partition_info (const char *geom_class,
+				    const char *devfile,
+				    int *number,
+				    char **type,
+				    char **scheme,
+				    guint64 *mediasize,
+				    guint64 *offset)
+{
+  g_return_val_if_fail(geom_class != NULL, FALSE);
+  g_return_val_if_fail(devfile != NULL, FALSE);
+  g_return_val_if_fail(number != NULL, FALSE);
+  g_return_val_if_fail(type != NULL, FALSE);
+  g_return_val_if_fail(scheme != NULL, FALSE);
+  g_return_val_if_fail(mediasize != NULL, FALSE);
+  g_return_val_if_fail(offset != NULL, FALSE);
+
+  if (strcmp(geom_class, "MBR") &&
+      strcmp(geom_class, "GPT") &&
+      strcmp(geom_class, "SUN") &&
+      strcmp(geom_class, "APPLE"))
+    return FALSE;
+
+  *mediasize = hf_probe_volume_getenv_uintmax("HF_VOLUME_SIZE");
+  if (*mediasize == 0)
+    return FALSE;
+
+  *offset = hf_probe_volume_getenv_uintmax("HF_VOLUME_OFFSET");
+
+  *number = hf_probe_volume_getenv_int("HF_VOLUME_PART_INDEX");
+  if (*number == 0)
+    {
+      size_t len;
+      char *partno;
+
+      partno = strrchr(devfile, 's');
+      if (! partno)
+        return FALSE;
+
+      len = strlen(partno) - 1;
+      if (len > 0 && strspn(partno + 1, "0123456789") == len)
+        *number = atoi(partno);
+      else
+        return FALSE;
+    }
+
+  *scheme = g_ascii_strdown(geom_class, -1);
+  if (! strcmp(*scheme, "apple"))
+    {
+      g_free(*scheme);
+      *scheme = g_strdup("apm");
+    }
+
+  if (! strcmp(*scheme, "mbr"))
+    *type = g_strdup_printf("0x%x",
+                            hf_probe_volume_getenv_int("HF_VOLUME_PART_TYPE"));
+  else
+    {
+      char *parttype;
+
+      parttype = getenv("HF_VOLUME_PART_TYPE");
+
+      if (parttype)
+        *type = g_strdup(parttype);
+      else
+        *type = g_strdup("");
+    }
+
+  return TRUE;
+}
+
+int
+main (int argc, char **argv)
+{
+  char *device_file;
+  char *parent_udi;
+  char *parent_drive_type;
+  int fd = -1;
+  struct volume_id *vid = NULL;
+  int ret = 1;
+  gboolean has_children;
+  gboolean is_swap;
+  gboolean is_cdrom;
+  gboolean is_partition = FALSE;
+  gboolean has_audio = FALSE;
+  gboolean has_data = FALSE;
+  gboolean is_blank = FALSE;
+  const char *usage;
+  char *label;
+  unsigned int sector_size = 0;
+  off_t media_size = 0;
+
+  if (! hfp_init(argc, argv))
+    goto end;
+
+  device_file = getenv("HAL_PROP_BLOCK_DEVICE");
+  if (! device_file)
+    goto end;
+
+  parent_udi = getenv("HAL_PROP_INFO_PARENT");
+  if (! parent_udi)
+    goto end;
+
+  /* give a meaningful process title for ps(1) */
+  setproctitle("%s", device_file);
+
+  has_children = hfp_getenv_bool("HF_HAS_CHILDREN");
+  is_swap = hfp_getenv_bool("HF_IS_SWAP");
+
+  fd = open(device_file, O_RDONLY);
+  if (fd < 0)
+    goto end;
+
+  parent_drive_type = libhal_device_get_property_string(hfp_ctx, parent_udi, "storage.drive_type", &hfp_error);
+  dbus_error_free(&hfp_error);
+
+  is_cdrom = parent_drive_type && ! strcmp(parent_drive_type, "cdrom");
+  g_free(parent_drive_type);
+
+  if (is_cdrom)
+    {
+      hf_probe_volume_get_disc_info(fd, &has_audio, &has_data);
+      is_blank = (! has_audio && ! has_data);
+    }
+
+  ioctl(fd, DIOCGMEDIASIZE, &media_size);
+
+  /*
+   * We only check for filesystems if the volume has no children,
+   * otherwise volume_id might find a filesystem in what is actually
+   * the first child partition of the volume.
+   *
+   * If hald (which has looked at the partition type) reports that it
+   * is a swap partition, we probe it nevertheless in case the
+   * partition type is incorrect.
+   */
+  if (! has_children)
+    {
+      vid = volume_id_open_fd(fd);
+      if (vid)
+	{
+	  if (volume_id_probe_all(vid, 0, media_size) == 0)
+	    has_data = TRUE;
+	  else
+	    {
+	      volume_id_close(vid);
+	      vid = NULL;
+	    }
+	}
+    }
+
+  if (! has_children && ! is_swap && ! has_audio && ! has_data && ! is_blank)
+    goto end;
+
+  libhal_device_add_capability(hfp_ctx, hfp_udi, "volume", &hfp_error);
+  if (is_cdrom)
+    {
+      HFPCDROM *cdrom;
+      int type;
+      guint64 capacity;
+
+      libhal_device_set_property_string(hfp_ctx, hfp_udi, "info.category", "volume.disc", &hfp_error);
+      libhal_device_add_capability(hfp_ctx, hfp_udi, "volume.disc", &hfp_error);
+
+      libhal_device_set_property_bool(hfp_ctx, hfp_udi, "volume.disc.has_audio", has_audio, &hfp_error);
+      libhal_device_set_property_bool(hfp_ctx, hfp_udi, "volume.disc.has_data", has_data, &hfp_error);
+      libhal_device_set_property_bool(hfp_ctx, hfp_udi, "volume.disc.is_vcd", FALSE, &hfp_error);
+      libhal_device_set_property_bool(hfp_ctx, hfp_udi, "volume.disc.is_svcd", FALSE, &hfp_error);
+      libhal_device_set_property_bool(hfp_ctx, hfp_udi, "volume.disc.is_videodvd", FALSE, &hfp_error);
+      libhal_device_set_property_bool(hfp_ctx, hfp_udi, "volume.disc.is_appendable", FALSE, &hfp_error);
+      libhal_device_set_property_bool(hfp_ctx, hfp_udi, "volume.disc.is_blank", is_blank, &hfp_error);
+      libhal_device_set_property_bool(hfp_ctx, hfp_udi, "volume.disc.is_rewritable", FALSE, &hfp_error);
+      libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.disc.type", "unknown", &hfp_error);
+
+      /* the following code was adapted from linux's probe-volume.c */
+
+      cdrom = hfp_cdrom_new_from_fd(fd, device_file);
+      if (cdrom)
+	{
+	  type = get_disc_type(cdrom);
+	  if (type != -1)
+	    switch (type)
+	      {
+	      case 0x08: /* CD-ROM */
+		libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.disc.type", "cd_rom", &hfp_error);
+		break;
+	      case 0x09: /* CD-R */
+		libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.disc.type", "cd_r", &hfp_error);
+		break;
+	      case 0x0a: /* CD-RW */
+		libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.disc.type", "cd_rw", &hfp_error);
+		libhal_device_set_property_bool(hfp_ctx, hfp_udi, "volume.disc.is_rewritable", TRUE, &hfp_error);
+		break;
+	      case 0x10: /* DVD-ROM */
+		libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.disc.type", "dvd_rom", &hfp_error);
+		break;
+	      case 0x11: /* DVD-R Sequential */
+		libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.disc.type", "dvd_r", &hfp_error);
+		break;
+	      case 0x12: /* DVD-RAM */
+		libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.disc.type", "dvd_ram", &hfp_error);
+		libhal_device_set_property_bool(hfp_ctx, hfp_udi, "volume.disc.is_rewritable", TRUE, &hfp_error);
+		break;
+	      case 0x13: /* DVD-RW Restricted Overwrite */
+		libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.disc.type", "dvd_rw", &hfp_error);
+		libhal_device_set_property_bool(hfp_ctx, hfp_udi, "volume.disc.is_rewritable", TRUE, &hfp_error);
+		break;
+	      case 0x14: /* DVD-RW Sequential */
+		libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.disc.type", "dvd_rw", &hfp_error);
+		libhal_device_set_property_bool(hfp_ctx, hfp_udi, "volume.disc.is_rewritable", TRUE, &hfp_error);
+		break;
+	      case 0x1A: /* DVD+RW */
+		libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.disc.type", "dvd_plus_rw", &hfp_error);
+		libhal_device_set_property_bool(hfp_ctx, hfp_udi, "volume.disc.is_rewritable", TRUE, &hfp_error);
+		break;
+	      case 0x1B: /* DVD+R */
+		libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.disc.type", "dvd_plus_r", &hfp_error);
+		break;
+	      case 0x2B: /* DVD+R Double Layer */
+		libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.disc.type", "dvd_plus_r_dl", &hfp_error);
+		break;
+	      case 0x40: /* BD-ROM  */
+		libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.disc.type", "bd_rom", &hfp_error);
+		break;
+	      case 0x41: /* BD-R Sequential */
+		libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.disc.type", "bd_r", &hfp_error);
+		break;
+	      case 0x42: /* BD-R Random */
+		libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.disc.type", "bd_r", &hfp_error);
+		break;
+	      case 0x43: /* BD-RE */
+		libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.disc.type", "bd_re", &hfp_error);
+		libhal_device_set_property_bool(hfp_ctx, hfp_udi, "volume.disc.is_rewritable", TRUE, &hfp_error);
+		break;
+	      case 0x50: /* HD DVD-ROM */
+		libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.disc.type", "hddvd_rom", &hfp_error);
+		break;
+	      case 0x51: /* HD DVD-R */
+		libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.disc.type", "hddvd_r", &hfp_error);
+		break;
+	      case 0x52: /* HD DVD-Rewritable */
+		libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.disc.type", "hddvd_rw", &hfp_error);
+		libhal_device_set_property_bool(hfp_ctx, hfp_udi, "volume.disc.is_rewritable", TRUE, &hfp_error);
+		break;
+	      }
+
+	  if (get_disc_capacity_for_type(cdrom, type, &capacity) == 0)
+	    libhal_device_set_property_uint64(hfp_ctx, hfp_udi, "volume.disc.capacity", capacity, &hfp_error);
+
+	  /*
+	   * linux's probe-volume.c: "on some hardware the get_disc_type
+	   * call fails, so we use this as a backup".
+	   */
+	  if (disc_is_rewritable(cdrom))
+	    libhal_device_set_property_bool(hfp_ctx, hfp_udi, "volume.disc.is_rewritable", TRUE, &hfp_error);
+	  if (disc_is_appendable(cdrom))
+	    libhal_device_set_property_bool (hfp_ctx, hfp_udi, "volume.disc.is_appendable", TRUE, &hfp_error);
+
+	  hfp_cdrom_free(cdrom);
+	}
+
+      if (has_data)
+	hf_probe_volume_advanced_disc_detect(device_file);
+    }
+  else
+    {
+      libhal_device_set_property_string(hfp_ctx, hfp_udi, "info.category", "volume", &hfp_error);
+
+      if (libhal_device_query_capability(hfp_ctx, parent_udi, "storage", &hfp_error))
+	{
+	  char *geom_class;
+	  char *type;
+	  char *scheme;
+	  int number;
+	  guint64 mediasize;
+	  guint64 offset;
+
+	  geom_class = getenv("HF_VOLUME_GEOM_CLASS");
+
+	  if (geom_class)
+            {
+              if (hf_probe_volume_get_partition_info(geom_class, device_file, &number, &type, &scheme, &mediasize, &offset))
+                {
+                  is_partition = TRUE;
+
+		  libhal_device_set_property_int(hfp_ctx, hfp_udi, "volume.partition.number", number, &hfp_error);
+		  libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.partition.scheme", scheme, &hfp_error);
+		  libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.partition.type", type, &hfp_error);
+
+		  /* FIXME We need to fill in the supported partition flags. */
+
+		  libhal_device_set_property_uint64(hfp_ctx, hfp_udi, "volume.partition.media_size", mediasize, &hfp_error);
+		  libhal_device_set_property_uint64(hfp_ctx, hfp_udi, "volume.partition.start", offset, &hfp_error);
+
+		  if (! strcmp(scheme, "gpt"))
+                    libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.partition.uuid", type, &hfp_error);
+
+		  if (! strcmp(scheme, "gpt") || ! strcmp(scheme, "apm"))
+                    libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.partition.label", "", &hfp_error);
+
+		  g_free(type);
+		  g_free(scheme);
+		}
+	    }
+	}
+      else
+	dbus_error_free(&hfp_error);
+    }
+
+  libhal_device_set_property_bool(hfp_ctx, hfp_udi, "volume.is_disc", is_cdrom, &hfp_error);
+  libhal_device_set_property_bool(hfp_ctx, hfp_udi, "volume.is_partition", is_partition, &hfp_error);
+
+  libhal_device_set_property_bool(hfp_ctx, hfp_udi, "volume.ignore", has_children || is_swap, &hfp_error);
+
+  if (has_children)
+    usage = "partitiontable";
+  else if (is_swap)
+    usage = "other";
+  else
+    switch (vid ? vid->usage_id : (enum volume_id_usage) -1)
+      {
+      case VOLUME_ID_FILESYSTEM:	usage = "filesystem"; break;
+      case VOLUME_ID_DISKLABEL:		usage = "disklabel"; break;
+      case VOLUME_ID_OTHER:		usage = "other"; break;
+      case VOLUME_ID_RAID:		usage = "raid"; break;
+      case VOLUME_ID_CRYPTO:		usage = "crypto"; break;
+      case VOLUME_ID_UNUSED:		usage = "unused"; break;
+      default:				usage = "unknown"; break;
+      }
+
+  libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.fsusage", usage, &hfp_error);
+  libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.fstype", vid ? vid->type: "", &hfp_error);
+  if (vid && *vid->type_version)
+    libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.fsversion", vid->type_version, &hfp_error);
+
+  label = hf_probe_volume_get_label(vid);
+  libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.label", label ? label : "", &hfp_error);
+  g_free(label);
+
+  libhal_device_set_property_string(hfp_ctx, hfp_udi, "volume.uuid", vid ? vid->uuid : "", &hfp_error);
+
+  ioctl(fd, DIOCGSECTORSIZE, &sector_size);
+
+  if (sector_size != 0)
+    libhal_device_set_property_uint64(hfp_ctx, hfp_udi, "volume.block_size", sector_size, &hfp_error);
+  if (media_size != 0)
+    libhal_device_set_property_uint64(hfp_ctx, hfp_udi, "volume.size", media_size, &hfp_error);
+  if (sector_size != 0 && media_size != 0)
+    libhal_device_set_property_uint64(hfp_ctx, hfp_udi, "volume.num_blocks", media_size / sector_size, &hfp_error);
+
+  ret = 0;			/* is a volume */
+
+ end:
+  return ret;
+}


More information about the hal-commit mailing list