/* 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;
}