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

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: