Codebase list ioport / debian/1.2-2 port.c
debian/1.2-2

Tree @debian/1.2-2 (Download .tar.gz)

port.c @debian/1.2-2raw · history · blame

/* I/O port access
 * Copyright (C) 2009 Red Hat Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <config.h>

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <errno.h>

#include <sys/io.h>

static int op = 0;		/* Operation, 0 = read, 1 = write. */
static int size = 1;		/* Operation size in bytes. */
static int rcode = 0;		/* If set, inX exits with return value. */
static int rhex = 0;		/* If set, print result in hex. */

static struct option long_options[] = {
  { "code",  0, 0, 'c' },
  { "read",  0, 0, 'r' },
  { "size",  1, 0, 's' },
  { "write", 0, 0, 'w' },
  { "hex",   0, 0, 'x' },
  { NULL,    0, 0, 0   },
};
static const char *options = "crs:w";

static void
usage ()
{
  printf ("\n\
Usage:\n\
  inb|inw|inl [--options] address\n\
  outb|outw|outl [--options] address data\n\
Options:\n\
--code    Exit with status code instead of printing value (inX only).\n\
--hex     Print hex instead of decimal (inX only).\n\
--read    Perform a read (in) operation.\n\
--write   Perform a write (out) operation.\n\
--size N  Set size to N bytes where N = 1, 2 or 4.\n\
\n\
For detailed information, see the manual page inb(1).\n\
");
}

static unsigned get_int_or_die (const char *);

int
main (int argc, char *argv[])
{
  int c, option_index;
  char *p;
  unsigned addr = 0;
  unsigned data = 0;

  /* Find out how the program was invoked. */
  p = strrchr (argv[0], '/');
  if (p == NULL) p = argv[0]; else p++;

  if (strncasecmp (p, "inb", 3) == 0) {
    op = 0; size = 1;
  } else if (strncasecmp (p, "outb", 4) == 0) {
    op = 1; size = 1;
  } else if (strncasecmp (p, "inw", 3) == 0) {
    op = 0; size = 2;
  } else if (strncasecmp (p, "outw", 4) == 0) {
    op = 1; size = 2;
  } else if (strncasecmp (p, "inl", 3) == 0) {
    op = 0; size = 4;
  } else if (strncasecmp (p, "outl", 4) == 0) {
    op = 1; size = 4;
  }

  /* Parse command line arguments. */
  while (1) {
    option_index = 0;
    c = getopt_long (argc, argv, options, long_options, &option_index);
    if (c == -1) break;

    switch (c) {
    case 'c':
      rcode = 1;
      break;

    case 'r':
      op = 0;
      break;

    case 's':
      size = get_int_or_die (optarg);
      if (!(size == 1 || size == 2 || size == 4)) {
	fprintf (stderr, "%s: size can only be 1, 2 or 4.\n", optarg);
	exit (1);
      }
      break;

    case 'w':
      op = 1;
      break;

    case 'x':
      rhex = 1;
      break;

    case 0:
      fprintf (stderr, "internal error in getopt_long\n");
      exit (1);

    default:
      usage ();
      exit (1);
    }
  }

  /* Parse the address (for read/write) and data (for writes). */
  if (optind >= argc) {
  missing:
    fprintf (stderr, "%s: missing parameter, see --help or man page\n",
	     argv[0]);
    exit (1);
  }

  addr = get_int_or_die (argv[optind++]);

  if (op == 1) {
    if (optind >= argc) goto missing;
    data = get_int_or_die (argv[optind++]);
  }

  if (optind != argc) {
    fprintf (stderr,
	     "%s: extra parameters on command line, see --help or man page\n",
	     argv[0]);
    exit (1);
  }

  /* Raise our privilege level. */
  if (iopl (3) == -1) {
    fprintf (stderr, "iopl failed: You may need to run as root or give the process the CAP_SYS_RAWIO\ncapability. On non-x86 architectures, this operation probably isn't possible.\n");
    perror ("iopl");
    exit (1);
  }

  /* Perform the operation. */
  switch (op) {
  case 0:
    switch (size) {
    case 1: data = inb (addr); break;
    case 2: data = inw (addr); break;
    case 4: data = inl (addr); break;
    }
    break;
  case 1:
    switch (size) {
    case 1: outb (data, addr); break;
    case 2: outw (data, addr); break;
    case 4: outl (data, addr); break;
    }
    break;
  }

  if (op == 0) {
    if (rcode == 0) {
      if (rhex == 0)
	printf ("%d\n", data);
      else
	printf ("%x\n", data);
    } else
      exit (data);
  }
  exit (0);
}

static unsigned
get_int_or_die (const char *str)
{
  char *endp;
  unsigned r;

  errno = 0;
  r = strtoul (str, &endp, 0);
  if ((errno == ERANGE && r == ULONG_MAX)
      || (errno != 0 && r == 0)) {
    perror (str);
    exit (1);
  }

  if (str == endp) {
    fprintf (stderr, "expecting a number, but found an empty string\n");
    exit (1);
  }
  if (*endp != '\0') {
    fprintf (stderr, "%s: trailing garbage after number\n", str);
    exit (1);
  }

  return r;
}