orinoco now. Uses a combination of tickdelay (where possible) and calibrated busywait (where necessary).
771 lines
28 KiB
C
Executable file
771 lines
28 KiB
C
Executable file
/*
|
|
* 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"
|
|
#include <sys/vm.h>
|
|
#include "assert.h"
|
|
#include <ibm/pci.h>
|
|
|
|
#include "string.h"
|
|
int this_proc;
|
|
|
|
#define MICROS_TO_TICKS(m) (((m)*HZ/1000000)+1)
|
|
|
|
/*****************************************************************************
|
|
* milli_delay *
|
|
* *
|
|
* Wait msecs milli seconds *
|
|
*****************************************************************************/
|
|
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;
|
|
this_proc = getprocnr();
|
|
}
|
|
|
|
|
|
/*****************************************************************************
|
|
* 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, i;
|
|
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_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 @ %x: 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%x\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 @ %x: 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 @ %x: 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 @ %x: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 resp0;
|
|
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 < 0) || (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_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 < 0) || (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_present *
|
|
* *
|
|
* Check whether we have access to the card. Does the SWSUPPORT0 contain the *
|
|
* value we put in it earlier? *
|
|
*****************************************************************************/
|
|
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_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 < 0) || (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);
|
|
printf("does not match type (0x%04x)\n", rid, 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_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_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 (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 (hermes_t * hw, u16_t off, u16_t val) {
|
|
int v = (int) val;
|
|
*(int *)(hw->locmem + (off << hw->reg_spacing)) = v;
|
|
}
|
|
|