/*
 * Copyright (C) 2006 Free Software Initiative of Japan
 *
 * Author: NIIBE Yutaka  <gniibe at fsij.org>
 *
 * This file can be distributed under the terms and conditions of the
 * GNU General Public License version 2 (or later).
 *
 */

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

#include <stdio.h>
#include <sys/ioctl.h>
#include <stdlib.h>

#define USBDEVFS_IOCTL             _IOWR('U', 18, struct usbdevfs_ioctl)
#define USBDEVFS_HUB_PORTCTRL      _IOW('U', 24, struct usbdevfs_hub_portctrl)

struct usbdevfs_ioctl {
  int ifno;
  int ioctl_code;
  void *data;
};

struct usbdevfs_hub_portctrl {
  char cmd;
  char port;
  char value;
};

static void
usage (const char *progname)
{
  fprintf (stderr, "Usage: %s PATH [-P PORT] [{-l [VALUE]}|{-p [VALUE]}]\n", progname);
}

#define COMMAND_SET_LED   0
#define COMMAND_SET_POWER 1
#define HUB_LED_GREEN 2

/*
 * HUB-CTRL  -  program to control port power/led of USB hub
 *
 *   $ hub-ctrl /dev/bus/usb/001/002 -P 1 -p            # Power off at port 1
 *   $ hub-ctrl /dev/bus/usb/001/002 -P 1 -p 1          # Power on at port 1
 *   $ hub-ctrl /dev/bus/usb/001/002 -P 2 -l            # LED on at port 1
 *
 * Requirement: USB hub which implements port power control / indicator control
 *              Elecom's U2H-G4S works fine
 *
 */
int
main (int argc, const char *argv[])
{
  int fd;
  struct usbdevfs_ioctl ioctl_data;
  struct usbdevfs_hub_portctrl portctrl_data;
  int i;
  const char *path;
  int port = 1;
  int cmd = COMMAND_SET_POWER;
  int value = 0;

  if (argc < 2)
    {
      usage (argv[0]);
      exit (1);
    }

  path = argv[1];

  for (i = 2; i < argc; i++)
    if (argv[i][0] == '-')
      switch (argv[i][1])
	{
	case 'P':
	  if (++i >= argc)
	    {
	      usage (argv[0]);
	      exit (1);
	    }
	  port = atoi (argv[i]);
	  break;

	case 'l':
	  cmd = COMMAND_SET_LED;
	  if (++i < argc)
	    value = atoi (argv[i]);
	  else
	    value = HUB_LED_GREEN;
	  break;

	case 'p':
	  cmd = COMMAND_SET_POWER;
	  if (++i < argc)
	    value = atoi (argv[i]);
	  else
	    value= 0;
	  break;

	default:
	  usage (argv[0]);
	  exit (1);
	}


  if ((fd = open (path, O_RDWR)) < 0)
    {
      perror ("open");
      exit (1);
    }

  ioctl_data.ifno = 0;
  ioctl_data.ioctl_code = USBDEVFS_HUB_PORTCTRL;
  ioctl_data.data = &portctrl_data;

  portctrl_data.cmd = cmd;
  portctrl_data.port = port;
  portctrl_data.value = value;

  if (ioctl (fd, USBDEVFS_IOCTL, &ioctl_data) < 0)
    {
      perror ("ioctl");
      close (fd);
      exit (1);
    }

  close (fd);
  exit (0);
}
