[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

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: