/* 
 * blinkraw.c
 *
 * fgmr 2012-09-22
 *
 * playing with the hidraw interface to blink(1)
 * 
 * Thank you Alan Ott for
 * http://lxr.free-electrons.com/source/samples/hidraw/hid-example.c
 */

#include <linux/types.h>
#include <linux/input.h>
#include <linux/hidraw.h>

#ifndef HIDIOCSFEATURE
#define HIDIOCSFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x06, len)
#define HIDIOCGFEATURE(len) _IOC(_IOC_WRITE|_IOC_READ, 'H', 0x07, len)
#endif /* HIDIOCSFEATURE */

#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stdarg.h>
#include <ctype.h>

static void
usage(const char* hunh) {
  if (NULL != hunh) {
    fprintf(stderr, "Can't understand %s\n", hunh);
  }

  fprintf(stderr,
          "Usage: blinkraw {arg, ...}\n"
          "  /dev/whatever  -- open device\n"
          "  ./whatever     -- open device\n"
          "  =R,G,B,t       -- fade to color\n"
          "  :R,G,B         -- set color (now)\n"
          "  @step:R,G,B,t  -- set step\n"
          "  +step          -- start playing at step\n"
          "  -[step]        -- stop playing at step (default zero)\n"
          "  %%              -- clear all steps\n"
          "  _              -- turn off\n"
          "  _t             -- fade off\n"
          "\n"
          "       step is on [0,15]\n",
          "       R, G, B are on [0, 255]\n"
          "       t is time in centiseconds\n"
          "\n"
          "    Arguments are applied in order.  A new device, which is\n"
          "    a valid blink(1) device, will become the new target.\n"
          "\n"
          "    Example:\n"
          "    # blinkraw /dev/hidraw* % =255,0,0,100\n"
          );
  exit(1);
}

static void
color(int fd, char action, int R, int G, int B, int T, int step) {
  char buf[16];
  int rc;

  if (-1 == fd) return;

  memset(buf, 0, sizeof(buf));

  if (R<0) R=0;
  if (G<0) G=0;
  if (B<0) B=0;
  if (T<0) T=0;
  if (step<0) step=0;

  if (R>255) R=255;
  if (G>255) G=255;
  if (B>255) B=255;
  if (T>65535) T=65535;
  if (step>15) step=15;
  
  buf[0] = 1;
  buf[1] = action;
  buf[2] = R; /* R */
  buf[3] = G; /* G */
  buf[4] = B; /* B */
  buf[5] = (T >>8);    /* time/cs high */
  buf[6] = (T & 0xff); /* time/cs low */
  buf[7] = step;
  buf[8] = 0;

  rc = ioctl(fd, HIDIOCSFEATURE(9), buf);
  if (rc < 0) perror("HIDIOCSFEATURE");
}

static void
play(int fd, char action, int play, int step) {
  char buf[16];
  int rc;

  if (-1 == fd) return;

  memset(buf, 0, sizeof(buf));

  buf[0] = 1;
  buf[1] = action;
  buf[2] = play;
  buf[3] = step;

  rc = ioctl(fd, HIDIOCSFEATURE(9), buf);
  if (rc < 0) perror("HIDIOCSFEATURE");
}

static int
isblink1(int fd) {
  int rc;
  struct hidraw_devinfo info;
  memset(&info, 0, sizeof(info));

  rc = ioctl(fd, HIDIOCGRAWINFO, &info);
  if (rc < 0) {
    perror("HIDIOCGRAWINFO");
    return 0;
  }

  if ((info.vendor == 0x27b8) && (info.product == 0x01ed)) {
    return 1;
  } else {
    return 0;
  }
}

int
main(int argc, char *argv[]) {
  int fd = -1;

  if (argc < 2) usage(NULL);

  while(++argv, --argc) {
    int rc = -1;
    int step = 0;
    int R = 0;
    int G = 0;
    int B = 0;
    int T = 0;
    char buf[16];

    memset(buf, 0, sizeof(buf));

    switch(**argv) {
    case '/': case '.':
      rc = open(*argv, O_RDWR|O_NONBLOCK);
      if (rc < 0) {
        perror(*argv);
        continue;
      }

      if (isblink1(rc)) {
        if (fd >= 0) close(fd);
        fd = rc;
      }

      break;
    case '=':
      rc = sscanf(*argv, "=%d,%d,%d,%d", &R, &G, &B, &T);
      if (rc != 4) usage(*argv);
      color(fd, 'c', R, G, B, T, 0);
      break;
    case ':':
      rc = sscanf(*argv, ":%d,%d,%d", &R, &G, &B);
      if (rc != 3) usage(*argv);
      color(fd, 'n', R, G, B, 0, 0);
      break;
    case '@':
      rc = sscanf(*argv, "@%d:%d,%d,%d,%d", &step, &R, &G, &B, &T);
      if (rc != 5) usage(*argv);
      if ((step < 0) || step > 15) usage(*argv);
      color(fd, 'P', R, G, B, T, step);
      break;
    case '_':
      rc = sscanf(*argv, "_%d", &T);
      if (rc == 1) color(fd, 'c', 0, 0, 0, T, 0);
      else color(fd, 'n', 0, 0, 0, 0, 0);
      break;
    case '+':
      rc = sscanf(*argv, "+%d", &step);
      if (rc != 1) usage(*argv);
      if ((step < 0) || step > 15) usage(*argv);
      play(fd, 'p', 1, step);
      break;
    case '-':
      rc = sscanf(*argv, "-%d", &step);
      if (rc != 1) step = 0;
      if ((step < 0) || step > 15) step = 0;
      play(fd, 'p', 0, step);
      break;
    case '%':
      for(step = 0; step < 16; ++step) {
        color(fd, 'P', 0, 0, 0, 0, step);
      }
      break;
    default:
      usage(*argv);
    }
  }

  close(fd);
  return 0;
}