| author | Joe Marcus Clarke <marcus@FreeBSD.org> | 2006-11-29 06:12:33 (GMT) |
|---|---|---|
| committer | Joe Marcus Clarke <marcus@FreeBSD.org> | 2006-11-29 06:12:33 (GMT) |
| commit | 76c310d0efb5d463f06291cb02100b3b3ce1da71 (patch) | |
| tree | f5357410ffe0b171ce393c0cfa8b89a3e15f45d0 | |
| parent | 630d5a55c08fcf645983859138081be974c1f21f (diff) | |
| download | hal-76c310d0efb5d463f06291cb02100b3b3ce1da71.zip hal-76c310d0efb5d463f06291cb02100b3b3ce1da71.tar.gz hal-76c310d0efb5d463f06291cb02100b3b3ce1da71.tar.bz2 | |
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@FreeBSD.org> and
Joe Marcus Clarke <marcus@FreeBSD.org>.
54 files changed, 10540 insertions, 2 deletions
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 --- a/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 --- a/dev/null +++ b/hald/freebsd/README @@ -0,0 +1,28 @@ +=============================================================================== + Hardware Abstraction Layer + FreeBSD notes + + Jean-Yves Lefort <jylefort@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 --- a/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 --- a/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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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@FreeBSD.org> + * Copyright (C) 2006 Joe Marcus Clarke <marcus@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 --- a/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@FreeBSD.org> + * Copyright (C) 2006 Joe Marcus Clarke <marcus@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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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@FreeBSD.org> + * Copyright (C) 2006 Joe Marcus Clarke <marcus@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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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@FreeBSD.org> + * Joe Marcus Clarke <marcus@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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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 --- a/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@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 --- a/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@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 --- a/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@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 --- a/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@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) |
