UnThinkPad
I have a Thinkpad. It's great, it works, brilliant 👍. But the ThinkPad is a prophesied machine of untapped potential and holds the key to attaining true freedom.
The following code and examples I write will be only for Linux. Windows users don't get much love from me.
The ThinkLight 💡
A relic from a bygone era.
Let's make it speak morse! First some basic understanding of how this would work:
For this we'll make use of the ec_sys
kernel module. This is a Linux kernel module that provides a sysfs interface to the Embedded Controller, which is a microcontroller responsible for controlling tasks related to battery, thermals, and in our case... the lights.
A good place to start would be by loading the module onto the kernel.
sudo modprobe -r ec_sys # incase its already loaded, unload it
sudo modprobe ec_sys write_support=1
Controlling the LED
Now manipulating the LED state should be as simple as writing to /sys/kernel/debug/ec/io
.
# Switches off the LED
echo -n -e \x0a | dd of="/sys/kernel/debug/ec/ec0/io" bs=1 seek=12 count=1 conv=notrunc 2> /dev/null
The value we write to the file is a hexadecimal number.
- The first nibble represents the state:
0
: off8
: onc
: blink
- The second nibble represents the LED in focus. Here are some from my tests:
a
: The red led on the thinkpad logo.e
: The mute led6
: Capslock LED0
: Power LED- There may be more I didn't bother finding (lol).
Extending this concept programatically, we can write the following C script...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#define EC_IO_PATH "/sys/kernel/debug/ec/ec0/io"
#define ON_BYTE 0x80
#define OFF_BYTE 0x00
#define LED_OFFSET 12
void run_command(const char *cmd) {
if (system(cmd) != 0) {
perror("Command failed");
}
}
void write_led(unsigned char value) {
int fd = open(EC_IO_PATH, O_WRONLY);
if (fd < 0) {
perror("Failed to open EC interface");
exit(EXIT_FAILURE);
}
if (lseek(fd, LED_OFFSET, SEEK_SET) < 0) {
perror("Seek failed");
close(fd);
exit(EXIT_FAILURE);
}
if (write(fd, &value, 1) != 1) {
perror("Write failed");
}
close(fd);
}
void dit() {
write_led(ON_BYTE);
usleep(100000);
write_led(OFF_BYTE);
usleep(100000);
}
void dah() {
write_led(ON_BYTE);
usleep(300000);
write_led(OFF_BYTE);
usleep(100000);
}
void morse(char c) {
switch (c) {
case '0': dah(); dah(); dah(); dah(); dah(); break;
case '1': dit(); dah(); dah(); dah(); dah(); break;
case '2': dit(); dit(); dah(); dah(); dah(); break;
case '3': dit(); dit(); dit(); dah(); dah(); break;
case '4': dit(); dit(); dit(); dit(); dah(); break;
case '5': dit(); dit(); dit(); dit(); dit(); break;
case '6': dah(); dit(); dit(); dit(); dit(); break;
case '7': dah(); dah(); dit(); dit(); dit(); break;
case '8': dah(); dah(); dah(); dit(); dit(); break;
case '9': dah(); dah(); dah(); dah(); dit(); break;
case 'a': dit(); dah(); break;
case 'b': dah(); dit(); dit(); dit(); break;
case 'c': dah(); dit(); dah(); dit(); break;
case 'd': dah(); dit(); dit(); break;
case 'e': dit(); break;
case 'f': dit(); dit(); dah(); dit(); break;
case 'g': dah(); dah(); dit(); break;
case 'h': dit(); dit(); dit(); dit(); break;
case 'i': dit(); dit(); break;
case 'j': dit(); dah(); dah(); dah(); break;
case 'k': dah(); dit(); dah(); break;
case 'l': dit(); dah(); dit(); dit(); break;
case 'm': dah(); dah(); break;
case 'n': dah(); dit(); break;
case 'o': dah(); dah(); dah(); break;
case 'p': dit(); dah(); dah(); dit(); break;
case 'q': dah(); dah(); dit(); dah(); break;
case 'r': dit(); dah(); dit(); break;
case 's': dit(); dit(); dit(); break;
case 't': dah(); break;
case 'u': dit(); dit(); dah(); break;
case 'v': dit(); dit(); dit(); dah(); break;
case 'w': dit(); dah(); dah(); break;
case 'x': dah(); dit(); dit(); dah(); break;
case 'y': dah(); dit(); dah(); dah(); break;
case 'z': dah(); dah(); dit(); dit(); break;
case ' ': usleep(600000); break;
}
usleep(200000);
}
void parse(const char *input) {
while (*input) {
morse(*input);
input++;
}
}
int main() {
char input[100];
run_command("modprobe -r ec_sys");
run_command("modprobe ec_sys write_support=1");
write_led(OFF_BYTE);
printf("Enter a word: ");
if (fgets(input, sizeof(input), stdin) == NULL) {
perror("Error reading input");
return EXIT_FAILURE;
}
input[strcspn(input, "\n")] = '\0'; // Remove newline from input
printf("Blinking \"%s\"\n", input);
parse(input);
sleep(1);
write_led(ON_BYTE);
run_command("modprobe -r ec_sys");
return 0;
}