kblevel - set keyboard illumination directly
I've extracted the keyboard illumination code from pbbuttonsd for my
own use. I post it here in case it may be useful to others.
I've tried to preserve PowerBook5,8/5,9 support, but I only have a
PowerBook5,4 here so I can't test it.
Thanks of course are due to Matthias Grimm for writing the original
pbbuttonsd code.
/*
* kblevel.c --- control PowerBook keyboard illumination
*
* Copyright 2002 Matthias Grimm
* Copyright 2006 Paul Collins <paul@briny.ondioline.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
/*
* This code was taken from Matthias Grimm's pbbuttonsd.
* Aside from the gratuitous style changes, I altered the code to read
* the machine type from /proc/device-tree/model insted of
* /proc/cpuinfo and added support for 255 brightness levels.
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <linux/adb.h>
#define ADB_BUFSIZE 32
#define I2C_SLAVE 0x0703
#define LMU_ADDR 0x42
int get_machine(void)
{
char buffer[32];
int machine = 0;
int n;
FILE *f;
if ((f = fopen("/proc/device-tree/model", "r"))) {
while (fgets(buffer, sizeof buffer, f)) {
if (!strncmp("PowerBook", buffer, 9)) {
machine = (atoi(&buffer[9]) & 0xf) << 4;
for (n = 9; buffer[n] != ',' && buffer[n] != '\0'; ++n)
;
if (buffer[n] == ',')
machine |= atoi(&buffer[n+1]) & 0xf;
}
break;
}
}
fclose(f);
return machine;
}
int have_i2c_ambient_sensor(int machine)
{
if ((machine >= 0x51) && (machine <= 0x57))
return 1;
return 0;
}
int have_pmu_ambient_sensor(int machine)
{
if ((machine >= 0x58) && (machine <= 0x59))
return 1;
return 0;
}
/*
* This function tries to find the I2C device that controls the
* keyboard illumination and the ambient light sensor used in some
* AlBooks. It returns an open fd on success, otherwise a negative
* error code. The loop starts with device 4 because that's the
* correct device on 17" AlBooks. Device 0 could be falsely detected
* because the function uses only circumstantial evidence to detect
* the correct device.
*/
int find_i2c_device(void)
{
char i2cdevice[20];
char buf[4];
int n;
int fd;
int rc = -ENODEV;
for(n = 4; n < 260; ++n) {
snprintf(i2cdevice, 19, "/dev/i2c-%d", (n & 255) );
if ((fd = open (i2cdevice, O_RDWR)) >= 0 ) {
if (ioctl (fd, I2C_SLAVE, LMU_ADDR) >= 0)
if (read (fd, buf, 4) == 4)
return fd;
close (fd);
} else if (errno == ENODEV) {
rc = -ENODEV;
break;
} else if (errno == EACCES) {
rc = -EACCES;
break;
}
}
return rc;
}
int send_pmu_request(int fd, unsigned char *buffer, int params, ...)
{
va_list list;
int n;
int x;
if (params < 0 || params > 30)
return -1;
buffer[0] = PMU_PACKET;
va_start(list, params);
for (x = 0; x < params; ++x)
buffer[x + 1] = va_arg(list, int);
va_end(list);
n = write(fd, buffer, x+1);
if ((n != x+1) || (n == -1))
return -1;
if ((n = read(fd, buffer, ADB_BUFSIZE)) < 0)
return -1;
return n;
}
int set_keyboard_illumination(unsigned short level)
{
int machine = get_machine();
unsigned char buf[ADB_BUFSIZE];
int fd;
if (level > 255)
level = 255;
if ((have_i2c_ambient_sensor(machine)) && (fd = find_i2c_device()) > 0) {
buf[0] = 0x01; /* i2c register */
/* The format appears to be:
byte 1 byte 2
|<---->| |<---->|
xxxx7654 3210xxxx
|<----->|
^-- brightness
*/
buf[1] = level >> 4;
buf[2] = (level & 0x0f) << 4;
write(fd, buf, 3);
close(fd);
return 1;
}
if (have_pmu_ambient_sensor(machine) && (fd = open("/dev/adb", O_RDWR)) > 0) {
send_pmu_request(fd, buf, 4, 0x4f, 0, 0, level);
return 1;
}
return 0;
}
int main(int argc, char *argv[])
{
int level;
if (argc == 2) {
/* Some special string values. */
if ((strcmp(argv[1], "on") == 0) || (strcmp(argv[1], "full") == 0)) {
level = 255;
} else if (strcmp(argv[1], "half") == 0) {
level = 127;
} else if (strcmp(argv[1], "quarter") == 0) {
level = 63;
} else if (strcmp(argv[1], "off") == 0) {
level = 0;
} else {
level = atoi(argv[1]);
}
} else {
fprintf(stderr, "usage: kblevel [ off | half | quarter | full | on | 0-255 ]\n");
exit(EXIT_FAILURE);
}
if (!set_keyboard_illumination(level)) {
fprintf(stderr, "failed to set keyboard illumination\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
--
Paul Collins
Melbourne, Australia
Dag vijandelijk luchtschip de huismeester is dood
Reply to: