/*
 * poke.c - Read or write any CPU register
 *
 * Copyright (C) 2008 by OpenMoko, Inc.
 * Written by Werner Almesberger <werner@openmoko.org>
 * All Rights Reserved
 *  
 * 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.
 */


#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>


#define PAGE_SIZE 4096
#define MEM(bits, addr) (*(uint##bits##_t *) (mem+((addr) & (PAGE_SIZE-1))))

static volatile void *mem;


/* ----- Command-line parsing ---------------------------------------------- */


static void __attribute__((noreturn)) usage(const char *name)
{
	fprintf(stderr, "usage: %s [-8|-16|-32] hex_address [value]\n", name);
	exit(1);
}


int main(int argc, char **argv)
{
	int fd;
	char *end;
	unsigned long addr;
	uint32_t val;
	int bits = 32;
	char **args = argv+1;

	fd = open("/dev/mem", O_RDWR);
        if (fd < 0) {
		perror("/dev/mem");
		exit(1);
	}
	if (argc > 1 && *argv[1] == '-') {
		if (!strcmp(argv[1], "-8"))
			bits = 8;
		else if (!strcmp(argv[1], "-16"))
			bits = 16;
		else if (!strcmp(argv[1], "-32"))
			bits = 32;
		else
		    usage(*argv);
		args++;
		argc--; /* dirty */
	}
	switch (argc) {
	case 3:
		val = strtoul(args[1], &end, 0);
		if (*end)
			usage(*argv);
		/* fall through */
	case 2:
		addr = strtoul(args[0], &end, 16);
		if (*end)
			usage(*argv);
		break;
	default:
		usage(*argv);
	}
	mem = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
	    addr & ~(PAGE_SIZE-1));
	if (mem == MAP_FAILED) {
		perror("mmap");
		exit(1);
	}
	if (argc == 2) {
		switch (bits) {
		case 8:
			printf("0x%02lx\n", (unsigned long) MEM(8, addr));
			break;
		case 16:
			printf("0x%04lx\n", (unsigned long) MEM(16, addr));
			break;
		case 32:
			printf("0x%08lx\n", (unsigned long) MEM(32, addr));
			break;
		}
	}
	else {
		switch (bits) {
		case 8:
			MEM(8, addr) = val;
			break;
		case 16:
			MEM(16, addr) = val;
			break;
		case 32:
			MEM(32, addr) = val;
			break;
		}
	}
	return 0;
}
