#define _XOPEN_SOURCE 500 #include #include #include #include #include #include #include #include #include #include #include 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); }