Initial import from ioport-1.2.
[ioport.git] / port.c
1 /* I/O port access
2  * Copyright (C) 2009 Red Hat Inc.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <limits.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <getopt.h>
27 #include <errno.h>
28
29 #include <sys/io.h>
30
31 static int op = 0;              /* Operation, 0 = read, 1 = write. */
32 static int size = 1;            /* Operation size in bytes. */
33 static int rcode = 0;           /* If set, inX exits with return value. */
34 static int rhex = 0;            /* If set, print result in hex. */
35
36 static struct option long_options[] = {
37   { "code",  0, 0, 'c' },
38   { "read",  0, 0, 'r' },
39   { "size",  1, 0, 's' },
40   { "write", 0, 0, 'w' },
41   { "hex",   0, 0, 'x' },
42   { NULL,    0, 0, 0   },
43 };
44 static const char *options = "crs:w";
45
46 static void
47 usage ()
48 {
49   printf ("\n\
50 Usage:\n\
51   inb|inw|inl [--options] address\n\
52   outb|outw|outl [--options] address data\n\
53 Options:\n\
54 --code    Exit with status code instead of printing value (inX only).\n\
55 --hex     Print hex instead of decimal (inX only).\n\
56 --read    Perform a read (in) operation.\n\
57 --write   Perform a write (out) operation.\n\
58 --size N  Set size to N bytes where N = 1, 2 or 4.\n\
59 \n\
60 For detailed information, see the manual page inb(1).\n\
61 ");
62 }
63
64 static unsigned get_int_or_die (const char *);
65
66 int
67 main (int argc, char *argv[])
68 {
69   int c, option_index;
70   char *p;
71   unsigned addr = 0;
72   unsigned data = 0;
73
74   /* Find out how the program was invoked. */
75   p = strrchr (argv[0], '/');
76   if (p == NULL) p = argv[0]; else p++;
77
78   if (strncasecmp (p, "inb", 3) == 0) {
79     op = 0; size = 1;
80   } else if (strncasecmp (p, "outb", 4) == 0) {
81     op = 1; size = 1;
82   } else if (strncasecmp (p, "inw", 3) == 0) {
83     op = 0; size = 2;
84   } else if (strncasecmp (p, "outw", 4) == 0) {
85     op = 1; size = 2;
86   } else if (strncasecmp (p, "inl", 3) == 0) {
87     op = 0; size = 4;
88   } else if (strncasecmp (p, "outl", 4) == 0) {
89     op = 1; size = 4;
90   }
91
92   /* Parse command line arguments. */
93   while (1) {
94     option_index = 0;
95     c = getopt_long (argc, argv, options, long_options, &option_index);
96     if (c == -1) break;
97
98     switch (c) {
99     case 'c':
100       rcode = 1;
101       break;
102
103     case 'r':
104       op = 0;
105       break;
106
107     case 's':
108       size = get_int_or_die (optarg);
109       if (!(size == 1 || size == 2 || size == 4)) {
110         fprintf (stderr, "%s: size can only be 1, 2 or 4.\n", optarg);
111         exit (1);
112       }
113       break;
114
115     case 'w':
116       op = 1;
117       break;
118
119     case 'x':
120       rhex = 1;
121       break;
122
123     case 0:
124       fprintf (stderr, "internal error in getopt_long\n");
125       exit (1);
126
127     default:
128       usage ();
129       exit (1);
130     }
131   }
132
133   /* Parse the address (for read/write) and data (for writes). */
134   if (optind >= argc) {
135   missing:
136     fprintf (stderr, "%s: missing parameter, see --help or man page\n",
137              argv[0]);
138     exit (1);
139   }
140
141   addr = get_int_or_die (argv[optind++]);
142
143   if (op == 1) {
144     if (optind >= argc) goto missing;
145     data = get_int_or_die (argv[optind++]);
146   }
147
148   if (optind != argc) {
149     fprintf (stderr,
150              "%s: extra parameters on command line, see --help or man page\n",
151              argv[0]);
152     exit (1);
153   }
154
155   /* Raise our privilege level. */
156   if (iopl (3) == -1) {
157     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");
158     perror ("iopl");
159     exit (1);
160   }
161
162   /* Perform the operation. */
163   switch (op) {
164   case 0:
165     switch (size) {
166     case 1: data = inb (addr); break;
167     case 2: data = inw (addr); break;
168     case 4: data = inl (addr); break;
169     }
170     break;
171   case 1:
172     switch (size) {
173     case 1: outb (data, addr); break;
174     case 2: outw (data, addr); break;
175     case 4: outl (data, addr); break;
176     }
177     break;
178   }
179
180   if (op == 0) {
181     if (rcode == 0) {
182       if (rhex == 0)
183         printf ("%d\n", data);
184       else
185         printf ("%x\n", data);
186     } else
187       exit (data);
188   }
189   exit (0);
190 }
191
192 static unsigned
193 get_int_or_die (const char *str)
194 {
195   char *endp;
196   unsigned r;
197
198   errno = 0;
199   r = strtoul (str, &endp, 0);
200   if ((errno == ERANGE && r == ULONG_MAX)
201       || (errno != 0 && r == 0)) {
202     perror (str);
203     exit (1);
204   }
205
206   if (str == endp) {
207     fprintf (stderr, "expecting a number, but found an empty string\n");
208     exit (1);
209   }
210   if (*endp != '\0') {
211     fprintf (stderr, "%s: trailing garbage after number\n", str);
212     exit (1);
213   }
214
215   return r;
216 }