From 7f278b8a9d38a5c0851f20f12a4709684571d3d1 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Mon, 19 Mar 2007 21:14:11 +0000 Subject: add support for the macbook backlight Apply a patch from Ryan Lortie to support the Macbook backlight. The code was written after the AppleIntelIntegratedFramebuffer.kext kernel extension was reverse engineered by Ryan. Rebased to master, and CodingStyle corrected by me, no other changes. --- hald/linux/addons/.gitignore | 1 + hald/linux/addons/Makefile.am | 6 + hald/linux/addons/addon-macbook-backlight.c | 274 ++++++++++++++++++++++++++++ 3 files changed, 281 insertions(+) create mode 100644 hald/linux/addons/addon-macbook-backlight.c (limited to 'hald') diff --git a/hald/linux/addons/.gitignore b/hald/linux/addons/.gitignore index 2b6fd5c9..7ecd42a9 100644 --- a/hald/linux/addons/.gitignore +++ b/hald/linux/addons/.gitignore @@ -11,6 +11,7 @@ hald-addon-storage hald-addon-usb-csr hald-addon-keyboard hald-addon-acpi-buttons-toshiba +hald-addon-macbook-backlight hald-addon-macbookpro-backlight hald-addon-cpufreq hald-addon-dell-backlight diff --git a/hald/linux/addons/Makefile.am b/hald/linux/addons/Makefile.am index bea0a17d..5d987092 100644 --- a/hald/linux/addons/Makefile.am +++ b/hald/linux/addons/Makefile.am @@ -38,6 +38,12 @@ hald_addon_macbookpro_backlight_SOURCES = addon-macbookpro-backlight.c ../../log 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 00000000..5314f3c8 --- /dev/null +++ b/hald/linux/addons/addon-macbook-backlight.c @@ -0,0 +1,274 @@ +/* + * 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; +} + -- cgit v1.2.3