un...@cpan.org once said:
> I reviewed the data sheet and the scripts should
> work, but does not.

The above scripts can work for some Intel cards
but probably not for all of them.

I wrote a program fifteen years ago to adjust the
brightness on an Intel MacBook from Linux. There
was a bit of experimentation involved to determine
register values that wouldn't cause problems. I've
attached it below.

Notice how I had to use a mask of 0xfffe instead
of 0xffff to keep the least significant bit zero
and how I had to be strict about maintaining the
maximum backlight level field when reading and
writing the register. I remember working through a
lot of blank and flickering screens before
arriving at something that worked.

I'm not near a machine with an Intel card at the
moment so I can't give you something that works on
Plan 9 but reading this old code may give you some
hints.

Cheers,
  Anthony

------------------------------------------
9fans: 9fans
Permalink: 
https://9fans.topicbox.com/groups/9fans/T4c4247ec1c0429f6-M13bf569fd6da2483ff3c4074
Delivery options: https://9fans.topicbox.com/groups/9fans/subscription
#define _XOPEN_SOURCE 500
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <pci/pci.h>

typedef unsigned short  ushort;
typedef unsigned int    uint;

// A barebones representation of a PCI device.
typedef struct Pcidev Pcidev;
struct Pcidev {
        ushort     vid;  // PCI vendor ID.
        ushort     did;  // PCI device ID.
        pciaddr_t  bar;  // Physical address of the first BAR.
        size_t     size; // Size of the first BAR.
        char*      addr; // Mapped virtual address of the first BAR.
};

// Macros to read and write 32-bit PCI memory registers.
#define reg32r(p, r)    (*(uint*)((p)->addr+(r)))
#define reg32w(p, r, v) (*(uint*)((p)->addr+(r)) = (v))

enum {
        // The PCI vendor and device ID for the Intel 945GM graphics chipset.
        Intel  = 0x8086,
        I945GM = 0x27a2,

        // The backlight PWM control register offset.
        Bctl = 0x061254,

        // The backlight duty cycle mask. The LSB must be zero.
        Bmask = 0xfffe,

        // The empirically observed range for valid brightness levels.
        Boff = 0x0000,
        Bmin = 0x003e,
        Bmax = 0x0128,

        // Without an explicit value, we increase or decrease by this
        // quantum so that there are only ten brightness levels.
        Bquant = (Bmax-Bmin)/(10-1),
};

// We don't expose the hardware-centric brightness levels to the user
// but instead shift the range down so it starts at one. These macros
// convert to and fro.
#define B(u)    (((u) == 0) ? Boff : (u)+Bmin-1)
#define U(b)    (((b) == Boff) ? 0 : (b)-Bmin+1)

// The set of operations that can be performed on the backlight.
enum {
        Onop = 1<<0,
        Oset = 1<<1,
        Oadd = 1<<2|Oset,
        Osub = 1<<3|Oset,
};

// The high sixteen bits of the opcode are only used for operations
// that require a numeric argument. These macros ease extraction.
#define OP(o)    ((o)&0xFFFF)
#define OPVAL(v) (((v)&0xFFFF)<<16)

char*   argv0;

void    fatal(char*, ...);
int     pcimatch(Pcidev*, int, int);

int
main(int argc, char *argv[])
{
        int o, fd, max, cur, new;
        char *s, *end;
        Pcidev p;

        // Determine which operation to perform.
        argv0 = argv[0];
        switch(argc){
        default:
                fprintf(stderr, "usage: %s [+-][val]\n", argv0);
                exit(EXIT_FAILURE);
        case 1:
                o = Onop;
                break;
        case 2:
                s = argv[1];
                if(*s == '+' || *s == '-'){
                        o = (*s == '+') ? Oadd : Osub;
                        s++;
                } else
                        o = Oset;
                if(o != Oset && *s == '\0')
                        o |= OPVAL(Bquant);
                else {
                        o |= OPVAL(strtol(s, &end, 10));
                        if(errno != 0 || end == s)
                                fatal("invalid number");
                }
                break;
        }

        // Map the GPU device into memory so we can access the backlight 
register.
        if(pcimatch(&p, Intel, I945GM) < 0)
                fatal("pcimatch failed");
        fd = open("/dev/mem", O_RDWR);
        if(fd < 0)
                fatal("open /dev/mem failed");
        p.addr = mmap(NULL, p.size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 
p.bar);
        if(p.addr == MAP_FAILED)
                fatal("map failed");
        close(fd);
        max = reg32r(&p, Bctl) >> 16;
        if(max != Bmax)
                fatal("invalid maximum: have %d want %d", max, Bmax);

        // Load the current brightness level and change it, if applicable.
        cur = reg32r(&p, Bctl) & Bmask;
        new = 0;
        switch(OP(o)){
        case Oset:
                new = B(o>>16);
                break;
        case Oadd:
                new = (cur == Boff) ? Bmin : cur + (o>>16);
                break;
        case Osub:
                new = (cur == Bmin) ? Boff : cur - (o>>16);
                break;
        }
        if(o & Oset){
                if(new < 0)
                        new = Boff;
                if(new > 0){
                        if(new < Bmin)
                                new = Bmin;
                        if(new > Bmax)
                                new = Bmax;
                }
                reg32w(&p, Bctl, (new&Bmask)|(Bmax<<16));
                cur = reg32r(&p, Bctl) & Bmask;
        }

        // Unmap the device and print the current backlight level.
        munmap(p.addr, p.size);
        printf("%d/%d\n", U(cur), U(Bmax));
        return 0;
}

#define nil ((void*)0)

static struct pci_access* pciacc;
static struct pci_dev*    pcilist;

int
pcimatch(Pcidev* p, int vid, int did)
{
        static int pcicfgmode = -1;
        struct pci_access *a;
        struct pci_dev *prev;
        int req;

        if(pcicfgmode == -1){
                a = pci_alloc();
                if(a == nil)
                        fatal("pci_alloc failed");
                pci_init(a);
                pci_scan_bus(a);
                pcilist = a->devices;
                pciacc = a;
                pcicfgmode = 0;
        }

        prev = pcilist;
        req = PCI_FILL_IDENT | PCI_FILL_BASES;
        pci_fill_info(prev, req);
        while(prev != nil){
                if((vid == 0 || prev->vendor_id == vid)
                && (did == 0 || prev->device_id == did))
                        break;
                prev = prev->next;
        }
        if(prev == nil)
                return -1;
        p->vid = prev->vendor_id;
        p->did = prev->device_id;

        p->bar = prev->base_addr[0] & PCI_ADDR_MEM_MASK;
        p->size = prev->size[0];
        pci_cleanup(pciacc);
        return 0;
}

void
fatal(char *fmt, ...)
{
        char buf[4096];
        va_list arg;
        int n;

        n = snprintf(buf, sizeof buf, "%s: ", argv0);
        va_start(arg, fmt);
        n += vsnprintf(buf+n, sizeof buf-n, fmt, arg);
        va_end(arg);
        if(errno)
                n += snprintf(buf+n, sizeof buf-n, ": %s", strerror(errno));
        n += snprintf(buf+n, sizeof buf-n, "\n");
        write(2, buf, n);
        exit(EXIT_FAILURE);
}

Reply via email to