/* * 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 enum { FALSE, TRUE }; #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 < 6; i++) { if (pdev->size[i] == 512 * 1024) { address = pdev->base_addr[i]; goto end; } } } end: pci_cleanup (pacc); return address; } static unsigned long register_get (void) { return register_page[ACCESS_INDEX]; } static void register_set (unsigned long value) { register_page[ACCESS_INDEX] = value; } static int map_register_page (void) { long address; int fd; address = determine_video_base_address (); if (address == 0) return FALSE; fd = open ("/dev/mem", O_RDWR); if (fd < 0) return FALSE; register_page = mmap (NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (address + REGISTER_OFFSET) & ~PAGE_MASK); close (fd); if (register_page == MAP_FAILED) return FALSE; return TRUE; } int main (void) { if (!map_register_page ()) fprintf (stderr, "cannot map register page!\n"); else register_set ((0x94 << 17) | (0x94 << 1)); return 0; }