17f904d7eSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2b3fd7368SPali Rohár /* Disk protection for HP/DELL machines.
3b3fd7368SPali Rohár *
4b3fd7368SPali Rohár * Copyright 2008 Eric Piel
5b3fd7368SPali Rohár * Copyright 2009 Pavel Machek <pavel@ucw.cz>
6b3fd7368SPali Rohár * Copyright 2012 Sonal Santan
7*149ed3d4SPali Rohár * Copyright 2014 Pali Rohár <pali@kernel.org>
8b3fd7368SPali Rohár */
9b3fd7368SPali Rohár
10b3fd7368SPali Rohár #include <stdio.h>
11b3fd7368SPali Rohár #include <stdlib.h>
12b3fd7368SPali Rohár #include <unistd.h>
13b3fd7368SPali Rohár #include <fcntl.h>
14b3fd7368SPali Rohár #include <sys/stat.h>
15b3fd7368SPali Rohár #include <sys/types.h>
16b3fd7368SPali Rohár #include <string.h>
17b3fd7368SPali Rohár #include <stdint.h>
18b3fd7368SPali Rohár #include <errno.h>
19b3fd7368SPali Rohár #include <signal.h>
20b3fd7368SPali Rohár #include <sys/mman.h>
21b3fd7368SPali Rohár #include <sched.h>
22b3fd7368SPali Rohár #include <syslog.h>
23b3fd7368SPali Rohár
24b3fd7368SPali Rohár static int noled;
25b3fd7368SPali Rohár static char unload_heads_path[64];
26b3fd7368SPali Rohár static char device_path[32];
27b3fd7368SPali Rohár static const char app_name[] = "FREE FALL";
28b3fd7368SPali Rohár
set_unload_heads_path(char * device)29b3fd7368SPali Rohár static int set_unload_heads_path(char *device)
30b3fd7368SPali Rohár {
31b3fd7368SPali Rohár if (strlen(device) <= 5 || strncmp(device, "/dev/", 5) != 0)
32b3fd7368SPali Rohár return -EINVAL;
33b3fd7368SPali Rohár strncpy(device_path, device, sizeof(device_path) - 1);
34b3fd7368SPali Rohár
35b3fd7368SPali Rohár snprintf(unload_heads_path, sizeof(unload_heads_path) - 1,
36b3fd7368SPali Rohár "/sys/block/%s/device/unload_heads", device+5);
37b3fd7368SPali Rohár return 0;
38b3fd7368SPali Rohár }
39b3fd7368SPali Rohár
valid_disk(void)40b3fd7368SPali Rohár static int valid_disk(void)
41b3fd7368SPali Rohár {
42b3fd7368SPali Rohár int fd = open(unload_heads_path, O_RDONLY);
43b3fd7368SPali Rohár
44b3fd7368SPali Rohár if (fd < 0) {
45b3fd7368SPali Rohár perror(unload_heads_path);
46b3fd7368SPali Rohár return 0;
47b3fd7368SPali Rohár }
48b3fd7368SPali Rohár
49b3fd7368SPali Rohár close(fd);
50b3fd7368SPali Rohár return 1;
51b3fd7368SPali Rohár }
52b3fd7368SPali Rohár
write_int(char * path,int i)53b3fd7368SPali Rohár static void write_int(char *path, int i)
54b3fd7368SPali Rohár {
55b3fd7368SPali Rohár char buf[1024];
56b3fd7368SPali Rohár int fd = open(path, O_RDWR);
57b3fd7368SPali Rohár
58b3fd7368SPali Rohár if (fd < 0) {
59b3fd7368SPali Rohár perror("open");
60b3fd7368SPali Rohár exit(1);
61b3fd7368SPali Rohár }
62b3fd7368SPali Rohár
63b3fd7368SPali Rohár sprintf(buf, "%d", i);
64b3fd7368SPali Rohár
65b3fd7368SPali Rohár if (write(fd, buf, strlen(buf)) != strlen(buf)) {
66b3fd7368SPali Rohár perror("write");
67b3fd7368SPali Rohár exit(1);
68b3fd7368SPali Rohár }
69b3fd7368SPali Rohár
70b3fd7368SPali Rohár close(fd);
71b3fd7368SPali Rohár }
72b3fd7368SPali Rohár
set_led(int on)73b3fd7368SPali Rohár static void set_led(int on)
74b3fd7368SPali Rohár {
75b3fd7368SPali Rohár if (noled)
76b3fd7368SPali Rohár return;
77b3fd7368SPali Rohár write_int("/sys/class/leds/hp::hddprotect/brightness", on);
78b3fd7368SPali Rohár }
79b3fd7368SPali Rohár
protect(int seconds)80b3fd7368SPali Rohár static void protect(int seconds)
81b3fd7368SPali Rohár {
82b3fd7368SPali Rohár const char *str = (seconds == 0) ? "Unparked" : "Parked";
83b3fd7368SPali Rohár
84b3fd7368SPali Rohár write_int(unload_heads_path, seconds*1000);
85b3fd7368SPali Rohár syslog(LOG_INFO, "%s %s disk head\n", str, device_path);
86b3fd7368SPali Rohár }
87b3fd7368SPali Rohár
on_ac(void)88b3fd7368SPali Rohár static int on_ac(void)
89b3fd7368SPali Rohár {
90b3fd7368SPali Rohár /* /sys/class/power_supply/AC0/online */
91b3fd7368SPali Rohár return 1;
92b3fd7368SPali Rohár }
93b3fd7368SPali Rohár
lid_open(void)94b3fd7368SPali Rohár static int lid_open(void)
95b3fd7368SPali Rohár {
96b3fd7368SPali Rohár /* /proc/acpi/button/lid/LID/state */
97b3fd7368SPali Rohár return 1;
98b3fd7368SPali Rohár }
99b3fd7368SPali Rohár
ignore_me(int signum)100b3fd7368SPali Rohár static void ignore_me(int signum)
101b3fd7368SPali Rohár {
102b3fd7368SPali Rohár protect(0);
103b3fd7368SPali Rohár set_led(0);
104b3fd7368SPali Rohár }
105b3fd7368SPali Rohár
main(int argc,char ** argv)106b3fd7368SPali Rohár int main(int argc, char **argv)
107b3fd7368SPali Rohár {
108b3fd7368SPali Rohár int fd, ret;
109b3fd7368SPali Rohár struct stat st;
110b3fd7368SPali Rohár struct sched_param param;
111b3fd7368SPali Rohár
112b3fd7368SPali Rohár if (argc == 1)
113b3fd7368SPali Rohár ret = set_unload_heads_path("/dev/sda");
114b3fd7368SPali Rohár else if (argc == 2)
115b3fd7368SPali Rohár ret = set_unload_heads_path(argv[1]);
116b3fd7368SPali Rohár else
117b3fd7368SPali Rohár ret = -EINVAL;
118b3fd7368SPali Rohár
119b3fd7368SPali Rohár if (ret || !valid_disk()) {
120b3fd7368SPali Rohár fprintf(stderr, "usage: %s <device> (default: /dev/sda)\n",
121b3fd7368SPali Rohár argv[0]);
122b3fd7368SPali Rohár exit(1);
123b3fd7368SPali Rohár }
124b3fd7368SPali Rohár
125b3fd7368SPali Rohár fd = open("/dev/freefall", O_RDONLY);
126b3fd7368SPali Rohár if (fd < 0) {
127b3fd7368SPali Rohár perror("/dev/freefall");
128b3fd7368SPali Rohár return EXIT_FAILURE;
129b3fd7368SPali Rohár }
130b3fd7368SPali Rohár
131b3fd7368SPali Rohár if (stat("/sys/class/leds/hp::hddprotect/brightness", &st))
132b3fd7368SPali Rohár noled = 1;
133b3fd7368SPali Rohár
134b3fd7368SPali Rohár if (daemon(0, 0) != 0) {
135b3fd7368SPali Rohár perror("daemon");
136b3fd7368SPali Rohár return EXIT_FAILURE;
137b3fd7368SPali Rohár }
138b3fd7368SPali Rohár
139b3fd7368SPali Rohár openlog(app_name, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
140b3fd7368SPali Rohár
141b3fd7368SPali Rohár param.sched_priority = sched_get_priority_max(SCHED_FIFO);
142b3fd7368SPali Rohár sched_setscheduler(0, SCHED_FIFO, ¶m);
143b3fd7368SPali Rohár mlockall(MCL_CURRENT|MCL_FUTURE);
144b3fd7368SPali Rohár
145b3fd7368SPali Rohár signal(SIGALRM, ignore_me);
146b3fd7368SPali Rohár
147b3fd7368SPali Rohár for (;;) {
148b3fd7368SPali Rohár unsigned char count;
149b3fd7368SPali Rohár
150b3fd7368SPali Rohár ret = read(fd, &count, sizeof(count));
151b3fd7368SPali Rohár alarm(0);
152b3fd7368SPali Rohár if ((ret == -1) && (errno == EINTR)) {
153b3fd7368SPali Rohár /* Alarm expired, time to unpark the heads */
154b3fd7368SPali Rohár continue;
155b3fd7368SPali Rohár }
156b3fd7368SPali Rohár
157b3fd7368SPali Rohár if (ret != sizeof(count)) {
158b3fd7368SPali Rohár perror("read");
159b3fd7368SPali Rohár break;
160b3fd7368SPali Rohár }
161b3fd7368SPali Rohár
162b3fd7368SPali Rohár protect(21);
163b3fd7368SPali Rohár set_led(1);
164b3fd7368SPali Rohár if (1 || on_ac() || lid_open())
165b3fd7368SPali Rohár alarm(2);
166b3fd7368SPali Rohár else
167b3fd7368SPali Rohár alarm(20);
168b3fd7368SPali Rohár }
169b3fd7368SPali Rohár
170b3fd7368SPali Rohár closelog();
171b3fd7368SPali Rohár close(fd);
172b3fd7368SPali Rohár return EXIT_SUCCESS;
173b3fd7368SPali Rohár }
174