/*
 * hermes.c
 *
 * This file contains the lower level access functions for Prism based 
 * wireless cards. The file is based on hermes.c of the Linux kernel
 *
 * Adjusted to Minix by Stevens Le Blond <slblond@few.vu.nl> 
 *		    and Michael Valkering <mjvalker@cs.vu.nl>
 */

/* Original copyright notices from Linux hermes.c
 * 
 * Copyright (C) 2000, David Gibson, Linuxcare Australia 
 * <hermes@gibson.dropbear.id.au>
 * Copyright (C) 2001, David Gibson, IBM <hermes@gibson.dropbear.id.au>
 * 
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License
 * at http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
 * the License for the specific language governing rights and
 * limitations under the License.
 *
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License version 2 (the "GPL"), in
 * which case the provisions of the GPL are applicable instead of the
 * above.  If you wish to allow the use of your version of this file
 * only under the terms of the GPL and not to allow others to use your
 * version of this file under the MPL, indicate your decision by
 * deleting the provisions above and replace them with the notice and
 * other provisions required by the GPL.  If you do not delete the
 * provisions above, a recipient may use your version of this file
 * under either the MPL or the GPL.
 */

#include "hermes.h"

/*****************************************************************************
 *            milli_delay                                                    *
 *                                                                           *
 * Wait msecs milli seconds                                                  *
 *****************************************************************************/
PRIVATE void milli_delay(unsigned int msecs)
{
	micro_delay((long)msecs * 1000);
}


/*****************************************************************************
 *            hermes_issue_cmd                                               *
 *                                                                           *
 * Issue a command to the chip. Waiting for it to complete is the caller's   *
 * problem. The only thing we have to do first is to see whether we can      *
 * actually write something in the CMD register: is it unbusy?               *
 * Returns -EBUSY if the command register is busy, 0 on success.             *
 *****************************************************************************/
static int hermes_issue_cmd (hermes_t * hw, u16_t cmd, u16_t param0) {
	int k = HERMES_CMD_BUSY_TIMEOUT;
	u16_t reg;

	/* First wait for the command register to unbusy */
	reg = hermes_read_reg (hw, HERMES_CMD);
	while ((reg & HERMES_CMD_BUSY) && k) {
		k--;
		micro_delay (1);
		reg = hermes_read_reg (hw, HERMES_CMD);
	}
	/* it takes too long. Bailing out */
	if (reg & HERMES_CMD_BUSY) {
		printf("Hermes: HERMES_CMD_BUSY timeout\n");
		return -EBUSY;
	}

	/* write the values to the right registers */
	hermes_write_reg (hw, HERMES_PARAM2, 0);
	hermes_write_reg (hw, HERMES_PARAM1, 0);
	hermes_write_reg (hw, HERMES_PARAM0, param0);
	hermes_write_reg (hw, HERMES_CMD, cmd);
	return 0;
}

/*****************************************************************************
 *            hermes_struct_init                                             *
 *                                                                           *
 * Initialize the hermes structure fields                                    *
 *****************************************************************************/
void hermes_struct_init (hermes_t * hw, u32_t address,
		    int io_space, int reg_spacing) {
	hw->iobase = address;
	hw->io_space = io_space;
	hw->reg_spacing = reg_spacing;
	hw->inten = 0x0;
}


/*****************************************************************************
 *            hermes_cor_reset                                               *
 *                                                                           *
 * This is the first step in initializing the card's firmware and hardware:  *
 * write HERMES_PCI_COR_MASK to the Configuration Option Register            *
 *****************************************************************************/
int hermes_cor_reset (hermes_t *hw) {
	int k;
	u16_t reg;

	/* Assert the reset until the card notice */
	hermes_write_reg (hw, HERMES_PCI_COR, HERMES_PCI_COR_MASK);

	milli_delay (HERMES_PCI_COR_ONT);

	/* Give time for the card to recover from this hard effort */
	hermes_write_reg (hw, HERMES_PCI_COR, 0x0000);

	milli_delay (HERMES_PCI_COR_OFFT);

	/* The card is ready when it's no longer busy */
	k = HERMES_PCI_COR_BUSYT;
	reg = hermes_read_reg (hw, HERMES_CMD);
	while (k && (reg & HERMES_CMD_BUSY)) {
		k--;
		milli_delay (1);
		reg = hermes_read_reg (hw, HERMES_CMD);
	}

	/* Did we timeout ? */
	if (reg & HERMES_CMD_BUSY) {
		printf ("Busy timeout after resetting the COR\n");
		return -1;
	}

	return (0);
}


/*****************************************************************************
 *            hermes_present                                                 *
 *                                                                           *
 * Check whether we have access to the card. Does the SWSUPPORT0 contain the *
 * value we put in it earlier?                                               *
 *****************************************************************************/
PRIVATE int hermes_present (hermes_t * hw) {
	int i = hermes_read_reg (hw, HERMES_SWSUPPORT0) == HERMES_MAGIC;
	if (!i)
		printf("Hermes: Error, card not present?\n");
	return i;
}


/*****************************************************************************
 *            hermes_init                                                    *
 *                                                                           *
 * Initialize the card                                                       *
 *****************************************************************************/
int hermes_init (hermes_t * hw)
{
	u32_t status, reg, resp0;
	int err = 0;
	int k;

	/* We don't want to be interrupted while resetting the chipset. By 
	 * setting the control mask for hardware interrupt generation to 0,
	 * we won't be disturbed*/
	hw->inten = 0x0;
	hermes_write_reg (hw, HERMES_INTEN, 0);

	/* Acknowledge any pending events waiting for acknowledgement. We 
	 * assume there won't be any important to take care off */
	hermes_write_reg (hw, HERMES_EVACK, 0xffff);

	/* Normally it's a "can't happen" for the command register to
	 * be busy when we go to issue a command because we are
	 * serializing all commands.  However we want to have some
	 * chance of resetting the card even if it gets into a stupid
	 * state, so we actually wait to see if the command register
	 * will unbusy itself here. */
	k = HERMES_CMD_BUSY_TIMEOUT;
	reg = hermes_read_reg (hw, HERMES_CMD);
	while (k && (reg & HERMES_CMD_BUSY)) {
		if (reg == 0xffff) {
			/* Special case - the card has probably 
			 *  been removed, so don't wait for the 
			 *  timeout */
			printf("Hermes: Card removed?\n");
			return -ENODEV;
		}

		k--;
		micro_delay (1);
		reg = hermes_read_reg (hw, HERMES_CMD);
	}

	/* No need to explicitly handle the timeout - if we've timed
	 * out hermes_issue_cmd() will probably return -EBUSY below.
	 * But i check to be sure :-) */
	if (reg & HERMES_CMD_BUSY) {
		printf("Hermes: Timeout waiting for the CMD_BUSY to unset\n");
		return -EBUSY;
	}

	/* According to the documentation, EVSTAT may contain
	 * obsolete event occurrence information.  We have to acknowledge
	 * it by writing EVACK. */
	reg = hermes_read_reg (hw, HERMES_EVSTAT);
	hermes_write_reg (hw, HERMES_EVACK, reg);

	err = hermes_issue_cmd (hw, HERMES_CMD_INIT, 0);
	if (err){
		printf("Hermes: errornr: 0x%x issueing HERMES_CMD_INIT\n",
			 err);
		return err;
	}

	/* here we start waiting for the above command,CMD_INIT, to complete.
	 * Completion is noticeable when the HERMES_EV_CMD bit in the 
	 * HERMES_EVSTAT register is set to 1 */
	reg = hermes_read_reg (hw, HERMES_EVSTAT);
	k = HERMES_CMD_INIT_TIMEOUT;
	while ((!(reg & HERMES_EV_CMD)) && k) {
		k--;
		micro_delay (10);
		reg = hermes_read_reg (hw, HERMES_EVSTAT);
	}


	/* the software support register 0 (there are 3) is filled with a 
	 * magic number. With this one can test the availability of the card */
	hermes_write_reg (hw, HERMES_SWSUPPORT0, HERMES_MAGIC);

	if (!hermes_present (hw)) {
		printf("Hermes: Card not present?: got mag. nr.0x%x\n",
			 hermes_read_reg (hw, HERMES_SWSUPPORT0));
	}

	if (!(reg & HERMES_EV_CMD)) {
		printf("hermes @ %lx: Timeout waiting for card to reset\n",
			hw->iobase);
		return -ETIMEDOUT;
	}

	status = hermes_read_reg (hw, HERMES_STATUS);
	resp0 = hermes_read_reg (hw, HERMES_RESP0);

	/* after having issued the command above, the completion set a bit in 
	 * the EVSTAT register. This has to be acknowledged, as follows */
	hermes_write_reg (hw, HERMES_EVACK, HERMES_EV_CMD);

	/* Was the status, the result of the issued command, ok? */
	/* The expression below should be zero. Non-zero means an error */
	if (status & HERMES_STATUS_RESULT) {
		printf("Hermes:Result of INIT_CMD wrong.error value: 0x%lx\n",
			(status & HERMES_STATUS_RESULT) >> 8);
		err = -EIO;
	}

	return err;
}

/*****************************************************************************
 *            hermes_docmd_wait                                              *
 *                                                                           *
 * Issue a command to the chip, and (busy) wait for it to complete.          *
 *****************************************************************************/
int hermes_docmd_wait (hermes_t * hw, u16_t cmd, u16_t parm0,
		   hermes_response_t * resp) {
	int err;
	int k;
	u16_t reg;
	u16_t status;

	err = hermes_issue_cmd (hw, cmd, parm0);
	if (err) {
		printf("hermes @ %lx: Error %d issuing command.\n",
			 hw->iobase, err);
		return err;
	}

	/* Reads the Event status register. When the command has completed,
	 * the fourth bit in the HERMES_EVSTAT register is a 1. We will be
	 * waiting for that to happen */
	reg = hermes_read_reg (hw, HERMES_EVSTAT);
	k = HERMES_CMD_COMPL_TIMEOUT;
	while ((!(reg & HERMES_EV_CMD)) && k) {
		k--;
		micro_delay (10);
		reg = hermes_read_reg (hw, HERMES_EVSTAT);
	}

	/* check for a timeout: has the command still not completed? */
	if (!(reg & HERMES_EV_CMD)) {
		printf("hermes @ %lx: Timeout waiting for command \
		completion.\n", hw->iobase);
		err = -ETIMEDOUT;
		return err;
	}

	status = hermes_read_reg (hw, HERMES_STATUS);
	/* some commands result in results residing in response registers.
	 * They have to be read before the acknowledgement below.
	 */
	if (resp) {
		resp->status = status;
		resp->resp0 = hermes_read_reg (hw, HERMES_RESP0);
		resp->resp1 = hermes_read_reg (hw, HERMES_RESP1);
		resp->resp2 = hermes_read_reg (hw, HERMES_RESP2);
	}

	/* After issueing a Command, the card expects an Acknowledgement */
	hermes_write_reg (hw, HERMES_EVACK, HERMES_EV_CMD);

	/* check whether there has been a valid value in the Status register. 
	 * the high order bits should have at least some value */
	if (status & HERMES_STATUS_RESULT) {
		printf("Hermes: EIO\n");
		err = -EIO;
	}

	return err;
}


/*****************************************************************************
 *            hermes_allocate                                                *
 *                                                                           *
 * Allocate bufferspace in the card, which will be then available for        *
 * writing by the host, TX buffers. The card will try to find enough memory  *
 * (creating a list of 128 byte blocks) and will return a pointer to the     *
 * first block. This pointer is a pointer to the frame identifier (fid),     *
 * holding information and data of the buffer. The fid is like a file        *
 * descriptor, a value indicating some resource                              *
 *****************************************************************************/
int hermes_allocate (hermes_t * hw, u16_t size, u16_t * fid) {
	int err = 0;
	int k;
	u16_t reg;

	if ((size < HERMES_ALLOC_LEN_MIN) || (size > HERMES_ALLOC_LEN_MAX))	{
		printf("Hermes: Invalid size\n");
		return -EINVAL;
	}

	/* Issue a allocation request to the card, waiting for the command 
	 * to complete */
	err = hermes_docmd_wait (hw, HERMES_CMD_ALLOC, size, NULL);
	if (err) {
		printf( "Hermes: docmd_wait timeout\n");
		return err;
	}

	/* Read the status event register to know whether the allocation
	 * succeeded. The HERMES_EV_ALLOC bit should be set */
	reg = hermes_read_reg (hw, HERMES_EVSTAT);
	k = HERMES_ALLOC_COMPL_TIMEOUT;
	while ((!(reg & HERMES_EV_ALLOC)) && k) {
		k--;
		micro_delay (10);
		reg = hermes_read_reg (hw, HERMES_EVSTAT);
	}

	/* tired of waiting to complete. Abort. */
	if (!(reg & HERMES_EV_ALLOC)) {
		printf("hermes @ %lx:Timeout waiting for frame allocation\n",
			 hw->iobase);
		return -ETIMEDOUT;
	}

	/* When we come here, everything has gone well. The pointer to the
	 * fid is in the ALLOCFID register. This fid is later on used
	 * to access this buffer */
	*fid = hermes_read_reg (hw, HERMES_ALLOCFID);

	/* always acknowledge the receipt of an event */
	hermes_write_reg (hw, HERMES_EVACK, HERMES_EV_ALLOC);

	return 0;
}



/*****************************************************************************
 *            hermes_bap_seek                                                *
 *                                                                           *
 * Set up a Buffer Access Path (BAP) to read a particular chunk of data      *
 * from card's internal buffer. Setting a bap register is like doing a fseek *
 * system call: setting an internal pointer to the right place in a buffer   *
 *****************************************************************************/
static int hermes_bap_seek (hermes_t * hw, int bap, u16_t id, u16_t offset) {

	/* There are 2 BAPs. This can be used to use the access buffers
	 * concurrently: 1 for writing in the TX buffer and 1 for reading
	 * a RX buffer in case of an RX interrupt.
	 * The BAP consists of 2 registers, together with which one can
	 * point to a single byte in the required buffer (additionally 
	 * there is a third register, but that one is not used in this 
	 * function, the data register). With the SELECT register one chooses 
	 * the fid, with the OFFSET register one chooses the offset in the fid 
	 * buffer */
	int sreg = bap ? HERMES_SELECT1 : HERMES_SELECT0;
	int oreg = bap ? HERMES_OFFSET1 : HERMES_OFFSET0;
	int k;
	u16_t reg;

	/* Check whether the offset is not too large, and whether it is a
	 * number of words. Offset can't be odd */
	if ((offset > HERMES_BAP_OFFSET_MAX) || (offset % 2)) {
		printf("Hermes: Offset error\n");
		return -EINVAL;
	}

	/* We can't write to the offset register when the busy flag is set. If 
	 * it is set, wait to automatically reset*/
	k = HERMES_BAP_BUSY_TIMEOUT;
	reg = hermes_read_reg (hw, oreg);
	while ((reg & HERMES_OFFSET_BUSY) && k)	{
		k--;
		micro_delay (1);
		reg = hermes_read_reg (hw, oreg);
	}

	/* For some reason, the busy flag didn't reset automatically. Return */
	if (reg & HERMES_OFFSET_BUSY) {
		printf("Hermes: HERMES_OFFSET_BUSY still set, oreg: 0x%x\n",
				 reg);
		return -ETIMEDOUT;
	}

	/* Now we actually set up the transfer. Write the fid in the select 
	 * register, and the offset in the offset register */
	hermes_write_reg (hw, sreg, id);
	hermes_write_reg (hw, oreg, offset);

	/* Wait for the BAP to be ready. This means that at first the 
	 * OFFSET_BUSY bit is set by the card once we have written the values 
	 * above. We wait until the card has done its internal processing and 
	 * unset the OFFSET_BUSY bit */
	k = HERMES_BAP_BUSY_TIMEOUT;
	reg = hermes_read_reg (hw, oreg);
	while ((reg & (HERMES_OFFSET_BUSY | HERMES_OFFSET_ERR)) && k) {
		k--;
		micro_delay (1);
		reg = hermes_read_reg (hw, oreg);
	}

	/* Busy bit didn't reset automatically */
	if (reg & HERMES_OFFSET_BUSY) {
		printf("Hermes: Error with fid 0x%x. Err: 0x%x\n", id, reg);
		return -ETIMEDOUT;
	}

	/* There has gone something wrong: offset is outside the buffer 
	 * boundary or the fid is not correct */
	if (reg & HERMES_OFFSET_ERR) {
		printf("Hermes: Error with fid 0x%x. Err: 0x%x\n", id, reg);
		return -EIO;
	}

	/* If we arrive here, the buffer can be accessed through the data 
	 * register associated with the BAP */
	return 0;
}


/*****************************************************************************
 *            hermes_bap_pread                                               *
 *                                                                           *
 * Read a block of data from the chip's buffer, via the BAP. len must be     *
 * even.                                                                     *
 *****************************************************************************/
int hermes_bap_pread (hermes_t * hw, int bap, void *buf, unsigned len,
		  u16_t id, u16_t offset) {
	/* The data register is the access point for the buffer made
	 * available by setting the BAP right. Which BAP does the user 
	 * want to use? there are 2 of them */
	int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
	int err = 0;

	/* reading (and writing) data goes a word a time, so should be even */
	if ((len % 2))	{
		printf("Hermes: Error in length to be read\n");
		return -EINVAL;
	}

	/* Set the cards internal pointer to the right fid and to the right
	 * offset */
	err = hermes_bap_seek (hw, bap, id, offset);
	if (err) {
		printf("Hermes: error hermes_bap_seek in hermes_bap_pread\n");
		return err;
	}
	/* Actually do the transfer. The length is divided by 2 because
	 * transfers go a word at a time as far as the card is concerned */
	hermes_read_words (hw, dreg, buf, len / 2);

	return err;
}

/*****************************************************************************
 *            hermes_write_words                                             *
 *                                                                           *
 * Write a sequence of words of the buffer to the card                       *
 *****************************************************************************/
void hermes_write_words (hermes_t * hw, int off, const void *buf, 
						unsigned count) {
	int i = 0;

	for (i = 0; i < count; i++)	{
		hermes_write_reg (hw, off, *((u16_t *) buf + i));
	}
}

/*****************************************************************************
 *            hermes_bap_pwrite                                              *
 *                                                                           *
 * Write a block of data to the chip's buffer, via the BAP. len must be even.*
 *****************************************************************************/
int hermes_bap_pwrite (hermes_t * hw, int bap, const void *buf, unsigned len,
		   u16_t id, u16_t offset) {

	/* This procedure is quite the same as the hermes_bap_read */
	int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
	int err = 0;

	if ((len % 2)) {
		printf("Hermes: Error in length to be written\n");
		return -EINVAL;
	}

	/* Set the cards internal pointer to the right fid and to the right
	 * offset */
	err = hermes_bap_seek (hw, bap, id, offset);
	if (err) {
		printf("Hermes: hermes_bap_seek error in hermes_bap_pwrite\n");
		return err;

	}

	/* Actually do the transfer */
	hermes_write_words (hw, dreg, buf, len / 2);

	return err;
}



/*****************************************************************************
 *            hermes_set_irqmask                                             *
 *                                                                           *
 * Which events should the card respond to with an interrupt?                *
 *****************************************************************************/
int hermes_set_irqmask (hermes_t * hw, u16_t events) {
	hw->inten = events;
	hermes_write_reg (hw, HERMES_INTEN, events);
	
	/* Compare written value with read value to check whether things 
	 * succeeded */
	if (hermes_read_reg (hw, HERMES_INTEN) != events) {
		printf("Hermes: error setting irqmask\n");
		return 1;
	}

	return (0);
}

/*****************************************************************************
 *            hermes_set_irqmask                                             *
 *                                                                           *
 * Which events does the card respond to with an interrupt?                  *
 *****************************************************************************/
u16_t hermes_get_irqmask (hermes_t * hw) {
	return hermes_read_reg (hw, HERMES_INTEN);
}


/*****************************************************************************
 *            hermes_read_ltv                                                *
 *                                                                           *
 * Read a Length-Type-Value record from the card. These are configurable     *
 * parameters in the cards firmware, like wepkey, essid, mac address etc.    *
 * Another name for them are 'rids', Resource Identifiers. See hermes_rids.h *
 * for all available rids                                                    *
 * If length is NULL, we ignore the length read from the card, and           *
 * read the entire buffer regardless. This is useful because some of         *
 * the configuration records appear to have incorrect lengths in             *
 * practice.                                                                 *
 *****************************************************************************/
int hermes_read_ltv (hermes_t * hw, int bap, u16_t rid, unsigned bufsize,
		 u16_t * length, void *buf) {
	int err = 0;
	int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
	u16_t rlength, rtype;
	unsigned nwords;

	if ((bufsize % 2))	{
		printf("Hermes: error in bufsize\n");
		return -EINVAL;
	}

	err = hermes_docmd_wait (hw, HERMES_CMD_ACCESS, rid, NULL);
	if (err) {
		printf("Hermes: error hermes_docmd_wait in hermes_read_ltv\n");
		return err;
	}

	err = hermes_bap_seek (hw, bap, rid, 0);
	if (err) {
		printf("Hermes: error hermes_bap_seek in hermes_read_ltv\n");
		return err;
	}

	rlength = hermes_read_reg (hw, dreg);

	if (!rlength) {
		printf( "Hermes: Error rlength\n");
		return -ENOENT;
	}

	rtype = hermes_read_reg (hw, dreg);

	if (length)
		*length = rlength;

	if (rtype != rid) {
		printf("hermes @ %lx: hermes_read_ltv(): rid  (0x%04x)",
			hw->iobase, rid);
		printf("does not match type (0x%04x)\n", rtype);
	}

	if (HERMES_RECLEN_TO_BYTES (rlength) > bufsize) {
		printf("hermes @ %lx: Truncating LTV record from ",
			hw->iobase);
		printf("%d to %d bytes. (rid=0x%04x, len=0x%04x)\n",
			HERMES_RECLEN_TO_BYTES (rlength), bufsize, rid,
			rlength);
	}
	nwords = MIN ((unsigned) rlength - 1, bufsize / 2);
	hermes_read_words (hw, dreg, buf, nwords);

	return 0;
}


/*****************************************************************************
 *            hermes_write_ltv                                               *
 *                                                                           *
 * Write a Length-Type-Value record to the card. These are configurable      *
 * parameters in the cards firmware, like wepkey, essid, mac address etc.    *
 * Another name for them are 'rids', Resource Identifiers. See hermes_rids.h *
 * for all available rids                                                    *
 *****************************************************************************/
int hermes_write_ltv (hermes_t * hw, int bap, u16_t rid,
		  u16_t length, const void *value) {
	int dreg = bap ? HERMES_DATA1 : HERMES_DATA0;
	int err = 0;
	unsigned count;

	if (length == 0) {
		printf("Hermes: length==0 in hermes_write_ltv\n");
		return -EINVAL;
	}

	err = hermes_bap_seek (hw, bap, rid, 0);
	if (err) {
		printf("Hermes: error hermes_bap_seek in hermes_write_ltv\n");
		return err;
	}

	hermes_write_reg (hw, dreg, length);
	hermes_write_reg (hw, dreg, rid);

	count = length - 1;

	hermes_write_words (hw, dreg, value, count);

	err = hermes_docmd_wait (hw, HERMES_CMD_ACCESS | HERMES_CMD_WRITE,
				 rid, NULL);
	if (err)
		printf("Hermes: error hermes_docmd_wait in hermes_write_ltv\n");

	return err;
}


/*****************************************************************************
 *            hermes_write_wordrec                                           *
 *                                                                           *
 * A shorthand for hermes_write_ltv when the field is 2 bytes long           *
 *****************************************************************************/
int hermes_write_wordrec (hermes_t * hw, int bap, u16_t rid, u16_t word) {

	u16_t rec;
	int err;
	rec = (word);

	err = hermes_write_ltv (hw, bap, rid,
				HERMES_BYTES_TO_RECLEN (sizeof (rec)), &rec);

	if (err)
		printf("Hermes: error in write_wordrec\n");
	return err;
}


/*****************************************************************************
 *            hermes_read_wordrec                                            *
 *                                                                           *
 * A shorthand for hermes_read_ltv when the field is 2 bytes long            *
 *****************************************************************************/
int hermes_read_wordrec (hermes_t * hw, int bap, u16_t rid, u16_t * word) {
	u16_t rec;
	int err;

	err = hermes_read_ltv (hw, bap, rid, sizeof (rec), NULL, &rec);
	*word = (rec);
	if (err)
		printf("Hermes: Error in read_wordrec\n");
	return err;
}


/*****************************************************************************
 *            hermes_read_words                                              *
 *                                                                           *
 * Read a sequence of words from the card to the buffer                      *
 *****************************************************************************/
void hermes_read_words (hermes_t * hw, int off, void *buf, unsigned count) {
	int i = 0;
	u16_t reg;

	for (i = 0; i < count; i++)	{
		reg = hermes_read_reg (hw, off);
		*((u16_t *) buf + i) = (u16_t) reg;
	}
}


/*****************************************************************************
 *            hermes_read_reg                                                *
 *                                                                           *
 * Read a value from a certain register. Currently only memory mapped        *
 * registers are supported, but accessing I/O spaced registers should be     *
 * quite trivial                                                             *
 *****************************************************************************/
u16_t hermes_read_reg (const hermes_t * hw, u16_t off) {
	int v = 0;
	v = *((int *)(hw->locmem + (off << hw->reg_spacing)));
	return (u16_t) v;
}

/*****************************************************************************
 *            hermes_write_reg                                               *
 *                                                                           *
 * Write a value to a certain register. Currently only memory mapped         *
 * registers are supported, but accessing I/O spaced registers should be     *
 * quite trivial                                                             *
 *****************************************************************************/
void hermes_write_reg (const hermes_t * hw, u16_t off, u16_t val) {
	int v = (int) val;	
	*(int *)(hw->locmem + (off << hw->reg_spacing)) = v;
}

