// Copyright(c) 2018-2020, Intel Corporation
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
// * Neither the name of Intel Corporation nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#include <opae/fpga.h>
#include "bmc.h"
#include "bmcdata.h"
#include <sys/types.h>
#include <string.h>
#ifndef _WIN32
#include <unistd.h>
#include <sys/ioctl.h>
#else
#include <io.h>
#endif
#include <fcntl.h>
#include <stdlib.h>
#include "bmc_ioctl.h"
#include <glob.h>
#define NULL_CHECK(x) \
do { \
if (NULL == (x)) { \
return FPGA_INVALID_PARAM; \
} \
} while (0)
fpga_result rawFromDouble(Values *details, double dbl, uint8_t *raw)
{
fpga_result res = FPGA_OK;
NULL_CHECK(details);
NULL_CHECK(raw);
int32_t R_exp = -(details->result_exp);
int32_t i;
for (i = 0; i < abs(R_exp); i++) {
if (R_exp < 0) {
dbl /= 10.0;
} else {
dbl *= 10.0;
}
}
dbl = (dbl - details->B) / details->M;
*raw = dbl > (double)0xff ? (uint8_t)0xff : (uint8_t)dbl;
return res;
}
void fill_set_request(Values *vals, threshold_list *thresh,
bmc_set_thresh_request *req)
{
fpga_result res = FPGA_OK;
uint8_t mask = 0;
if (thresh->upper_nr_thresh.is_valid) {
mask |= UNR_thresh;
res += rawFromDouble(vals, thresh->upper_nr_thresh.value,
&req->UNR);
} else {
mask &= ~UNR_thresh;
}
if (thresh->upper_c_thresh.is_valid) {
mask |= UC_thresh;
res += rawFromDouble(vals, thresh->upper_c_thresh.value,
&req->UC);
} else {
mask &= ~UC_thresh;
}
if (thresh->upper_nc_thresh.is_valid) {
mask |= UNC_thresh;
res += rawFromDouble(vals, thresh->upper_nc_thresh.value,
&req->UNC);
} else {
mask &= ~UNC_thresh;
}
if (thresh->lower_nr_thresh.is_valid) {
mask |= LNR_thresh;
res += rawFromDouble(vals, thresh->lower_nr_thresh.value,
&req->LNR);
} else {
mask &= ~LNR_thresh;
}
if (thresh->lower_c_thresh.is_valid) {
mask |= LC_thresh;
res += rawFromDouble(vals, thresh->lower_c_thresh.value,
&req->LC);
} else {
mask &= ~LC_thresh;
}
if (thresh->lower_nc_thresh.is_valid) {
mask |= LNC_thresh;
res += rawFromDouble(vals, thresh->lower_nc_thresh.value,
&req->LNC);
} else {
mask &= ~LNC_thresh;
}
if (FPGA_OK == res) {
req->mask = mask;
}
}
fpga_result _bmcSetThreshold(int fd, uint32_t sensor,
bmc_set_thresh_request *req)
{
bmc_xact xact = {0};
bmc_set_thresh_response resp;
fpga_result res = FPGA_OK;
xact.argsz = sizeof(xact);
xact.txlen = sizeof(bmc_set_thresh_request);
xact.rxlen = sizeof(bmc_set_thresh_response);
xact.txbuf = (uint64_t)req;
xact.rxbuf = (uint64_t)&resp;
req->sens_num = sensor;
req->header[0] = BMC_THRESH_HEADER_0;
req->header[1] = BMC_THRESH_HEADER_1;
req->header[2] = BMC_SET_THRESH_CMD;
if (ioctl(fd, _IOWR(AVMMI_BMC_MAGIC, 0, struct avmmi_bmc_xact), &xact)
!= 0) {
res = FPGA_INVALID_PARAM;
goto out_close;
}
if (resp.cc) {
res = FPGA_EXCEPTION;
}
out_close:
return res;
}
fpga_result _bmcGetThreshold(int fd, uint32_t sensor,
bmc_get_thresh_response *resp)
{
bmc_xact xact = {0};
bmc_get_thresh_request req;
fpga_result res = FPGA_OK;
xact.argsz = sizeof(xact);
xact.txlen = sizeof(bmc_get_thresh_request);
xact.rxlen = sizeof(bmc_get_thresh_response);
xact.txbuf = (uint64_t)&req;
xact.rxbuf = (uint64_t)resp;
req.sens_num = sensor;
req.header[0] = BMC_THRESH_HEADER_0;
req.header[1] = BMC_THRESH_HEADER_1;
req.header[2] = BMC_GET_THRESH_CMD;
if (ioctl(fd, _IOWR(AVMMI_BMC_MAGIC, 0, struct avmmi_bmc_xact), &xact)
!= 0) {
res = FPGA_INVALID_PARAM;
goto out_close;
}
if (resp->cc) {
res = FPGA_EXCEPTION;
}
out_close:
return res;
}
fpga_result bmcSetHWThresholds(bmc_sdr_handle sdr_h, uint32_t sensor,
threshold_list *thresh)
{
fpga_result res = FPGA_OK;
char sysfspath[SYSFS_PATH_MAX] = { 0, };
int fd = 0;
bmc_set_thresh_request req;
Values *vals;
sensor_reading read;
bmc_get_thresh_response resp;
size_t len;
NULL_CHECK(sdr_h);
NULL_CHECK(thresh);
struct _sdr_rec *sdr = (struct _sdr_rec *)sdr_h;
if (BMC_SDR_MAGIC != sdr->magic) {
return FPGA_INVALID_PARAM;
}
if (sensor >= sdr->num_records) {
return FPGA_INVALID_PARAM;
}
if (snprintf(sysfspath, sizeof(sysfspath),
"%s/" SYSFS_AVMMI_DIR, sdr->sysfs_path) < 0) {
OPAE_ERR("snprintf buffer overflow");
return FPGA_EXCEPTION;
}
glob_t pglob;
int gres = glob(sysfspath, GLOB_NOSORT, NULL, &pglob);
if ((gres) || (1 != pglob.gl_pathc)) {
globfree(&pglob);
return FPGA_NOT_FOUND;
}
char *avmmi = strrchr(pglob.gl_pathv[0], '/');
if (NULL == avmmi) {
globfree(&pglob);
return FPGA_NOT_FOUND;
}
strncpy(sysfspath, "/dev/", 6);
len = strnlen(&avmmi[1], sizeof(sysfspath) - 6);
strncat(sysfspath, &avmmi[1], len + 1);
fd = open(sysfspath, O_RDWR);
globfree(&pglob);
if (fd < 0) {
return FPGA_NOT_FOUND;
}
memset(&req, 0, sizeof(req));
memset(&read, 0, sizeof(read));
vals = bmc_build_values(&read, &sdr->contents[sensor].header,
&sdr->contents[sensor].key,
&sdr->contents[sensor].body);
if (NULL == vals) {
close(fd);
return FPGA_NO_MEMORY;
}
res = _bmcGetThreshold(fd, sensor, &resp);
if (FPGA_OK != res) {
fprintf(stderr, "Error returned from _bmcGetThreshold\n");
}
lseek(fd, 0, SEEK_SET);
req.mask = resp.mask;
req.LNC = resp.LNC;
req.LC = resp.LC;
req.LNR = resp.LNR;
req.UNC = resp.UNC;
req.UC = resp.UC;
req.UNR = resp.UNR;
fill_set_request(vals, thresh, &req);
if (vals->name)
free(vals->name);
if (vals)
free(vals);
res = _bmcSetThreshold(fd, sensor, &req);
close(fd);
return res;
}