diff options
author | Jiri Kosina <jkosina@suse.cz> | 2011-04-26 10:22:15 +0200 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2011-04-26 10:22:59 +0200 |
commit | 07f9479a40cc778bc1462ada11f95b01360ae4ff (patch) | |
tree | 0676cf38df3844004bb3ebfd99dfa67a4a8998f5 /Documentation/laptops/hpfall.c | |
parent | ath9k_hw: don't touch with treewide double semicolon removal (diff) | |
parent | Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ecr... (diff) | |
download | linux-07f9479a40cc778bc1462ada11f95b01360ae4ff.tar.xz linux-07f9479a40cc778bc1462ada11f95b01360ae4ff.zip |
Merge branch 'master' into for-next
Fast-forwarded to current state of Linus' tree as there are patches to be
applied for files that didn't exist on the old branch.
Diffstat (limited to 'Documentation/laptops/hpfall.c')
-rw-r--r-- | Documentation/laptops/hpfall.c | 146 |
1 files changed, 146 insertions, 0 deletions
diff --git a/Documentation/laptops/hpfall.c b/Documentation/laptops/hpfall.c new file mode 100644 index 000000000000..a4a8fc5d05d4 --- /dev/null +++ b/Documentation/laptops/hpfall.c @@ -0,0 +1,146 @@ +/* Disk protection for HP machines. + * + * Copyright 2008 Eric Piel + * Copyright 2009 Pavel Machek <pavel@ucw.cz> + * + * GPLv2. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <string.h> +#include <stdint.h> +#include <errno.h> +#include <signal.h> +#include <sys/mman.h> +#include <sched.h> + +char unload_heads_path[64]; + +int set_unload_heads_path(char *device) +{ + char devname[64]; + + if (strlen(device) <= 5 || strncmp(device, "/dev/", 5) != 0) + return -EINVAL; + strncpy(devname, device + 5, sizeof(devname)); + + snprintf(unload_heads_path, sizeof(unload_heads_path), + "/sys/block/%s/device/unload_heads", devname); + return 0; +} +int valid_disk(void) +{ + int fd = open(unload_heads_path, O_RDONLY); + if (fd < 0) { + perror(unload_heads_path); + return 0; + } + + close(fd); + return 1; +} + +void write_int(char *path, int i) +{ + char buf[1024]; + int fd = open(path, O_RDWR); + if (fd < 0) { + perror("open"); + exit(1); + } + sprintf(buf, "%d", i); + if (write(fd, buf, strlen(buf)) != strlen(buf)) { + perror("write"); + exit(1); + } + close(fd); +} + +void set_led(int on) +{ + write_int("/sys/class/leds/hp::hddprotect/brightness", on); +} + +void protect(int seconds) +{ + write_int(unload_heads_path, seconds*1000); +} + +int on_ac(void) +{ +// /sys/class/power_supply/AC0/online +} + +int lid_open(void) +{ +// /proc/acpi/button/lid/LID/state +} + +void ignore_me(void) +{ + protect(0); + set_led(0); +} + +int main(int argc, char **argv) +{ + int fd, ret; + struct sched_param param; + + if (argc == 1) + ret = set_unload_heads_path("/dev/sda"); + else if (argc == 2) + ret = set_unload_heads_path(argv[1]); + else + ret = -EINVAL; + + if (ret || !valid_disk()) { + fprintf(stderr, "usage: %s <device> (default: /dev/sda)\n", + argv[0]); + exit(1); + } + + fd = open("/dev/freefall", O_RDONLY); + if (fd < 0) { + perror("/dev/freefall"); + return EXIT_FAILURE; + } + + daemon(0, 0); + param.sched_priority = sched_get_priority_max(SCHED_FIFO); + sched_setscheduler(0, SCHED_FIFO, ¶m); + mlockall(MCL_CURRENT|MCL_FUTURE); + + signal(SIGALRM, ignore_me); + + for (;;) { + unsigned char count; + + ret = read(fd, &count, sizeof(count)); + alarm(0); + if ((ret == -1) && (errno == EINTR)) { + /* Alarm expired, time to unpark the heads */ + continue; + } + + if (ret != sizeof(count)) { + perror("read"); + break; + } + + protect(21); + set_led(1); + if (1 || on_ac() || lid_open()) + alarm(2); + else + alarm(20); + } + + close(fd); + return EXIT_SUCCESS; +} |