#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/spi/spidev.h>
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define SPI_DEV "/dev/spidev0.0"

int spi_open(const char *dev)
{
	int fd = -1;
	uint8_t spi_mode = SPI_MODE_0;

	if ((fd = open(dev, O_RDWR)) < 0)
		goto err;
	if (ioctl(fd, SPI_IOC_WR_MODE, &spi_mode) < 0)
		goto err;
	if (ioctl(fd, SPI_IOC_RD_MODE, &spi_mode) < 0)
		goto err;
	return fd;
err:
	if (fd >= 0)
		close(fd);
	return -1;
}

int transfer(int fd, int write, uint16_t addr, uint8_t val)
{
	struct spi_ioc_transfer tr[1];
	uint8_t tx[5];
	uint8_t rx[5];
	int ret;

	memset(tr, 0, sizeof(tr[0]));
	memset(tx, 0, sizeof(tx));
	memset(rx, 0, sizeof(rx));

	if (write) {
		tx[0] = 0x40;
		tx[4] = val;
	} else {
		tx[0] = 0x60;
	}

	tx[1] = (addr & 0xf800) >> 11;
	tx[2] = (addr & 0x7f8) >> 3;
	tx[3] = (addr & 0x7) << 5;

	tr[0].tx_buf = (unsigned long)tx;
	tr[0].rx_buf = (unsigned long)rx;
	tr[0].len = sizeof(tx);
	tr[0].delay_usecs = 0;
	tr[0].speed_hz = 1000000;
	tr[0].bits_per_word = 8;
	tr[0].cs_change = 0;

#if 0
	{
		int i;
		for (i = 0; i < sizeof(tx); i++) {
			printf("%02x ", tx[i]);
		}
		printf("\n");
	}
#endif
	if ((ret = ioctl(fd, SPI_IOC_MESSAGE(1), tr)) < 0)
		fprintf(stderr, "Error: Could not transfer(%d).\n", ret);
	else {
		if (write == 0)
			printf("0x%04x == 0x%02x\n", addr, rx[4]);
		else
			printf("0x%04x = 0x%02x\n", addr, tx[4]);
	}
	return ret;
}

void dump_usage(const char *cmd, int e)
{
	printf("%s <address>[ <value>]\n", cmd);
	printf("  address: 0x0000 - 0xffff\n");
	printf("  value  : 0x00 - 0xff\n");
	_exit(e);
}

int main(int argc, char *argv[])
{
	int fd = -1;
	int addr = -1;
	int val = -1;
	int write = 0;
	int r;

	if (!argv[1])
		dump_usage(argv[0], 1);
	addr = strtol(argv[1], NULL, 0);
	if (addr < 0 || addr > 0xffff)
		dump_usage(argv[0], 1);

	if (argv[2]) {
		val = strtol(argv[2], NULL, 0);
		if (val < 0 || val > 0xff)
			dump_usage(argv[0], 1);
		write = 1;
	}

	if ((fd = spi_open(SPI_DEV)) < 0) {
		fprintf(stderr, "Error: Could not open device(%s).\n", SPI_DEV);
		_exit(2);
	}
	if ((r = transfer(fd, write, (uint16_t)addr, (uint8_t)val)) < 0) {
		fprintf(stderr, "Error: Could not transfer(%d).\n", r);
		_exit(3);
	}
	close(fd);
	_exit(0);
}
