2009-02-19 01:48:23 +03:00
/* Disk protection for HP machines.
*
* Copyright 2008 Eric Piel
2010-07-18 16:27:13 +04:00
* Copyright 2009 Pavel Machek < pavel @ ucw . cz >
2009-02-19 01:48:23 +03:00
*
* 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>
2009-07-25 22:55:15 +04:00
# include <sys/mman.h>
# include <sched.h>
2009-02-19 01:48:23 +03:00
2009-07-25 23:00:12 +04:00
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 ;
}
2009-02-19 01:48:23 +03:00
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 )
{
2009-07-25 23:00:12 +04:00
write_int ( unload_heads_path , seconds * 1000 ) ;
2009-02-19 01:48:23 +03:00
}
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 ) ;
}
2009-07-25 23:00:12 +04:00
int main ( int argc , char * * argv )
2009-02-19 01:48:23 +03:00
{
2009-06-22 13:08:36 +04:00
int fd , ret ;
2009-07-25 22:55:15 +04:00
struct sched_param param ;
2009-02-19 01:48:23 +03:00
2009-07-25 23:00:12 +04:00
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 ) ;
}
2009-06-22 13:08:36 +04:00
fd = open ( " /dev/freefall " , O_RDONLY ) ;
if ( fd < 0 ) {
2009-07-25 23:00:12 +04:00
perror ( " /dev/freefall " ) ;
2009-06-22 13:08:36 +04:00
return EXIT_FAILURE ;
}
2009-02-19 01:48:23 +03:00
2009-07-25 22:55:15 +04:00
daemon ( 0 , 0 ) ;
param . sched_priority = sched_get_priority_max ( SCHED_FIFO ) ;
sched_setscheduler ( 0 , SCHED_FIFO , & param ) ;
mlockall ( MCL_CURRENT | MCL_FUTURE ) ;
2009-02-19 01:48:23 +03:00
signal ( SIGALRM , ignore_me ) ;
2009-06-22 13:08:36 +04:00
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 ) ;
}
2009-02-19 01:48:23 +03:00
2009-06-22 13:08:36 +04:00
close ( fd ) ;
return EXIT_SUCCESS ;
2009-02-19 01:48:23 +03:00
}