18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* Disk protection for HP/DELL machines. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright 2008 Eric Piel 58c2ecf20Sopenharmony_ci * Copyright 2009 Pavel Machek <pavel@ucw.cz> 68c2ecf20Sopenharmony_ci * Copyright 2012 Sonal Santan 78c2ecf20Sopenharmony_ci * Copyright 2014 Pali Rohár <pali@kernel.org> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <stdio.h> 118c2ecf20Sopenharmony_ci#include <stdlib.h> 128c2ecf20Sopenharmony_ci#include <unistd.h> 138c2ecf20Sopenharmony_ci#include <fcntl.h> 148c2ecf20Sopenharmony_ci#include <sys/stat.h> 158c2ecf20Sopenharmony_ci#include <sys/types.h> 168c2ecf20Sopenharmony_ci#include <string.h> 178c2ecf20Sopenharmony_ci#include <stdint.h> 188c2ecf20Sopenharmony_ci#include <errno.h> 198c2ecf20Sopenharmony_ci#include <signal.h> 208c2ecf20Sopenharmony_ci#include <sys/mman.h> 218c2ecf20Sopenharmony_ci#include <sched.h> 228c2ecf20Sopenharmony_ci#include <syslog.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_cistatic int noled; 258c2ecf20Sopenharmony_cistatic char unload_heads_path[64]; 268c2ecf20Sopenharmony_cistatic char device_path[32]; 278c2ecf20Sopenharmony_cistatic const char app_name[] = "FREE FALL"; 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int set_unload_heads_path(char *device) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci if (strlen(device) <= 5 || strncmp(device, "/dev/", 5) != 0) 328c2ecf20Sopenharmony_ci return -EINVAL; 338c2ecf20Sopenharmony_ci strncpy(device_path, device, sizeof(device_path) - 1); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci snprintf(unload_heads_path, sizeof(unload_heads_path) - 1, 368c2ecf20Sopenharmony_ci "/sys/block/%s/device/unload_heads", device+5); 378c2ecf20Sopenharmony_ci return 0; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic int valid_disk(void) 418c2ecf20Sopenharmony_ci{ 428c2ecf20Sopenharmony_ci int fd = open(unload_heads_path, O_RDONLY); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if (fd < 0) { 458c2ecf20Sopenharmony_ci perror(unload_heads_path); 468c2ecf20Sopenharmony_ci return 0; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci close(fd); 508c2ecf20Sopenharmony_ci return 1; 518c2ecf20Sopenharmony_ci} 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_cistatic void write_int(char *path, int i) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci char buf[1024]; 568c2ecf20Sopenharmony_ci int fd = open(path, O_RDWR); 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci if (fd < 0) { 598c2ecf20Sopenharmony_ci perror("open"); 608c2ecf20Sopenharmony_ci exit(1); 618c2ecf20Sopenharmony_ci } 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci sprintf(buf, "%d", i); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (write(fd, buf, strlen(buf)) != strlen(buf)) { 668c2ecf20Sopenharmony_ci perror("write"); 678c2ecf20Sopenharmony_ci exit(1); 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci close(fd); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic void set_led(int on) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci if (noled) 768c2ecf20Sopenharmony_ci return; 778c2ecf20Sopenharmony_ci write_int("/sys/class/leds/hp::hddprotect/brightness", on); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic void protect(int seconds) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci const char *str = (seconds == 0) ? "Unparked" : "Parked"; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci write_int(unload_heads_path, seconds*1000); 858c2ecf20Sopenharmony_ci syslog(LOG_INFO, "%s %s disk head\n", str, device_path); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic int on_ac(void) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci /* /sys/class/power_supply/AC0/online */ 918c2ecf20Sopenharmony_ci return 1; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic int lid_open(void) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci /* /proc/acpi/button/lid/LID/state */ 978c2ecf20Sopenharmony_ci return 1; 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_cistatic void ignore_me(int signum) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci protect(0); 1038c2ecf20Sopenharmony_ci set_led(0); 1048c2ecf20Sopenharmony_ci} 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ciint main(int argc, char **argv) 1078c2ecf20Sopenharmony_ci{ 1088c2ecf20Sopenharmony_ci int fd, ret; 1098c2ecf20Sopenharmony_ci struct stat st; 1108c2ecf20Sopenharmony_ci struct sched_param param; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci if (argc == 1) 1138c2ecf20Sopenharmony_ci ret = set_unload_heads_path("/dev/sda"); 1148c2ecf20Sopenharmony_ci else if (argc == 2) 1158c2ecf20Sopenharmony_ci ret = set_unload_heads_path(argv[1]); 1168c2ecf20Sopenharmony_ci else 1178c2ecf20Sopenharmony_ci ret = -EINVAL; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci if (ret || !valid_disk()) { 1208c2ecf20Sopenharmony_ci fprintf(stderr, "usage: %s <device> (default: /dev/sda)\n", 1218c2ecf20Sopenharmony_ci argv[0]); 1228c2ecf20Sopenharmony_ci exit(1); 1238c2ecf20Sopenharmony_ci } 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci fd = open("/dev/freefall", O_RDONLY); 1268c2ecf20Sopenharmony_ci if (fd < 0) { 1278c2ecf20Sopenharmony_ci perror("/dev/freefall"); 1288c2ecf20Sopenharmony_ci return EXIT_FAILURE; 1298c2ecf20Sopenharmony_ci } 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci if (stat("/sys/class/leds/hp::hddprotect/brightness", &st)) 1328c2ecf20Sopenharmony_ci noled = 1; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (daemon(0, 0) != 0) { 1358c2ecf20Sopenharmony_ci perror("daemon"); 1368c2ecf20Sopenharmony_ci return EXIT_FAILURE; 1378c2ecf20Sopenharmony_ci } 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci openlog(app_name, LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci param.sched_priority = sched_get_priority_max(SCHED_FIFO); 1428c2ecf20Sopenharmony_ci sched_setscheduler(0, SCHED_FIFO, ¶m); 1438c2ecf20Sopenharmony_ci mlockall(MCL_CURRENT|MCL_FUTURE); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci signal(SIGALRM, ignore_me); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci for (;;) { 1488c2ecf20Sopenharmony_ci unsigned char count; 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci ret = read(fd, &count, sizeof(count)); 1518c2ecf20Sopenharmony_ci alarm(0); 1528c2ecf20Sopenharmony_ci if ((ret == -1) && (errno == EINTR)) { 1538c2ecf20Sopenharmony_ci /* Alarm expired, time to unpark the heads */ 1548c2ecf20Sopenharmony_ci continue; 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (ret != sizeof(count)) { 1588c2ecf20Sopenharmony_ci perror("read"); 1598c2ecf20Sopenharmony_ci break; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci protect(21); 1638c2ecf20Sopenharmony_ci set_led(1); 1648c2ecf20Sopenharmony_ci if (1 || on_ac() || lid_open()) 1658c2ecf20Sopenharmony_ci alarm(2); 1668c2ecf20Sopenharmony_ci else 1678c2ecf20Sopenharmony_ci alarm(20); 1688c2ecf20Sopenharmony_ci } 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci closelog(); 1718c2ecf20Sopenharmony_ci close(fd); 1728c2ecf20Sopenharmony_ci return EXIT_SUCCESS; 1738c2ecf20Sopenharmony_ci} 174