diff options
Diffstat (limited to 'recipes/sccd/files')
-rw-r--r-- | recipes/sccd/files/Makefile | 52 | ||||
-rw-r--r-- | recipes/sccd/files/README | 220 | ||||
-rw-r--r-- | recipes/sccd/files/init-sccd | 38 | ||||
-rw-r--r-- | recipes/sccd/files/scc-disk.c | 167 | ||||
-rw-r--r-- | recipes/sccd/files/scc-utils.c | 376 | ||||
-rw-r--r-- | recipes/sccd/files/scc.1 | 120 | ||||
-rw-r--r-- | recipes/sccd/files/scc.c | 412 | ||||
-rw-r--r-- | recipes/sccd/files/scc.h | 118 | ||||
-rw-r--r-- | recipes/sccd/files/sccd.c | 523 |
9 files changed, 2026 insertions, 0 deletions
diff --git a/recipes/sccd/files/Makefile b/recipes/sccd/files/Makefile new file mode 100644 index 0000000000..650404643d --- /dev/null +++ b/recipes/sccd/files/Makefile @@ -0,0 +1,52 @@ +# +# Copyright (c) 2006 +# Protium Computing, Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. 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. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by Protium Computing, Inc. +# 4. The name of Protium Computing, Inc. may not be used to endorse or +# promote products derived from this software without specific prior +# written permission. +# +# THIS SOFTWARE IS PROVIDED BY PROTIUM COMPUTING ``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 PROTIUM COMPUTING 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. +# +TARGETS=sccd scc + +all: $(TARGETS) + +sccd: scc-disk.o scc-utils.o sccd.o + $(CC) $(CFLAGS) -o sccd scc-utils.o scc-disk.o sccd.o + +scc: scc-utils.o scc.o + $(CC) $(CFLAGS) -o scc scc-utils.o scc.o + +install: $(TARGETS) + install -d $(DESTDIR)/sbin + install -m 755 sccd $(DESTDIR)/sbin/sccd + install -m 755 scc $(DESTDIR)/sbin/scc + install -d $(DESTDIR)/etc/init.d + install -m 755 init-sccd $(DESTDIR)/etc/init.d/sccd + install -d $(DESTDIR)/usr/share/man/man1 + install -m 644 scc.1 $(DESTDIR)/usr/share/man/man1/scc.1 + +clean: + rm -f $(TARGETS) *.o core diff --git a/recipes/sccd/files/README b/recipes/sccd/files/README new file mode 100644 index 0000000000..d2d1934dd4 --- /dev/null +++ b/recipes/sccd/files/README @@ -0,0 +1,220 @@ + Iomega G2 MC68HC908QY4 Support Notes + October 2006 + +The Iomega G2 Version 3.x and 5.x boards have a secondary microcontroller +aboard, a Freescale MC68HC908QY4. This microcontroller is connected to the +board's LED, fan, digital thermometer, power switch, and main processor, the +MPC8241. The connection to the main processor is through the 8241's second +UART and possibly its reset lines. + +The microcontroller can perform the following functions: + o The LED can be set to off, blue, red, blue flash, red flash, + alternate (blue->red->blue) and some boards alternate3 + (blue->blue->blue->red->red->red). The flash and alernate rates + can be be set. Alternate3 rate is fixed. + o Fan can be set to on or auto. Auto mode is a thermostat function + that turns the fan on and off based on two temprature settings: + Fan Temp High and Fan Temp Low. + o The system can be reset, causing a MPC8241 reset, or stopped, + causing a full power down. + o The microcontroller can detect if the soft power switch has been + activated. There appears to a 20s delay after the power switch + has been depressed before the microcontroller causes an actual + power off. This event can be detected by polling the + microcontroller. + +The running system communicates with microcontroller and ultimately controls +the devices connected to it via the MPC8241's second UART. The connection +settings are: 9600,8,n,1. The serial protocol is very simple, an alternating +send and receive of data packets. Communication is done in 8 byte data packets +and is initiated by the host processor. Once the host processor has sent 8 +bytes, the microcontroller responds with 8 bytes. The packet contains bytes +that can affect the state of the power, led and fan. It also contains bits +reflecting external soft power events. Both packets sent and received follow +the same structure. + +The 8 bytes are decoded as follows: + + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Power | LED | LED | Fan | + | State | State | Flash Rate | State | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | Fan High Temp | Fan Low Temp | | | + | ON | OFF | ID | CheckSum | + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + + Hex Ascii Comments + --- ----- -------------------------------------------------- +Power State: + 0x62 b Running, nominal running state + 0x63 c Stop, power down immediately (ID == HOST) OR + Stop, the power switch has been activated, stop in 20s + (ID == CONTROLLERxx) OR + Stop, the host has advised Stop + (ID == CONTROLLERxx) + 0x64 d Advise Stop, the host is annoucing its intention + to stop (ID == HOST) + 0x65 e Restart, restart immediately (ID == HOST) OR + Restart, the host has advised Restart + (ID == CONTROLLERxx) + 0x66 f Advise Restart, the host is annoucing its intention + to restart (ID == HOST) + 0x67 g Reset, reset immediately (ID == HOST) OR + Reset, the host has advised Reset + (ID == CONTROLLERxx) + 0x68 h Advise Reset, the host is annoucing its intention + to reset (ID == HOST) + +Except for Running, the Power states are grouped in pairs: Advise [state] and +[state], where state can be: Stop, Restart, or Reset. The Advise state lets +the controller know that there is an intention to either stop, restart or +reset the board. The controller responds by moving to that state and setting +the LED to redflash, but it does not enact the power change. Instead it waits +for the host to issue the actual state before performing the power commands. +For example, assume the microcontroller is reporting the power state to be +[Running, LED Blue]. After the host issues a Advise Stop, the microcontroller +reports [Stop, LED Redflash] and then waits, indefinitely. The Host then +issues a Stop, the microcontroller immediately turns the power off. + +There is nothing in the protocol that requires the Advise [state] packets. The +Host can issue a Stop, Restart, or Reset directly from the Running state and +the microcontroller will immediately enact the change. + +There is nothing in the protocol that forbids moving back to Running from the +Stop, Restart or Reset states, i.e. undoing an Advise [state] packet. + +The exception to that rule is in the Stop state. The Stop state can be reached +by either issuing a Advise Stop or from a softpower switch activation (someone +hits the power switch). If the latter, then a request to move to any other +state is ignored and the power will go off in approximately 20s. + +NOTE 1: There does not appear to be any difference in processing the Restart +and Reset state changes. They both deliver a system restart. + +NOTE 2: Obvious absence of an 'a' state. Both the LED and Fan controls use 'a' +as a state, but Power starts with 'b'. No evidence has shown 'a' to be a +valid state. + +LED State: + 0x61 a Off + 0x62 b Blue + 0x63 c Red + 0x64 d Blue Flash + 0x65 e Red Flash + 0x66 f Alternate1 Blue/Red + 0x67 g Alternate3 3 Blue/3 Red (only on version 5.x boards) + +LED States are self explanatory with one exception. Although state 'g' or +Alternate3 has been observed, it only seems to function on IOMEGA G2 Version +5.x boards and does not respond to rate changes. + +LED Flash Rate: +The flash rate seems to be 1/x seconds on, then 1/x seconds off. So one cycle +of off and on at an LED Flash rate of 1 is almost 2s. The value spans 8 bits +but functionally 0 is off and 36-40 is on. Testing has shown an oddity, the +value 35 is rejected by the microcontroller software and is not set. + +Fan State: + 0x61 a Auto thermostat function + 0x62 b On always on + +Fan High Temp: +High Temp is the temprature in Celsius where the fan is be turned on. + +Fan Low Temp: +Low Temp is the temprature in Celsius where the fan is be turned off. + +ID: +The real purpose of this field is not known. It is known that it is a constant +depending upon the direction of the packets and the board rev. It has been +designated ID as a place holder but it could as easily be defined as version +or it may be something else entirely. + + 0x00 DC2 CONTROLLER00 + IOMEGA G2 Version 5.x Controller ID (recv'd packets) + 0x07 BEL HOST (sent packets) + 0x12 DC2 CONTROLLER12 + IOMEGA G2 Version 3.x Controller ID (recv'd packets) + +Checksum: +The check sum is an 8 bit sum of the first 7 bytes with the most significant +bit cleared. + + Sum = ((b1 + b2 + b3 +b4 +b5 +b6 + b7) & 0x7f) + +The bytes and their meanings were determined by watching the serial port +chatter during specific events such as LED Blue, LED Red, Fan ON, etc. So only +events observed have been decoded. Although all the significant events were +observed and decoded, there are some holes in the understanding of the +protocol. + +There is one special packet that causes the microcontroller to reset. This +packet is: + 0x23696f6d 0x65676115 + (or as a string "#iomega\025") + +The resulting packet from the microcontroller is: + 0x62000000 0x00000062 + +Testing also yielded that a null packet will cause the microcontroller to +feed back the current state. So this packet: + 0x00000000 0x00000000 + +will yield something like this (this is the default state): + 0x62620a61 0x322d1220 + +The default state when the board powers on is: + State: Run + LED: Red + LED Rate: 10 + Fan: On + Fan Temp High: 50C + Fan Temp Low: 45C + +Below is some of the data collected while watching the serial port chatter: + +w: 0x23696f6d 0x65676115 #iomega\025 +r: 0x62000000 0x00000062 b\0\0\0\0\0\0b +w: 0x62630a61 0x322d0716 bc\na2-\7\26 red state +r: 0x62630a61 0x322d1221 bc\na2-\22! +w: 0x62641161 0x322d071e bd\21a2-\7\36 blue flash +r: 0x62641161 0x322d1229 bd\21a2-\22) +w: 0x62641161 0x322d071e bd\21a2-\7\36 blue flash + +LED ok +w: 0x62620a61 0x322d0715 bb\na2-\7\25 +r: 0x62630a61 0x322d1220 bb\na2-\22 + +LED alt +w: 0x626f0a61 0x322d0719 bf\na2-\7\31 +r: 0x626f0a61 0x322d1224 bf\na2-\22$ + +reset +w: 0x68620a61 0x322d071b hb\na2-\7\33 reset +r: 0x67620a61 0x322d0013 gb\na2-\0\23 resetting + +restart +w: 0x66620a61 0x322d0719 fb\na2-\7\31 restart +r: 0x65620a61 0x322d1223 eb\na2-\22# restarting + +shutdown +w: 0x64620a61 0x322d0717 db\na2-\7\27 shutdown +r: 0x63620a61 0x322d1221 cb\na2-\22! power shutting down + +soft Power +r: 0x63620a61 0x322d000f cb\na2-\0\17 power shutting down + + + + + + + + + + + + + diff --git a/recipes/sccd/files/init-sccd b/recipes/sccd/files/init-sccd new file mode 100644 index 0000000000..372bfeae91 --- /dev/null +++ b/recipes/sccd/files/init-sccd @@ -0,0 +1,38 @@ +#! /bin/sh +# +# This is an init script for open protium for storcenter +# Copy it to /etc/init.d/sccd and type +# > update-rc.d sccd defaults 60 +# +sccd=/sbin/sccd +test -x "$sccd" || exit 0 + +case "$1" in + start) + echo -n "Starting StorCenter Control Daemon" + start-stop-daemon --start --quiet --exec $sccd + echo "." + ;; + stop) + echo -n "Stopping StorCenter Control Daemon" + start-stop-daemon --stop --quiet --pidfile /var/run/sccd.pid + echo "." + ;; + reload|force-reload) + start-stop-daemon --stop --quiet --signal 1 --exec $sccd + ;; + restart) + echo -n "Stopping StorCenter Control Daemon" + start-stop-daemon --stop --quiet --pidfile /var/run/sccd.pid + echo "." + sleep 1 + echo -n "Starting StorCenter Control Daemon" + start-stop-daemon --start --quiet --exec $sccd + echo "." + ;; + *) + echo "Usage: /etc/init.d/sccd {start|stop|reload|restart|force-reload}" + exit 1 +esac + +exit 0 diff --git a/recipes/sccd/files/scc-disk.c b/recipes/sccd/files/scc-disk.c new file mode 100644 index 0000000000..4fd494203a --- /dev/null +++ b/recipes/sccd/files/scc-disk.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2006 + * Protium Computing, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Protium Computing, Inc. + * 4. The name of Protium Computing, Inc. may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY PROTIUM COMPUTING ``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 PROTIUM COMPUTING 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 <stdio.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/un.h> +#include <sys/syslog.h> +#include <sys/param.h> + +struct watch_table { + char *wt_path; + int wt_status; + int wt_reads; + int wt_writes; +#define WT_INVALID 0 +#define WT_VALID 1 +#define WT_CHECK 2 +} iowt[] = { + { "/sys/block/hda/stat", WT_CHECK, 0, 0}, + { "/sys/block/hdb/stat", WT_CHECK, 0, 0}, + { "/sys/block/hdc/stat", WT_CHECK, 0, 0}, + { "/sys/block/hdd/stat", WT_CHECK, 0, 0}, + { NULL, WT_INVALID, -1, -1} +}; + +/* + * disk_activity() returns back the total number of read and writes done + * by the devices listed in the io watch table since the last call to + * disk_activity. The number of reads and writes are determined by + * opening and reading each of the valid files listed in the io watch + * table.The number of reads and writes are stored in the io watch table so + * that the next call to this routine can compare the current number of read + * and writes to those stored in the io watch table. The difference for + * each device is summed in the activity variable. The routine lazy + * evaluates the existance of the devices in the table. + * + * For the storcenter, we are only concerned with internal drives. + * + * The wt_path element must point at a diskstat file in the sysfs filesystem. + * File format can be found at: /usr/src/linux/Documentation/iostats.txt + * For this routine: + * nr, nw - number read and writes + * nmr, nmw - number of merged reads and writes + * nsr, nsw - number of the sectors read and written + * tr, tw - time spent reading and writing + * nio - raw number of ios + * tio, wtio - time spent and weighted time spent doing io + */ +int +disk_activity() +{ + int activity = 0; + char mesg[256]; + int rc, nr, nmr, nsr, tr, nw, nmw, nsw, tw, nio, tio, wtio; + FILE *f; + + struct stat st; + struct watch_table *w; + + for (w = iowt; w->wt_path; w++) { + /* + * If status ids set to check, do the lazy existence + * evaluation. If stat fails set to invalid. Don't + * worry about perms here. + */ + if ((w->wt_status == WT_CHECK) && + (stat(w->wt_path, &st) < 0)) { + sprintf(mesg, "%s not available", w->wt_path); + syslog(LOG_INFO, mesg); + w->wt_status = WT_INVALID; + } + + /* + * Short circuit the loop if invalid. + */ + if (w->wt_status == WT_INVALID) + continue; + + /* + * If it can't be opened rdonly, set to invalid + */ + if ((f = fopen(w->wt_path, "r")) < 0) { + sprintf(mesg, "Unable to open %s, no longer watching", + w->wt_path); + syslog(LOG_INFO, mesg); + w->wt_status = WT_INVALID; + continue; + } + + rc = fscanf(f, "%d %d %d %d %d %d %d %d %d %d %d", + &nr, &nmr, &nsr, &tr, + &nw, &nmw, &nsw, &tw, + &nio, &tio, &wtio); + + fclose(f); + + if (rc != 11) { + sprintf(mesg, "Unable to read %s", w->wt_path); + syslog(LOG_INFO, mesg); + continue; + } + + /* + * If we haven't seen any activity on this device before + * then just save the values and go on. This, although + * not strictly necessary, prevents the initial call to + * disk_activity returning back the base set io activity. + * Remember it takes two calls to get a true difference. + */ + if ((w->wt_reads == 0) && (w->wt_writes == 0)) { + w->wt_reads = nr; + w->wt_writes = nw; + continue; + } + + activity += (nr - w->wt_reads); + activity += (nw - w->wt_writes); + + w->wt_reads = nr; + w->wt_writes = nw; + + /* + printf("%s: %d %d %d %d %d %d %d %d %d %d %d\n", + w->wt_path, + nr, nmr, nsr, tr, + nw, nmw, nsw, tw, + nio, tio, wtio); + */ + } + + return(activity); +} diff --git a/recipes/sccd/files/scc-utils.c b/recipes/sccd/files/scc-utils.c new file mode 100644 index 0000000000..ab48ecb677 --- /dev/null +++ b/recipes/sccd/files/scc-utils.c @@ -0,0 +1,376 @@ +/* + * Copyright (c) 2006 + * Protium Computing, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Protium Computing, Inc. + * 4. The name of Protium Computing, Inc. may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY PROTIUM COMPUTING ``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 PROTIUM COMPUTING 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 <stdio.h> +#include <fcntl.h> +#include <time.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/select.h> +#include <sys/syslog.h> +#include <sys/resource.h> +#include <termios.h> + +#include "scc.h" + +int scc_die = 0; + +char +scc_cksum(int *i, int *j) +{ + unsigned char s; + unsigned char *b; + + b = (unsigned char *)i; + s = b[0] + b[1] + b[2] + b[3]; + b = (unsigned char *)j; + s = s + b[0] + b[1] + b[2]; + s = (s & 0x7f); + + return((char)s); +} + +int +scc_validate(int *i, int *j, int verbose) +{ + int r, s; + + /* if (verbose) printf("Packet: [0x%08x 0x%08x]\n", *i, *j); */ + + switch (*i & SCC_POWERMASK) { + case SCC_RUN: + if (verbose) printf("\tPower: run\n"); + break; + case SCC_STOP: + if (verbose) printf("\tPower: stop\n"); + break; + case SCC_ADVISESTOP: + if (verbose) printf("\tPower: advise stop\n"); + break; + case SCC_RESTART: + if (verbose) printf("\tPower: restart\n"); + break; + case SCC_ADVISERESTART: + if (verbose) printf("\tPower: advise restart\n"); + break; + case SCC_RESET: + if (verbose) printf("\tPower: reset\n"); + break; + case SCC_ADVISERESET: + if (verbose) printf("\tPower: advise reset\n"); + break; + default: + if (verbose) printf("validate: bad power 0x%08x\n", *i); + return(-1); + } + + switch (*i & SCC_LEDMASK) { + case SCC_LEDOFF: + if (verbose) printf("\tLed: off\n"); + break; + case SCC_LEDBLUE: + if (verbose) printf("\tLed: blue\n"); + break; + case SCC_LEDRED: + if (verbose) printf("\tLed: red\n"); + break; + case SCC_LEDBLUEFLASH: + if (verbose) printf("\tLed: blue flash\n"); + break; + case SCC_LEDREDFLASH: + if (verbose) printf("\tLed: red flash\n"); + break; + case SCC_LEDALTERNATE1: + if (verbose) printf("\tLed: alternate1\n"); + break; + case SCC_LEDALTERNATE3: + if (verbose) printf("\tLed: alternate3\n"); + break; + default: + if (verbose) printf("validate: bad led value 0x%08x\n", *i); + return(-1); + } + + r = ((*i & SCC_LEDRATEMASK) >> SCC_LEDRATESHIFT); + + if (verbose) { + printf("\tLed Flash Rate: %d\n", r); + } + + if (r >= SCC_LEDRATEHI) + printf("warning: led rate too high - led on (%d)\n", r); + if (r <= SCC_LEDRATELO) + printf("warning: led rate too low - led off (%d)\n", r); + + + switch (*i & SCC_FANMASK) { + case SCC_FANAUTO: + if (verbose) printf("\tFan: auto\n"); + break; + case SCC_FANON: + if (verbose) printf("\tFan: on\n"); + break; + default: + if (verbose) printf("validate: bad fan value 0x%08x\n", *i); + return(-1); + } + + r = ((*j & SCC_FANTEMPONMASK) >> SCC_FANTEMPONSHIFT); + s = ((*j & SCC_FANTEMPOFFMASK) >> SCC_FANTEMPOFFSHIFT); + + if (verbose) { + printf("\tFan On Temprature: %dC\n", r); + printf("\tFan Off Temprature: %dC\n", s); + } + + if (r >= SCC_FANTEMPONHI) + printf("warning: fan on temp too high - fan off (%dC)\n", r); + if (s <= SCC_FANTEMPONLO) + printf("warning: fan on temp too low - fan on (%dC)\n", s); + if ((r - s) < SCC_FANTEMPDIFF) { + printf("warning: fan on/off temp too close or on < off \n"); + printf("warning:\tfan on (%dC) fan off (%dC) \n", r, s); + } + + r = *j & SCC_IDMASK; + if ((r != SCC_HOST) && + (r != SCC_CTLR00) && + (r != SCC_CTLR12)) { + printf("warning: bad id %08x\n", r); + } + + r = *j & SCC_CKSUMMASK; + if (r != scc_cksum(i, j)) { + printf("warning: checksum incorrect (%02x != %02x)\n", + r, scc_cksum(i, j)); + } + return(0); +} + +int +scc_setval(int *i, int mask, int val) +{ + /* clear the field */ + *i = (*i & (mask ^ 0xffffffff)); + + /* insert the field */ + *i = (*i ^ val); + return(0); +} + +int +scc_setrate(int *i, int mask, int shift, int val) +{ + /* Assumption: val is < field width */ + + /* clear the field */ + *i = (*i & (mask ^ 0xffffffff)); + + /* insert the field */ + *i = (*i ^ (val << shift)); + return(0); +} + +int +scc_defaults(int *i, int *j) +{ + scc_setval(i, SCC_POWERMASK, SCC_POWERDEFAULT); + scc_setval(i, SCC_LEDMASK, SCC_LEDDEFAULT); + scc_setrate(i, SCC_LEDRATEMASK, SCC_LEDRATESHIFT, SCC_LEDRATEDEFAULT); + scc_setval(i, SCC_FANMASK, SCC_FANDEFAULT); + + scc_setrate(j, SCC_FANTEMPONMASK, + SCC_FANTEMPONSHIFT, SCC_FANTEMPONDEFAULT); + scc_setrate(j, SCC_FANTEMPOFFMASK, + SCC_FANTEMPOFFSHIFT, SCC_FANTEMPOFFDEFAULT); + return(0); +} + +void +scc_sighandler(int sig) +{ + /* + * Just catch quit and term and set die + */ + switch(sig) { + case SIGQUIT: + case SIGTERM: + syslog(LOG_INFO, "Terminating"); + scc_die=1; + break; + } +} + +int +scc_daemonize(int cores) +{ + int fd, i; + char pidstr[20]; + pid_t pid; + + struct rlimit limit[1] = {{ 0, 0 }}; + + + if (!cores) { + /* + * No corefiles please + */ + if (getrlimit(RLIMIT_CORE, limit) == -1) { + perror("getrlimit"); + return -1; + } + + limit->rlim_cur = 0; + + if (setrlimit(RLIMIT_CORE, limit) != 0) { + perror("setrlimit"); + return -1; + } + } + + /* + * Must be root + */ + if (getuid() != 0) { + fprintf(stderr, "Must be root\n"); + return -1; + } + + /* + * If parent isn't init, make it init. + */ + if (getppid() != 1) { + + pid = fork(); + if (pid == -1) { + return -1; + } + if (pid != 0) { + exit(EXIT_SUCCESS); + } + + setsid(); + + /* + * ignore sig hup so that when our new padre exits + * the kinder won't exit + */ + signal(SIGHUP, SIG_IGN); + + pid = fork(); + if (pid == -1) { + return -1; + } + if (pid != 0) { + exit(EXIT_SUCCESS); + } + } + + /* + * Set working dir to root + */ + chdir("/"); + + /* + * Make sure file creations are created with explicit perms + */ + umask(0); + + /* + * Close all FDs + */ + for (i = 0; i < sysconf(_SC_OPEN_MAX); i++) { + close(i); + } + + /* + * set std in, out, err to /dev/null + */ + for (i = 0; i < 3; i++) { + + if ((fd = open("/dev/null", (i==0?O_RDONLY:O_WRONLY))) == -1) { + perror("setting up stdin, stdout, stderr"); + return -1; + } + + if (i != fd) { + if (dup2(fd, i) == -1) { + perror("setting up stdin, stdout, stderr"); + return -1; + } + close(fd); + } + } + + /* + * Open and lock the pid file. Ensures only one daemon + * Write our pid into the file + */ + fd = open(SCC_PIDFILE, O_RDWR|O_CREAT, 0640); + if (fd < 0) { + return(-1); + + } + + if (lockf(fd, F_TLOCK, 0) < 0) { + /* we are not alone */ + close(fd); + return(-1); + } + + sprintf(pidstr,"%-6d\n", getpid()); + write(fd, pidstr, strlen(pidstr)); + + /* + * Open syslog + */ + openlog("sccd", LOG_PID, LOG_DAEMON); + syslog(LOG_INFO, "Starting"); + + /* + * Ignore some signals and handle others + */ + signal(SIGCHLD, SIG_IGN); + signal(SIGTSTP, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + signal(SIGTTIN, SIG_IGN); + signal(SIGQUIT, scc_sighandler); + signal(SIGTERM, scc_sighandler); + + return(0); +} + + diff --git a/recipes/sccd/files/scc.1 b/recipes/sccd/files/scc.1 new file mode 100644 index 0000000000..be27a569c3 --- /dev/null +++ b/recipes/sccd/files/scc.1 @@ -0,0 +1,120 @@ +.\" Copyright (c) 2006 +.\" Protium Computing, Inc. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. 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. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Protium Computing, Inc. +.\" 4. The name of Protium Computing, Inc. may not be used to endorse or +.\" promote products derived from this software without specific prior +.\" written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY PROTIUM COMPUTING ``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 PROTIUM COMPUTING 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. +.\" +.TH SCC "1" "November 2006" "scc 1.0" "User Commands" +.SH NAME +scc \- StorCenter Control Utility +.SH SYNOPSIS +.B scc +\fI[OPTIONS]\fR ... +.SH DESCRIPTION +scc controls the Iomega StorCenter's LED, fan and soft power. It does this +either by sending a message to the StorCenter Control Daemon, if available, +or directly communicating with the StorCenter's microcontroller responsible +for these devices. +.PP +The scc utility can perform the following functions: +.TP +\fB\fR +The LED can be set to off, blue, red, blue flash, red flash, +alternate (blue->red->blue) and on some boards alternate3 +(blue->blue->blue->red->red->red). The flash and alernate rates +can be be set. Alternate3 rate is fixed. +.TP +\fB\fR +Fan can be set to on or auto. Auto mode is a thermostat function +that turns the fan on and off based on two temprature settings: +Fan Temp High and Fan Temp Low. +.TP +\fB\fR +The system can be reset, causing a StorCenter reset, or stopped, +causing a full power down. +.SH OPTIONS +.TP +\fB\-?\fR +display this help and exit +.TP +\fB\-d\fR +send send directly, stop/restart only +.TP +\fB\-p POWER\fR +control soft power +.br +POWER=[run|off|restart] +.TP +\fB\-l LED\fR +control LED state +.br +LED=[off|blue|red|blueflash|redflash|alternate|alternate3] +.TP +\fB\-r RATE\fR +Rate the LED flashes (0<=RATE<256) +.TP +\fB\-f FAN\fR +control fan operation +.br +FAN=[auto|on] +.TP +\fB\-h TEMP\fR +Hot temp when FAN=auto should go on (0<=TEMP<256) +.TP +\fB\-c TEMP\fR +Cold temp when FAN=auto should go off (0<=TEMP<256) +.SH NOTES +Care should be taken when changing the LED. The boot scripts and the +.B sccd +daemon use LED colors to indicate boot and disk activity. Changing the LED can +cause some of these functions to be disabled. +.SH AUTHOR +Written by Philip Kufeldt. +.SH "REPORTING BUGS" +Report bugs at www.openprotium.org. +.SH COPYRIGHT +Copyright \(co 2006 Protium Computing, Inc. All rights reserved. +.br +This is free software. Redistribution and use in source and binary forms, +with or without modification, are permitted provided that the following +conditions are met: +.IP +Redistributions of source code must retain the above copyright notice,this list of conditions and the following disclaimer. +.br +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. +.br +All advertising materials mentioning features or use of this software must display the following acknowledgement: +.br + This product includes software developed by Protium Computing, Inc. +.br +The name of Protium Computing, Inc. may not be used to endorse or promote products derived from this software without specific prior written permission. +.PP +.SH "SEE ALSO" +The full documentation for +.B scc +can be found at www.openprotium.org + diff --git a/recipes/sccd/files/scc.c b/recipes/sccd/files/scc.c new file mode 100644 index 0000000000..b917bf1ee8 --- /dev/null +++ b/recipes/sccd/files/scc.c @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2006 + * Protium Computing, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Protium Computing, Inc. + * 4. The name of Protium Computing, Inc. may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY PROTIUM COMPUTING ``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 PROTIUM COMPUTING 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 <stdio.h> +#include <fcntl.h> +#include <time.h> +#include <unistd.h> +#include <stdlib.h> +#include <strings.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/select.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <termios.h> + +#include "scc.h" + +char * prog; +int initialize = 0; +int verbose = 0; + +scc_cmd_t powercmds[] = { + { "a", 0x61000000}, + { "run", SCC_RUN}, + { "off", SCC_STOP}, + { "advoff", SCC_ADVISESTOP}, + { "restart", SCC_RESTART,}, + { "advrestart", SCC_ADVISERESTART}, + { "reset", SCC_RESET,}, + { "advreset", SCC_ADVISERESET}, + { "EOT", -1}}; + +scc_cmd_t ledcmds[] = { + { "off", SCC_LEDOFF}, + { "blue", SCC_LEDBLUE}, + { "red", SCC_LEDRED}, + { "blueflash", SCC_LEDBLUEFLASH}, + { "redflash", SCC_LEDREDFLASH}, + { "alternate", SCC_LEDALTERNATE1}, + { "alternate3", SCC_LEDALTERNATE3}, + { "EOT", -1}}; + +scc_cmd_t fancmds[]= { + { "auto", SCC_FANAUTO}, + { "on", SCC_FANON}, + { "EOT", -1}}; + +void +usage(void) +{ + int i; + scc_cmd_t *c; + + fprintf(stderr, "Usage: %s OPTIONS\n", prog); + fprintf(stderr,"%s controls the Iomega StorCenter's LED, fan and soft power.\n\n", prog); + fprintf(stderr," -?\t\tthis message\n"); + fprintf(stderr," -v\t\tverbose, show all packets\n"); + fprintf(stderr," -d\t\tsend send directly, stop/restart only\n"); + fprintf(stderr,"\n -p POWER\tcontrol soft power\n"); + fprintf(stderr,"\t\tPOWER=["); + for (c = powercmds,i=0; c->scc_cmdval != -1; c++,i++) { + if (i) fprintf(stderr,"|"); + fprintf(stderr,"%s", c->scc_cmd); + } + fprintf(stderr,"]\n\n"); + + fprintf(stderr," -l LED\tcontrol LED state\n"); + fprintf(stderr,"\t\tLED=["); + for (c = ledcmds,i=0; c->scc_cmdval != -1; c++,i++) { + if (i) fprintf(stderr,"|"); + fprintf(stderr,"%s", c->scc_cmd); + } + fprintf(stderr,"]\n -r RATE\tRate the LED flashes (0<=RATE<256)\n\n"); + + + fprintf(stderr," -f FAN\tcontrol fan operation\n"); + fprintf(stderr,"\t\tFAN=["); + for (c = fancmds,i=0; c->scc_cmdval != -1; c++,i++) { + if (i) fprintf(stderr,"|"); + fprintf(stderr,"%s", c->scc_cmd); + } + fprintf(stderr,"]\n -h TEMP\tHot temp when FAN=auto should go on (0<=TEMP<256)\n"); + fprintf(stderr," -c TEMP\tCold temp when FAN=auto should go off (0<=TEMP<256)\n"); + + (void)exit(2); +} + +int +sendrecv(int fd, int *i, int *j) +{ + char buf[8]; + int rc; + fd_set fds; + struct timeval sec = {1, 0}; + struct timespec tenth_sec = {0, 100000000}; + + + /* build the buffer */ + *(int *)(buf) = *i; + *(int *)(buf + 4) = *j; + + if (verbose) + printf("Send: [0x%08x 0x%08x]\n", *i, *j); + + if ((rc = write(fd, buf, 8)) != 8) { + perror("write failed failed"); + return(-1); + } + + /* Wait for data */ + FD_ZERO(&fds); + FD_SET(fd, &fds); + rc = select(1024, &fds, NULL, NULL, &sec); + + if (rc == -1) { + perror("select failed"); + return (-1); + } else if (!rc) { + perror("select timedout"); + return (-1); + } + + /* Consume it */ + if ((rc = read(fd, buf, 8)) != 8) { + perror("read failed failed"); + return(-1); + } + + *i = *(int *)(buf); + *j = *(int *)(buf + 4); + + if (verbose) + printf("Recv: [0x%08x 0x%08x]\n", *i, *j); + + /* Let it rest */ + nanosleep(&tenth_sec, NULL); + + return(0); +} + +int +setup_clnt() +{ + int s, len; + struct sockaddr_un remote; + + if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + return(-1); + } + + remote.sun_family = AF_UNIX; + strcpy(remote.sun_path, SCC_SOCKET); + len = strlen(remote.sun_path) + sizeof(remote.sun_family); + if (connect(s, (struct sockaddr *)&remote, len) == -1) { + return(-1); + } + + return(s); +} + +int +setup_cntlr(int *i, int *j) +{ + int fd; + char *dev = SCC_DEVICE; + struct termios ti; + + /* setup the serial connection to 9600, 8n1 */ + if ((fd = open(dev, O_RDWR|O_NOCTTY)) < 0) { + perror("Open failed"); + return(-1); + } + + if (ioctl(fd, TCGETS, &ti) < 0) { + perror("TCGETS failed"); + return(-1); + } + + if (ioctl(fd, TCFLSH, 0) < 0) { + perror("TCFLSH failed"); + return(-1); + } + + ti.c_iflag = IGNPAR; + ti.c_oflag = 0; + ti.c_cflag = (B9600 | CS8 | CREAD | CLOCAL); + ti.c_lflag = 0; + ti.c_line = N_TTY; + bzero(ti.c_cc, NCCS); + ti.c_cc[VMIN] = 0x08; + + if (ioctl(fd, TCSETS, &ti) < 0) { + perror("TCSETS failed"); + return(-1); + } + + if (verbose) printf("Resetting microcontroller\n"); + *i = SCC_RESETW1; + *j = SCC_RESETW2; + scc_setval(i, SCC_IDMASK, SCC_HOST); + scc_setval(j, SCC_CKSUMMASK, (int)scc_cksum(i, j)); + if (sendrecv(fd, i, j) < 0) + return(-1); + + return(fd); +} + +int +main(int argc, char *argv[]) +{ + + int devfd, w1, w2, h1, h2; + int coldtemp, hottemp, fancmd; + int ledrate, ledcmd; + int powercmd; + int direct; + int c; + char buf[SCC_PACKETLEN]; + scc_cmd_t *ct; + + extern char *optarg; + extern int optind, opterr, optopt; + + prog = argv[0]; + fancmd = coldtemp = hottemp = -1; + ledcmd = ledrate = -1; + powercmd = -1; + direct = 0; + + while ((c = getopt(argc, argv, "c:df:h:l:p:r:wv?")) != EOF) { + switch (c) { + case 'c': + if (coldtemp != -1) /* more than one -c arg */ + usage(); + + coldtemp = atoi(optarg); + if ((coldtemp > 255) || (coldtemp < 0)) + usage(); + break; + case 'd': + direct = 1; + break; + case 'f': + if (fancmd != -1) /* more than one -f arg */ + usage(); + + for (ct = fancmds; ct->scc_cmdval != -1; ct++) { + if (!strcmp(ct->scc_cmd, optarg)) { + fancmd = ct->scc_cmdval; + } + } + + if (fancmd == -1) /* Failed to find the req */ + usage(); + + break; + case 'h': + if (hottemp != -1) /* more than one -h arg */ + usage(); + + hottemp = atoi(optarg); + if ((hottemp > 255) || (hottemp < 0)) + usage(); + break; + case 'l': + if (ledcmd != -1) /* more than one -l arg */ + usage(); + + for (ct = ledcmds; ct->scc_cmdval != -1; ct++) { + if (!strcmp(ct->scc_cmd, optarg)) { + ledcmd = ct->scc_cmdval; + } + } + + if (ledcmd == -1) /* Failed to find the req */ + usage(); + break; + case 'p': + if (powercmd != -1) /* more than one -p arg */ + usage(); + + for (ct = powercmds; ct->scc_cmdval != -1; ct++) { + if (!strcmp(ct->scc_cmd, optarg)) { + powercmd = ct->scc_cmdval; + } + } + + if (powercmd == -1) /* Failed to find the req */ + usage(); + break; + case 'r': + if (ledrate != -1) /* more than one -h arg */ + usage(); + + ledrate = atoi(optarg); + if ((ledrate > 255) || (ledrate < 0)) + usage(); + break; + case 'v': + verbose=1; + break; + case '?': + usage(); + break; + + } + } + + if (direct) { + /* + * Setup the cntlr connection + */ + if ((devfd = setup_cntlr(&w1, &w2)) < 0) + exit(1); + scc_defaults(&w1, &w2); + } else { + /* + * Setup the clnt connection, if we get an connection + * refused that means the daemon isn't listening. + * so go direct + */ + w1 = 0; + w2 = 0; + if ((devfd = setup_clnt()) < 0) { + /* log it */ + if ((devfd = setup_cntlr(&w1, &w2)) < 0) + exit(1); + direct = 1; + scc_defaults(&w1, &w2); + } + } + + /* + * Direct (direct to the cntlr): + * w1 and w2 are setup with the default cntlr packet. + * Now lets poke the command line args into the packet. + * + * Nondirect (via the daemon): + * w1 and w2 are setup with nulls. The packet sent to the daemon + * contains only the changes to the current state. + */ + if (powercmd != -1) + scc_setval(&w1, SCC_POWERMASK, powercmd); + if (ledcmd != -1) + scc_setval(&w1, SCC_LEDMASK, ledcmd); + if (ledrate != -1) + scc_setrate(&w1, SCC_LEDRATEMASK, SCC_LEDRATESHIFT, ledrate); + if (fancmd != -1) + scc_setval(&w1, SCC_FANMASK, fancmd); + + if (coldtemp != -1) + scc_setrate(&w2, + SCC_FANTEMPOFFMASK, SCC_FANTEMPOFFSHIFT, coldtemp); + if (hottemp != -1) + scc_setrate(&w2, + SCC_FANTEMPONMASK, SCC_FANTEMPONSHIFT, hottemp); + + scc_setval(&w2, SCC_IDMASK, SCC_HOST); + scc_setval(&w2, SCC_CKSUMMASK, (int)scc_cksum(&w1, &w2)); + + + if (direct) { + if (verbose) { + printf("New State: \n"); + scc_validate(&w1, &w2, verbose); + } + + if (sendrecv(devfd, &w1, &w2) < 0) + exit(1); + } else { + *(int *)(&buf[0]) = w1; + *(int *)(&buf[4]) = w2; + + if (send(devfd, buf, SCC_PACKETLEN, 0) == -1) { + perror("send"); + exit(1); + } + } + + close(devfd); + exit(0); +} diff --git a/recipes/sccd/files/scc.h b/recipes/sccd/files/scc.h new file mode 100644 index 0000000000..b920791f66 --- /dev/null +++ b/recipes/sccd/files/scc.h @@ -0,0 +1,118 @@ +#ifndef _SCC_H +#define _SCC_H +/* + * Copyright (c) 2006 + * Protium Computing, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Protium Computing, Inc. + * 4. The name of Protium Computing, Inc. may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY PROTIUM COMPUTING ``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 PROTIUM COMPUTING 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. + */ + +#define SCC_PIDFILE "/var/run/sccd.pid" +#define SCC_DEVICE "/dev/ttyS1" +#define SCC_SOCKET "/dev/sccd" + +#define SCC_PACKETLEN 8 + +#define SCC_RESETW1 0x23696f6d +#define SCC_RESETW2 0x65676100 +#define SCC_QUERYW1 0x00000000 +#define SCC_QUERYW2 QUERYW1 + +/* word 1 defines */ +#define SCC_RUN 0x62000000 +#define SCC_STOP 0x63000000 +#define SCC_ADVISESTOP 0x64000000 +#define SCC_RESTART 0x65000000 +#define SCC_ADVISERESTART 0x66000000 +#define SCC_RESET 0x67000000 +#define SCC_ADVISERESET 0x68000000 +#define SCC_POWERMASK 0xFF000000 + +#define SCC_LEDOFF 0x00610000 +#define SCC_LEDBLUE 0x00620000 +#define SCC_LEDRED 0x00630000 +#define SCC_LEDBLUEFLASH 0x00640000 +#define SCC_LEDREDFLASH 0x00650000 +#define SCC_LEDALTERNATE1 0x00660000 +#define SCC_LEDALTERNATE3 0x00670000 +#define SCC_LEDMASK 0x00FF0000 + +#define SCC_LEDRATEMASK 0x0000FF00 +#define SCC_LEDRATESHIFT 8 +#define SCC_LEDRATEHI 40 /* limit: where flash == on */ +#define SCC_LEDRATELO 0 /* limit: where flash == off */ +#define SCC_LEDIORATE 20 + +#define SCC_FANAUTO 0x00000061 +#define SCC_FANON 0x00000062 +#define SCC_FANMASK 0x000000FF + +/* word 2 defines */ +#define SCC_FANTEMPONMASK 0xFF000000 +#define SCC_FANTEMPONSHIFT 24 +#define SCC_FANTEMPONHI 60 /* limit: board on fire 140F */ + +#define SCC_FANTEMPOFFMASK 0x00FF0000 +#define SCC_FANTEMPOFFSHIFT 16 +#define SCC_FANTEMPONLO 15 /* limit: never turn off 60F */ +#define SCC_FANTEMPDIFF 2 /* limit: if the diff between + hi and lo is < then this the + fan could rapidly sccillate + or never turn on */ +#define SCC_HOST 0x00000700 +#define SCC_CTLR00 0x00000000 +#define SCC_CTLR12 0x00001200 +#define SCC_IDMASK 0x0000FF00 + +#define SCC_CKSUMMASK 0x000000FF + +#define SCC_POWERDEFAULT SCC_RUN +#define SCC_LEDDEFAULT SCC_LEDRED +#define SCC_LEDRATEDEFAULT 10 +#define SCC_FANDEFAULT SCC_FANAUTO +#define SCC_FANTEMPONDEFAULT 50 +#define SCC_FANTEMPOFFDEFAULT 45 + +struct scc_cmd_table { + char *scc_cmd; + int scc_cmdval; +}; + +typedef struct scc_cmd_table scc_cmd_t; + +char scc_cksum(int *, int *); +int scc_validate(int *, int *, int); +int scc_setval(int *, int, int); +int scc_setrate(int *, int, int, int); +int scc_defaults(int *, int *); +int scc_daemonize(int); +int disk_activity(void); + + +#endif /* _SCC_H */ + diff --git a/recipes/sccd/files/sccd.c b/recipes/sccd/files/sccd.c new file mode 100644 index 0000000000..4f74c1d49c --- /dev/null +++ b/recipes/sccd/files/sccd.c @@ -0,0 +1,523 @@ +/* + * Copyright (c) 2006 + * Protium Computing, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Protium Computing, Inc. + * 4. The name of Protium Computing, Inc. may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY PROTIUM COMPUTING ``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 PROTIUM COMPUTING 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 <stdio.h> +#include <fcntl.h> +#include <time.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> +#include <errno.h> +#include <utmp.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/select.h> +#include <sys/socket.h> +#include <sys/stat.h> +#include <sys/un.h> +#include <sys/syslog.h> +#include <sys/param.h> +#include <termios.h> + +#include "scc.h" + +#define HALT "/sbin/halt" +#define REBOOT "/sbin/reboot" + +char * prog; +int verbose = 0; + +extern int scc_die; + +void +usage(void) +{ + fprintf(stderr, "Usage: %s OPTIONS\n", prog); + fprintf(stderr, + "StorCenter Control Daemon manages the StorCenter's LED, fan and soft power.\n\n"); + fprintf(stderr," -?\t\tthis message\n"); + fprintf(stderr," -h\t\tthis message\n"); + fprintf(stderr," -c\t\tallow core files\n"); + fprintf(stderr," -f\t\trun in the foreground\n"); + fprintf(stderr," -v\t\tverbose, show all packets (must be used with -f)\n"); + (void)exit(2); +} + +int +sendrecv(int fd, int *i, int *j) +{ + char buf[8]; + int rc; + fd_set fds; + struct timeval sec = {1, 0}; + struct timespec tenth_sec = {0, 100000}; + + + /* build the buffer */ + *(int *)(buf) = *i; + *(int *)(buf + 4) = *j; + + if (verbose) + printf("Send: [0x%08x 0x%08x]\n", *i, *j); + + if ((rc = write(fd, buf, 8)) != 8) { + syslog(LOG_ERR, "Write failed to controller"); + return(-1); + } + + /* Wait for data */ + FD_ZERO(&fds); + FD_SET(fd, &fds); + rc = select(1024, &fds, NULL, NULL, &sec); + + if (rc == -1) { + syslog(LOG_ERR, "Controller select failed"); + return (-1); + } else if (!rc) { + syslog(LOG_ERR, "Controller timeout"); + return (-1); + } + + /* Consume it */ + if ((rc = read(fd, buf, 8)) != 8) { + syslog(LOG_ERR, "Read from controller failed"); + return(-1); + } + + *i = *(int *)(buf); + *j = *(int *)(buf + 4); + + if (verbose) + printf("Recv: [0x%08x 0x%08x]\n", *i, *j); + + /* Let it rest */ + nanosleep(&tenth_sec, NULL); + + return(0); +} + +int +setup_clnt() +{ + int s; + struct sockaddr_un local; + int len; + + if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) { + perror("socket"); + return(-1); + } + + local.sun_family = AF_UNIX; + strcpy(local.sun_path, SCC_SOCKET); + unlink(local.sun_path); + + len = strlen(local.sun_path) + sizeof(local.sun_family); + if (bind(s, (struct sockaddr *)&local, len) == -1) { + perror("bind"); + close(s); + return(-1); + } + + if (listen(s, 5) == -1) { + perror("listen"); + close(s); + return(-1); + } + + /* Force the permission on the unix socket */ + if (chmod(SCC_SOCKET, (S_IWUSR | S_IRUSR)) < 0) { + perror("chmod socket"); + } + + return(s); +} + +int +setup_cntlr() +{ + int i, j, fd; + char *dev = SCC_DEVICE; + struct termios ti; + + /* setup the serial connection to 9600, 8n1 */ + if ((fd = open(dev, O_RDWR|O_NOCTTY)) < 0) { + perror("Open failed"); + return(-1); + } + + if (ioctl(fd, TCGETS, &ti) < 0) { + perror("TCGETS failed"); + return(-1); + } + + if (ioctl(fd, TCFLSH, 0) < 0) { + perror("TCFLSH failed"); + return(-1); + } + + ti.c_iflag = IGNPAR; + ti.c_oflag = 0; + ti.c_cflag = (B9600 | CS8 | CREAD | CLOCAL); + ti.c_lflag = 0; + ti.c_line = N_TTY; + bzero(ti.c_cc, NCCS); + ti.c_cc[VMIN] = 0x08; + + if (ioctl(fd, TCSETS, &ti) < 0) { + perror("TCSETS failed"); + return(-1); + } + + if (verbose) printf("Resetting microcontroller\n"); + i = SCC_RESETW1; + j = SCC_RESETW2; + scc_setval(&j, SCC_CKSUMMASK, (int)scc_cksum(&i, &j)); + if (sendrecv(fd, &i, &j) < 0) { + return(-1); + } + + return(fd); +} + +int +handle_clnt(int clnt, int *w1, int *w2) +{ + char buf[SCC_PACKETLEN]; + int s, t, i, j, *x, *y; + struct sockaddr_un remote; + + t = sizeof(remote); + s = accept(clnt, (struct sockaddr *)&remote, &t); + if (s < 0) { + syslog(LOG_ERR, "Unable to accept client"); + return(-1); + } + + if (recv(s, buf, SCC_PACKETLEN, 0) != SCC_PACKETLEN) { + return(-1); + } + close(s); + + x = (int *)&buf[0]; + y = (int *)&buf[4]; + + /* + * Clients only send fields they want enacted. This allows this daemon + * to keep the other values constant without informing the clients + * of the current setting. It allows the protocol to unidirectional. + * + * Take the data from the client and add it to the current + * state then validate the resulting state. If valid, return + * the new state. If not don't corrupt the existing state. + */ + i = *w1; + j = *w2; + + if (*x & SCC_POWERMASK) + scc_setval(&i, SCC_POWERMASK, (*x & SCC_POWERMASK)); + if (*x & SCC_LEDMASK) + scc_setval(&i, SCC_LEDMASK, (*x & SCC_LEDMASK)); + if (*x & SCC_LEDRATEMASK) + scc_setval(&i, SCC_LEDRATEMASK, (*x & SCC_LEDRATEMASK)); + if (*x & SCC_FANMASK) + scc_setval(&i, SCC_FANMASK, (*x & SCC_FANMASK)); + + if (*y & SCC_FANTEMPONMASK) + scc_setval(&j, SCC_FANTEMPONMASK, (*y & SCC_FANTEMPONMASK)); + if (*y & SCC_FANTEMPOFFMASK) + scc_setval(&j, SCC_FANTEMPOFFMASK, (*y & SCC_FANTEMPOFFMASK)); + scc_setval(&j, SCC_IDMASK, SCC_HOST); + scc_setval(&j, SCC_CKSUMMASK, (int)scc_cksum(&i, &j)); + + if (scc_validate(&i, &j, verbose) < 0) { + /* log it and return */ + return(-1); + } + + /* + * If we are here we got a good packet, return it. + */ + *w1 = i; + *w2 = j; + return(0); +} + +#ifdef NOTUSED +char +runlevel() +{ + struct utmp *ut; + time_t boot; + + /* + * Find last boot time + */ + time(&boot); + boot -= (times(NULL) / HZ); + + setutent(); + while ((ut = getutent()) != NULL) { + /* + * Find runlevel after our last boot + */ + if (ut->ut_type == RUN_LVL && ut->ut_time > boot) + return(ut->ut_pid & 0xff); + } + endutent(); + + return(0xff); +} +#endif + +int +work_loop(int cntlr, int clnt) +{ + + int rc, w1, w2, c1, c2, saverate; + int shutdown_called = 0; + pid_t pid; + fd_set fds; + struct timeval tv; + + /* + * If we are booting set to red flash and + * wait for someone to change it. + * If we are already in runlevel 5 then set it to blue + */ + scc_defaults(&w1, &w2); + scc_setval(&w1, SCC_LEDMASK, SCC_LEDREDFLASH); + scc_setval(&w2, SCC_IDMASK, SCC_HOST); + scc_setval(&w2, SCC_CKSUMMASK, (int)scc_cksum(&w1, &w2)); + + if (verbose) { + printf("New State: \n"); + scc_validate(&w1, &w2, verbose); + } + + if (sendrecv(cntlr, &w1, &w2) < 0) { + /* log then carry on */ + } + + while (!scc_die) { + /* + * Wait for data. How long? Well detecting soft power + * events should happen fairly quickly, 1 second between + * probes should be enough, but detecting disk activity + * probably needs to be a little faster. So we try 0.5s or + * 500,000 micro seconds. If that steals too much CPU + * then we backoff here. + */ + FD_ZERO(&fds); + FD_SET(clnt, &fds); + tv.tv_sec = 0; + tv.tv_usec = 500000; + rc = select(1024, &fds, NULL, NULL, &tv); + + if (rc < 0) { + /* Select failed try again */ + continue; + } else if (rc) { + /* Got a clnt request */ + if (handle_clnt(clnt, &w1, &w2) < 0) + continue; + } else { + /* timeout - just fallthrough to refresh ctlr */ + } + + /* + * Here's how we do blinking lights for disk activity. + * If there is disk activity and the LED is blue, then + * set the led to blueflash and the rate to io rate. + * Next time through if there is still activity, the + * LED is unchanged. If no activity and the led is flashing + * blue then return it to solid blue and restore the rate. + */ + if (disk_activity()) { + if ((w1 & SCC_LEDMASK) == SCC_LEDBLUE) { + saverate = (w1 & SCC_LEDRATEMASK); + scc_setval(&w1, SCC_LEDMASK, SCC_LEDBLUEFLASH); + scc_setrate(&w1, SCC_LEDRATEMASK, + SCC_LEDRATESHIFT, SCC_LEDIORATE); + } + } else { + if ((w1 & SCC_LEDMASK) == SCC_LEDBLUEFLASH) { + scc_setval(&w1, SCC_LEDMASK, SCC_LEDBLUE); + scc_setval(&w1, SCC_LEDRATEMASK, saverate); + } + } + + /* + * Getting here because of a timeout or client request + * in either case refresh the cntlr. Don't use w1 and w2 + * to refresh, cause the daemon should not incorporate + * the controller's view of the state into the daemon's + * view. Only a client should alter w1 and w2. + */ + c1 = w1; + c2 = w2; + if (sendrecv(cntlr, &c1, &c2) < 0) { + continue; + } + + /* + * Now examine the packet from the controller to see + * if action needs to be taken. + * If the power state is either: stop, restart or reset + * start shutting down the box. Once shutdown (reboot or halt) + * has beed call don't do it again. + */ + if (shutdown_called) + continue; + + switch (c1 & SCC_POWERMASK) { + case SCC_STOP: + /* exec halt */ + syslog(LOG_INFO, "Halt requested"); + if ((pid = fork()) == -1) { + break; + } + + if (pid == 0) { + /* i am child */ + char *argv[] = {HALT, NULL}; + execv(HALT, argv); + exit(EXIT_FAILURE); + } + + shutdown_called = 1; + break; + + case SCC_RESTART: + case SCC_RESET: + /* exec reboot */ + syslog(LOG_INFO, "Reboot requested"); + if ((pid = fork()) == -1) { + break; + } + if (pid == 0) { + /* i am child */ + char *argv[] = {REBOOT, NULL}; + execv(REBOOT, argv); + exit(EXIT_FAILURE); + } + + shutdown_called = 1; + break; + + default: + /* Nothing to do */ + break; + } + } +} + +int +main(int argc, char *argv[]) +{ + + int cntlr, clnt, w1, w2; + int c, daemon, cores; + + struct stat st; + extern char *optarg; + extern int optind, opterr, optopt; + + prog = argv[0]; + daemon = 1; + cores = 0; + + while ((c = getopt(argc, argv, "cfhv?")) != EOF) { + switch (c) { + case 'h': + usage(); + break; + case 'c': + cores=1; + break; + case 'f': + daemon=0; + break; + case 'v': + verbose=1; + break; + case '?': + usage(); + break; + + } + } + + if (verbose && daemon) + usage(); + + if (stat(HALT, &st) < 0) { + perror("Couldn't find halt"); + exit(EXIT_FAILURE); + } + + if (stat(REBOOT, &st) < 0) { + perror("Couldn't find reboot"); + exit(EXIT_FAILURE); + } + + if (daemon) { + if (scc_daemonize(cores) < 0) + exit(EXIT_FAILURE); + } else { + /* + * Open syslog + */ + openlog("sccd", LOG_PID, LOG_DAEMON); + syslog(LOG_INFO, "Starting"); + } + + /* + * Setup the controller connection + */ + if ((cntlr = setup_cntlr()) < 0) + exit(EXIT_FAILURE); + + /* + * Setup the client connection + */ + if ((clnt = setup_clnt()) < 0) + exit(EXIT_FAILURE); + + work_loop(cntlr, clnt); + + close(cntlr); + close(clnt); + unlink(SCC_PIDFILE); + exit(EXIT_SUCCESS); +} + |