diff --git a/configure.in b/configure.in index c479de3..ac9948e 100644 --- a/configure.in +++ b/configure.in @@ -540,6 +540,32 @@ elif test "x$with_macbookpro" = "x" ; th fi AM_CONDITIONAL(BUILD_MACBOOKPRO, test x$BUILD_MACBOOKPRO = xyes) +dnl macbook backlight support +AC_ARG_WITH(macbook, [ --with-macbook Include support for Macbook backlight (auto)]) +BUILD_MACBOOK=no +if test "x$with_macbook" = "xyes" ; then + BUILD_MACBOOK=yes +elif test "x$with_macbook" = "x" ; then + if test "x$USE_LIBPCI" != "xno" ; then + case "${HALD_BACKEND}" in + linux) + case "${host}" in + i[[3456]]86-*-*) + BUILD_MACBOOK=yes + ;; + *) + ;; + esac + ;; + *) + ;; + esac + fi +fi +AM_CONDITIONAL(BUILD_MACBOOK, test x$BUILD_MACBOOK = xyes) + + + AC_ARG_WITH(nokia770, [ --with-nokia770 Whether to build Nokia 770 utils (no)]) BUILD_N770=no if test "x$with_nokia770" = "xyes"; then @@ -696,6 +722,7 @@ echo " use APM: ${msg_apm} Macbook Pro utils: ${BUILD_MACBOOKPRO} (Linux only, x86 only, requires libpci) + Macbook backlight support: ${BUILD_MACBOOK} (Linux only, x86 only, requires libpci) CPU frequency scaling: ${BUILD_CPUFREQ} (Linux only) USB wireless mouse power: ${BUILD_USBCSR} (Linux only, requires libusb) Dell Backlight ${BUILD_DELL} (Linux only, requires libsmbios) diff --git a/fdi/policy/10osvendor/10-macbook-backlight.fdi b/fdi/policy/10osvendor/10-macbook-backlight.fdi new file mode 100644 index 0000000..8dba0df --- /dev/null +++ b/fdi/policy/10osvendor/10-macbook-backlight.fdi @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + laptop_panel + MacBook Backlight Control + custom + 118 + hald-addon-macbook-backlight + + + diff --git a/fdi/policy/10osvendor/Makefile.am b/fdi/policy/10osvendor/Makefile.am index 734eead..2fa0481 100644 --- a/fdi/policy/10osvendor/Makefile.am +++ b/fdi/policy/10osvendor/Makefile.am @@ -13,6 +13,10 @@ if BUILD_MACBOOKPRO dist_fdi_DATA += 10-macbookpro-utils.fdi endif +if BUILD_MACBOOK +dist_fdi_DATA += 10-macbook-backlight.fdi +endif + if BUILD_CPUFREQ dist_fdi_DATA += 10-cpufreq.fdi endif diff --git a/hald/linux/addons/Makefile.am b/hald/linux/addons/Makefile.am index 32bfb4d..3a1288c 100644 --- a/hald/linux/addons/Makefile.am +++ b/hald/linux/addons/Makefile.am @@ -36,6 +36,12 @@ hald_addon_macbookpro_backlight_SOURCES hald_addon_macbookpro_backlight_LDADD = $(top_builddir)/libhal/libhal.la -lpci @GLIB_LIBS@ endif +if BUILD_MACBOOK +libexec_PROGRAMS += hald-addon-macbook-backlight +hald_addon_macbook_backlight_SOURCES = addon-macbook-backlight.c ../../logger.c ../../util_helper.c +hald_addon_macbook_backlight_LDADD = $(top_builddir)/libhal/libhal.la -lpci @GLIB_LIBS@ +endif + if BUILD_CPUFREQ libexec_PROGRAMS += hald-addon-cpufreq hald_addon_cpufreq_SOURCES = addon-cpufreq.c addon-cpufreq.h addon-cpufreq-userspace.h \ diff --git a/hald/linux/addons/addon-macbook-backlight.c b/hald/linux/addons/addon-macbook-backlight.c new file mode 100644 index 0000000..92b66ae --- /dev/null +++ b/hald/linux/addons/addon-macbook-backlight.c @@ -0,0 +1,290 @@ +/* + * Macbook Backlight Control + * Copyright © 2006 Ryan Lortie + * + * HAL integration Copyright © 2007 Ryan Lortie + * using code Copyright © 2006 David Zeuthen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of version 2 of the GNU General Public License as + * published by the Free Software Foundation. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02110 USA + * + * This program was written after I reverse engineered the + * AppleIntelIntegratedFramebuffer.kext kernel extension in Mac OS X and + * played with the register at the memory location I found therein. + * + * From my experiments, the register appears to have two halves. + * + * yyyyyyyyyyyyyyy0xxxxxxxxxxxxxxx0 + * + * The top (y) bits appear to be the maximum brightness level and the + * bottom (x) bits are the current brightness level. 0s are always 0. + * The brightness level is, therefore, x/y. + * + * As my Macbook boots, y is set to 0x94 and x is set to 0x1f. Going below + * 0x1f produces odd results. For example, if you come from above, the + * backlight will completely turn off at 0x12 (18). Coming from below, + * however, you need to get to 0x15 (21) before the backlight comes back on. + * + * Since there is no clear cut boundry, I assume that this value specifies + * a raw voltage. Also, it appears that the bootup value of 0x1f corresponds + * to the lowest level that Mac OS X will set the backlight I choose this + * value as a minimum. + * + * For the maximum I do not let the value exceed the value in the upper 15 + * bits. + * + * Turning the backlight off entirely is not supported (as this is supported + * by the kernel itself). This utility is only for setting the brightness + * of the backlight when it is enabled. + */ + +#include + +#include + +#include +#include +#include +#include +#include + +#define DBUS_API_SUBJECT_TO_CHANGE + +#include +#include +#include "libhal/libhal.h" +#include "../../util_helper.h" + +#define REGISTER_OFFSET 0x00061254 +#define PAGE_SIZE 4096 +#define PAGE_MASK (PAGE_SIZE - 1) + +#define ACCESS_OFFSET (REGISTER_OFFSET & PAGE_MASK) +#define ACCESS_INDEX (ACCESS_OFFSET >> 2) + +unsigned int *register_page; + +static unsigned long +determine_video_base_address (void) +{ + struct pci_access *pacc; + struct pci_dev *pdev; + unsigned long address; + int i; + + address = 0; + + pacc = pci_alloc (); + pci_init (pacc); + pci_scan_bus (pacc); + + for (pdev = pacc->devices; pdev; pdev = pdev->next) + { + pci_fill_info (pdev, PCI_FILL_IDENT | PCI_FILL_BASES); + + if (pdev->vendor_id == 0x8086 && pdev->device_id == 0x27a2) + for (i = 0; i < (int) G_N_ELEMENTS (pdev->base_addr); i++) + { + if (pdev->size[i] == 512 * 1024) + { + address = pdev->base_addr[i]; + goto end; + } + } + } + +end: + pci_cleanup (pacc); + + return address; +} + +static gboolean +map_register_page (void) +{ + long address; + int fd; + + address = determine_video_base_address (); + + if (address == 0) + { + g_error ("Unable to locate video base address"); + return FALSE; + } + + fd = open ("/dev/mem", O_RDWR); + + if (fd < 0) + { + g_error ("failed to open /dev/mem"); + return FALSE; + } + + register_page = mmap (NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, + MAP_SHARED, fd, + (address + REGISTER_OFFSET) & ~PAGE_MASK); + + close (fd); + + drop_privileges (FALSE); + + if (register_page == MAP_FAILED) + { + g_error ("failed to mmap"); + return FALSE; + } + + return TRUE; +} + +static unsigned long +register_get (void) +{ + return register_page[ACCESS_INDEX]; +} + +static void +register_set (unsigned long value) +{ + register_page[ACCESS_INDEX] = value; +} + +static gboolean +backlight_set (long value) +{ + long max; + + max = register_get () >> 17; + + /* sanity check: this should always be 0x94 */ + if (max != 0x94) + return FALSE; + + value = CLAMP (value, 0x1f, max); + + register_set ((max << 17) | (value << 1)); + + return TRUE; +} + +static long +backlight_get (void) +{ + return (register_get() >> 1) & 0x7fff; +} + +#define BACKLIGHT_OBJECT \ + "/org/freedesktop/Hal/devices/macbook_backlight" +#define BACKLIGHT_IFACE \ + "org.freedesktop.Hal.Device.LaptopPanel" +#define INTERFACE_DESCRIPTION \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" + +static DBusHandlerResult +filter_function (DBusConnection *connection, + DBusMessage *message, + void *userdata) +{ + DBusMessage *reply; + DBusError err; + int level; + int ret; + + reply = NULL; + ret = 0; + + dbus_error_init (&err); + + if (dbus_message_is_method_call (message, BACKLIGHT_IFACE, "SetBrightness")) + { + + if (dbus_message_get_args (message, &err, DBUS_TYPE_INT32, + &level, DBUS_TYPE_INVALID)) + { + backlight_set (level + 0x1f); + + if ((reply = dbus_message_new_method_return (message))) + dbus_message_append_args (reply, DBUS_TYPE_INT32, + &ret, DBUS_TYPE_INVALID); + } + } + else + if (dbus_message_is_method_call (message, BACKLIGHT_IFACE, "GetBrightness")) + { + if (dbus_message_get_args (message, &err, DBUS_TYPE_INVALID)) + { + level = backlight_get () - 0x1f; + level = CLAMP (level, 0, 117); + + if ((reply = dbus_message_new_method_return (message))) + dbus_message_append_args (reply, DBUS_TYPE_INT32, + &level, DBUS_TYPE_INVALID); + } + } + + if (reply) + { + dbus_connection_send (connection, reply, NULL); + return DBUS_HANDLER_RESULT_HANDLED; + } + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +int +main (int argc, char **argv) +{ + LibHalContext *halctx; + DBusConnection *conn; + GMainLoop *main_loop; + const char *udi; + DBusError err; + + udi = getenv ("UDI"); + + if (udi == NULL) + g_error ("no device specified"); + + dbus_error_init (&err); + if ((halctx = libhal_ctx_init_direct (&err)) == NULL) + g_error ("cannot connect to hald"); + + dbus_error_init (&err); + if (!libhal_device_addon_is_ready (halctx, udi, &err)) + return -4; + + if (!map_register_page ()) + g_error ("failed to gain access to the video card"); + + conn = libhal_ctx_get_dbus_connection (halctx); + dbus_connection_setup_with_g_main (conn, NULL); + + dbus_connection_add_filter (conn, filter_function, NULL, NULL); + + if (!libhal_device_claim_interface (halctx, BACKLIGHT_OBJECT, + BACKLIGHT_IFACE, + INTERFACE_DESCRIPTION, &err)) + g_error ("cannot claim interface"); + + main_loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (main_loop); + + return 0; +}