meta-facebook/meta-yosemite/recipes-yosemite/front-paneld/files/front-paneld.c (499 lines of code) (raw):
/*
*
* Copyright 2015-present Facebook. All Rights Reserved.
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <syslog.h>
#include <string.h>
#include <pthread.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <time.h>
#include <sys/time.h>
#include <openbmc/ipmi.h>
#include <openbmc/ipmb.h>
#include <openbmc/pal.h>
#define BTN_MAX_SAMPLES 200
#define BTN_POWER_OFF 40
#define MAX_NUM_SLOTS 4
#define HB_SLEEP_TIME (5 * 60)
#define HB_TIMESTAMP_COUNT (60 * 60 / HB_SLEEP_TIME)
#define LED_ON 1
#define LED_OFF 0
#define ID_LED_ON 0
#define ID_LED_OFF 1
#define LED_ON_TIME_IDENTIFY 200
#define LED_OFF_TIME_IDENTIFY 200
#define LED_ON_TIME_HEALTH 900
#define LED_OFF_TIME_HEALTH 100
#define LED_ON_TIME_BMC_SELECT 500
#define LED_OFF_TIME_BMC_SELECT 500
static uint8_t g_sync_led[MAX_NUM_SLOTS+1] = {0x0};
static uint8_t m_pos = 0xff;
static int
get_handsw_pos(uint8_t *pos) {
if ((m_pos > HAND_SW_BMC) || (m_pos < HAND_SW_SERVER1))
return -1;
*pos = m_pos;
return 0;
}
// Thread for monitoring debug card hotswap
static void *
debug_card_handler() {
int curr = -1;
int prev = -1;
uint8_t prsnt = 0;
uint8_t pos;
uint8_t prev_pos = 0xff;
uint8_t lpc;
int ret;
while (1) {
ret = pal_get_hand_sw(&pos);
if (ret) {
goto debug_card_out;
}
if (pos == prev_pos) {
goto debug_card_prs;
}
msleep(10);
ret = pal_get_hand_sw(&pos);
if (ret) {
goto debug_card_out;
}
m_pos = pos;
ret = pal_switch_usb_mux(pos);
if (ret) {
goto debug_card_out;
}
debug_card_prs:
// Check if debug card present or not
ret = pal_is_debug_card_prsnt(&prsnt);
if (ret) {
goto debug_card_out;
}
curr = prsnt;
// Check if Debug Card was either inserted or removed
if (curr != prev) {
if (!curr) {
// Debug Card was removed
syslog(LOG_WARNING, "Debug Card Extraction\n");
// Switch UART mux to BMC
ret = pal_switch_uart_mux(HAND_SW_BMC);
if (ret) {
goto debug_card_out;
}
} else {
// Debug Card was inserted
syslog(LOG_WARNING, "Debug Card Insertion\n");
}
}
// If Debug Card is present
if (curr) {
if ((pos == prev_pos) && (curr == prev)) {
goto debug_card_out;
}
// Switch UART mux based on hand switch
ret = pal_switch_uart_mux(pos);
if (ret) {
goto debug_card_out;
}
// Enable POST code based on hand switch
if (pos == HAND_SW_BMC) {
// For BMC, there is no need to have POST specific code
goto debug_card_done;
}
// Make sure the server at selected position is ready
ret = pal_is_fru_ready(pos, &prsnt);
if (ret || !prsnt) {
goto debug_card_done;
}
// Enable POST codes for all slots
ret = pal_post_enable(pos);
if (ret) {
goto debug_card_done;
}
// Get last post code and display it
ret = pal_post_get_last(pos, &lpc);
if (ret) {
goto debug_card_done;
}
ret = pal_post_handle(pos, lpc);
if (ret) {
goto debug_card_out;
}
}
debug_card_done:
prev = curr;
prev_pos = pos;
debug_card_out:
if (curr == 1)
msleep(500);
else
sleep(1);
}
return NULL;
}
// Thread to monitor Reset Button and propagate to selected server
static void *
rst_btn_handler() {
int ret;
uint8_t pos;
int i;
uint8_t btn;
while (1) {
// Check the position of hand switch
ret = get_handsw_pos(&pos);
if (ret || pos == HAND_SW_BMC) {
// For BMC, no need to handle Reset Button
sleep(1);
continue;
}
// Check if reset button is pressed
ret = pal_get_rst_btn(&btn);
if (ret || !btn) {
goto rst_btn_out;
}
// Pass the reset button to the selected slot
syslog(LOG_WARNING, "Reset button pressed\n");
ret = pal_set_rst_btn(pos, 0);
if (ret) {
goto rst_btn_out;
}
// Wait for the button to be released
for (i = 0; i < BTN_MAX_SAMPLES; i++) {
ret = pal_get_rst_btn(&btn);
if (ret || btn) {
msleep(100);
continue;
}
pal_update_ts_sled();
syslog(LOG_WARNING, "Reset button released\n");
syslog(LOG_CRIT, "Reset Button pressed for FRU: %d\n", pos);
ret = pal_set_rst_btn(pos, 1);
goto rst_btn_out;
}
// handle error case
if (i == BTN_MAX_SAMPLES) {
pal_update_ts_sled();
syslog(LOG_WARNING, "Reset button seems to stuck for long time\n");
goto rst_btn_out;
}
rst_btn_out:
msleep(100);
}
return NULL;
}
// Thread to handle Power Button and power on/off the selected server
static void *
pwr_btn_handler() {
int ret;
uint8_t pos, btn, cmd;
int i;
uint8_t power;
while (1) {
// Check the position of hand switch
ret = get_handsw_pos(&pos);
if (ret || pos == HAND_SW_BMC) {
sleep(1);
continue;
}
// Check if power button is pressed
ret = pal_get_pwr_btn(&btn);
if (ret || !btn) {
goto pwr_btn_out;
}
syslog(LOG_WARNING, "power button pressed\n");
// Wait for the button to be released
for (i = 0; i < BTN_POWER_OFF; i++) {
ret = pal_get_pwr_btn(&btn);
if (ret || btn ) {
msleep(100);
continue;
}
syslog(LOG_WARNING, "power button released\n");
break;
}
// Get the current power state (power on vs. power off)
ret = pal_get_server_power(pos, &power);
if (ret) {
goto pwr_btn_out;
}
// Set power command should reverse of current power state
cmd = !power;
// To determine long button press
if (i >= BTN_POWER_OFF) {
pal_update_ts_sled();
syslog(LOG_CRIT, "Power Button Long Press for FRU: %d\n", pos);
} else {
// If current power state is ON and it is not a long press,
// the power off should be Graceful Shutdown
if (power == SERVER_POWER_ON)
cmd = SERVER_GRACEFUL_SHUTDOWN;
pal_update_ts_sled();
syslog(LOG_CRIT, "Power Button Press for FRU: %d\n", pos);
}
// Reverse the power state of the given server
ret = pal_set_server_power(pos, cmd);
pwr_btn_out:
msleep(100);
}
return NULL;
}
// Thread to monitor SLED Cycles by using time stamp
static void *
ts_handler() {
int count = 0;
struct timespec ts;
struct timespec mts;
char tstr[64] = {0};
char buf[128] = {0};
uint8_t time_init = 0;
long time_sled_on;
long time_sled_off;
// Read the last timestamp from KV storage
pal_get_key_value("timestamp_sled", tstr);
time_sled_off = (long) strtoul(tstr, NULL, 10);
while (1) {
// Make sure the time is initialized properly
// Since there is no battery backup, the time could be reset to build time
if (time_init < 100) { // wait 100s at most, to prevent infinite waiting
// Read current time
clock_gettime(CLOCK_REALTIME, &ts);
if ((ts.tv_sec < time_sled_off) && (++time_init < 100)) {
sleep(1);
continue;
}
// If current time is more than the stored time, the date is correct
time_init = 100;
// Need to log SLED ON event, if this is Power-On-Reset
if (pal_is_bmc_por()) {
ctime_r(&time_sled_off, buf);
syslog(LOG_CRIT, "SLED Powered OFF at %s", buf);
// Get uptime
clock_gettime(CLOCK_MONOTONIC, &mts);
// To find out when SLED was on, subtract the uptime from current time
time_sled_on = ts.tv_sec - mts.tv_sec;
ctime_r(&time_sled_on, buf);
// Log an event if this is Power-On-Reset
syslog(LOG_CRIT, "SLED Powered ON at %s", buf);
}
}
// Store timestamp every one hour to keep track of SLED power
if (count++ == HB_TIMESTAMP_COUNT) {
pal_update_ts_sled();
count = 0;
}
sleep(HB_SLEEP_TIME);
}
return NULL;
}
// Thread to handle LED state of the server at given slot
static void *
led_handler() {
int ret;
uint8_t pos;
uint8_t slot;
uint8_t ready;
uint8_t power[MAX_NUM_SLOTS+1] = {0};
uint8_t hlth[MAX_NUM_SLOTS+1] = {0};
int led_on_time, led_off_time;
while (1) {
// Get hand switch position to see if this is selected server
ret = get_handsw_pos(&pos);
if (ret != 0) {
sleep(1);
continue;
}
for (slot = 1; slot <= MAX_NUM_SLOTS; slot++) {
// Check if this LED is managed by sync_led thread
if (g_sync_led[slot]) {
continue;
}
ret = pal_is_fru_ready(slot, &ready);
if (!ret && ready) {
// Get power status for this slot
ret = pal_get_server_power(slot, &power[slot]);
if (ret) {
continue;
}
// Get health status for this slot
ret = pal_get_fru_health(slot, &hlth[slot]);
if (ret) {
continue;
}
} else {
power[slot] = SERVER_POWER_OFF;
hlth[slot] = FRU_STATUS_GOOD;
}
if ((pos == slot) || (power[slot] == SERVER_POWER_ON)) {
if (hlth[slot] == FRU_STATUS_GOOD) {
pal_set_led(slot, LED_ON);
pal_set_id_led(slot, ID_LED_OFF);
} else {
pal_set_led(slot, LED_OFF);
pal_set_id_led(slot, ID_LED_ON);
}
} else {
pal_set_led(slot, LED_OFF);
pal_set_id_led(slot, ID_LED_OFF);
}
}
if (pos > MAX_NUM_SLOTS) {
sleep(1);
continue;
}
if (g_sync_led[pos]) {
sleep(1);
continue;
}
// Set blink rate
if (power[pos] == SERVER_POWER_ON) {
led_on_time = 900;
led_off_time = 100;
} else {
led_on_time = 100;
led_off_time = 900;
}
msleep(led_on_time);
if (hlth[pos] == FRU_STATUS_GOOD) {
pal_set_led(pos, LED_OFF);
} else {
pal_set_id_led(pos, ID_LED_OFF);
}
msleep(led_off_time);
if (power[pos] == SERVER_POWER_ON) {
if (hlth[pos] == FRU_STATUS_GOOD) {
pal_set_led(pos, LED_ON);
} else {
pal_set_id_led(pos, ID_LED_ON);
}
}
}
return NULL;
}
// Thread to handle LED state of the SLED
static void *
led_sync_handler() {
int ret;
uint8_t pos;
uint8_t ident = 0;
char identify[16] = {0};
char tstr[64] = {0};
char id_arr[5] = {0};
uint8_t slot;
uint8_t spb_hlth = 0;
uint8_t nic_hlth = 0;
#ifdef DEBUG
syslog(LOG_INFO, "led_handler for slot %d\n", slot);
#endif
while (1) {
// Handle Slot IDENTIFY condition
memset(identify, 0x0, 16);
ret = pal_get_key_value("identify_sled", identify);
if (ret == 0 && !strcmp(identify, "on")) {
// Turn OFF Blue LED
for (slot = 1; slot <= MAX_NUM_SLOTS; slot++) {
g_sync_led[slot] = 1;
pal_set_led(slot, LED_OFF);
}
// Start blinking the ID LED
for (slot = 1; slot <= MAX_NUM_SLOTS; slot++) {
pal_set_id_led(slot, ID_LED_ON);
}
msleep(LED_ON_TIME_IDENTIFY);
for (slot = 1; slot <= MAX_NUM_SLOTS; slot++) {
pal_set_id_led(slot, ID_LED_OFF);
}
msleep(LED_OFF_TIME_IDENTIFY);
continue;
}
// Handle Sled level health condition
ret = pal_get_fru_health(FRU_SPB, &spb_hlth);
if (ret) {
sleep(1);
continue;
}
ret = pal_get_fru_health(FRU_NIC, &nic_hlth);
if (ret) {
sleep(1);
continue;
}
if (spb_hlth == FRU_STATUS_BAD || nic_hlth == FRU_STATUS_BAD) {
// Turn OFF Blue LED
for (slot = 1; slot <= MAX_NUM_SLOTS; slot++) {
g_sync_led[slot] = 1;
pal_set_led(slot, LED_OFF);
}
// Start blinking the Yellow/ID LED
for (slot = 1; slot <= MAX_NUM_SLOTS; slot++) {
pal_set_id_led(slot, ID_LED_ON);
}
msleep(LED_ON_TIME_HEALTH);
ret = get_handsw_pos(&pos);
if ((ret) || (pos == HAND_SW_BMC)) {
for (slot = 1; slot <= MAX_NUM_SLOTS; slot++) {
pal_set_id_led(slot, ID_LED_OFF);
}
} else {
pal_set_id_led(pos, ID_LED_OFF);
}
msleep(LED_OFF_TIME_HEALTH);
continue;
}
// Check if slot needs to be identified
ident = 0;
for (slot = 1; slot <= MAX_NUM_SLOTS; slot++) {
id_arr[slot] = 0x0;
sprintf(tstr, "identify_slot%d", slot);
memset(identify, 0x0, 16);
ret = pal_get_key_value(tstr, identify);
if (ret == 0 && !strcmp(identify, "on")) {
id_arr[slot] = 0x1;
ident = 1;
}
}
// Get hand switch position to see if this is selected server
ret = get_handsw_pos(&pos);
if (ret) {
sleep(1);
continue;
}
// Handle BMC select condition when no slot is being identified
if ((pos == HAND_SW_BMC) && (ident == 0)) {
// Turn OFF Yellow LED
for (slot = 1; slot <= MAX_NUM_SLOTS; slot++) {
g_sync_led[slot] = 1;
pal_set_id_led(slot, ID_LED_OFF);
}
// Start blinking Blue LED
for (slot = 1; slot <= 4; slot++) {
pal_set_led(slot, LED_ON);
}
msleep(LED_ON_TIME_BMC_SELECT);
for (slot = 1; slot <= 4; slot++) {
pal_set_led(slot, LED_OFF);
}
msleep(LED_OFF_TIME_BMC_SELECT);
continue;
}
// Handle individual identify slot condition
if (ident) {
for (slot = 1; slot <=4; slot++) {
if (id_arr[slot]) {
g_sync_led[slot] = 1;
pal_set_led(slot, LED_OFF);
pal_set_id_led(slot, ID_LED_ON);
} else {
g_sync_led[slot] = 0;
}
}
msleep(LED_ON_TIME_IDENTIFY);
for (slot = 1; slot <=4; slot++) {
if (id_arr[slot]) {
pal_set_id_led(slot, ID_LED_OFF);
}
}
msleep(LED_OFF_TIME_IDENTIFY);
continue;
}
for (slot = 1; slot <= 4; slot++) {
g_sync_led[slot] = 0;
}
msleep(500);
}
return NULL;
}
int
main (int argc, char * const argv[]) {
pthread_t tid_debug_card;
pthread_t tid_rst_btn;
pthread_t tid_pwr_btn;
pthread_t tid_ts;
pthread_t tid_sync_led;
pthread_t tid_led;
int rc;
int pid_file;
pid_file = open("/var/run/front-paneld.pid", O_CREAT | O_RDWR, 0666);
rc = flock(pid_file, LOCK_EX | LOCK_NB);
if(rc) {
if(EWOULDBLOCK == errno) {
printf("Another front-paneld instance is running...\n");
exit(-1);
}
} else {
daemon(0, 1);
openlog("front-paneld", LOG_CONS, LOG_DAEMON);
}
if (pthread_create(&tid_debug_card, NULL, debug_card_handler, NULL) < 0) {
syslog(LOG_WARNING, "pthread_create for debug card error\n");
exit(1);
}
if (pthread_create(&tid_rst_btn, NULL, rst_btn_handler, NULL) < 0) {
syslog(LOG_WARNING, "pthread_create for reset button error\n");
exit(1);
}
if (pthread_create(&tid_pwr_btn, NULL, pwr_btn_handler, NULL) < 0) {
syslog(LOG_WARNING, "pthread_create for power button error\n");
exit(1);
}
if (pthread_create(&tid_ts, NULL, ts_handler, NULL) < 0) {
syslog(LOG_WARNING, "pthread_create for time stamp error\n");
exit(1);
}
if (pthread_create(&tid_sync_led, NULL, led_sync_handler, NULL) < 0) {
syslog(LOG_WARNING, "pthread_create for led sync error\n");
exit(1);
}
if (pthread_create(&tid_led, NULL, led_handler, NULL) < 0) {
syslog(LOG_WARNING, "pthread_create for led error\n");
exit(1);
}
pthread_join(tid_debug_card, NULL);
pthread_join(tid_rst_btn, NULL);
pthread_join(tid_pwr_btn, NULL);
pthread_join(tid_ts, NULL);
pthread_join(tid_sync_led, NULL);
pthread_join(tid_led, NULL);
return 0;
}