xref: /openbmc/linux/tools/laptop/freefall/freefall.c (revision 149ed3d4)
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
7149ed3d4SPali 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, &param);
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