// This program monitors the input voltage to the MFM reader/emulator
// and powers down the board when input power is lost.
//
// Copyright 2014 David Gesswein.
// This file is part of MFM disk utilities.
//
// 09/12/23 JST/DJG Changes to avoid reboot loop if 12V always low.
// 11/16/18 DJG Adapt sleep time to wait value specified
// 06/05/16 DJG Fix using read buffer when read failed
// 09/07/14 DJG Ignore temporary A/D read failures
//
// MFM disk utilities is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// MFM disk utilities is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with MFM disk utilities. If not, see .
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "msg.h"
#include "parse_cmdline.h"
#define ARRAYSIZE(x) (sizeof(x) / sizeof(x[0]))
// process id of sub command
int pid = 0;
void shutdown_signal(void);
// Main routine.
int main(int argc, char *argv[])
{
// Program parameters
DRIVE_PARAMS drive_params;
// Access to A/D channel input voltage is connected to
int fd;
char *ad_dev = "/sys/bus/iio/devices/iio:device0/in_voltage0_raw";
char buf[100];
int rc;
// Counts voltage under threhold. We require more than one to prevent
// shutdown from a transient
int under_count = 0;
// Minimum number of counts under threshold to power down
int min_under_count;
// For collecting statistics for debug print
int stat_count = 0;
float ad_sum = 0;
float ad_min = 999;
float ad_max = -999;
float voltage;
// A/D read failures
int err_counter = 0;
int sleep_time_us;
// flag to track if we have seen a valid voltage ever
int valid = 0;
int valid_err_printed = 0;
// Cleanup when we exit
signal(SIGINT,(__sighandler_t) shutdown_signal);
// Find out what we should do
parse_cmdline(argc, argv, &drive_params);
fd = open(ad_dev, O_RDONLY);
if (fd < 0) {
msg(MSG_FATAL, "Unable to open A/D device %s:\n %s\n",ad_dev,
strerror(errno));
exit(1);
}
// If a command was specifed execute it in a separate process
if (drive_params.command != NULL ) {
int rc;
pid = fork();
if (pid == -1) {
msg(MSG_FATAL, "Fork failed %s", strerror(errno));
exit(1);
}
if (pid == 0) {
setpgid(0,0);
rc = system(drive_params.command);
if (rc == -1) {
msg(MSG_FATAL, "Executing command failed: %s\n",
strerror(errno));
}
exit(WEXITSTATUS(rc));
}
}
// If wait time >= 20 milliseconds check twice otherwise check
// once. Double check probably not needed but leaving alone in case
if (drive_params.wait >= .02) {
sleep_time_us = round(drive_params.wait / 2.0 * 1e6);
min_under_count = 2;
} else {
sleep_time_us = round(drive_params.wait * 1e6);
min_under_count = 1;
}
// Read A/D and if enough samples are under threshold tell command
// we executed to terminate then execute poweroff command
while(1) {
rc = pread(fd, buf, sizeof(buf), 0);
// For unknown reasions the read occasionally fails with
// Resource temporarily unavailable. Don't give up unless it
// happens too often.
if (rc < 0) {
if (err_counter++ > 5) {
msg(MSG_FATAL, "A/D read failed %s\n",strerror(errno));
exit(1);
} else {
msg(MSG_INFO, "A/D read failed %d %s\n",err_counter,
strerror(errno));
}
} else if (rc == 0) {
msg(MSG_FATAL, "A/D read returned no data\n");
exit(1);
} else {
err_counter = 0;
voltage = atoi(buf) * 1.8 / 4096.0 / drive_params.scale;
if (stat_count <= 50 && drive_params.debug) {
ad_sum += voltage;
if (voltage > ad_max) {
ad_max = voltage;
}
if (voltage < ad_min) {
ad_min = voltage;
}
stat_count++;
if (stat_count == 50) {
msg(MSG_INFO, "Average %.2fV max %.2fV min %.2fV\n",
ad_sum / stat_count, ad_max, ad_min);
}
}
if (voltage < drive_params.threshold) {
if (drive_params.debug) {
msg(MSG_INFO, "Voltage %.2f under threshold %.2f count %d\n",
voltage, drive_params.threshold, under_count);
}
// do not enter a boot loop if BB is powered but cape is not
if (++under_count >= min_under_count) {
if (valid) {
msg(MSG_FATAL, "Voltage under threshold too long\n");
if (pid != 0) {
kill(-pid, SIGINT);
waitpid(pid, NULL, 0);
}
rc = system(drive_params.powercmd);
if (rc == -1) {
msg(MSG_FATAL, "Executing power off command failed: %s\n",
strerror(errno));
}
exit(0);
} else {
if (!valid_err_printed) {
msg(MSG_ERR, "Voltage under threshold too long but never valid\n");
valid_err_printed = 1;
}
}
}
} else {
if (voltage >= drive_params.threshold) {
valid = 1;
under_count = 0;
}
}
}
usleep(sleep_time_us);
}
return (0);
}
// SIGINT/ control-c handler. Call our shutdown and exit
void shutdown_signal(void)
{
if (pid != 0) {
kill(-pid, SIGINT);
waitpid(pid, NULL, 0);
}
exit(1);
}