summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBill Richardson <wfrichar@chromium.org>2014-02-14 13:37:58 -0800
committerchrome-internal-fetch <chrome-internal-fetch@google.com>2014-02-15 00:50:01 +0000
commit33e967eee16ee94e10868ef5a9f49d869f37d0d9 (patch)
tree119bb7692c95fea9556eb58f8367086ace06759c
parente43074ebcf3349819b118aceeb422716100c2718 (diff)
downloadchrome-ec-33e967eee16ee94e10868ef5a9f49d869f37d0d9.tar.gz
Update util/lbplay.c to use the sysfs interface.
This is just an example, demonstrating how a userspace program can access and control the Pixel lighbar. This change reflects the new unprivileged access methods. You can run this program to drive the lightbar without being root. BUG=chromium:239205 BRANCH=none TEST=manual Nothing builds this by default, but you can test it with cd src/platform/ec gcc -static util/lbplay.c then copy a.out to your Pixel and run it (from /tmp, since other directories are mounted noexec). Change-Id: I7c07512087c924d16c1c03df6176fba995fcd4f4 Signed-off-by: Bill Richardson <wfrichar@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/186672 Reviewed-by: Alec Berg <alecaberg@chromium.org>
-rw-r--r--util/lbplay.c309
1 files changed, 138 insertions, 171 deletions
diff --git a/util/lbplay.c b/util/lbplay.c
index da093a1807..d4a57e0567 100644
--- a/util/lbplay.c
+++ b/util/lbplay.c
@@ -1,198 +1,165 @@
-/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+/* Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
+#include <errno.h>
+#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
-#include <sys/io.h>
+#include <strings.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
#include <unistd.h>
-#include "comm-host.h"
-#include "compile_time_macros.h"
-#include "lightbar.h"
-#include "lock/gec_lock.h"
-
-#define LB_SIZES(SUBCMD) { \
- sizeof(((struct ec_params_lightbar *)0)->SUBCMD) \
- + sizeof(((struct ec_params_lightbar *)0)->cmd), \
- sizeof(((struct ec_response_lightbar *)0)->SUBCMD) }
-static const struct {
- uint8_t insize;
- uint8_t outsize;
-} lb_command_paramcount[] = {
- LB_SIZES(dump),
- LB_SIZES(off),
- LB_SIZES(on),
- LB_SIZES(init),
- LB_SIZES(brightness),
- LB_SIZES(seq),
- LB_SIZES(reg),
- LB_SIZES(rgb),
- LB_SIZES(get_seq),
- LB_SIZES(demo),
- LB_SIZES(get_params),
- LB_SIZES(set_params),
- LB_SIZES(version)
-};
-#undef LB_SIZES
-
-
-#define GEC_LOCK_TIMEOUT_SECS 30 /* 30 secs */
-#define LOCK do { \
- if (acquire_gec_lock(GEC_LOCK_TIMEOUT_SECS) < 0) { \
- fprintf(stderr, "Could not acquire GEC lock.\n"); \
- exit(1); \
- } } while (0)
-
-#define UNLOCK do { release_gec_lock(); } while (0)
-
-
-static void lb_cmd_noargs(enum lightbar_command cmd)
-{
- struct ec_params_lightbar param;
- struct ec_response_lightbar resp;
- param.cmd = cmd;
- ec_command(EC_CMD_LIGHTBAR_CMD, 0,
- &param, lb_command_paramcount[param.cmd].insize,
- &resp, lb_command_paramcount[param.cmd].outsize);
-}
-
-inline void lightbar_off(void)
-{
- lb_cmd_noargs(LIGHTBAR_CMD_OFF);
-}
-
-inline void lightbar_on(void)
-{
- lb_cmd_noargs(LIGHTBAR_CMD_ON);
-}
-
-inline void lightbar_init_vals(void)
-{
- lb_cmd_noargs(LIGHTBAR_CMD_INIT);
-}
+#define LIGHTBAR "/sys/devices/virtual/chromeos/cros_ec/lightbar"
-void lightbar_brightness(int newval)
-{
- struct ec_params_lightbar param;
- struct ec_response_lightbar resp;
- param.cmd = LIGHTBAR_CMD_BRIGHTNESS;
- param.brightness.num = newval;
- ec_command(EC_CMD_LIGHTBAR_CMD, 0,
- &param, lb_command_paramcount[param.cmd].insize,
- &resp, lb_command_paramcount[param.cmd].outsize);
-}
-
-void lightbar_sequence(enum lightbar_sequence num)
-{
- struct ec_params_lightbar param;
- struct ec_response_lightbar resp;
- param.cmd = LIGHTBAR_CMD_SEQ;
- param.seq.num = num;
- ec_command(EC_CMD_LIGHTBAR_CMD, 0,
- &param, lb_command_paramcount[param.cmd].insize,
- &resp, lb_command_paramcount[param.cmd].outsize);
-}
-
-void lightbar_reg(uint8_t ctrl, uint8_t reg, uint8_t val)
+int main(int argc, char **argv)
{
- struct ec_params_lightbar param;
- struct ec_response_lightbar resp;
- param.cmd = LIGHTBAR_CMD_REG;
- param.reg.ctrl = ctrl;
- param.reg.reg = reg;
- param.reg.value = val;
- ec_command(EC_CMD_LIGHTBAR_CMD, 0,
- &param, lb_command_paramcount[param.cmd].insize,
- &resp, lb_command_paramcount[param.cmd].outsize);
-}
+ int major, minor, fd_v;
+ int i, tries, fd_s, fd_l;
+ char buf[80];
+ int ret = 1;
+
+ /* Check version */
+ fd_v = open(LIGHTBAR "/version", O_RDONLY);
+ if (fd_v < 0) {
+ perror("can't open version file");
+ goto out;
+ }
+ ret = read(fd_v, buf, sizeof(buf));
+ if (ret <= 0) {
+ perror("can't read version");
+ close(fd_v);
+ goto out;
+ }
+ buf[ret] = '\0';
+ close(fd_v);
+
+ errno = 0;
+ /* Expect "MAJOR MINOR" */
+ if (2 != sscanf(buf, "%d %d", &major, &minor)) {
+ if (errno)
+ perror("can't parse version string");
+ else
+ fprintf(stderr, "can't parse version string\n");
+ goto out;
+ }
+ /* Pixel is "0 0". Minor change will be compatible, Major may not */
+ if (major != 0) {
+ fprintf(stderr, "Don't know how to handle version %d.%d\n",
+ major, minor);
+ goto out;
+ }
-void lightbar_rgb(int led, int red, int green, int blue)
-{
- struct ec_params_lightbar param;
- struct ec_response_lightbar resp;
- param.cmd = LIGHTBAR_CMD_RGB;
- param.rgb.led = led;
- param.rgb.red = red;
- param.rgb.green = green;
- param.rgb.blue = blue;
- ec_command(EC_CMD_LIGHTBAR_CMD, 0,
- &param, lb_command_paramcount[param.cmd].insize,
- &resp, lb_command_paramcount[param.cmd].outsize);
-}
+ /* Take over lightbar sequencing. */
+ fd_s = open(LIGHTBAR "/sequence", O_RDWR | O_SYNC);
+ if (fd_s < 0) {
+ perror("can't open sequence control");
+ goto out;
+ }
-void wait_for_ec_to_stop(void)
-{
- int r;
- struct ec_params_lightbar param;
- struct ec_response_lightbar resp;
- int count = 0;
+ /* NOTE: Cooperative locking only. Rude programs may not play nice. */
+ if (flock(fd_s, LOCK_EX | LOCK_NB) < 0) {
+ perror("can't lock sequence control");
+ goto out_close;
+ }
- do {
- usleep(100000);
- param.cmd = LIGHTBAR_CMD_GET_SEQ;
- r = ec_command(EC_CMD_LIGHTBAR_CMD, 0,
- &param,
- lb_command_paramcount[param.cmd].insize,
- &resp,
- lb_command_paramcount[param.cmd].outsize);
- if (count++ > 10) {
- fprintf(stderr, "EC isn't responding\n");
- UNLOCK;
- exit(1);
+ /*
+ * If power events are changing the sequence our request to stop may
+ * be missed, so try a few times before giving up.
+ *
+ * Note that every write to a control file should be prefaced with an
+ * lseek() to the beginning. sysfs files don't work quite like normal
+ * files.
+ */
+ tries = 3;
+ while (1) {
+ lseek(fd_s, 0, SEEK_SET);
+ if (read(fd_s, buf, sizeof(buf)) <= 0) {
+ perror("can't read sequence control");
+ goto out_unlock;
}
- } while (r < 0 && resp.get_seq.num != LIGHTBAR_STOP);
-}
-int main(int argc, char **argv)
-{
- int i;
+ if (!strncasecmp(buf, "stop", 4))
+ break;
- BUILD_ASSERT(ARRAY_SIZE(lb_command_paramcount) == LIGHTBAR_NUM_CMDS);
-
- LOCK;
+ if (!tries--) {
+ fprintf(stderr, "couldn't get EC to stop\n");
+ goto out_unlock;
+ }
- if (comm_init() < 0) {
- fprintf(stderr, "comm_init() failed\n");
- UNLOCK;
- return 2;
+ lseek(fd_s, 0, SEEK_SET);
+ strcpy(buf, "stop");
+ if (write(fd_s, buf, strlen(buf) + 1) <= 0) {
+ perror("can't write sequence control");
+ goto out_unlock;
+ }
}
- /* Tell the EC to let us drive. */
- lightbar_sequence(LIGHTBAR_STOP);
-
- /* Wait until it's listening (or die trying) */
- wait_for_ec_to_stop();
-
- /* Initialize it */
- lightbar_off();
- lightbar_init_vals();
- lightbar_brightness(0xff);
- lightbar_on();
-
- UNLOCK;
+ /* Turn the brightness all the way up */
+ fd_l = open(LIGHTBAR "/brightness", O_WRONLY | O_SYNC);
+ if (fd_l < 0) {
+ perror("can't open brightness control");
+ goto out_run;
+ }
+ strcpy(buf, "255");
+ if (write(fd_l, buf, strlen(buf) + 1) < 0) {
+ perror("can't write brightness control");
+ goto out_led;
+ }
+ close(fd_l);
- /* Play a bit */
- for (i = 0; i <= 255; i += 4) {
- LOCK;
- lightbar_rgb(4, 0, i, 0);
- UNLOCK;
- usleep(100000);
+ /* Now let's drive the colors. */
+ fd_l = open(LIGHTBAR "/led_rgb", O_WRONLY | O_SYNC);
+ if (fd_l < 0) {
+ perror("can't open led control");
+ goto out_run;
}
- for (; i >= 0; i -= 4) {
- LOCK;
- lightbar_rgb(4, i, 0, 0);
- UNLOCK;
+ /* Cycle through some colors. We can update multiple LEDs at once,
+ * but there's a limit on how often we can send commands to the
+ * lightbar. Going too fast will block, although buffering combined
+ * with lseek() may just cause data to be lost. Read "/interval_msec"
+ * to see what the limit is. The default is 50msec (20Hz).
+ */
+ for (i = 0; i < 256; i += 4) {
+ sprintf(buf, "0 %d %d %d 1 %d %d %d 2 %d %d %d 3 %d %d %d",
+ i, 0, 0,
+ 0, 0, i,
+ 255-i, 255, 0,
+ 0, 255, 255-i);
+ lseek(fd_l, 0, SEEK_SET);
+ if (write(fd_l, buf, strlen(buf) + 1) < 0)
+ perror("write to led control");
+
usleep(100000);
}
- /* Let the EC drive again */
- LOCK;
- lightbar_sequence(LIGHTBAR_RUN);
- UNLOCK;
- return 0;
+ /* all white */
+ strcpy(buf, "4 255 255 255");
+ lseek(fd_l, 0, SEEK_SET);
+ if (write(fd_l, buf, strlen(buf) + 1) < 0)
+ perror("write to led control");
+
+ usleep(400000);
+
+ /* Done. */
+ ret = 0;
+out_led:
+ close(fd_l);
+out_run:
+ /* Let EC drive lightbar again */
+ strcpy(buf, "run");
+ lseek(fd_s, 0, SEEK_SET);
+ if (write(fd_s, buf, strlen(buf) + 1) < 0)
+ perror("write to sequence control");
+out_unlock:
+ flock(fd_s, LOCK_UN);
+out_close:
+ close(fd_s);
+out:
+ return ret;
}