/*	$OpenBSD: nvram.c,v 1.4 2015/02/10 21:56:09 miod Exp $ */

/*
 * Copyright (c) 2004 Joshua Stein <jcs@openbsd.org>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/uio.h>
#include <sys/fcntl.h>
#include <sys/conf.h>

#include <dev/ic/mc146818reg.h>
#include <i386/isa/nvram.h>

/* checksum is calculated over bytes 2 to 31 and stored in byte 32 */
#define NVRAM_CSUM_START	(MC_NVRAM_START + 2)
#define NVRAM_CSUM_END		(MC_NVRAM_START + 31)
#define NVRAM_CSUM_LOC		(MC_NVRAM_START + 32)

#define NVRAM_SIZE		(128 - MC_NVRAM_START)

/* #define NVRAM_DEBUG 1 */

void nvramattach(int);

int nvramopen(dev_t dev, int flag, int mode, struct proc *p);
int nvramclose(dev_t dev, int flag, int mode, struct proc *p);
int nvramread(dev_t dev, struct uio *uio, int flags);

int nvram_csum_valid(void);
int nvram_get_byte(int byteno);

static int nvram_initialized;

void
nvramattach(int num)
{
	if (num > 1)
		return;

	if (nvram_initialized || nvram_csum_valid()) {
#ifdef NVRAM_DEBUG
		printf("nvram: initialized\n");
#endif
		nvram_initialized = 1;
	} else
		printf("nvram: invalid checksum\n");
}

int
nvramopen(dev_t dev, int flag, int mode, struct proc *p)
{
	/* TODO: re-calc checksum on every open? */

	if ((minor(dev) != 0) || (!nvram_initialized))
		return (ENXIO);

	if ((flag & FWRITE))
		return (EPERM);

	return (0);
}

int
nvramclose(dev_t dev, int flag, int mode, struct proc *p)
{
	return (0);
}

int
nvramread(dev_t dev, struct uio *uio, int flags)
{
	u_char buf[NVRAM_SIZE];
	u_int pos = uio->uio_offset;
	u_char *tmp;
	int count = min(sizeof(buf), uio->uio_resid);
	int ret;

	if (!nvram_initialized)
		return (ENXIO);

	if (uio->uio_resid == 0)
		return (0);

#ifdef NVRAM_DEBUG
	printf("attempting to read %d bytes at offset %d\n", count, pos);
#endif

	for (tmp = buf; count-- > 0 && pos < NVRAM_SIZE; ++pos, ++tmp)
		*tmp = nvram_get_byte(pos);

#ifdef NVRAM_DEBUG
	printf("nvramread read %d bytes (%s)\n", (tmp - buf), tmp);
#endif

	ret = uiomovei((caddr_t)buf, (tmp - buf), uio);

	uio->uio_offset += uio->uio_resid;

	return (ret);
}

int
nvram_get_byte(int byteno)
{
	if (!nvram_initialized)
		return (ENXIO);

	return (mc146818_read(NULL, byteno + MC_NVRAM_START) & 0xff);
}

int
nvram_csum_valid()
{
	u_short csum = 0;
	u_short csumexpect;
	int nreg;

	for (nreg = NVRAM_CSUM_START; nreg <= NVRAM_CSUM_END; nreg++)
		csum += mc146818_read(NULL, nreg);

	csumexpect = mc146818_read(NULL, NVRAM_CSUM_LOC) << 8 |
	    mc146818_read(NULL, NVRAM_CSUM_LOC + 1);

#ifdef NVRAM_DEBUG
	printf("nvram: checksum is %x, expecting %x\n", (csum & 0xffff),
		csumexpect);
#endif

	return ((csum & 0xffff) == csumexpect);
}
