summaryrefslogtreecommitdiffstats
path: root/meta-facebook/meta-wedge/recipes-wedge/rackmon
diff options
context:
space:
mode:
authorOri Bernstein <orib@fb.com>2015-09-03 13:06:18 -0700
committerOri Bernstein <orib@fb.com>2015-09-23 18:10:45 -0700
commit67f44805b41a532b7b440773f7ee6d17dc0e97f4 (patch)
treec21a66b12c4d92a765e1ee0d47b96e2a2b9cd0d2 /meta-facebook/meta-wedge/recipes-wedge/rackmon
parent2a51b7c1c2165ddb188c511e192b75f0aa0fbead (diff)
downloadast2050-yocto-openbmc-67f44805b41a532b7b440773f7ee6d17dc0e97f4.zip
ast2050-yocto-openbmc-67f44805b41a532b7b440773f7ee6d17dc0e97f4.tar.gz
Openbmc dev snapshot.
Diffstat (limited to 'meta-facebook/meta-wedge/recipes-wedge/rackmon')
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/Makefile34
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/hexfile.py174
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbus.c181
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbus.h47
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbuscmd.c152
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbussim.c2
-rwxr-xr-xmeta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/psu-update-delta.py269
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/rackmon-config.py93
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/rackmond.c637
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/rackmond.h46
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/rackmondata.c58
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/setup-rackmond.sh20
-rw-r--r--meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon_0.1.bb33
13 files changed, 1636 insertions, 110 deletions
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/Makefile b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/Makefile
index 4a3c25d..926bf52 100644
--- a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/Makefile
+++ b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/Makefile
@@ -1,16 +1,40 @@
# Copyright 2014-present Facebook. All Rights Reserved.
-all: modbuscmd gpiowatch modbussim
+#
+# This program file 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; version 2 of the License.
+#
+# 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 in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
+
+override CFLAGS+=-D_GNU_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=199309 -Wall -Werror -std=c99
+override LDFLAGS+=-pthread
+all: modbuscmd gpiowatch modbussim rackmond rackmondata
+
+rackmondata: rackmondata.c modbus.c
+ $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
+
+rackmond: rackmond.c modbus.c
+ $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
modbuscmd: modbuscmd.c modbus.c
- $(CC) -D_BSD_SOURCE -Wall -Werror -std=c99 -o $@ $^ $(LDFLAGS)
+ $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
modbussim: modbussim.c modbus.c
- $(CC) -D_BSD_SOURCE -Wall -Werror -std=c99 -o $@ $^ $(LDFLAGS)
+ $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
gpiowatch: gpiowatch.c
- $(CC) -D_BSD_SOURCE -Wall -Werror -std=c99 -o $@ $^ $(LDFLAGS)
+ $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
.PHONY: clean
clean:
- rm -rf *.o modbuscmd gpiowatch modbussim
+ rm -rf *.o modbuscmd gpiowatch modbussim rackmond rackmondata
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/hexfile.py b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/hexfile.py
new file mode 100644
index 0000000..c484fdf
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/hexfile.py
@@ -0,0 +1,174 @@
+# The MIT License (MIT)
+# =====================
+#
+# Copyright (c) 2014 Ryan Sturmer
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+# https://github.com/ryansturmer/hexfile/blob/master/hexfile/core.py
+
+import itertools
+
+def short(msb,lsb):
+ return (msb<<8) | lsb
+
+class HexFile(object):
+ def __init__(self, segments):
+ self.segments = segments
+
+ def __getitem__(self, val):
+ if isinstance(val, slice):
+ address = val.start
+ else:
+ address = val
+
+ for segment in self.segments:
+ if address in segment:
+ return segment[val]
+
+ raise IndexError('No segment contains address 0x%x' % address)
+
+ def __len__(self):
+ return sum(map(len, self.segments))
+
+ @property
+ def size(self):
+ return len(self)
+
+ def __iter__(self):
+ return itertools.chain(*self.segments)
+
+ @staticmethod
+ def load(filename):
+ segments = [Segment(0)]
+
+ with open(filename) as fp:
+ lines = fp.readlines()
+
+ extended_linear_address = 0
+ current_address = 0
+ end_of_file = False
+
+ lineno = 0
+ for line in lines:
+ lineno += 1
+ line = line.strip();
+ if not line.startswith(':'):
+ continue
+
+ if end_of_file:
+ raise Exception("Record found after end of file on line %d" % lineno)
+
+ bytes = [int(line[i:i+2], 16) for i in range(1,len(line), 2)]
+ byte_count = bytes[0]
+ address = short(*bytes[1:3])
+ record_type = bytes[3]
+ checksum = bytes[-1]
+ data = bytes[4:-1]
+ computed_checksum = ((1 << 8)-(sum(bytes[:-1]) & 0xff)) & 0xff
+
+ if(computed_checksum != checksum):
+ raise Exception("Record checksum doesn't match on line %d" % lineno)
+
+ if record_type == 0:
+ if byte_count == len(data):
+ current_address = (address | extended_linear_address)
+ have_segment = False
+ for segment in segments:
+ if segment.end_address == current_address:
+ segment.data.extend(data)
+ have_segment = True
+ break
+ if not have_segment:
+ segments.append(Segment(current_address, data))
+ else:
+ raise Exception("Data record reported size does not match actual size on line %d" % lineno)
+ elif record_type == 1:
+ end_of_file = True
+ elif record_type == 4:
+ if byte_count != 2 or len(data) != 2:
+ raise Exception("Byte count misreported in extended linear address record on line %d" % lineno)
+ extended_linear_address = short(*data) << 16
+
+ else:
+ raise Exception("Unknown record type: %s" % record_type)
+ return HexFile(segments)
+
+ def pretty_string(self, stride=16):
+ retval = []
+ for segment in self.segments:
+ retval.append('Segment @ 0x%08x (%d bytes)' % (segment.start_address, segment.size))
+ retval.append(segment.pretty_string(stride=stride))
+ retval.append('')
+ return '\n'.join(retval)
+
+def load(filename):
+ return HexFile.load(filename)
+
+class Segment(object):
+ def __init__(self, start_address, data = None):
+ self.start_address = start_address
+ self.data = data or []
+
+ def pretty_string(self, stride=16):
+ retval = []
+ addresses = self.addresses
+ ranges = [addresses[i:i+stride] for i in range(0, self.size, stride)]
+ for r in ranges:
+ retval.append('%08x ' % r[0] + ' '.join(['%02x' % self[addr] for addr in r]))
+ return '\n'.join(retval)
+
+ def __str__(self):
+ return '<%d byte segment @ 0x%08x>' % (self.size, self.start_address)
+ def __repr__(self):
+ return str(self)
+
+ @property
+ def end_address(self):
+ return self.start_address + len(self.data)
+
+ @property
+ def size(self):
+ return len(self.data)
+
+ def __contains__(self, address):
+ return address >= self.start_address and address < self.end_address
+
+ def __getitem__(self, address):
+ if isinstance(address, slice):
+ if address.start not in self or address.stop-1 not in self:
+ raise IndexError('Address out of range for this segment')
+ else:
+ d = self.data[address.start-self.start_address:address.stop-self.start_address:address.step]
+ start_address = address.start + self.start_address
+ return Segment(start_address, d)
+ else:
+ if not address in self:
+ raise IndexError("Address 0x%x is not in this segment" % address)
+ return self.data[address-self.start_address]
+
+ @property
+ def addresses(self):
+ return range(self.start_address, self.end_address)
+
+ def __len__(self):
+ return len(self.data)
+
+ def __iter__(self):
+ return iter(self.data)
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbus.c b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbus.c
index 46618a9..8fb0835 100644
--- a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbus.c
+++ b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbus.c
@@ -16,6 +16,7 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <time.h>
#include "modbus.h"
#include <termios.h>
#include <unistd.h>
@@ -26,9 +27,15 @@
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
+#include <sched.h>
+#include <pthread.h>
-static int loops = 0;
-void waitfd(int fd) {
+int verbose = 0;
+
+#define TIOCSERWAITTEMT 0x5499
+int waitfd(int fd) {
+ int loops = 0;
+ ioctl(fd, TIOCSERWAITTEMT, DEFAULT_GPIO);
while(1) {
int lsr;
int ret = ioctl(fd, TIOCSERGETLSR, &lsr);
@@ -37,8 +44,10 @@ void waitfd(int fd) {
break;
}
if(lsr & TIOCSER_TEMT) break;
+ // never should hit this with new ioctl
loops++;
}
+ return loops;
}
void gpio_on(int fd) {
@@ -60,8 +69,7 @@ void decode_hex_in_place(char* buf, size_t* len) {
void append_modbus_crc16(char* buf, size_t* len) {
uint16_t crc = modbus_crc16(buf, *len);
- if (verbose)
- fprintf(stderr, "[*] Append Modbus CRC16 %04x\n", crc);
+ dbg("[*] Append Modbus CRC16 %04x\n", crc);
buf[(*len)++] = crc >> 8;
buf[(*len)++] = crc & 0x00FF;
}
@@ -78,7 +86,7 @@ size_t read_wait(int fd, char* dst, size_t maxlen, int mdelay_us) {
size_t read_size = 0;
size_t pos = 0;
memset(dst, 0, maxlen);
- for(;;) {
+ while(pos < maxlen) {
FD_ZERO(&fdset);
FD_SET(fd, &fdset);
timeout.tv_sec = 0;
@@ -95,10 +103,11 @@ size_t read_wait(int fd, char* dst, size_t maxlen, int mdelay_us) {
fprintf(stderr, "read error: %s\n", strerror(errno));
exit(1);
}
- if((pos + read_size) < maxlen) {
+ if((pos + read_size) <= maxlen) {
memcpy(dst + pos, read_buf, read_size);
pos += read_size;
} else {
+ return pos;
fprintf(stderr, "Response buffer overflowed!\n");
}
}
@@ -181,3 +190,163 @@ uint16_t modbus_crc16(char* buffer, size_t buffer_length) {
return (crc_hi << 8 | crc_lo);
}
+
+
+double ts_diff (struct timespec* begin, struct timespec* end) {
+ return 1000.0 * (end->tv_sec) + (1e-6 * end->tv_nsec)
+ - (1000.0 * (begin->tv_sec) + (1e-6 * begin->tv_nsec));
+}
+
+static long success = 0;
+static long crcfail = 0;
+static long timeout = 0;
+static long stat_wait = 0;
+
+int modbuscmd(modbus_req *req) {
+ int error = 0;
+ struct termios tio;
+ char modbus_cmd[req->cmd_len + 2];
+ size_t cmd_len = req->cmd_len;
+
+ if (verbose)
+ fprintf(stderr, "[*] Setting TTY flags!\n");
+ memset(&tio, 0, sizeof(tio));
+ // CREAD should be left *off* until we've confirmed THRE
+ // to avoid catching false character starts
+ cfsetspeed(&tio,B19200);
+ tio.c_cflag |= PARENB;
+ tio.c_cflag |= CLOCAL;
+ tio.c_cflag |= CS8;
+ tio.c_iflag |= INPCK;
+ tio.c_cc[VMIN] = 1;
+ tio.c_cc[VTIME] = 0;
+ CHECK(tcsetattr(req->tty_fd,TCSANOW,&tio));
+
+ memcpy(modbus_cmd, req->modbus_cmd, cmd_len);
+ append_modbus_crc16(modbus_cmd, &cmd_len);
+
+ // print command as sent
+ if (verbose) {
+ fprintf(stderr, "Will send: ");
+ print_hex(stderr, modbus_cmd, cmd_len);
+ fprintf(stderr, "\n");
+ }
+
+ dbg("[*] Writing!\n");
+
+ // hoped adding the ioctl to do the switching would have alleviated the
+ // need to do SCHED_FIFO, but we still get preempted between the write and
+ // ioctl syscalls w/o it often enough to break f/w updates.
+ struct sched_param sp;
+ sp.sched_priority = 50;
+ int policy = SCHED_FIFO;
+ CHECKP(sched, pthread_setschedparam(pthread_self(), policy, &sp));
+ // gpio on, write, wait, gpio off
+ gpio_on(req->gpio_fd);
+ struct timespec write_begin;
+ struct timespec wait_begin;
+ struct timespec wait_end;
+ struct timespec read_end;
+ clock_gettime(CLOCK_MONOTONIC_RAW, &write_begin);
+ write(req->tty_fd, modbus_cmd, cmd_len);
+ clock_gettime(CLOCK_MONOTONIC_RAW, &wait_begin);
+ int waitloops = waitfd(req->tty_fd);
+ clock_gettime(CLOCK_MONOTONIC_RAW, &wait_end);
+ gpio_off(req->gpio_fd);
+ sp.sched_priority = 0;
+ // Enable UART read
+ tio.c_cflag |= CREAD;
+ CHECK(tcsetattr(req->tty_fd,TCSANOW,&tio));
+ policy = SCHED_OTHER;
+ CHECKP(sched, pthread_setschedparam(pthread_self(), policy, &sp));
+
+ dbg("[*] waitfd loops: %d\n", waitloops);
+ dbg("[*] reading any response...\n");
+ // Read back response
+ size_t mb_pos = 0;
+ memset(req->dest_buf, 0, req->dest_limit);
+ if(req->expected_len == 0) {
+ req->expected_len = req->dest_limit;
+ }
+ if(req->expected_len > req->dest_limit) {
+ return -1;
+ }
+ mb_pos = read_wait(req->tty_fd, req->dest_buf, req->expected_len, req->timeout);
+ clock_gettime(CLOCK_MONOTONIC_RAW, &read_end);
+ req->dest_len = mb_pos;
+ if(mb_pos >= 4) {
+ uint16_t crc = modbus_crc16(req->dest_buf, mb_pos - 2);
+ dbg("Modbus response CRC: %04X\n ", crc);
+ if((req->dest_buf[mb_pos - 2] == (crc >> 8)) &&
+ (req->dest_buf[mb_pos - 1] == (crc & 0x00FF))) {
+ dbg("CRC OK!\n");
+ } else {
+ dbg("BAD CRC :(\n");
+ fprintf(stderr, "bad crc timings:");
+ fprintf(stderr, " write: %.2f ms", ts_diff(&write_begin, &wait_begin));
+ fprintf(stderr, " wait: %.2f ms", ts_diff(&wait_begin, &wait_end));
+ fprintf(stderr, " read: %.2f ms\n", ts_diff(&wait_end, &read_end));
+ if(!req->scan) {
+ crcfail++;
+ }
+ if(verbose) {
+ print_hex(stderr, req->dest_buf, mb_pos);
+ }
+ return MODBUS_BAD_CRC;
+ }
+ } else {
+ fprintf(stderr, "timeout timings:");
+ fprintf(stderr, " write: %.2f ms", ts_diff(&write_begin, &wait_begin));
+ fprintf(stderr, " wait: %.2f ms", ts_diff(&wait_begin, &wait_end));
+ fprintf(stderr, " wait: %d iters", waitloops);
+ fprintf(stderr, " read: %.2f ms\n", ts_diff(&wait_end, &read_end));
+ dbg("No response :(\n");
+ if(!req->scan) {
+ timeout++;
+ }
+ return MODBUS_RESPONSE_TIMEOUT;
+ }
+
+cleanup:
+ if(error != 0) {
+ error = -1;
+ fprintf(stderr, "%s\n", strerror(errno));
+ } else {
+ //fprintf(stderr, "success, timings:");
+ //fprintf(stderr, " write: %.2f ms", ts_diff(&write_begin, &wait_begin));
+ //fprintf(stderr, " wait: %.2f ms", ts_diff(&wait_begin, &wait_end));
+ //fprintf(stderr, " read: %.2f ms -- ", ts_diff(&wait_end, &read_end));
+ if(stat_wait == 0 && !req->scan) {
+ fprintf(stderr, "success: %.2f%% crcfail %.2f%%, timeout %.2f%%\n",
+ ((double) 100.0 * success / (success + crcfail + timeout)),
+ ((double) 100.0 * crcfail / (success + crcfail + timeout)),
+ ((double) 100.0 * timeout / (success + crcfail + timeout)));
+ stat_wait = 1000;
+ fprintf(stderr, "success timings:");
+ fprintf(stderr, " write: %.2f ms", ts_diff(&write_begin, &wait_begin));
+ fprintf(stderr, " wait: %.2f ms", ts_diff(&wait_begin, &wait_end));
+ fprintf(stderr, " wait: %d iters", waitloops);
+ fprintf(stderr, " read: %.2f ms\n", ts_diff(&wait_end, &read_end));
+ } else if (!req->scan) {
+ stat_wait--;
+ }
+ if(!req->scan) {
+ success++;
+ }
+ }
+ return 0;
+}
+
+const char* modbus_strerror(int mb_err) {
+ if (mb_err < 0) {
+ mb_err = -mb_err;
+ }
+ switch(mb_err) {
+ case 4:
+ return "timed out";
+ case 5:
+ return "crc check failed";
+ default:
+ return "unknown";
+ }
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbus.h b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbus.h
index 435d518..1354feb 100644
--- a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbus.h
+++ b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbus.h
@@ -26,12 +26,27 @@ uint16_t modbus_crc16(char* buffer, size_t length);
#define DEFAULT_TTY "/dev/ttyS3"
#define DEFAULT_GPIO 45
-#define CHECK(x) { if((x) < 0) { \
- error = x; \
+extern int verbose;
+#define dbg(...) if(verbose) { fprintf(stderr, __VA_ARGS__); }
+#define log(...) { fprintf(stderr, __VA_ARGS__); }
+
+#define CHECK(expr) { int _check = expr; if((_check) < 0) { \
+ error = _check; \
+ goto cleanup; \
+} }
+#define CHECKP(name, expr) { int _check = expr; if((_check) < 0) { \
+ error = _check; \
+ perror(#name); \
goto cleanup; \
} }
+#define BAIL(...) { \
+ fprintf(stderr, __VA_ARGS__); \
+ fflush(stderr); \
+ error = -1; \
+ goto cleanup; \
+}
-void waitfd(int fd);
+int waitfd(int fd);
void gpio_on(int fd);
void gpio_off(int fd);
void decode_hex_in_place(char* buf, size_t* len);
@@ -41,6 +56,30 @@ void print_hex(FILE* f, char* buf, size_t len);
// Read until maxlen bytes or no bytes in mdelay_us microseconds
size_t read_wait(int fd, char* dst, size_t maxlen, int mdelay_us);
-extern int verbose;
+
+typedef struct _modbus_req {
+ int tty_fd;
+ int gpio_fd;
+ const char *modbus_cmd;
+ size_t cmd_len;
+ int timeout;
+ size_t expected_len;
+ char *dest_buf;
+ size_t dest_limit;
+ size_t dest_len;
+ int scan;
+} modbus_req;
+
+int modbuscmd(modbus_req *req);
+// Modbus errors
+
+#define MODBUS_RESPONSE_TIMEOUT -4
+#define MODBUS_BAD_CRC -5
+
+const char* modbus_strerror(int mb_err);
+
+// Modbus constants
+#define MODBUS_READ_HOLDING_REGISTERS 3
+
#endif
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbuscmd.c b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbuscmd.c
index 2d33039..78f62a1 100644
--- a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbuscmd.c
+++ b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbuscmd.c
@@ -16,51 +16,54 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include <termios.h>
-#include <unistd.h>
+#include <errno.h>
#include <fcntl.h>
-#include <stdlib.h>
+#include <getopt.h>
+#include <sched.h>
#include <stdint.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
-#include <errno.h>
+#include <unistd.h>
#include <sys/ioctl.h>
-#include <getopt.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
#include "modbus.h"
-
-int verbose;
+#include "rackmond.h"
void usage() {
fprintf(stderr,
- "modbuscmd [-v] [-t <tty>] [-g <gpio>] modbus_command\n"
- "\ttty defaults to %s\n"
- "\tgpio defaults to %d\n"
+ "modbuscmd [-v] [-t <timeout in ms>] [-x <expected response length>] modbus_command\n"
"\tmodbus command should be specified in hex\n"
- "\teg:\ta40300000008\n",
- DEFAULT_TTY, DEFAULT_GPIO);
+ "\teg:\ta40300000008\n"
+ "\tif an expected response length is provided, modbuscmd will stop receving and check crc immediately "
+ "after receiving that many bytes\n");
exit(1);
}
+
int main(int argc, char **argv) {
int error = 0;
- int fd;
- struct termios tio;
- char gpio_filename[255];
- int gpio_fd = 0;
- int gpio_n = DEFAULT_GPIO;
- char *tty = DEFAULT_TTY;
char *modbus_cmd = NULL;
size_t cmd_len = 0;
+ int expected = 0;
+ uint32_t timeout = 0;
verbose = 0;
+ rackmond_command *cmd = NULL;
+ char *response = NULL;
+ int clisock;
+ uint16_t response_len_actual;
+ struct sockaddr_un rackmond_addr;
int opt;
- while((opt = getopt(argc, argv, "t:g:v")) != -1) {
+ while((opt = getopt(argc, argv, "w:x:t:g:v")) != -1) {
switch (opt) {
- case 't':
- tty = optarg;
+ case 'x':
+ expected = atoi(optarg);
break;
- case 'g':
- gpio_n = atoi(optarg);
+ case 't':
+ timeout = atol(optarg);
break;
case 'v':
verbose = 1;
@@ -77,29 +80,6 @@ int main(int argc, char **argv) {
usage();
}
- if (verbose)
- fprintf(stderr, "[*] Opening TTY\n");
- fd = open(tty, O_RDWR | O_NOCTTY);
- CHECK(fd);
-
- if (verbose)
- fprintf(stderr, "[*] Opening GPIO %d\n", gpio_n);
- snprintf(gpio_filename, 255, "/sys/class/gpio/gpio%d/value", gpio_n);
- gpio_fd = open(gpio_filename, O_WRONLY | O_SYNC);
- CHECK(gpio_fd);
-
- if (verbose)
- fprintf(stderr, "[*] Setting TTY flags!\n");
- memset(&tio, 0, sizeof(tio));
- cfsetspeed(&tio,B19200);
- tio.c_cflag |= PARENB;
- tio.c_cflag |= CLOCAL;
- tio.c_cflag |= CS8;
- tio.c_iflag |= INPCK;
- tio.c_cc[VMIN] = 1;
- tio.c_cc[VTIME] = 0;
- CHECK(tcsetattr(fd,TCSANOW,&tio));
-
//convert hex to bytes
cmd_len = strlen(modbus_cmd);
if(cmd_len < 4) {
@@ -107,57 +87,45 @@ int main(int argc, char **argv) {
exit(1);
}
decode_hex_in_place(modbus_cmd, &cmd_len);
- append_modbus_crc16(modbus_cmd, &cmd_len);
- // print command as sent
- if (verbose) {
- fprintf(stderr, "Will send: ");
- print_hex(stderr, modbus_cmd, cmd_len);
- fprintf(stderr, "\n");
- }
-
- if (verbose)
- fprintf(stderr, "[*] Writing!\n");
-
- // gpio on, write, wait, gpio off
- gpio_on(gpio_fd);
- write(fd, modbus_cmd, cmd_len);
- waitfd(fd);
- gpio_off(gpio_fd);
+ cmd = malloc(sizeof(rackmond_command) + cmd_len);
+ cmd->type = COMMAND_TYPE_RAW_MODBUS;
+ cmd->raw_modbus.length = cmd_len;
+ cmd->raw_modbus.custom_timeout = timeout;
+ memcpy(cmd->raw_modbus.data, modbus_cmd, cmd_len);
+ cmd->raw_modbus.expected_response_length = expected;
+ response = malloc(expected ? expected : 1024);
+ uint16_t wire_cmd_len = sizeof(rackmond_command) + cmd_len;
- // Enable UART read
- tio.c_cflag |= CREAD;
- CHECK(tcsetattr(fd,TCSANOW,&tio));
-
- if(verbose)
- fprintf(stderr, "[*] reading any response...\n");
- // Read back response
- char modbus_buf[255];
- size_t mb_pos = 0;
- memset(modbus_buf, 0, sizeof(modbus_buf));
- mb_pos = read_wait(fd, modbus_buf, sizeof(modbus_buf), 90000);
- if(mb_pos >= 4) {
- uint16_t crc = modbus_crc16(modbus_buf, mb_pos - 2);
- if(verbose)
- fprintf(stderr, "Modbus response CRC: %04X\n ", crc);
- if((modbus_buf[mb_pos - 2] == (crc >> 8)) &&
- (modbus_buf[mb_pos - 1] == (crc & 0x00FF))) {
- if(verbose)
- fprintf(stderr, "CRC OK!\n");
- print_hex(stdout, modbus_buf, mb_pos);
- printf("\n");
- } else {
- fprintf(stderr, "BAD CRC :(\n");
- return 5;
- }
- } else {
- fprintf(stderr, "No response :(\n");
- return 4;
+ clisock = socket(AF_UNIX, SOCK_STREAM, 0);
+ CHECKP(socket, clisock);
+ rackmond_addr.sun_family = AF_UNIX;
+ strcpy(rackmond_addr.sun_path, "/var/run/rackmond.sock");
+ int addr_len = strlen(rackmond_addr.sun_path) + sizeof(rackmond_addr.sun_family);
+ CHECKP(connect, connect(clisock, (struct sockaddr*) &rackmond_addr, addr_len));
+ CHECKP(send, send(clisock, &wire_cmd_len, sizeof(wire_cmd_len), 0));
+ CHECKP(send, send(clisock, cmd, wire_cmd_len, 0));
+ CHECKP(recv, recv(clisock, &response_len_actual, sizeof(response_len_actual), 0));
+ if(response_len_actual == 0) {
+ uint16_t errcode = 0;
+ CHECKP(recv, recv(clisock, &errcode, sizeof(errcode), 0));
+ fprintf(stderr, "modbus error: %d (%s)\n", errcode, modbus_strerror(errcode));
+ error = 1;
+ goto cleanup;
+ }
+ CHECKP(recv, recv(clisock, response, response_len_actual, 0));
+ if(error == 0) {
+ printf("Response: ");
+ print_hex(stdout, response, response_len_actual);
+ printf("\n");
}
-
cleanup:
+ free(cmd);
+ free(response);
if(error != 0) {
+ if(errno != 0) {
+ fprintf(stderr, "errno err: %s\n", strerror(errno));
+ }
error = 1;
- fprintf(stderr, "%s\n", strerror(errno));
}
return error;
}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbussim.c b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbussim.c
index bf8c6c8..e276501 100644
--- a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbussim.c
+++ b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/modbussim.c
@@ -28,8 +28,6 @@
#include <getopt.h>
#include "modbus.h"
-int verbose = 0;
-
void usage() {
fprintf(stderr,
"modbussim [-v] [-t <tty>] [-g <gpio>] modbus_request modbus_reply\n"
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/psu-update-delta.py b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/psu-update-delta.py
new file mode 100755
index 0000000..a92cf8e
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/psu-update-delta.py
@@ -0,0 +1,269 @@
+#!/usr/bin/env python
+from __future__ import print_function
+
+import os.path
+import socket
+import struct
+import sys
+import argparse
+import traceback
+
+import hexfile
+
+
+def auto_int(x):
+ return int(x, 0)
+
+
+parser = argparse.ArgumentParser()
+parser.add_argument('--addr', type=auto_int, required=True,
+ help="PSU Modbus Address")
+parser.add_argument('file', help="firmware file")
+
+
+class ModbusTimeout(Exception):
+ pass
+
+
+class ModbusCRCFail(Exception):
+ pass
+
+
+class ModbusUnknownError(Exception):
+ pass
+
+
+class BadMEIResponse(Exception):
+ pass
+
+
+def rackmon_command(cmd):
+ srvpath = "/var/run/rackmond.sock"
+ replydata = []
+ if os.path.exists(srvpath):
+ client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ client.connect(srvpath)
+ cmdlen = struct.pack("@H", len(cmd))
+ client.send(cmdlen)
+ client.send(cmd)
+ while True:
+ data = client.recv(1024)
+ if not data:
+ break
+ replydata.append(data)
+ client.close()
+ return ''.join(replydata)
+
+
+def pause_monitoring():
+ COMMAND_TYPE_PAUSE_MONITORING = 0x04
+ command = struct.pack("@Hxx", COMMAND_TYPE_PAUSE_MONITORING)
+ result = rackmon_command(command)
+ (res_n, ) = struct.unpack("@B", result)
+ if res_n == 1:
+ print("Monitoring was already paused when tried to pause")
+ elif res_n == 0:
+ print("Monitoring paused")
+ else:
+ print("Unknown response pausing monitoring: %d" % res_n)
+
+
+def resume_monitoring():
+ COMMAND_TYPE_START_MONITORING = 0x05
+ command = struct.pack("@Hxx", COMMAND_TYPE_START_MONITORING)
+ result = rackmon_command(command)
+ (res_n, ) = struct.unpack("@B", result)
+ if res_n == 1:
+ print("Monitoring was already running when tried to resume")
+ elif res_n == 0:
+ print("Monitoring resumed")
+ else:
+ print("Unknown response resuming monitoring: %d" % res_n)
+
+
+def modbuscmd(raw_cmd, expected=0, timeout=0):
+ COMMAND_TYPE_RAW_MODBUS = 1
+ send_command = struct.pack("@HxxHHL",
+ COMMAND_TYPE_RAW_MODBUS,
+ len(raw_cmd),
+ expected,
+ timeout) + raw_cmd
+ result = rackmon_command(send_command)
+ if len(result) == 0:
+ raise ModbusUnknownError()
+ (resp_len,) = struct.unpack("@H", result[:2])
+ if resp_len == 0:
+ (error, ) = struct.unpack("@H", result[2:4])
+ if error == 4:
+ raise ModbusTimeout()
+ if error == 5:
+ raise ModbusCRCFail()
+ print("Unknown modbus error: " + str(error))
+ raise ModbusUnknownError()
+ return result[2:resp_len]
+
+
+def mei_command(addr, func_code, mei_type=0x64, data=None, timeout=0):
+ i_data = data
+ if i_data is None:
+ i_data = ("\xFF" * 7)
+ if len(i_data) < 7:
+ i_data = i_data + ("\xFF" * (7 - len(i_data)))
+ assert len(i_data) == 7
+ command = struct.pack("BBBB", addr, 0x2b, mei_type, func_code) + i_data
+ return modbuscmd(command, expected=13, timeout=timeout)
+
+
+def enter_bootloader(addr):
+ try:
+ print("Entering bootloader...")
+ mei_command(addr, 0xFB, timeout=4000)
+ except ModbusTimeout:
+ print("Enter bootloader timed out (expected.)")
+ pass
+
+
+def mei_expect(response, addr, data_pfx, error, success_mei_type=0x71):
+ expected = struct.pack("BBB", addr, 0x2B, success_mei_type) + \
+ data_pfx + ("\xFF" * (8 - len(data_pfx)))
+ if response != expected:
+ print(error + ", response: " + response.encode('hex'))
+ raise BadMEIResponse()
+
+
+def start_programming(addr):
+ print("Send start programming...")
+ response = mei_command(addr, 0x70, timeout=10000)
+ mei_expect(response, addr, "\xB0", "Start programming failed")
+ print("Start programming succeeded.")
+
+
+def get_challenge(addr):
+ print("Send get seed")
+ response = mei_command(addr, 0x27, timeout=3000)
+ expected = struct.pack("BBBB", addr, 0x2B, 0x71, 0x67)
+ if response[:len(expected)] != expected:
+ print("Bad response to get seed: " + response.encode('hex'))
+ raise BadMEIResponse()
+ challenge = response[len(expected):len(expected) + 4]
+ print("Got seed: " + challenge.encode('hex'))
+ return challenge
+
+
+def send_key(addr, key):
+ print("Send key")
+ response = mei_command(addr, 0x28, data=key, timeout=3000)
+ mei_expect(response, addr, "\x68", "Start programming failed")
+ print("Send key successful.")
+
+
+def delta_seccalckey(challenge):
+ (seed, ) = struct.unpack(">L", challenge)
+ for i in range(32):
+ if seed & 1 != 0:
+ seed = seed ^ 0xc758a5b6
+ seed = (seed >> 1) & 0x7fffffff
+ seed = seed ^ 0x06854137
+ return struct.pack(">L", seed)
+
+
+def verify_flash(addr):
+ print("Verifying program...")
+ response = mei_command(addr, 0x76, timeout=60000)
+ mei_expect(response, addr, "\xB6", "Program verification failed")
+
+
+def set_write_address(psu_addr, flash_addr):
+ # print("Set write address to " + hex(flash_addr))
+ data = struct.pack(">LB", flash_addr, 0xEA)
+ response = mei_command(psu_addr, 0x61, data=data, timeout=3000)
+ mei_expect(response, psu_addr, "\xA1\xEA", "Set address failed")
+
+
+def write_data(addr, data):
+ assert(len(data) == 8)
+ command = struct.pack(">BBB", addr, 0x2b, 0x65) + data
+ response = modbuscmd(command, expected=13, timeout=3000)
+ expected = struct.pack(">B", addr) +\
+ "\x2b\x73\xf0\xaa\xff\xff\xff\xff\xff\xff"
+ if response != expected:
+ print("Bad response to writing data: " +
+ response.encode('hex'))
+ raise BadMEIResponse()
+
+
+def send_image(addr, fwimg):
+ total_chunks = sum([len(s) for s in fwimg.segments]) / 8
+ sent_chunks = 0
+ for s in fwimg.segments:
+ if len(s) == 0:
+ continue
+ print("Sending " + str(s))
+ set_write_address(addr, s.start_address)
+ for i in xrange(0, len(s), 8):
+ chunk = s.data[i:i+8]
+ if len(chunk) < 8:
+ chunk = chunk + ("\xFF" * (8 - len(chunk)))
+ sent_chunks += 1
+ print("\r[%.2f%%] Sending chunk %d of %d..." %
+ (sent_chunks * 100.0 / total_chunks,
+ sent_chunks, total_chunks), end="")
+ sys.stdout.flush()
+ write_data(addr, str(bytearray(chunk)))
+ print("")
+
+
+def reset_psu(addr):
+ print("Resetting PSU...")
+ try:
+ response = mei_command(addr, 0x72, timeout=10000)
+ except ModbusTimeout:
+ print("No reply from PSU reset (expected.)")
+ return
+ expected = struct.pack(">BBBB", addr, 0x2b, 0x71, 0xb2) +\
+ ("\xFF" * 7)
+ if response != expected:
+ print("Bad response to unit reset request: " +
+ response.encode('hex'))
+ raise BadMEIResponse()
+
+
+def erase_flash(addr):
+ print("Erasing flash... ")
+ sys.stdout.flush()
+ response = mei_command(addr, 0x65, timeout=30000)
+ expected = struct.pack(">BBBB", addr, 0x2b, 0x71, 0xa5) +\
+ ("\xFF" * 7)
+ if response != expected:
+ print("Bad response to erasing flash: " +
+ response.encode('hex'))
+ raise BadMEIResponse()
+
+
+def update_psu(addr, filename):
+ pause_monitoring()
+ fwimg = hexfile.load(filename)
+ enter_bootloader(addr)
+ start_programming(addr)
+ challenge = get_challenge(addr)
+ send_key(addr, delta_seccalckey(challenge))
+ erase_flash(addr)
+ send_image(addr, fwimg)
+ verify_flash(addr)
+ reset_psu(addr)
+
+
+def main():
+ args = parser.parse_args()
+ try:
+ update_psu(args.addr, args.file)
+ except:
+ traceback.print_exc()
+ print("Firmware update failed")
+ resume_monitoring()
+ sys.exit(1)
+ resume_monitoring()
+ sys.exit(0)
+
+if __name__ == "__main__":
+ main()
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/rackmon-config.py b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/rackmon-config.py
new file mode 100644
index 0000000..e93dfae
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/rackmon-config.py
@@ -0,0 +1,93 @@
+from __future__ import print_function
+import struct
+import socket
+import os, os.path
+
+reglist = [
+ {"begin": 0x0, #MFR_MODEL
+ "length": 8},
+ {"begin": 0x10, #MFR_DATE
+ "length": 8},
+ {"begin": 0x20, #FB Part #
+ "length": 8},
+ {"begin": 0x30, #HW Revision
+ "length": 4},
+ {"begin": 0x38, #FW Revision
+ "length": 4},
+ {"begin": 0x40, #MFR Serial #
+ "length": 16},
+ {"begin": 0x60, #Workorder #
+ "length": 4},
+ {"begin": 0x68, #PSU Status
+ "length": 1,
+ "keep": 10, # 10-sample ring buffer
+ "flags": 1},
+ {"begin": 0x69, #Battery Status
+ "length": 1,
+ "keep": 10, # 10-sample ring buffer
+ "flags": 1},
+ {"begin": 0x80, #Input VAC
+ "length": 1,
+ "keep": 10},
+ {"begin": 0x82, #Input Current AC
+ "length": 1,
+ "keep": 10},
+ {"begin": 0x84, #Battery Voltage
+ "length": 1,
+ "keep": 10},
+ {"begin": 0x86, #Battery Current Output
+ "length": 1},
+ {"begin": 0x88, #Battery Current Input
+ "length": 1},
+ {"begin": 0x8A, #Output Voltage (main converter)
+ "length": 1,
+ "keep": 10},
+ {"begin": 0x8C, #Output Current (main converter)
+ "length": 1,
+ "keep": 10},
+ {"begin": 0x8E, #IT Load Voltage Output
+ "length": 1},
+ {"begin": 0x90, #IT Load Current Output
+ "length": 1},
+ {"begin": 0x92, #Bulk Cap Voltage
+ "length": 1},
+ {"begin": 0x94, #Input Power
+ "length": 1,
+ "keep": 10},
+ {"begin": 0x96, #Output Power
+ "length": 1,
+ "keep": 10},
+ {"begin": 0x98, #RPM Fan 0
+ "length": 1},
+ {"begin": 0x9A, #RPM Fan 1
+ "length": 1},
+ {"begin": 0x9E, #Temp 0
+ "length": 1},
+ {"begin": 0xA0, #Temp 1
+ "length": 1},
+]
+
+def main():
+ COMMAND_TYPE_SET_CONFIG = 2
+ config_command = struct.pack("@HxxH",
+ COMMAND_TYPE_SET_CONFIG,
+ len(reglist))
+ for r in reglist:
+ keep = 1
+ if "keep" in r:
+ keep = r["keep"]
+ flags = 0
+ if "flags" in r:
+ flags = r["flags"]
+ monitor_interval = struct.pack("@HHHH", r["begin"], r["length"], keep, flags)
+ config_command += monitor_interval
+
+ config_packet = struct.pack("H", len(config_command)) + config_command
+ srvpath = "/var/run/rackmond.sock"
+ if os.path.exists(srvpath):
+ client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+ client.connect(srvpath)
+ client.send(config_packet)
+
+if __name__ == "__main__":
+ main()
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/rackmond.c b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/rackmond.c
new file mode 100644
index 0000000..cce3ba4
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/rackmond.c
@@ -0,0 +1,637 @@
+#include "modbus.h"
+#include "rackmond.h"
+#include <string.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <poll.h>
+#include <time.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <signal.h>
+
+#define MAX_ACTIVE_ADDRS 12
+#define REGISTER_PSU_STATUS 0x68
+
+struct _lock_holder {
+ pthread_mutex_t *lock;
+ int held;
+};
+
+#define lock_holder(holder_name, lock_expr) \
+ struct _lock_holder holder_name; \
+ holder_name.lock = lock_expr; \
+ holder_name.held = 0;
+
+#define lock_take(holder_name) { \
+ pthread_mutex_lock(holder_name.lock); \
+ holder_name.held = 1; \
+}
+
+#define lock_release(holder_name) { \
+ if(holder_name.held) { \
+ pthread_mutex_unlock(holder_name.lock); \
+ holder_name.held = 0; \
+ } \
+}
+
+int scanning = 0;
+
+typedef struct _rs485_dev {
+ // hold this for the duration of a command
+ pthread_mutex_t lock;
+ int tty_fd;
+ int gpio_fd;
+} rs485_dev;
+
+typedef struct _register_req {
+ uint16_t begin;
+ int num;
+} register_req;
+
+typedef struct register_range_data {
+ monitor_interval* i;
+ void* mem_begin;
+ size_t mem_pos;
+} register_range_data;
+
+typedef struct monitoring_data {
+ uint8_t addr;
+ register_range_data range_data[1];
+} monitoring_data;
+
+typedef struct _rackmond_data {
+ // global rackmond lock
+ pthread_mutex_t lock;
+ // number of register read commands to send to each PSU
+ int num_reqs;
+ // register read commands (begin+length)
+ register_req *reqs;
+ monitoring_config *config;
+
+ uint8_t num_active_addrs;
+ uint8_t active_addrs[MAX_ACTIVE_ADDRS];
+ monitoring_data* stored_data[MAX_ACTIVE_ADDRS];
+ FILE *status_log;
+
+ // timeout in nanosecs
+ int modbus_timeout;
+
+ int paused;
+
+ rs485_dev rs485;
+} rackmond_data;
+
+rackmond_data world;
+
+char psu_address(int rack, int shelf, int psu) {
+ int rack_a = ((rack & 3) << 3);
+ int shelf_a = ((shelf & 1) << 2);
+ int psu_a = (psu & 3);
+ return 0xA0 | rack_a | shelf_a | psu_a;
+}
+
+int modbus_command(rs485_dev* dev, int timeout, char* command, size_t len, char* destbuf, size_t dest_limit, size_t expect) {
+ int error = 0;
+ lock_holder(devlock, &dev->lock);
+ modbus_req req;
+ req.tty_fd = dev->tty_fd;
+ req.gpio_fd = dev->gpio_fd;
+ req.modbus_cmd = command;
+ req.cmd_len = len;
+ req.dest_buf = destbuf;
+ req.dest_limit = dest_limit;
+ req.timeout = timeout;
+ req.expected_len = expect != 0 ? expect : dest_limit;
+ req.scan = scanning;
+ lock_take(devlock);
+ int cmd_error = modbuscmd(&req);
+ CHECK(cmd_error);
+cleanup:
+ lock_release(devlock);
+ if (error >= 0) {
+ return req.dest_len;
+ }
+
+ return error;
+}
+
+int read_registers(rs485_dev *dev, int timeout, uint8_t addr, uint16_t begin, uint16_t num, uint16_t* out) {
+ int error = 0;
+ // address, function, begin, length in # of regs
+ char command[sizeof(addr) + 1 + sizeof(begin) + sizeof(num)];
+ // address, function, length (1 byte), data (2 bytes per register), crc
+ // (VLA)
+ char response[sizeof(addr) + 1 + 1 + (2 * num) + 2];
+ command[0] = addr;
+ command[1] = MODBUS_READ_HOLDING_REGISTERS;
+ command[2] = begin << 8;
+ command[3] = begin & 0xFF;
+ command[4] = num << 8;
+ command[5] = num & 0xFF;
+
+ int dest_len =
+ modbus_command(
+ dev, timeout,
+ command, sizeof(addr) + 1 + sizeof(begin) + sizeof(num),
+ response, sizeof(addr) + 1 + 1 + (2 * num) + 2, 0);
+ CHECK(dest_len);
+
+ if (dest_len >= 5) {
+ memcpy(out, response + 3, num * 2);
+ } else {
+ log("Unexpected short but CRC correct response!\n");
+ error = -1;
+ goto cleanup;
+ }
+ if (response[0] != addr) {
+ log("Got response for addr %02x when expected %02x\n", response[0], addr);
+ error = -1;
+ goto cleanup;
+ }
+ if (response[2] != (num * 2)) {
+ log("Got %d register data bytes when expecting %d\n", response[2], (num * 2));
+ error = -1;
+ goto cleanup;
+ }
+cleanup:
+ return error;
+}
+
+int sub_uint8s(const void* a, const void* b) {
+ return (*(uint8_t*)a) - (*(uint8_t*)b);
+}
+
+int check_active_psus() {
+ int error = 0;
+ lock_holder(worldlock, &world.lock);
+ lock_take(worldlock);
+ if (world.paused == 1) {
+ usleep(1000);
+ goto cleanup;
+ }
+ if (world.config == NULL) {
+ lock_release(worldlock);
+ usleep(5000);
+ goto cleanup;
+ }
+ world.num_active_addrs = 0;
+
+ scanning = 1;
+ //fprintf(stderr, "Begin presence check: ");
+ for(int rack = 0; rack < 3; rack++) {
+ for(int shelf = 0; shelf < 2; shelf++) {
+ for(int psu = 0; psu < 3; psu++) {
+ char addr = psu_address(rack, shelf, psu);
+ uint16_t status = 0;
+ int err = read_registers(&world.rs485, world.modbus_timeout, addr, REGISTER_PSU_STATUS, 1, &status);
+ if (err == 0) {
+ world.active_addrs[world.num_active_addrs] = addr;
+ world.num_active_addrs++;
+ //fprintf(stderr, "%02x - active (%04x) ", addr, status);
+ } else {
+ dbg("%02x - %d; ", addr, err);
+ }
+ }
+ }
+ }
+ //its the only stdlib sort
+ qsort(world.active_addrs, world.num_active_addrs,
+ sizeof(uint8_t), sub_uint8s);
+cleanup:
+ scanning = 0;
+ lock_release(worldlock);
+ return error;
+}
+
+monitoring_data* alloc_monitoring_data(uint8_t addr) {
+ size_t size = sizeof(monitoring_data) +
+ sizeof(register_range_data) * world.config->num_intervals;
+ for(int i = 0; i < world.config->num_intervals; i++) {
+ monitor_interval *iv = &world.config->intervals[i];
+ int pitch = sizeof(uint32_t) + (sizeof(uint16_t) * iv->len);
+ int data_size = pitch * iv->keep;
+ size += data_size;
+ }
+ monitoring_data* d = calloc(1, size);
+ if (d == NULL) {
+ log("Failed to allocate memory for sensor data.\n");
+ return NULL;
+ }
+ d->addr = addr;
+ void* mem = d;
+ mem = mem + (sizeof(monitoring_data) +
+ sizeof(register_range_data) * world.config->num_intervals);
+ for(int i = 0; i < world.config->num_intervals; i++) {
+ monitor_interval *iv = &world.config->intervals[i];
+ int pitch = sizeof(uint32_t) + (sizeof(uint16_t) * iv->len);
+ int data_size = pitch * iv->keep;
+ d->range_data[i].i = iv;
+ d->range_data[i].mem_begin = mem;
+ d->range_data[i].mem_pos = 0;
+ mem = mem + data_size;
+ }
+ return d;
+}
+
+int sub_storeptrs(const void* va, const void *vb) {
+ //more *s than i like :/
+ monitoring_data* a = *(monitoring_data**)va;
+ monitoring_data* b = *(monitoring_data**)vb;
+ //nulls to the end
+ if (b == NULL && a == NULL) {
+ return 0;
+ }
+ if (b == NULL) {
+ return -1;
+ }
+ if (a == NULL) {
+ return 1;
+ }
+ return a->addr - b->addr;
+}
+
+int alloc_monitoring_datas() {
+ int error = 0;
+ if (world.config == NULL) {
+ goto cleanup;
+ }
+ qsort(world.stored_data, MAX_ACTIVE_ADDRS,
+ sizeof(monitoring_data*), sub_storeptrs);
+ int data_pos = 0;
+ for(int i = 0; i < world.num_active_addrs; i++) {
+ uint8_t addr = world.active_addrs[i];
+ while(world.stored_data[data_pos] != NULL &&
+ world.stored_data[data_pos]->addr != addr) {
+ data_pos++;
+ }
+ if (world.stored_data[data_pos] == NULL) {
+ log("Detected PSU at address 0x%02x\n", addr);
+ //syslog(LOG_INFO, "Detected PSU at address 0x%02x", addr);
+ world.stored_data[data_pos] = alloc_monitoring_data(addr);
+ if (world.stored_data[data_pos] == NULL) {
+ BAIL("allocation failed\n");
+ }
+ //reset search pos after alloc (post-sorted addrs may already be alloc'd, need to check again)
+ data_pos = 0;
+ continue;
+ }
+ if (world.stored_data[data_pos]->addr == addr) {
+ continue;
+ }
+ BAIL("shouldn't get here!\n");
+ }
+cleanup:
+ return error;
+}
+
+void record_data(register_range_data* rd, uint32_t time, uint16_t* regs) {
+ int n_regs = (rd->i->len);
+ int pitch = sizeof(time) + (sizeof(uint16_t) * n_regs);
+ int mem_size = pitch * rd->i->keep;
+
+ memcpy(rd->mem_begin + rd->mem_pos, &time, sizeof(time));
+ rd->mem_pos += sizeof(time);
+ memcpy(rd->mem_begin + rd->mem_pos, regs, n_regs * sizeof(uint16_t));
+ rd->mem_pos += n_regs * sizeof(uint16_t);
+ rd->mem_pos = rd->mem_pos % mem_size;
+}
+
+int fetch_monitored_data() {
+ int error = 0;
+ int data_pos = 0;
+ lock_holder(worldlock, &world.lock);
+ lock_take(worldlock);
+ if (world.paused == 1) {
+ usleep(1000);
+ goto cleanup;
+ }
+ if (world.config == NULL) {
+ goto cleanup;
+ }
+ lock_release(worldlock);
+
+ usleep(1000); // wait a sec btween PSUs to not overload RT scheduling
+ // threshold
+ while(world.stored_data[data_pos] != NULL && data_pos < MAX_ACTIVE_ADDRS) {
+ uint8_t addr = world.stored_data[data_pos]->addr;
+ //log("readpsu %02x\n", addr);
+ for(int r = 0; r < world.config->num_intervals; r++) {
+ register_range_data* rd = &world.stored_data[data_pos]->range_data[r];
+ monitor_interval* i = rd->i;
+ uint16_t regs[i->len];
+ int err = read_registers(&world.rs485,
+ world.modbus_timeout, addr, i->begin, i->len, regs);
+ if (err) {
+ log("Error %d reading %02x registers at %02x from %02x\n",
+ err, i->len, i->begin, addr);
+ continue;
+ }
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ uint32_t timestamp = ts.tv_sec;
+ if (rd->i->flags & MONITOR_FLAG_ONLY_CHANGES) {
+ int pitch = sizeof(timestamp) + (sizeof(uint16_t) * i->len);
+ int lastpos = rd->mem_pos - pitch;
+ if (lastpos < 0) {
+ lastpos = (pitch * rd->i->keep) - pitch;
+ }
+ if (!memcmp(rd->mem_begin + lastpos + sizeof(timestamp),
+ regs, sizeof(uint16_t) * i->len) &&
+ memcmp(rd->mem_begin, "\x00\x00\x00\x00", 4)) {
+ continue;
+ }
+
+ if (world.status_log) {
+ time_t rawt;
+ struct tm* ti;
+ time(&rawt);
+ ti = localtime(&rawt);
+ char timestr[80];
+ strftime(timestr, sizeof(timestr), "%b %e %T", ti);
+ fprintf(world.status_log,
+ "%s: Change to status register %02x on address %02x. New value: %02x\n",
+ timestr, i->begin, addr, regs[0]);
+ fflush(world.status_log);
+ }
+
+ }
+ lock_take(worldlock);
+ record_data(rd, timestamp, regs);
+ lock_release(worldlock);
+ }
+ data_pos++;
+ }
+cleanup:
+ lock_release(worldlock);
+ return error;
+}
+
+// check for new psus every N rounds of sensor reads
+#define SEARCH_PSUS_EVERY 200
+void* monitoring_loop(void* arg) {
+ (void) arg;
+ int until_search = 0;
+ world.status_log = fopen("/var/log/psu-status.log", "a+");
+ while(1) {
+ if (until_search == 0) {
+ check_active_psus();
+ alloc_monitoring_datas();
+ until_search = SEARCH_PSUS_EVERY;
+ } else {
+ until_search--;
+ }
+ fetch_monitored_data();
+ }
+ return NULL;
+}
+
+int open_rs485_dev(const char* tty_filename, int gpio_num, rs485_dev *dev) {
+ int error = 0;
+ int tty_fd, gpio_fd;
+ char gpio_filename[128];
+ dbg("[*] Opening TTY\n");
+ tty_fd = open(tty_filename, O_RDWR | O_NOCTTY);
+ CHECK(tty_fd);
+
+ dbg("[*] Opening GPIO %d\n", gpio_num);
+ snprintf(gpio_filename, sizeof(gpio_filename), "/sys/class/gpio/gpio%d/value", gpio_num);
+ gpio_fd = open(gpio_filename, O_WRONLY | O_SYNC);
+ CHECK(gpio_fd);
+
+ dev->tty_fd = tty_fd;
+ dev->gpio_fd = gpio_fd;
+ pthread_mutex_init(&dev->lock, NULL);
+cleanup:
+ return error;
+}
+
+int do_command(int sock, rackmond_command* cmd) {
+ int error = 0;
+ lock_holder(worldlock, &world.lock);
+ switch(cmd->type) {
+ case COMMAND_TYPE_RAW_MODBUS:
+ {
+ uint16_t expected = cmd->raw_modbus.expected_response_length;
+ int timeout = world.modbus_timeout;
+ if (cmd->raw_modbus.custom_timeout) {
+ //ms to us
+ timeout = cmd->raw_modbus.custom_timeout * 1000;
+ }
+ if (expected == 0) {
+ expected = 1024;
+ }
+ char response[expected];
+ int response_len = modbus_command(
+ &world.rs485, timeout,
+ cmd->raw_modbus.data, cmd->raw_modbus.length,
+ response, expected, expected);
+ uint16_t response_len_wire = response_len;
+ if(response_len < 0) {
+ uint16_t error = -response_len;
+ response_len_wire = 0;
+ send(sock, &response_len_wire, sizeof(uint16_t), 0);
+ send(sock, &error, sizeof(uint16_t), 0);
+ break;
+ }
+ send(sock, &response_len_wire, sizeof(uint16_t), 0);
+ send(sock, response, response_len, 0);
+ break;
+ }
+ case COMMAND_TYPE_SET_CONFIG:
+ {
+ lock_take(worldlock);
+ if (world.config != NULL) {
+ BAIL("rackmond already configured\n");
+ }
+ size_t config_size = sizeof(monitoring_config) +
+ (sizeof(monitor_interval) * cmd->set_config.config.num_intervals);
+ world.config = calloc(1, config_size);
+ memcpy(world.config, &cmd->set_config.config, config_size);
+ syslog(LOG_INFO, "got configuration");
+ lock_release(worldlock);
+ break;
+ }
+ case COMMAND_TYPE_DUMP_DATA_JSON:
+ {
+ lock_take(worldlock);
+ if (world.config == NULL) {
+ send(sock, "[]", 2, 0);
+ } else {
+ struct timespec ts;
+ clock_gettime(CLOCK_REALTIME, &ts);
+ uint32_t now = ts.tv_sec;
+ send(sock, "[", 1, 0);
+ int data_pos = 0;
+ while(world.stored_data[data_pos] != NULL && data_pos < MAX_ACTIVE_ADDRS) {
+ dprintf(sock, "{\"addr\":%d,\"now\":%d,\"ranges\":[",
+ world.stored_data[data_pos]->addr, now);
+ for(int i = 0; i < world.config->num_intervals; i++) {
+ uint32_t time;
+ register_range_data *rd = &world.stored_data[data_pos]->range_data[i];
+ char* mem_pos = rd->mem_begin;
+ dprintf(sock,"{\"begin\":%d,\"readings\":[", rd->i->begin);
+ // want to cut the list off early just before
+ // the first entry with time == 0
+ memcpy(&time, mem_pos, sizeof(time));
+ for(int j = 0; j < rd->i->keep && time != 0; j++) {
+ mem_pos += sizeof(time);
+ dprintf(sock, "{\"time\":%d,\"data\":\"", time);
+ for(int c = 0; c < rd->i->len * 2; c++) {
+ dprintf(sock, "%02x", *mem_pos);
+ mem_pos++;
+ }
+ send(sock, "\"}", 2, 0);
+ memcpy(&time, mem_pos, sizeof(time));
+ if (time == 0) {
+ break;
+ }
+ if ((j+1) < rd->i->keep) {
+ send(sock, ",", 1, 0);
+ }
+ }
+ send(sock, "]}", 2, 0);
+ if ((i+1) < world.config->num_intervals) {
+ send(sock, ",", 1, 0);
+ }
+ }
+ data_pos++;
+ if (data_pos < MAX_ACTIVE_ADDRS && world.stored_data[data_pos] != NULL) {
+ send(sock, "]},", 3, 0);
+ } else {
+ send(sock, "]}", 2, 0);
+ }
+ }
+ send(sock, "]", 1, 0);
+ }
+ lock_release(worldlock);
+ break;
+ }
+ case COMMAND_TYPE_PAUSE_MONITORING:
+ {
+ lock_take(worldlock);
+ uint8_t was_paused = world.paused;
+ world.paused = 1;
+ send(sock, &was_paused, sizeof(was_paused), 0);
+ lock_release(worldlock);
+ break;
+ }
+ case COMMAND_TYPE_START_MONITORING:
+ {
+ lock_take(worldlock);
+ uint8_t was_started = !world.paused;
+ world.paused = 0;
+ send(sock, &was_started, sizeof(was_started), 0);
+ lock_release(worldlock);
+ break;
+ }
+ default:
+ CHECK(-1);
+ }
+cleanup:
+ lock_release(worldlock);
+ return error;
+}
+
+typedef enum {
+ CONN_WAITING_LENGTH,
+ CONN_WAITING_BODY
+} rackmond_connection_state;
+
+// receive the command as a length prefixed block
+// (uint16_t, followed by data)
+// this is all over a local socket, won't be doing
+// endian flipping, clients should only be local procs
+// compiled for the same arch
+int handle_connection(int sock) {
+ int error = 0;
+ rackmond_connection_state state = CONN_WAITING_LENGTH;
+ char bodybuf[1024];
+ uint16_t expected_len = 0;
+ struct pollfd pfd;
+ int recvret = 0;
+ pfd.fd = sock;
+ pfd.events = POLLIN | POLLERR | POLLHUP;
+ // if you don't do anything for a whole second we bail
+next:
+ CHECKP(poll, poll(&pfd, 1, 1000));
+ if (pfd.revents & (POLLERR | POLLHUP)) {
+ goto cleanup;
+ }
+ switch(state) {
+ case CONN_WAITING_LENGTH:
+ recvret = recv(sock, &expected_len, 2, MSG_DONTWAIT);
+ if (recvret == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+ goto next;
+ }
+ if (expected_len == 0 || expected_len > sizeof(bodybuf)) {
+ // bad length; bail
+ goto cleanup;
+ }
+ state = CONN_WAITING_BODY;
+ goto next;
+ break;
+ case CONN_WAITING_BODY:
+ recvret = recv(sock, &bodybuf, expected_len, MSG_DONTWAIT);
+ if (recvret == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
+ goto next;
+ }
+ CHECK(do_command(sock, (rackmond_command*) bodybuf));
+ }
+cleanup:
+ close(sock);
+ if (error != 0) {
+ fprintf(stderr, "Warning: possible error handling user connection (%d)\n", error);
+ }
+ return 0;
+}
+
+int main(int argc, char** argv) {
+ if (getenv("RACKMOND_FOREGROUND") == NULL) {
+ daemon(0, 0);
+ }
+ signal(SIGPIPE, SIG_IGN);
+ int error = 0;
+ world.paused = 0;
+ world.modbus_timeout = 300000;
+ if (getenv("RACKMOND_TIMEOUT") != NULL) {
+ world.modbus_timeout = atoll(getenv("RACKMOND_TIMEOUT"));
+ fprintf(stderr, "Timeout from env: %dms\n",
+ (world.modbus_timeout / 1000));
+ }
+ world.config = NULL;
+ pthread_mutex_init(&world.lock, NULL);
+ verbose = getenv("RACKMOND_VERBOSE") != NULL ? 1 : 0;
+ openlog("rackmond", 0, LOG_USER);
+ syslog(LOG_INFO, "rackmon/modbus service starting");
+ CHECK(open_rs485_dev(DEFAULT_TTY, DEFAULT_GPIO, &world.rs485));
+ pthread_t monitoring_thread;
+ pthread_create(&monitoring_thread, NULL, monitoring_loop, NULL);
+ struct sockaddr_un local, client;
+ int sock = socket(AF_UNIX, SOCK_STREAM, 0);
+ strcpy(local.sun_path, "/var/run/rackmond.sock");
+ local.sun_family = AF_UNIX;
+ int socknamelen = sizeof(local.sun_family) + strlen(local.sun_path);
+ unlink(local.sun_path);
+ CHECKP(bind, bind(sock, (struct sockaddr *)&local, socknamelen));
+ CHECKP(listen, listen(sock, 5));
+ syslog(LOG_INFO, "rackmon/modbus service listening");
+ while(1) {
+ socklen_t clisocklen = sizeof(struct sockaddr_un);
+ int clisock = accept(sock, (struct sockaddr*) &client, &clisocklen);
+ CHECKP(accept, clisock);
+ CHECK(handle_connection(clisock));
+ }
+
+cleanup:
+ if (error != 0) {
+ error = 1;
+ }
+ return error;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/rackmond.h b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/rackmond.h
new file mode 100644
index 0000000..2c0e8a0
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/rackmond.h
@@ -0,0 +1,46 @@
+#include <stdint.h>
+
+//would've been nice to have thrift
+
+// Raw modbus command
+// Response is just the raw response data
+typedef struct raw_modbus_command {
+ uint16_t length;
+ uint16_t expected_response_length;
+ uint32_t custom_timeout; // 0 for default
+ char data[1];
+} raw_modbus_command;
+
+// only store new value if different from most recent
+// (for watching changes to status flags registers)
+#define MONITOR_FLAG_ONLY_CHANGES 0x1
+
+typedef struct monitor_interval {
+ uint16_t begin;
+ uint16_t len;
+ uint16_t keep; // How long of a history to keep?
+ uint16_t flags;
+} monitor_interval;
+
+typedef struct monitoring_config {
+ uint16_t num_intervals;
+ monitor_interval intervals[1];
+} monitoring_config;
+
+typedef struct set_config_command {
+ monitoring_config config;
+} set_config_command;
+
+#define COMMAND_TYPE_RAW_MODBUS 0x01
+#define COMMAND_TYPE_SET_CONFIG 0x02
+#define COMMAND_TYPE_DUMP_DATA_JSON 0x03
+#define COMMAND_TYPE_PAUSE_MONITORING 0x04
+#define COMMAND_TYPE_START_MONITORING 0x05
+
+typedef struct rackmond_command {
+ uint16_t type;
+ union {
+ raw_modbus_command raw_modbus;
+ set_config_command set_config;
+ };
+} rackmond_command;
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/rackmondata.c b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/rackmondata.c
new file mode 100644
index 0000000..391b5be
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/rackmondata.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014-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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include "modbus.h"
+#include "rackmond.h"
+
+int main(int argc, char **argv) {
+ int error = 0;
+ rackmond_command cmd;
+ int clisock;
+ uint16_t wire_cmd_len = sizeof(cmd);
+ struct sockaddr_un rackmond_addr;
+ cmd.type = COMMAND_TYPE_DUMP_DATA_JSON;
+ clisock = socket(AF_UNIX, SOCK_STREAM, 0);
+ CHECKP(socket, clisock);
+ rackmond_addr.sun_family = AF_UNIX;
+ strcpy(rackmond_addr.sun_path, "/var/run/rackmond.sock");
+ int addr_len = strlen(rackmond_addr.sun_path) + sizeof(rackmond_addr.sun_family);
+ CHECKP(connect, connect(clisock, (struct sockaddr*) &rackmond_addr, addr_len));
+ CHECKP(send, send(clisock, &wire_cmd_len, sizeof(wire_cmd_len), 0));
+ CHECKP(send, send(clisock, &cmd, wire_cmd_len, 0));
+ char readbuf[256];
+ ssize_t n_read;
+ while((n_read = read(clisock, readbuf, sizeof(readbuf))) > 0) {
+ write(1, readbuf, n_read);
+ }
+cleanup:
+ if(error != 0) {
+ if(errno != 0) {
+ fprintf(stderr, "%s\n", strerror(errno));
+ }
+ error = 1;
+ }
+ return error;
+}
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/setup-rackmond.sh b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/setup-rackmond.sh
new file mode 100644
index 0000000..85a1e22
--- /dev/null
+++ b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon/setup-rackmond.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+#
+# Copyright 2014-present Facebook. All Rights Reserved.
+#
+### BEGIN INIT INFO
+# Provides: setup-rackmond
+# Required-Start:
+# Required-Stop:
+# Default-Start: S
+# Default-Stop:
+# Short-Description: Start Rackmon service
+### END INIT INFO
+
+echo -n "Starting rackmon background service..."
+/usr/local/bin/rackmond
+echo "done."
+
+echo -n "Configuring rackmon service..."
+python /etc/rackmon-config.py
+echo "done."
diff --git a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon_0.1.bb b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon_0.1.bb
index d3e79e4..399f7c0 100644
--- a/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon_0.1.bb
+++ b/meta-facebook/meta-wedge/recipes-wedge/rackmon/rackmon_0.1.bb
@@ -1,4 +1,19 @@
# Copyright 2014-present Facebook. All Rights Reserved.
+#
+# This program file 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; version 2 of the License.
+#
+# 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 in a file named COPYING; if not, write to the
+# Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301 USA
SUMMARY = "Rackmon Functionality"
DESCRIPTION = "Rackmon Functionality"
SECTION = "base"
@@ -6,7 +21,7 @@ PR = "r1"
LICENSE = "GPLv2"
LIC_FILES_CHKSUM = "file://modbus.c;beginline=4;endline=16;md5=da35978751a9d71b73679307c4d296ec"
-#DEPENDS_append = " update-rc.d-native"
+DEPENDS_append = " update-rc.d-native"
SRC_URI = "file://Makefile \
file://modbuscmd.c \
@@ -14,6 +29,13 @@ SRC_URI = "file://Makefile \
file://modbus.c \
file://modbus.h \
file://gpiowatch.c \
+ file://rackmond.c \
+ file://rackmond.h \
+ file://rackmondata.c \
+ file://setup-rackmond.sh \
+ file://rackmon-config.py \
+ file://psu-update-delta.py \
+ file://hexfile.py \
"
S = "${WORKDIR}"
@@ -21,6 +43,10 @@ S = "${WORKDIR}"
binfiles = "modbuscmd \
modbussim \
gpiowatch \
+ rackmond \
+ rackmondata \
+ psu-update-delta.py \
+ hexfile.py \
"
#otherfiles = "README"
@@ -36,6 +62,11 @@ do_install() {
install -m 755 $f ${dst}/$f
ln -snf ../fbpackages/${pkgdir}/$f ${bin}/$f
done
+ install -d ${D}${sysconfdir}/init.d
+ install -d ${D}${sysconfdir}/rcS.d
+ install -m 755 setup-rackmond.sh ${D}${sysconfdir}/init.d/setup-rackmond.sh
+ install -m 755 rackmon-config.py ${D}${sysconfdir}/rackmon-config.py
+ update-rc.d -r ${D} setup-rackmond.sh start 95 2 3 4 5 .
}
FBPACKAGEDIR = "${prefix}/local/fbpackages"
OpenPOWER on IntegriCloud