770 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			770 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;
 | |
| 
 | |
| /*****************************************************************************
 | |
|  *            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;
 | |
| }
 | |
| 
 | 
