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, ¶ms, &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, §or_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