Re: kblevel - set keyboard illumination directly
Johannes Berg <johannes@sipsolutions.net> writes:
> On Tue, 2006-06-20 at 20:23 +1000, Paul Collins wrote:
>
>> > Btw, it's in my device-tree as
>> > uni-n@f8000000/i2c@f8001000/i2c-bus@0/lmu-micro@84 with a compatible
>> > 'lmu-controller' property.
>>
>> Does this mean that of_find_node_by_name("lmu-controller", 0) will
>> return the correct node? I don't know much about device tree stuff.
>
> No, you'll probably need to do of_find_compatible_node(NULL, NULL,
> "lmu-controller") or something like that, if your compatible property
> includes 'lmu-controller' too.
Hmm, no of_find_compatible_node or anything like it that I can see in
the of-lib I pulled today. And delightfully there doesn't seem to be
any 'compatible' property in my lmu-controller node.
Anyway, here's the current version with the sysfs-guided I2C probing.
/*
* 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.
*
* We now scan sysfs for likely candidates instead of opening every
* possible i2c device and trying to see if it responds as we expect
* the LMU to.
*/
#include <dirent.h>
#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
/* TODO: read this from the device tree. */
#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) == 0) {
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;
}
/* Guess whether an i2c device is the LMU by trying to read sensor data. */
int lmu_probe(const char *dev)
{
char buf[4];
int fd;
if ((fd = open(dev, O_RDWR)) < 0)
return -1;
if (ioctl(fd, I2C_SLAVE, LMU_ADDR) < 0) {
close(fd);
return -1;
}
if (read (fd, buf, 4) == 4) {
return fd;
} else {
close(fd);
return -1;
}
}
/* Find I2C devices that might be the LMU controller and probe them. */
int find_i2c_device(void)
{
char buf[48];
DIR *d;
struct dirent *de;
int rc = -1;
int fd;
int i;
snprintf(buf, sizeof buf - 1, "/sys/class/i2c-dev");
if (d = opendir(buf)) {
while (de = readdir(d)) {
if (de->d_name[0] == '.')
continue;
snprintf(buf, sizeof buf, "/sys/class/i2c-dev/%s/name", de->d_name);
if ((fd = open(buf, O_RDONLY)) < 0) {
continue;
}
if ((i = read(fd, buf, sizeof buf)) < 0) {
close(fd);
continue;
}
if (i >= sizeof buf) {
close(fd);
continue;
}
buf[i] = '\0';
if (!(strncmp("uni-n ", buf, 6) == 0)) {
close(fd);
continue;
}
snprintf(buf, sizeof buf, "/dev/%s", de->d_name);
if ((rc = lmu_probe(buf)) >= 0) {
close(fd);
goto out;
}
}
}
out:
if (d)
closedir(d);
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: