summaryrefslogtreecommitdiffstats
path: root/usr.sbin/xntpd/xntpd/refclock_irig.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/xntpd/xntpd/refclock_irig.c')
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_irig.c569
1 files changed, 130 insertions, 439 deletions
diff --git a/usr.sbin/xntpd/xntpd/refclock_irig.c b/usr.sbin/xntpd/xntpd/refclock_irig.c
index 6167af2..bc93fd7 100644
--- a/usr.sbin/xntpd/xntpd/refclock_irig.c
+++ b/usr.sbin/xntpd/xntpd/refclock_irig.c
@@ -1,8 +1,8 @@
/*
* refclock_irig - clock driver for the IRIG audio decoder
*/
+#if defined(REFCLOCK) && defined(IRIG) && defined(sun)
-#if defined(REFCLOCK) && defined(IRIG)
#include <stdio.h>
#include <ctype.h>
#include <sys/time.h>
@@ -19,72 +19,72 @@
* This driver supports the IRIG audio decoder. This clever gadget uses
* a modified BSD audio driver for the Sun SPARCstation which provides
* a timestamp, raw binary timecode, status byte and decoded ASCII
- * timecode. The data are represented in the structure:
+ * timecode. The data are represented in the structure in the
+ * sys/bsd_audioirig.h header file:
*
* struct irig_time {
- * struct timeval stamp; timestamp
- * u_char bits[13]; 100 irig data bits
- * u_char status; status byte
- * char time[14]; time string (null terminated)
+ * struct timeval stamp; timestamp
+ * u_char bits[13]; 100 IRIG data bits
+ * u_char status; status byte
+ * char time[14]; time string (null terminated)
*
* where stamp represents a timestamp at the zero crossing of the index
* marker at the second's epoch, bits is a 13-octet, zero-padded binary-
* coded string representing code elements 1 through 100 in the IRIG-B
- * code format and status is a status bute, The decoded timestamp is a
+ * code format, and status is a status bute, The decoded timestamp is a
* 13-octet, null-terminated ASCII string "ddd hh:mm:ss*", where ddd is
- * the day of year, hh:mm:ss the time of day and * is a status indicator,
- * with " " indicating valid time and "?" indicating invalid time. The
- * timestamp is in unix timeval format, consisting of two 32-bit
- * longwords, the first of which is the seconds since 1970 and the second
- * is the fraction of the second in microseconds. The status byte is zero
+ * the day of year, hh:mm:ss the time of day and * is a status
+ * indicator, with " " indicating valid time and "?" indicating
+ * something wrong.
+ *
+ * The timestamp is in Unix timeval format, consisting of two 32-bit
+ * words, the first of which is the seconds since 1970 and the second is
+ * the fraction of the second in microseconds. The status byte is zero
* if (a) the input signal is within amplitude tolerances, (b) the raw
* binary timecode contains only valid code elements, (c) 11 position
- * identifiers have been found at the expected element positions, (d) the
- * clock status byte contained in the timecode is valid, and (e) a time
- * determination has been made since the last read() system call.
+ * identifiers have been found at the expected element positions, (d)
+ * the clock status byte contained in the timecode is valid, and (e) a
+ * time determination has been made since the last read() system call.
*
* The 100 elements of the IRIG-B timecode are numbered from 0 through
* 99. Position identifiers occur at elements 0, 9, 19 and every ten
- * thereafter to 99. The control function elements begin at element 50,
- * which is control-field element 1, and extend to element 78, which is
- * control-field element 27. The control functions have different
- * interpretations in various devices. The straight-binary-seconds(SBS)
- * field begins at element 80 and is 17 bits long.
+ * thereafter to 99. The control function (CF) elements begin at element
+ * 50 (CF 1) and extend to element 78 (CF 27). The straight-binary-
+ * seconds (SBS) field, which encodes the seconds of the UTC day, begins
+ * at element 80 (CF 28) and extends to element 97 (CF 44). The encoding
+ * of elements 50 (CF 1) through 78 (CF 27) is device dependent. This
+ * driver presently does not use the CF elements.
+ *
+ * Where feasible, the interface should be operated with signature
+ * control, so that, if the IRIG signal is lost or malformed, the
+ * interface produces an unmodulated signal, rather than possibly random
+ * digits. The driver will declare "unsynchronized" in this case.
*
* Spectracom Netclock/2 WWVB Synchronized Clock
- * 6 time sync status
- * 10-13 bcd year units
- * 15-18 bcd year tens
*
- * Austron 2201A GPS Receiver (speculative)
+ * Element CF Function
+ * -------------------------------------
+ * 55 6 time sync status
+ * 60-63 10-13 bcd year units
+ * 65-68 15-18 bcd year tens
+ *
*/
/*
- * Definitions
+ * IRIG interface definitions
*/
-#define MAXUNITS 1 /* max number of irig units */
-#define IRIGFD "/dev/irig" /* name of driver device */
+#define DEVICE "/dev/irig%d" /* device name and unit */
+#define PRECISION (-13) /* precision assumed (100 us) */
+#define REFID "IRIG" /* reference ID */
+#define DESCRIPTION "IRIG Audio Decoder" /* WRU */
-/*
- * IRIG interface parameters.
- */
-#define IRIGPRECISION (-20) /* precision assumed (1 us) */
-#define IRIGREFID "IRIG" /* reference id */
-#define IRIGDESCRIPTION "IRIG audio decoder" /* who we are */
-#define IRIGHSREFID 0x7f7f0c0a /* 127.127.6.10 refid hi strata */
-#define GMT 0 /* hour offset from Greenwich */
+#define NSAMPLES 3 /* stages of median filter */
#define IRIG_FORMAT 1 /* IRIG timestamp format */
-#define BMAX 40 /* length of decoded timecode */
-
-/*
- * Hack to avoid excercising the multiplier. I have no pride.
- */
-#define MULBY10(x) (((x)<<3) + ((x)<<1))
/*
* Imported from ntp_timer module
*/
-extern U_LONG current_time; /* current time (s) */
+extern u_long current_time; /* current time (s) */
/*
* Imported from ntpd module
@@ -92,282 +92,115 @@ extern U_LONG current_time; /* current time (s) */
extern int debug; /* global debug flag */
/*
- * irig unit control structure.
- */
-struct irigunit {
- struct peer *peer; /* associated peer structure */
- struct refclockio io; /* given to the I/O handler */
- l_fp lastrec; /* last local time */
- l_fp lastref; /* last timecode time */
- char lastcode[BMAX]; /* decoded timecode */
- char bincode[BMAX]; /* raw irig message */
- int lencode; /* lengthof last timecode */
- U_LONG lasttime; /* last time clock heard from */
- u_char unit; /* unit number for this guy */
- u_char status; /* clock status */
- u_char lastevent; /* last clock event */
- u_char year; /* year of eternity */
- u_short day; /* day of year */
- u_char hour; /* hour of day */
- u_char minute; /* minute of hour */
- u_char second; /* seconds of minute */
- U_LONG yearstart; /* start of current year */
- u_char leap; /* leap indicators */
- /*
- * Status tallies
- */
- U_LONG polls; /* polls sent */
- U_LONG noreply; /* no replies to polls */
- U_LONG coderecv; /* timecodes received */
- U_LONG badformat; /* bad format */
- U_LONG baddata; /* bad data */
- U_LONG timestarted; /* time we started this */
-};
-
-/*
- * Data space for the unit structures. Note that we allocate these on
- * the fly, but never give them back.
- */
-static struct irigunit *irigunits[MAXUNITS];
-static u_char unitinuse[MAXUNITS];
-
-/*
- * Keep the fudge factors separately so they can be set even
- * when no clock is configured.
- */
-static l_fp fudgefactor[MAXUNITS];
-static u_char stratumtouse[MAXUNITS];
-static u_char sloppyclockflag[MAXUNITS];
-
-/*
* Function prototypes
*/
-static void irig_init P(());
-static int irig_start P((u_int, struct peer *));
-static void irig_shutdown P((int));
-static void irig_report_event P((struct irigunit *, int));
-static void irig_poll P((int, struct peer *));
-static void irig_control P((u_int, struct refclockstat *, struct refclockstat *));
-static void irig_buginfo P((int, struct refclockbug *));
+static int irig_start P((int, struct peer *));
+static void irig_shutdown P((int, struct peer *));
+static void irig_poll P((int, struct peer *));
/*
* Transfer vector
*/
-struct refclock refclock_irig = {
- irig_start, irig_shutdown, irig_poll,
- irig_control, irig_init, irig_buginfo, NOFLAGS
+struct refclock refclock_irig = {
+ irig_start, /* start up driver */
+ irig_shutdown, /* shut down driver */
+ irig_poll, /* transmit poll message */
+ noentry, /* not used (old irig_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old irig_buginfo) */
+ NOFLAGS /* not used */
};
-/*
- * irig_init - initialize internal irig driver data
- */
-static void
-irig_init()
-{
- register int i;
-
- /*
- * Just zero the data arrays
- */
- memset((char *) irigunits, 0, sizeof irigunits);
- memset((char *) unitinuse, 0, sizeof unitinuse);
-
- /*
- * Initialize fudge factors to default.
- */
- for (i = 0; i < MAXUNITS; i++) {
- fudgefactor[i].l_ui = 0;
- fudgefactor[i].l_uf = 0;
- stratumtouse[i] = 0;
- sloppyclockflag[i] = 0;
- }
-}
-
/*
- * irig_start - open the irig device and initialize data for processing
+ * irig_start - open the device and initialize data for processing
*/
static int
irig_start(unit, peer)
- u_int unit;
-struct peer *peer;
+ int unit;
+ struct peer *peer;
{
- register struct irigunit *irig;
- register int i;
- int fd_irig;
- int format = IRIG_FORMAT;
+ register struct refclockproc *pp;
+ char device[20];
+ int fd;
+ int format = IRIG_FORMAT;
/*
- * Check configuration info.
- */
- if (unit >= MAXUNITS) {
- syslog(LOG_ERR, "irig_start: unit %d invalid", unit);
- return (0);
- }
- if (unitinuse[unit]) {
- syslog(LOG_ERR, "irig_start: unit %d in use", unit);
- return (0);
- }
- /*
- * Open IRIG device and set format
+ * Open audio device and set format
*/
- fd_irig = open(IRIGFD, O_RDONLY | O_NDELAY, 0777);
- if (fd_irig == -1) {
- syslog(LOG_ERR, "irig_start: open of %s: %m", IRIGFD);
+ (void)sprintf(device, DEVICE, unit);
+ fd = open(device, O_RDONLY | O_NDELAY, 0777);
+ if (fd == -1) {
+ syslog(LOG_ERR, "irig_start: open of %s: %m", device);
return (0);
}
- if (ioctl(fd_irig, AUDIO_IRIG_OPEN, 0) < 0) {
- syslog(LOG_ERR,
- "irig_start: ioctl(%s, AUDIO_IRIG_OPEN): %m", IRIGFD);
- close(fd_irig);
+ if (ioctl(fd, AUDIO_IRIG_OPEN, 0) < 0) {
+ syslog(LOG_ERR, "irig_start: AUDIO_IRIG_OPEN %m");
+ close(fd);
return (0);
}
- if (ioctl(fd_irig, AUDIO_IRIG_SETFORMAT, (char *) &format) < 0) {
- syslog(LOG_ERR,
- "irig_start: ioctl(%s, AUDIO_IRIG_SETFORMAT): %m", IRIGFD);
- close(fd_irig);
+ if (ioctl(fd, AUDIO_IRIG_SETFORMAT, (char *)&format) < 0) {
+ syslog(LOG_ERR, "irig_start: AUDIO_IRIG_SETFORMAT %m",
+ DEVICE);
+ close(fd);
return (0);
}
+ pp = peer->procptr;
+ pp->io.clock_recv = noentry;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
/*
- * Allocate unit structure
- */
- if (irigunits[unit] != 0) {
- irig = irigunits[unit]; /* The one we want is okay */
- } else {
- for (i = 0; i < MAXUNITS; i++) {
- if (!unitinuse[i] && irigunits[i] != 0)
- break;
- }
- if (i < MAXUNITS) {
- /*
- * Reclaim this one
- */
- irig = irigunits[i];
- irigunits[i] = 0;
- } else {
- irig = (struct irigunit *)
- emalloc(sizeof(struct irigunit));
- }
- }
- memset((char *) irig, 0, sizeof(struct irigunit));
-
- irigunits[unit] = irig;
-
- /*
- * Set up the structures
- */
- irig->peer = peer;
- irig->unit = (u_char) unit;
- irig->timestarted = current_time;
-
- irig->io.clock_recv = noentry;
- irig->io.srcclock = (caddr_t) irig;
- irig->io.datalen = 0;
- irig->io.fd = fd_irig;
-
- /*
- * All done. Initialize a few random peer variables, then
- * return success. Note that root delay and root dispersion are
- * always zero for this clock.
- */
- peer->precision = IRIGPRECISION;
- peer->rootdelay = 0;
- peer->rootdispersion = 0;
- peer->stratum = stratumtouse[unit];
- if (stratumtouse[unit] <= 1)
- memmove((char *) &peer->refid, IRIGREFID, 4);
- else
- peer->refid = htonl(IRIGHSREFID);
- unitinuse[unit] = 1;
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
return (1);
}
-/*
- * irig_shutdown - shut down a irig clock
- */
-static void
-irig_shutdown(unit)
- int unit;
-{
- register struct irigunit *irig;
-
- if (unit >= MAXUNITS) {
- syslog(LOG_ERR, "irig_shutdown: unit %d invalid", unit);
- return;
- }
- if (!unitinuse[unit]) {
- syslog(LOG_ERR, "irig_shutdown: unit %d not in use", unit);
- return;
- }
- /*
- * Tell the I/O module to turn us off. We're history.
- */
- irig = irigunits[unit];
- io_closeclock(&irig->io);
- unitinuse[unit] = 0;
-}
-
/*
- * irig_report_event - note the occurance of an event
- *
- * This routine presently just remembers the report and logs it, but
- * does nothing heroic for the trap handler.
+ * irig_shutdown - shut down the clock
*/
static void
-irig_report_event(irig, code)
- struct irigunit *irig;
-int code;
-{
+irig_shutdown(unit, peer)
+ int unit;
struct peer *peer;
+{
+ struct refclockproc *pp;
- peer = irig->peer;
- if (irig->status != (u_char) code) {
- irig->status = (u_char) code;
- if (code != CEVNT_NOMINAL)
- irig->lastevent = (u_char) code;
- syslog(LOG_INFO,
- "clock %s event %x", ntoa(&peer->srcadr), code);
- }
+ pp = peer->procptr;
+ io_closeclock(&pp->io);
}
+
/*
* irig_poll - called by the transmit procedure
*/
static void
irig_poll(unit, peer)
- int unit;
-struct peer *peer;
+ int unit;
+ struct peer *peer;
{
- struct irigunit *irig;
+ struct refclockproc *pp;
struct irig_time buf;
- register u_char *dpt;
- register char *cp, *dp;
- l_fp tstmp;
- int i;
+ char *cp, *dp;
+ u_char *dpt;
+ int i;
- if (unit >= MAXUNITS) {
- syslog(LOG_ERR, "irig_poll: unit %d invalid", unit);
- return;
- }
- if (!unitinuse[unit]) {
- syslog(LOG_ERR, "irig_poll: unit %d not in use", unit);
- return;
- }
- irig = irigunits[unit];
- irig->polls++;
-
- if (read(irig->io.fd, (char *) &buf, sizeof(buf)) != sizeof(buf)) {
- syslog(LOG_ERR, "irig_poll: unit %d: %m", irig->unit);
- irig_report_event(irig, CEVNT_FAULT);
+ pp = peer->procptr;
+ if (read(pp->io.fd, (char *) &buf, sizeof(buf)) != sizeof(buf)) {
+ refclock_report(peer, CEVNT_FAULT);
return;
}
+ pp->polls++;
#ifdef DEBUG
if (debug) {
- dpt = (u_char *) & buf;
+ dpt = (u_char *)&buf;
printf("irig: ");
for (i = 0; i < sizeof(buf); i++)
printf("%02x", *dpt++);
@@ -375,194 +208,52 @@ struct peer *peer;
}
#endif
- buf.stamp.tv_sec += (U_LONG) JAN_1970;
- TVTOTS(&buf.stamp, &irig->lastrec);
- dpt = buf.bits;
- dp = irig->bincode;
- for (i = 0; i < sizeof(buf.bits); i++) {
- *dp++ = *dpt++;
- }
+ buf.stamp.tv_sec += JAN_1970;
+ TVTOTS(&buf.stamp, &pp->lastrec);
cp = buf.time;
- dp = irig->lastcode;
+ dp = pp->lastcode;
for (i = 0; i < sizeof(buf.time); i++)
*dp++ = *cp++;
- dp--;
- *dp = '\0';
- cp = irig->lastcode;
- irig->lencode = dp - cp;
+ *--dp = '\0';
+ pp->lencode = dp - pp->lastcode;
#ifdef DEBUG
if (debug)
- printf("irig: timecode %d %s %s\n",
- irig->lencode, ulfptoa(&irig->lastrec, 6), irig->lastcode);
+ printf("irig: time %s timecode %d %s\n",
+ ulfptoa(&pp->lastrec, 6), pp->lencode,
+ pp->lastcode);
#endif
-
- irig->lasttime = current_time;
+ record_clock_stats(&peer->srcadr, pp->lastcode);
/*
- * Get irig time and convert to timestamp format.
+ * Get IRIG time and convert to timestamp format
*/
- if (irig->lencode < 13 || !isdigit(cp[0]) || !isdigit(cp[1]) ||
- !isdigit(cp[2]) ||
- cp[3] != ' ' || !isdigit(cp[4]) || !isdigit(cp[5]) ||
- cp[6] != ':' || !isdigit(cp[7]) || !isdigit(cp[8]) ||
- cp[9] != ':' || !isdigit(cp[10]) || !isdigit(cp[11])) {
- irig->badformat++;
- irig_report_event(irig, CEVNT_BADREPLY);
- return;
- }
- record_clock_stats(&(irig->peer->srcadr), irig->lastcode);
- irig->day = cp[0] - '0';
- irig->day = MULBY10(irig->day) + cp[1] - '0';
- irig->day = MULBY10(irig->day) + cp[2] - '0';
- irig->hour = MULBY10(cp[4] - '0') + cp[5] - '0';
- irig->minute = MULBY10(cp[7] - '0') + cp[8] - '0';
- irig->second = MULBY10(cp[10] - '0') + cp[11] - '0';
- if (cp[12] = ' ')
- irig->leap = 0;
- else
- irig->leap = LEAP_NOTINSYNC;
- if (irig->day < 1 || irig->day > 366) {
- irig->baddata++;
- irig_report_event(irig, CEVNT_BADDATE);
+ if (sscanf(pp->lastcode, "%3d %2d:%2d:%2d",
+ &pp->day, &pp->hour, &pp->minute, &pp->second) != 4) {
+ refclock_report(peer, CEVNT_BADREPLY);
return;
}
- if (irig->hour > 23 || irig->minute > 59 || irig->second > 59) {
- irig->baddata++;
- irig_report_event(irig, CEVNT_BADTIME);
- return;
+ if (pp->lastcode[12] != ' ') {
+ pp->leap = LEAP_NOTINSYNC;
+ } else {
+ pp->leap = 0;
+ pp->lasttime = current_time;
}
/*
- * Now, compute the reference time value. Use the heavy
- * machinery for the seconds and the millisecond field for the
- * fraction when present. If an error in conversion to internal
- * format is found, the program declares bad data and exits.
- * Note that this code does not yet know how to do the years and
- * relies on the clock-calendar chip for sanity.
- */
- if (!clocktime(irig->day, irig->hour, irig->minute,
- irig->second, GMT, irig->lastrec.l_ui,
- &irig->yearstart, &irig->lastref.l_ui)) {
- irig->baddata++;
- irig_report_event(irig, CEVNT_BADTIME);
- return;
- }
- tstmp = irig->lastref;
- L_SUB(&tstmp, &irig->lastrec);
- irig->coderecv++;
- L_ADD(&tstmp, &(fudgefactor[irig->unit]));
- refclock_receive(irig->peer, &tstmp, GMT, 0,
- &irig->lastrec, &irig->lastrec, irig->leap);
-}
-
-/*
- * irig_control - set fudge factors, return statistics
- */
-static void
-irig_control(unit, in, out)
- u_int unit;
- struct refclockstat *in;
- struct refclockstat *out;
-{
- register struct irigunit *irig;
-
- if (unit >= MAXUNITS) {
- syslog(LOG_ERR, "irig_control: unit %d invalid", unit);
- return;
- }
- if (in != 0) {
- if (in->haveflags & CLK_HAVETIME1)
- fudgefactor[unit] = in->fudgetime1;
- if (in->haveflags & CLK_HAVEVAL1) {
- stratumtouse[unit] = (u_char) (in->fudgeval1 & 0xf);
- if (unitinuse[unit]) {
- struct peer *peer;
-
- /*
- * Should actually reselect clock, but
- * will wait for the next timecode
- */
- irig = irigunits[unit];
- peer = irig->peer;
- peer->stratum = stratumtouse[unit];
- if (stratumtouse[unit] <= 1)
- memmove((char *) &peer->refid,
- IRIGREFID, 4);
- else
- peer->refid = htonl(IRIGHSREFID);
- }
- }
- if (in->haveflags & CLK_HAVEFLAG1) {
- sloppyclockflag[unit] = in->flags & CLK_FLAG1;
- }
- }
- if (out != 0) {
- out->type = REFCLK_IRIG_AUDIO;
- out->haveflags
- = CLK_HAVETIME1 | CLK_HAVEVAL1 | CLK_HAVEVAL2 | CLK_HAVEFLAG1;
- out->clockdesc = IRIGDESCRIPTION;
- out->fudgetime1 = fudgefactor[unit];
- out->fudgetime2.l_ui = 0;
- out->fudgetime2.l_uf = 0;
- out->fudgeval1 = (LONG) stratumtouse[unit];
- out->fudgeval2 = 0;
- out->flags = sloppyclockflag[unit];
- if (unitinuse[unit]) {
- irig = irigunits[unit];
- out->lencode = irig->lencode;
- out->lastcode = irig->lastcode;
- out->timereset = current_time - irig->timestarted;
- out->polls = irig->polls;
- out->noresponse = irig->noreply;
- out->badformat = irig->badformat;
- out->baddata = irig->baddata;
- out->lastevent = irig->lastevent;
- out->currentstatus = irig->status;
- } else {
- out->lencode = 0;
- out->lastcode = "";
- out->polls = out->noresponse = 0;
- out->badformat = out->baddata = 0;
- out->timereset = 0;
- out->currentstatus = out->lastevent = CEVNT_NOMINAL;
- }
- }
-}
-
-/*
- * irig_buginfo - return clock dependent debugging info
- */
-static void
-irig_buginfo(unit, bug)
- int unit;
- register struct refclockbug *bug;
-{
- register struct irigunit *irig;
-
- if (unit >= MAXUNITS) {
- syslog(LOG_ERR, "irig_buginfo: unit %d invalid", unit);
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion. We use lastrec as both
+ * the reference time and receive time in order to avoid being
+ * cute, like setting the reference time later than the receive
+ * time, which may cause a paranoid protocol module to chuck out
+ * the data.
+ */
+ if (!refclock_process(pp, NSAMPLES, NSAMPLES)) {
+ refclock_report(peer, CEVNT_BADTIME);
return;
}
- if (!unitinuse[unit])
- return;
- irig = irigunits[unit];
-
- bug->nvalues = 8;
- bug->ntimes = 2;
- if (irig->lasttime != 0)
- bug->values[0] = current_time - irig->lasttime;
- else
- bug->values[0] = 0;
- bug->values[2] = (U_LONG) irig->year;
- bug->values[3] = (U_LONG) irig->day;
- bug->values[4] = (U_LONG) irig->hour;
- bug->values[5] = (U_LONG) irig->minute;
- bug->values[6] = (U_LONG) irig->second;
- bug->values[7] = irig->yearstart;
- bug->stimes = 0x1c;
- bug->times[0] = irig->lastref;
- bug->times[1] = irig->lastrec;
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion,
+ &pp->lastrec, &pp->lastrec, pp->leap);
}
#endif
OpenPOWER on IntegriCloud