aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbryan newbold <bnewbold@leaflabs.com>2013-12-04 11:46:33 -0500
committerbryan newbold <bnewbold@leaflabs.com>2013-12-04 13:03:30 -0500
commit6799bf57fda003c2e8e3a1fa397ed69b7b8d4e18 (patch)
treea839c73d45e6551a6b713604783b6789705353a5
downloaduioctl-6799bf57fda003c2e8e3a1fa397ed69b7b8d4e18.tar.gz
uioctl-6799bf57fda003c2e8e3a1fa397ed69b7b8d4e18.zip
initial commit
-rw-r--r--.gitignore8
-rw-r--r--README54
-rw-r--r--uioctl.c232
3 files changed, 294 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f466019
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+*.o
+*.a
+*.pyc
+*~
+*.swp
+.*
+*.tmp
+*.old
diff --git a/README b/README
new file mode 100644
index 0000000..1030a30
--- /dev/null
+++ b/README
@@ -0,0 +1,54 @@
+
+ _ _ _
+ _ _(_) ___ ___| |_| |
+ | | | | |/ _ \ / __| __| |
+ | |_| | | (_) | (__| |_| |
+ \__,_|_|\___/ \___|\__|_|
+
+
+trivial utility for manipulating simple Linux UIO devices ("Userspace I/O").
+eg, reading and writing bytes to memory mapped devices or monitoring
+interrupts.
+can list all devices and their mappings.
+little-endian by default.
+
+-------------------------------------------------------------------------------
+
+gcc uioctl.c -o uioctl
+
+-------------------------------------------------------------------------------
+
+./uioctl -h
+little-endian by default
+ -w select word width (default: 4)
+ -s size of mmap (default: base + length, rounded up to PAGE_SIZE)
+
+./uioctl /dev/uio0 0 -n 100 -w 1 -r 0
+./uioctl /dev/uio0 0x818 0
+./uioctl /dev/uio0 -m
+./uioctl -l
+
+also, secret trick:
+
+sudo ./uioctl /dev/mem 0x818 -n 5
+
+BUT WAIT?
+
+hexdump -C /dev/uio0 -s 0x
+
+-------------------------------------------------------------------------------
+
+UIO: user-space drivers (2007)
+https://lwn.net/Articles/232575/
+
+The Userspace I/O HOWTO (2006-2009)
+https://www.kernel.org/doc/htmldocs/uio-howto/
+
+Simple userland drivers for FPGA interfaces (eg, AXI)
+http://svenand.blogdrive.com/archive/150.html
+
+devmem2.c: Simple program to read/write from/to any location in memory
+http://sources.buildroot.net/devmem2.c
+
+pydevmem
+https://github.com/kylemanna/pydevmem
diff --git a/uioctl.c b/uioctl.c
new file mode 100644
index 0000000..6558844
--- /dev/null
+++ b/uioctl.c
@@ -0,0 +1,232 @@
+/*
+ * uioctl.c: Userspace I/O manipulation utility
+ *
+ * Copyright (C) 2013, Bryan Newbold <bnewbold@leaflabs.com>
+ *
+ * ISC License: Permission to use, copy, modify, and/or distribute this
+ * software for any purpose with or without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice appear in all
+ * copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Changelog:
+ * 2013-12-04: bnewbold: Initial version; missing width management, list mode
+ *
+ */
+
+#include <errno.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/timeb.h>
+
+#define DEFAULT_WIDTH 4
+#define PROGRAM_NAME "uioctl"
+
+enum mode_type {
+ MODE_READ,
+ MODE_WRITE,
+ MODE_LIST,
+ MODE_MONITOR
+};
+
+static void usage(int exit_status) {
+ fprintf(exit_status == EXIT_SUCCESS ? stdout : stderr,
+ "Usage: %s [options] [-l] [/dev/uioX [-m] [<addr> [<value>]]]\n"
+ "\n"
+ "Functions:\n"
+ " monitor (-m) the device for interrupts\n"
+ " list (-l) all devices and their mappings\n"
+ " read words from <addr>\n"
+ " write <value> to <addr> (will zero-pad word width)\n"
+ "\n"
+ "Options:\n"
+ " -r\tselect the device's memory region to map (default: 0)\n"
+ " -w\tword size (in bytes; default: %d)\n"
+ " -n\tnumber of words to read (in words; default: 1)\n"
+ " -x\texit with success after the first interrupt (implies -m mode)\n"
+ ,
+ PROGRAM_NAME, DEFAULT_WIDTH);
+ exit(exit_status);
+}
+
+static void list_devices() {
+ // TODO:
+ // - try to list /sys/devices/uio*
+ // - for each in the above, print memory regions
+ fprintf(stderr, "listing not yet implemented\n");
+ exit(EXIT_FAILURE);
+}
+
+static void monitor(char *fpath, int forever) {
+ char buf[4];
+ int bytes;
+ int fd;
+ struct timeb tp;
+ char startval[] = {0,0,0,1};
+
+ printf("Waiting for interrupts on %s\n", fpath);
+ fd = open(fpath, O_RDWR);
+ if (fd < 1) {
+ perror("uioctl");
+ fprintf(stderr, "Couldn't open UIO device file: %s\n", fpath);
+ exit(EXIT_FAILURE);
+ }
+ do {
+ bytes = pwrite(fd, &startval, 4, 0);
+ if (bytes != 4) {
+ perror("uioctl");
+ fprintf(stderr, "Problem clearing device file\n");
+ exit(EXIT_FAILURE);
+ }
+ //lseek(fd, 0, SEEK_SET);
+ bytes = pread(fd, buf, 4, 0);
+ ftime(&tp);
+ if (bytes != 4) {
+ perror("uioctl");
+ fprintf(stderr, "Problem reading from device file\n");
+ exit(EXIT_FAILURE);
+ }
+ printf("[%ld.%03d] interrupt: %d\n", tp.time, tp.millitm,
+ (buf[3] * 16777216 + buf[2] * 65536 + buf[1] * 256 + buf[0]));
+ } while (forever);
+ close(fd);
+ exit(EXIT_SUCCESS);
+}
+
+int main(int argc, char *argv[]) {
+ int c;
+ int fd;
+ enum mode_type mode = MODE_READ;
+ char *fpath;
+ void *ptr;
+ int map_size, value, addr;
+ int region = 0;
+ int count = 1;
+ int width = DEFAULT_WIDTH;
+ int forever = 1;
+
+ while((c = getopt(argc, argv, "hlmxr:n:w:")) != -1) {
+ errno = 0;
+ switch(c) {
+ case 'h':
+ usage(EXIT_SUCCESS);
+ case 'l':
+ list_devices();
+ case 'm':
+ mode = MODE_MONITOR;
+ break;
+ case 'x':
+ mode = MODE_MONITOR;
+ forever = 0;
+ break;
+ case 'r':
+ region = strtoul(optarg, NULL, 0);
+ if (errno) {
+ perror("uioctl");
+ exit(EXIT_FAILURE);
+ }
+ if (region != 0) {
+ fprintf(stderr, "region != 0 not yet implemented\n");
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'n':
+ count = strtoul(optarg, NULL, 0);
+ if (errno) {
+ perror("uioctl");
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'w':
+ width = strtoul(optarg, NULL, 0);
+ if (errno) {
+ perror("uioctl");
+ exit(EXIT_FAILURE);
+ }
+ if (width != 4) {
+ fprintf(stderr, "width != 4 not yet implemented\n");
+ exit(EXIT_FAILURE);
+ }
+ break;
+ default:
+ fprintf(stderr, "Unexpected argument; try -h\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ if (mode == MODE_MONITOR) {
+ if ((argc - optind) != 1) {
+ fprintf(stderr, "Wrong number of arguments; try -h\n");
+ exit(EXIT_FAILURE);
+ }
+ fpath = argv[optind];
+ monitor(fpath, forever);
+ }
+ if (mode == MODE_READ) {
+ if ((argc - optind) == 2) {
+ fpath = argv[optind++];
+ errno = 0;
+ addr = strtoul(argv[optind++], NULL, 0);
+ if (errno) {
+ perror("uioctl");
+ exit(EXIT_FAILURE);
+ }
+ //printf("addr: 0x%08x\n", addr);
+ } else
+ if ((argc - optind) == 3) {
+ mode = MODE_WRITE;
+ fpath = argv[optind++];
+ errno = 0;
+ addr = strtoul(argv[optind++], NULL, 0);
+ value = strtoul(argv[optind++], NULL, 0);
+ if (errno) {
+ perror("uioctl");
+ exit(EXIT_FAILURE);
+ }
+ //printf("addr: 0x%08x\n", addr);
+ //printf("value: 0x%08x\n", value);
+ } else {
+ fprintf(stderr, "Wrong number of arguments; try -h\n");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ map_size = count * width;
+
+ fd = open(fpath, O_RDWR|O_SYNC);
+ if (fd < 1) {
+ perror("uioctl");
+ fprintf(stderr, "Couldn't open UIO device file: %s\n", fpath);
+ return EXIT_FAILURE;
+ }
+ ptr = mmap(NULL, map_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+
+ /* TODO: do the things... */
+ /* value = *((unsigned *) (ptr + offset); */
+ if (mode == MODE_READ) {
+ for (; count > 0; count--) {
+ value = *((unsigned *) (ptr + addr));
+ printf("0x%08x\t%08x\n", addr, value);
+ addr += width;
+ }
+ } else {
+ *((unsigned *)(ptr + addr)) = value;
+ }
+
+ /* clean up */
+ munmap(ptr, map_size);
+ close(fd);
+
+ return EXIT_SUCCESS;
+}