From: Ben Gras Date: Tue, 24 Jul 2007 14:49:09 +0000 (+0000) Subject: a driver for wireless pci cards with the Prism chipset from Intersil X-Git-Tag: v3.1.4~368 X-Git-Url: http://zhaoyanbai.com/repos/dnssec-verify.html?a=commitdiff_plain;h=816f5dd55007616d62bc4caac437df3ee255cc0a;p=minix.git a driver for wireless pci cards with the Prism chipset from Intersil Original version, by Stevens Le Blond and Michael Valkering. --- diff --git a/drivers/Makefile b/drivers/Makefile index 2315c8941..d3b0ef8f4 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -21,6 +21,7 @@ all install depend clean: cd ./floppy && $(MAKE) $@ cd ./printer && $(MAKE) $@ cd ./rtl8139 && $(MAKE) $@ + cd ./orinoco && $(MAKE) $@ cd ./fxp && $(MAKE) $@ cd ./dpeth && $(MAKE) $@ cd ./log && $(MAKE) $@ diff --git a/drivers/orinoco/INSTALL.txt b/drivers/orinoco/INSTALL.txt new file mode 100755 index 000000000..b8d554ad2 --- /dev/null +++ b/drivers/orinoco/INSTALL.txt @@ -0,0 +1,40 @@ +The driver has been tested on Minix 3.1.3, svn revision 2928: + http://derelict.cs.vu.nl/images/minix3_1_3_ide_r2928.iso.bz2 + + +To install the driver do: +#sh install_script + +This script will patch, compile and install 2 code files in Minix: + /usr/src/services/rs/service.c + /usr/src/commands/dhcpd/devices.c + (see the docs directory for more information) +Furthermore, the script will patch 1 configuration file: + /etc/drivers.conf + (adding an entry for 'orinoco') +and 1 script file: + /usr/etc/rc + (adding an entry in a for-loop to start orinoco) +Finally, the script will copy the orinoco driver into the drivers directory, +compile and install it. + +When the script completes successfully, the inet.conf file has to be adjusted +so that Minix will use the orinoco driver. Add an entry like: + + eth0 orinoco 0 {default;}; + +The last step is to set the essid and WEP key of the network in the boot +monitor. When in the boot monitor, type: + essid= + wep= + save + +The essid is the name of the wireless network +The WEP key is a string of 13 ASCII characters, being the key of the wireless +network. This variable shouldn't be set when the network is not WEP protected. +If the essid is not set, the card will just try connect a network quite at random + +N.B.: WPA is not supported +N.B.: WEP seems to be buggy. At my place it works, at the VU it doesnt. So if +things don't work, try without WEP + diff --git a/drivers/orinoco/Makefile b/drivers/orinoco/Makefile new file mode 100755 index 000000000..256fed944 --- /dev/null +++ b/drivers/orinoco/Makefile @@ -0,0 +1,42 @@ +# Makefile for the Orinoco wireless card (Prism chipset) +DRIVER = orinoco + +# directories +u = /usr +i = $u/include +s = $i/sys +m = $i/minix +b = $i/ibm +d = .. + +# programs, flags, etc. +CC = exec cc +CFLAGS = -I$i $(CPROFILE) +LDFLAGS = -i +LIBS = -lsysutil -lsys -ltimers + +OBJ = orinoco.o hermes.o + +# build local binary +all build: $(DRIVER) +$(DRIVER): $(OBJ) + $(CC) -o $@ $(LDFLAGS) $(OBJ) $(LIBS) + install -S 100kw $(DRIVER) + +# install with other drivers +install: /usr/sbin/$(DRIVER) +/usr/sbin/$(DRIVER): $(DRIVER) + install -o root -cs $? $@ + +# clean up local files +clean: + rm -f $(DRIVER) *.o *.bak + +depend: + mkdep "$(CC) -E $(CPPFLAGS)" *.c > .depend + +# Include generated dependencies. +include .depend + + + diff --git a/drivers/orinoco/hermes.c b/drivers/orinoco/hermes.c new file mode 100755 index 000000000..4f904e252 --- /dev/null +++ b/drivers/orinoco/hermes.c @@ -0,0 +1,789 @@ +/* + * 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 + * and Michael Valkering + */ + +/* Original copyright notices from Linux hermes.c + * + * Copyright (C) 2000, David Gibson, Linuxcare Australia + * + * Copyright (C) 2001, David Gibson, IBM + * + * 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 +#include "assert.h" +#include + +#include "string.h" +int this_proc; + +#define MICROS_TO_TICKS(m) (((m)*HZ/1000000)+1) + +/***************************************************************************** + * milli_delay * + * * + * Wait usecs micro seconds. Clearly needs revision * + *****************************************************************************/ +static void micro_delay(unsigned long usecs) +{ + int i, j; + + if(usecs >= 100) { + /* If the delay is long, we might as well use ticks */ + tickdelay(MICROS_TO_TICKS(usecs)); + } else { + /* use another type of hack :-), or a proper implementation */ + for(i=0; i < 1000 * usecs; i++){j+=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; +} + diff --git a/drivers/orinoco/hermes.h b/drivers/orinoco/hermes.h new file mode 100755 index 000000000..0d5607513 --- /dev/null +++ b/drivers/orinoco/hermes.h @@ -0,0 +1,339 @@ +/* + * hermes.h + * + * Constants, structures and prototypes needed for the low level access of + * Prism cards. The hermes.h file was used as the basis of this file + * + * Adjusted to Minix by Stevens Le Blond + * and Michael Valkering + */ + +/* Original copyright notices from hermes.h of the Linux kernel + * + * Copyright (C) 2000, David Gibson, Linuxcare Australia + * + * Portions taken from hfa384x.h, Copyright (C) 1999 AbsoluteValue Systems, Inc. + * All Rights Reserved. + * This file distributed under the GPL, version 2. + */ +#ifndef _HERMES_H +#define _HERMES_H + +#include "../drivers.h" +#include +#include +#include +#include + +/***************************************************************************** + * HERMES CONSTANTS * + *****************************************************************************/ +#define HERMES_ALLOC_LEN_MIN (4) +#define HERMES_ALLOC_LEN_MAX (2400) +#define HERMES_LTV_LEN_MAX (34) +#define HERMES_BAP_DATALEN_MAX (4096) +#define HERMES_BAP_OFFSET_MAX (4096) +#define HERMES_PORTID_MAX (7) +#define HERMES_NUMPORTS_MAX (HERMES_PORTID_MAX+1) +#define HERMES_PDR_LEN_MAX (260) +#define HERMES_PDA_RECS_MAX (200) +#define HERMES_PDA_LEN_MAX (1024) +#define HERMES_SCANRESULT_MAX (35) +#define HERMES_CHINFORESULT_MAX (8) +#define HERMES_MAX_MULTICAST (16) +#define HERMES_MAGIC (0x69ff) + +/* + * Hermes register offsets + */ +#define HERMES_CMD (0x00) +#define HERMES_PARAM0 (0x02) +#define HERMES_PARAM1 (0x04) +#define HERMES_PARAM2 (0x06) +#define HERMES_STATUS (0x08) +#define HERMES_RESP0 (0x0A) +#define HERMES_RESP1 (0x0C) +#define HERMES_RESP2 (0x0E) +#define HERMES_INFOFID (0x10) +#define HERMES_RXFID (0x20) +#define HERMES_ALLOCFID (0x22) +#define HERMES_TXCOMPLFID (0x24) +#define HERMES_SELECT0 (0x18) +#define HERMES_OFFSET0 (0x1C) +#define HERMES_DATA0 (0x36) +#define HERMES_SELECT1 (0x1A) +#define HERMES_OFFSET1 (0x1E) +#define HERMES_DATA1 (0x38) +#define HERMES_EVSTAT (0x30) +#define HERMES_INTEN (0x32) +#define HERMES_EVACK (0x34) +#define HERMES_CONTROL (0x14) +#define HERMES_SWSUPPORT0 (0x28) +#define HERMES_SWSUPPORT1 (0x2A) +#define HERMES_SWSUPPORT2 (0x2C) +#define HERMES_AUXPAGE (0x3A) +#define HERMES_AUXOFFSET (0x3C) +#define HERMES_AUXDATA (0x3E) + +/* + * CMD register bitmasks + */ +#define HERMES_CMD_BUSY (0x8000) +#define HERMES_CMD_AINFO (0x7f00) +#define HERMES_CMD_MACPORT (0x0700) +#define HERMES_CMD_RECL (0x0100) +#define HERMES_CMD_WRITE (0x0100) +#define HERMES_CMD_PROGMODE (0x0300) +#define HERMES_CMD_CMDCODE (0x003f) + +/* + * STATUS register bitmasks + */ +#define HERMES_STATUS_RESULT (0x7f00) +#define HERMES_STATUS_CMDCODE (0x003f) + +/* + * OFFSET register bitmasks + */ +#define HERMES_OFFSET_BUSY (0x8000) +#define HERMES_OFFSET_ERR (0x4000) +#define HERMES_OFFSET_DATAOFF (0x0ffe) + +/* + * Event register bitmasks (INTEN, EVSTAT, EVACK) + */ +#define HERMES_EV_TICK (0x8000) +#define HERMES_EV_WTERR (0x4000) +#define HERMES_EV_INFDROP (0x2000) +#define HERMES_EV_INFO (0x0080) +#define HERMES_EV_DTIM (0x0020) +#define HERMES_EV_CMD (0x0010) +#define HERMES_EV_ALLOC (0x0008) +#define HERMES_EV_TXEXC (0x0004) +#define HERMES_EV_TX (0x0002) +#define HERMES_EV_RX (0x0001) + +/* + * COR reset options + */ +#define HERMES_PCI_COR (0x26) +#define HERMES_PCI_COR_MASK (0x0080) +/* It appears that the card needs quite some time to recover: */ +#define HERMES_PCI_COR_ONT (250) /* ms */ +#define HERMES_PCI_COR_OFFT (500) /* ms */ +#define HERMES_PCI_COR_BUSYT (500) /* ms */ +/* + * Command codes + */ +/*--- Controller Commands --------------------------*/ +#define HERMES_CMD_INIT (0x0000) +#define HERMES_CMD_ENABLE (0x0001) +#define HERMES_CMD_DISABLE (0x0002) +#define HERMES_CMD_DIAG (0x0003) + +/*--- Buffer Mgmt Commands --------------------------*/ +#define HERMES_CMD_ALLOC (0x000A) +#define HERMES_CMD_TX (0x000B) +#define HERMES_CMD_CLRPRST (0x0012) + +/*--- Regulate Commands --------------------------*/ +#define HERMES_CMD_NOTIFY (0x0010) +#define HERMES_CMD_INQUIRE (0x0011) + +/*--- Configure Commands --------------------------*/ +#define HERMES_CMD_ACCESS (0x0021) +#define HERMES_CMD_DOWNLD (0x0022) + +/*--- Debugging Commands -----------------------------*/ +#define HERMES_CMD_MONITOR (0x0038) +#define HERMES_MONITOR_ENABLE (0x000b) +#define HERMES_MONITOR_DISABLE (0x000f) + +/* + * Frame structures and constants + */ + +#define HERMES_DESCRIPTOR_OFFSET (0) +#define HERMES_802_11_OFFSET (14) +#define HERMES_802_3_OFFSET (14+32) +#define HERMES_802_2_OFFSET (14+32+14) + +struct hermes_rx_descriptor +{ + u16_t status; + u16_t time_lefthalf; + u16_t time_righthalf; + u8_t silence; + u8_t signal; + u8_t rate; + u8_t rxflow; + u16_t reserved1; + u16_t reserved2; +}; + +#define HERMES_RXSTAT_ERR (0x0003) +#define HERMES_RXSTAT_BADCRC (0x0001) +#define HERMES_RXSTAT_UNDECRYPTABLE (0x0002) +#define HERMES_RXSTAT_MACPORT (0x0700) +#define HERMES_RXSTAT_PCF (0x1000) +#define HERMES_RXSTAT_MSGTYPE (0xE000) +#define HERMES_RXSTAT_1042 (0x2000) /* RFC-1042 frame */ +#define HERMES_RXSTAT_TUNNEL (0x4000) /* bridge-tunnel + * encoded frame */ +#define HERMES_RXSTAT_WMP (0x6000) /* Wavelan-II + * Management + * Protocol frame */ + +struct hermes_tx_descriptor +{ + u16_t status; + u16_t reserved1; + u16_t reserved2; + u16_t sw_support_lefthalf; + u16_t sw_support_righthalf; + u8_t retry_count; + u8_t tx_rate; + u16_t tx_control; +}; + +#define HERMES_TXSTAT_RETRYERR (0x0001) +#define HERMES_TXSTAT_AGEDERR (0x0002) +#define HERMES_TXSTAT_DISCON (0x0004) +#define HERMES_TXSTAT_FORMERR (0x0008) + +#define HERMES_TXCTRL_TX_OK (0x0002) +#define HERMES_TXCTRL_TX_EX (0x0004) +#define HERMES_TXCTRL_802_11 (0x0008) +#define HERMES_TXCTRL_ALT_RTRY (0x0020) + +/* Inquiry constants and data types */ + +#define HERMES_INQ_TALLIES (0xF100) +#define HERMES_INQ_SCAN (0xF101) +#define HERMES_INQ_LINKSTATUS (0xF200) + + +/* The tallies are retrieved, but these fields are not processed until now */ +struct hermes_tallies_frame +{ + u16_t TxUnicastFrames; + u16_t TxMulticastFrames; + u16_t TxFragments; + u16_t TxUnicastOctets; + u16_t TxMulticastOctets; + u16_t TxDeferredTransmissions; + u16_t TxSingleRetryFrames; + u16_t TxMultipleRetryFrames; + u16_t TxRetryLimitExceeded; + u16_t TxDiscards; + u16_t RxUnicastFrames; + u16_t RxMulticastFrames; + u16_t RxFragments; + u16_t RxUnicastOctets; + u16_t RxMulticastOctets; + u16_t RxFCSErrors; + u16_t RxDiscards_NoBuffer; + u16_t TxDiscardsWrongSA; + u16_t RxWEPUndecryptable; + u16_t RxMsgInMsgFragments; + u16_t RxMsgInBadMsgFragments; + u16_t RxDiscards_WEPICVError; + u16_t RxDiscards_WEPExcluded; +}; + +#define HERMES_LINKSTATUS_NOT_CONNECTED (0x0000) +#define HERMES_LINKSTATUS_CONNECTED (0x0001) +#define HERMES_LINKSTATUS_DISCONNECTED (0x0002) +#define HERMES_LINKSTATUS_AP_CHANGE (0x0003) +#define HERMES_LINKSTATUS_AP_OUT_OF_RANGE (0x0004) +#define HERMES_LINKSTATUS_AP_IN_RANGE (0x0005) +#define HERMES_LINKSTATUS_ASSOC_FAILED (0x0006) + +struct hermes_linkstatus +{ + u16_t linkstatus; /* Link status */ +}; + +/* Timeouts. These are maximum timeouts. Most often, card wil react + * much faster */ +#define HERMES_BAP_BUSY_TIMEOUT (10000) /* In iterations of ~1us */ +#define HERMES_CMD_BUSY_TIMEOUT (100) /* In iterations of ~1us */ +#define HERMES_CMD_INIT_TIMEOUT (50000) /* in iterations of ~10us */ +#define HERMES_CMD_COMPL_TIMEOUT (20000) /* in iterations of ~10us */ +#define HERMES_ALLOC_COMPL_TIMEOUT (1000) /* in iterations of ~10us */ + +/* WEP settings */ +#define HERMES_AUTH_OPEN (1) +#define HERMES_AUTH_SHARED_KEY (2) +#define HERMES_WEP_PRIVACY_INVOKED (0x0001) +#define HERMES_WEP_EXCL_UNENCRYPTED (0x0002) +#define HERMES_WEP_HOST_ENCRYPT (0x0010) +#define HERMES_WEP_HOST_DECRYPT (0x0080) + + +/* Basic control structure */ +typedef struct hermes +{ + u32_t iobase; + int io_space; /* 1 if we IO-mapped IO, 0 for memory-mapped + * IO */ +#define HERMES_IO 1 +#define HERMES_MEM 0 + int reg_spacing; +#define HERMES_16BIT_REGSPACING 0 +#define HERMES_32BIT_REGSPACING 1 + u16_t inten; /* Which interrupts should be enabled? */ + char *locmem; +} hermes_t; + +typedef struct hermes_response +{ + u16_t status, resp0, resp1, resp2; +} hermes_response_t; + +struct hermes_idstring +{ + u16_t len; + u16_t val[16]; +}; + +#define HERMES_BYTES_TO_RECLEN(n) ( (((n)+1)/2) + 1 ) +#define HERMES_RECLEN_TO_BYTES(n) ( ((n)-1) * 2 ) + +/* Function prototypes */ +_PROTOTYPE (u16_t hermes_read_reg, (hermes_t * hw, u16_t off)); +_PROTOTYPE (void hermes_write_reg, (hermes_t * hw, u16_t off, u16_t val)); +_PROTOTYPE (void hermes_struct_init, (hermes_t * hw, u32_t address, + int io_space, int reg_spacing)); +_PROTOTYPE (int hermes_init, (hermes_t * hw)); +_PROTOTYPE (int hermes_docmd_wait, (hermes_t * hw, u16_t cmd, + u16_t parm0, hermes_response_t * resp)); +_PROTOTYPE (int hermes_allocate, (hermes_t * hw, u16_t size, u16_t * fid)); +_PROTOTYPE (int hermes_bap_pread, (hermes_t * hw, int bap, void *buf, + unsigned len, u16_t id, u16_t offset)); +_PROTOTYPE (int hermes_bap_pwrite, (hermes_t * hw, int bap, + const void *buf, unsigned len, u16_t id, + u16_t offset)); +_PROTOTYPE (void hermes_read_words, (hermes_t * hw, int off, void *buf, + unsigned count)); +_PROTOTYPE (void hermes_write_words, (hermes_t * hw, int off, + const void *buf, unsigned count)); +_PROTOTYPE (int hermes_read_ltv, (hermes_t * hw, int bap, u16_t rid, + unsigned buflen, u16_t * length, + void *buf)); +_PROTOTYPE (int hermes_write_ltv, (hermes_t * hw, int bap, u16_t rid, + u16_t length, const void *value)); +_PROTOTYPE (int hermes_present, (hermes_t * hw)); +_PROTOTYPE (int myfunc_read, (vir_bytes src)); +_PROTOTYPE (void myfunc_write, (vir_bytes dst, int val)); +_PROTOTYPE (void hermes_print_ioarea, (hermes_t * hw, int first_reg, + int last_reg)); +_PROTOTYPE (int hermes_set_irqmask, (hermes_t * hw, u16_t events)); +_PROTOTYPE (u16_t hermes_get_irqmask, (hermes_t * hw)); +_PROTOTYPE (int hermes_read_wordrec, (hermes_t * hw, int bap, u16_t rid, + u16_t * word)); +_PROTOTYPE (int hermes_write_wordrec, (hermes_t * hw, int bap, u16_t rid, + u16_t word)); +_PROTOTYPE (int hermes_cor_reset, (hermes_t *hw)); +_PROTOTYPE (void milli_delay, (unsigned int msecs)); +#endif /* _HERMES_H */ diff --git a/drivers/orinoco/hermes_rid.h b/drivers/orinoco/hermes_rid.h new file mode 100755 index 000000000..23b03088d --- /dev/null +++ b/drivers/orinoco/hermes_rid.h @@ -0,0 +1,149 @@ +/* hermes_rid.h + * + * This file contains the RIDs (Resource IDentifiers). These are data items + * which are configurable in the card. Most of them are not used in our driver + * currently, as you can see there are really a lot which can be configured. + */ + + +#ifndef _HERMES_RID_H +#define _HERMES_RID_H + +#define HERMES_RID_CNFPORTTYPE 0xFC00 +#define HERMES_RID_CNFOWNMACADDR 0xFC01 +#define HERMES_RID_CNFDESIREDSSID 0xFC02 +#define HERMES_RID_CNFOWNCHANNEL 0xFC03 +#define HERMES_RID_CNFOWNSSID 0xFC04 +#define HERMES_RID_CNFOWNATIMWINDOW 0xFC05 +#define HERMES_RID_CNFSYSTEMSCALE 0xFC06 +#define HERMES_RID_CNFMAXDATALEN 0xFC07 +#define HERMES_RID_CNFWDSADDRESS 0xFC08 +#define HERMES_RID_CNFPMENABLED 0xFC09 +#define HERMES_RID_CNFPMEPS 0xFC0A +#define HERMES_RID_CNFMULTICASTRECEIVE 0xFC0B +#define HERMES_RID_CNFMAXSLEEPDURATION 0xFC0C +#define HERMES_RID_CNFPMHOLDOVERDURATION 0xFC0D +#define HERMES_RID_CNFOWNNAME 0xFC0E +#define HERMES_RID_CNFOWNDTIMPERIOD 0xFC10 +#define HERMES_RID_CNFWDSADDRESS1 0xFC11 +#define HERMES_RID_CNFWDSADDRESS2 0xFC12 +#define HERMES_RID_CNFWDSADDRESS3 0xFC13 +#define HERMES_RID_CNFWDSADDRESS4 0xFC14 +#define HERMES_RID_CNFWDSADDRESS5 0xFC15 +#define HERMES_RID_CNFWDSADDRESS6 0xFC16 +#define HERMES_RID_CNFMULTICASTPMBUFFERING 0xFC17 +#define HERMES_RID_CNFWEPENABLED_AGERE 0xFC20 +#define HERMES_RID_CNFAUTHENTICATION_AGERE 0xFC21 +#define HERMES_RID_CNFMANDATORYBSSID_SYMBOL 0xFC21 +#define HERMES_RID_CNFWEPDEFAULTKEYID 0xFC23 +#define HERMES_RID_CNFDEFAULTKEY0 0xFC24 +#define HERMES_RID_CNFDEFAULTKEY1 0xFC25 +#define HERMES_RID_CNFMWOROBUST_AGERE 0xFC25 +#define HERMES_RID_CNFDEFAULTKEY2 0xFC26 +#define HERMES_RID_CNFDEFAULTKEY3 0xFC27 +#define HERMES_RID_CNFWEPFLAGS_INTERSIL 0xFC28 +#define HERMES_RID_CNFWEPKEYMAPPINGTABLE 0xFC29 +#define HERMES_RID_CNFAUTHENTICATION 0xFC2A +#define HERMES_RID_CNFMAXASSOCSTA 0xFC2B +#define HERMES_RID_CNFKEYLENGTH_SYMBOL 0xFC2B +#define HERMES_RID_CNFTXCONTROL 0xFC2C +#define HERMES_RID_CNFROAMINGMODE 0xFC2D +#define HERMES_RID_CNFHOSTAUTHENTICATION 0xFC2E +#define HERMES_RID_CNFRCVCRCERROR 0xFC30 +#define HERMES_RID_CNFMMLIFE 0xFC31 +#define HERMES_RID_CNFALTRETRYCOUNT 0xFC32 +#define HERMES_RID_CNFBEACONINT 0xFC33 +#define HERMES_RID_CNFAPPCFINFO 0xFC34 +#define HERMES_RID_CNFSTAPCFINFO 0xFC35 +#define HERMES_RID_CNFPRIORITYQUSAGE 0xFC37 +#define HERMES_RID_CNFTIMCTRL 0xFC40 +#define HERMES_RID_CNFTHIRTY2TALLY 0xFC42 +#define HERMES_RID_CNFENHSECURITY 0xFC43 +#define HERMES_RID_CNFGROUPADDRESSES 0xFC80 +#define HERMES_RID_CNFCREATEIBSS 0xFC81 +#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD 0xFC82 +#define HERMES_RID_CNFRTSTHRESHOLD 0xFC83 +#define HERMES_RID_CNFTXRATECONTROL 0xFC84 +#define HERMES_RID_CNFPROMISCUOUSMODE 0xFC85 +#define HERMES_RID_CNFBASICRATES_SYMBOL 0xFC8A +#define HERMES_RID_CNFPREAMBLE_SYMBOL 0xFC8C +#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD0 0xFC90 +#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD1 0xFC91 +#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD2 0xFC92 +#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD3 0xFC93 +#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD4 0xFC94 +#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD5 0xFC95 +#define HERMES_RID_CNFFRAGMENTATIONTHRESHOLD6 0xFC96 +#define HERMES_RID_CNFRTSTHRESHOLD0 0xFC97 +#define HERMES_RID_CNFRTSTHRESHOLD1 0xFC98 +#define HERMES_RID_CNFRTSTHRESHOLD2 0xFC99 +#define HERMES_RID_CNFRTSTHRESHOLD3 0xFC9A +#define HERMES_RID_CNFRTSTHRESHOLD4 0xFC9B +#define HERMES_RID_CNFRTSTHRESHOLD5 0xFC9C +#define HERMES_RID_CNFRTSTHRESHOLD6 0xFC9D +#define HERMES_RID_CNFHOSTSCAN_SYMBOL 0xFCAB +#define HERMES_RID_CNFSHORTPREAMBLE 0xFCB0 +#define HERMES_RID_CNFWEPKEYS_AGERE 0xFCB0 +#define HERMES_RID_CNFEXCLUDELONGPREAMBLE 0xFCB1 +#define HERMES_RID_CNFTXKEY_AGERE 0xFCB1 +#define HERMES_RID_CNFAUTHENTICATIONRSPTO 0xFCB2 +#define HERMES_RID_CNFSCANSSID_AGERE 0xFCB2 +#define HERMES_RID_CNFBASICRATES 0xFCB3 +#define HERMES_RID_CNFSUPPORTEDRATES 0xFCB4 +#define HERMES_RID_CNFTICKTIME 0xFCE0 +#define HERMES_RID_CNFSCANREQUEST 0xFCE1 +#define HERMES_RID_CNFJOINREQUEST 0xFCE2 +#define HERMES_RID_CNFAUTHENTICATESTATION 0xFCE3 +#define HERMES_RID_CNFCHANNELINFOREQUEST 0xFCE4 +#define HERMES_RID_CNFHOSTSCAN 0xFCE5 +#define HERMES_RID_MAXLOADTIME 0xFD00 +#define HERMES_RID_DOWNLOADBUFFER 0xFD01 +#define HERMES_RID_PRIID 0xFD02 +#define HERMES_RID_PRISUPRANGE 0xFD03 +#define HERMES_RID_CFIACTRANGES 0xFD04 +#define HERMES_RID_NICSERNUM 0xFD0A +#define HERMES_RID_NICID 0xFD0B +#define HERMES_RID_MFISUPRANGE 0xFD0C +#define HERMES_RID_CFISUPRANGE 0xFD0D +#define HERMES_RID_CHANNELLIST 0xFD10 +#define HERMES_RID_REGULATORYDOMAINS 0xFD11 +#define HERMES_RID_TEMPTYPE 0xFD12 +#define HERMES_RID_CIS 0xFD13 +#define HERMES_RID_STAID 0xFD20 +#define HERMES_RID_STASUPRANGE 0xFD21 +#define HERMES_RID_MFIACTRANGES 0xFD22 +#define HERMES_RID_CFIACTRANGES2 0xFD23 +#define HERMES_RID_SECONDARYVERSION_SYMBOL 0xFD24 +#define HERMES_RID_PORTSTATUS 0xFD40 +#define HERMES_RID_CURRENTSSID 0xFD41 +#define HERMES_RID_CURRENTBSSID 0xFD42 +#define HERMES_RID_COMMSQUALITY 0xFD43 +#define HERMES_RID_CURRENTTXRATE 0xFD44 +#define HERMES_RID_CURRENTBEACONINTERVAL 0xFD45 +#define HERMES_RID_CURRENTSCALETHRESHOLDS 0xFD46 +#define HERMES_RID_PROTOCOLRSPTIME 0xFD47 +#define HERMES_RID_SHORTRETRYLIMIT 0xFD48 +#define HERMES_RID_LONGRETRYLIMIT 0xFD49 +#define HERMES_RID_MAXTRANSMITLIFETIME 0xFD4A +#define HERMES_RID_MAXRECEIVELIFETIME 0xFD4B +#define HERMES_RID_CFPOLLABLE 0xFD4C +#define HERMES_RID_AUTHENTICATIONALGORITHMS 0xFD4D +#define HERMES_RID_PRIVACYOPTIONIMPLEMENTED 0xFD4F +#define HERMES_RID_DBMCOMMSQUALITY_INTERSIL 0xFD51 +#define HERMES_RID_CURRENTTXRATE1 0xFD80 +#define HERMES_RID_CURRENTTXRATE2 0xFD81 +#define HERMES_RID_CURRENTTXRATE3 0xFD82 +#define HERMES_RID_CURRENTTXRATE4 0xFD83 +#define HERMES_RID_CURRENTTXRATE5 0xFD84 +#define HERMES_RID_CURRENTTXRATE6 0xFD85 +#define HERMES_RID_OWNMACADDR 0xFD86 +#define HERMES_RID_SCANRESULTSTABLE 0xFD88 +#define HERMES_RID_PHYTYPE 0xFDC0 +#define HERMES_RID_CURRENTCHANNEL 0xFDC1 +#define HERMES_RID_CURRENTPOWERSTATE 0xFDC2 +#define HERMES_RID_CCAMODE 0xFDC3 +#define HERMES_RID_SUPPORTEDDATARATES 0xFDC6 +#define HERMES_RID_BUILDSEQ 0xFFFE +#define HERMES_RID_FWID 0xFFFF + +#endif diff --git a/drivers/orinoco/orinoco.c b/drivers/orinoco/orinoco.c new file mode 100755 index 000000000..c26056d41 --- /dev/null +++ b/drivers/orinoco/orinoco.c @@ -0,0 +1,2383 @@ +/* + * orinoco.c + * + * This file contains a wireless device driver for Prism based wireless + * cards. + * + * Created by Stevens Le Blond + * and Michael Valkering + * + * * The valid messages and their parameters are: + * + * m_type DL_PORT DL_PROC DL_COUNT DL_MODE DL_ADDR DL_GRANT + * |------------+----------+---------+----------+---------+---------+---------| + * | HARDINT | | | | | | | + * |------------|----------|---------|----------|---------|---------|---------| + * | DL_WRITE | port nr | proc nr | count | mode | address | | + * |------------|----------|---------|----------|---------|---------|---------| + * | DL_WRITEV | port nr | proc nr | count | mode | address | | + * |------------|----------|---------|----------|---------|---------|---------| + * | DL_WRITEV_S| port nr | proc nr | count | mode | | grant | + * |------------|----------|---------|----------|---------|---------|---------| + * | DL_READ | port nr | proc nr | count | | address | | + * |------------|----------|---------|----------|---------|---------|---------| + * | DL_READV | port nr | proc nr | count | | address | | + * |------------|----------|---------|----------|---------|---------|---------| + * | DL_READV_S | port nr | proc nr | count | | | grant | + * |------------|----------|---------|----------|---------|---------|---------| + * | DL_CONF | port nr | proc nr | | mode | address | | + * |------------|----------|---------|----------|---------|---------|---------| + * | DL_GETSTAT | port nr | proc nr | | | address | | + * |------------|----------|---------|----------|---------|---------|---------| + * |DL_GETSTAT_S| port nr | proc nr | | | | grant | + * |------------|----------|---------|----------|---------|---------|---------| + * | DL_STOP | port_nr | | | | | | + * |------------|----------|---------|----------|---------|---------|---------| + * + * The messages sent are: + * + * m_type DL_PORT DL_PROC DL_COUNT DL_STAT DL_CLCK + * |------------|----------|---------|----------|---------|---------| + * |DL_TASK_REPL| port nr | proc nr | rd-count | err|stat| clock | + * |------------|----------|---------|----------|---------|---------| + * + * m_type m3_i1 m3_i2 m3_ca1 + * |------------|---------|-----------|---------------| + * |DL_CONF_REPL| port nr | last port | ethernet addr | + * |------------|---------|-----------|---------------| + * + * m_type DL_PORT DL_STAT + * |------------|---------|-----------| + * |DL_STAT_REPL| port nr | err | + * |------------|---------|-----------| + * + */ + +#include "../drivers.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../../kernel/const.h" +#include "../../kernel/config.h" +#include "../../kernel/type.h" + +#define tmra_ut timer_t +#define tmra_inittimer(tp) tmr_inittimer(tp) +#define VERBOSE 1 /* display message during init */ + +PRIVATE struct pcitab { + u16_t vid; + u16_t did; + int checkclass; +} pcitab[]= +{ + { 0x1260, 0x3873, 0 }, + { 0x1186, 0x1300, 0 }, + { 0x0000, 0x0000, 0 } +}; + + +static tmra_ut or_watchdog; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "assert.h" +#include "hermes.h" +#include "hermes_rid.h" +#include "orinoco.h" + +#define ERR -1 + +#define debug 0 + +#define OR_M_ENABLED 1 +#define OR_M_DISABLED 0 +#define OR_F_EMPTY 0 +#define OR_F_MULTI 1 +#define OR_F_BROAD (1<<1) +#define OR_F_ENABLED (1<<2) +#define OR_F_PROMISC (1<<3) +#define OR_F_READING (1<<4) +#define OR_F_SEND_AVAIL (1<<5) +#define OR_F_PACK_SENT (1<<6) +#define OR_F_PACK_RECV (1<<7) +#define ORINOCO_INTEN ( HERMES_EV_RX | HERMES_EV_ALLOC |\ + HERMES_EV_WTERR | HERMES_EV_TXEXC|\ + HERMES_EV_INFO | HERMES_EV_INFDROP|\ + HERMES_EV_TX) + +#define NO_FID (-1) +#define ETH_ALEN 6 +#define USER_BAP 0 +#define IRQ_BAP 1 +#define ETH_HLEN 14 + +static int or_nr_task = ANY; +static t_or or_table[OR_PORT_NR]; + +struct ethhdr { + u8_t h_dest[ETH_ALEN]; + u8_t h_src[ETH_ALEN]; + u16_t h_proto; +}; + +struct header_struct { + /* 802.3 */ + u8_t dest[ETH_ALEN]; + u8_t src[ETH_ALEN]; + u16_t len; + /* 802.2 */ + u8_t dsap; + u8_t ssap; + u8_t ctrl; + /* SNAP */ + u8_t oui[3]; + u16_t ethertype; +}; + +#define RUP_EVEN(x) (((x) + 1) & (~1)) + +u8_t encaps_hdr[] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; +#define ENCAPS_OVERHEAD (sizeof (encaps_hdr) + 2) + +/******************************************************************** + * Data tables * + ********************************************************************/ + +/* The frequency of each channel in MHz */ +const long channel_frequency[] = { + 2412, 2417, 2422, 2427, 2432, 2437, 2442, + 2447, 2452, 2457, 2462, 2467, 2472, 2484 +}; + +#define NUM_CHANNELS (sizeof(channel_frequency) / sizeof(channel_frequency[0])) + +/* This tables gives the actual meanings of the bitrate IDs returned by the + * firmware. Not used yet */ +struct { + int bitrate; /* in 100s of kilobits */ + int automatic; + u16_t txratectrl; +} bitrate_table[] = +{ + {110, 1, 15}, /* Entry 0 is the default */ + {10, 0, 1}, + {10, 1, 1}, + {20, 0, 2}, + {20, 1, 3}, + {55, 0, 4}, + {55, 1, 7}, + {110, 0, 8},}; + +#define BITRATE_TABLE_SIZE (sizeof(bitrate_table) / sizeof(bitrate_table[0])) + + +_PROTOTYPE (static void sig_handler, (void)); +_PROTOTYPE (static void or_writev, (message * mp, int from_int, int vectored)); +_PROTOTYPE (static void or_readv, (message * mp, int from_int, int vectored)); +_PROTOTYPE (static void or_writev_s, (message * mp, int from_int)); +_PROTOTYPE (static void or_readv_s, (message * mp, int from_int)); +_PROTOTYPE (static void reply, (t_or * orp, int err, int may_block)); +_PROTOTYPE (static int or_probe, (t_or *)); +_PROTOTYPE (static void or_ev_info, (t_or *)); +_PROTOTYPE (static void or_init, (message *)); +_PROTOTYPE (static void or_pci_conf, (void)); +_PROTOTYPE (static void or_init_struct, (t_or *)); +_PROTOTYPE (static void map_hw_buffer, (t_or *)); +_PROTOTYPE (static void or_init_hw, (t_or *)); +_PROTOTYPE (static void or_check_ints, (t_or *)); +_PROTOTYPE (static void or_writerids, (hermes_t *, t_or *)); +_PROTOTYPE (static void or_readrids, (hermes_t *, t_or *)); +_PROTOTYPE (static void or_rec_mode, (t_or *)); +_PROTOTYPE (static void mess_reply, (message *, message *)); +_PROTOTYPE (static u32_t or_get_bar, (int devind, t_or * orp)); +_PROTOTYPE (static void or_getstat, (message * mp)); +_PROTOTYPE (static void or_getstat_s, (message * mp)); +_PROTOTYPE (static void print_linkstatus, (t_or * orp, u16_t status)); +_PROTOTYPE (static int or_get_recvd_packet, (t_or *orp, u16_t rxfid, + u8_t *databuf)); +_PROTOTYPE (static void orinoco_stop, (void)); +_PROTOTYPE (static void or_reset, (void)); +_PROTOTYPE (static void or_watchdog_f, (timer_t *tp) ); +_PROTOTYPE (static void setup_wepkey, (t_or *orp, char *wepkey0) ); +_PROTOTYPE (static void or_getstat, (message *m)); +_PROTOTYPE (static int do_hard_int, (void)); +_PROTOTYPE (static void check_int_events, (void)); +_PROTOTYPE (static void or_getname, (message *m)); +_PROTOTYPE (static int or_handler, (t_or *orp)); +_PROTOTYPE (static void or_dump, (message *m)); + +/* The message used in the main loop is made global, so that rl_watchdog_f() + * can change its message type to fake a HARD_INT message. + */ +PRIVATE message m; +PRIVATE int int_event_check; /* set to TRUE if events arrived */ + +static char *progname; +extern int errno; + +/***************************************************************************** + * main * + * * + * * + * The main function of the driver, receiving and processing messages * + *****************************************************************************/ +int main(int argc, char *argv[]) { + int fkeys, sfkeys, r, i, ret; + u32_t inet_proc_nr; + long v = 0; + t_or *orp; + + (progname=strrchr(argv[0],'/')) ? progname++ : (progname=argv[0]); + + env_setargs(argc, argv); + + /* Observe some function key for debug dumps. */ + fkeys = sfkeys = 0; bit_set(sfkeys, 11); + if ((r=fkey_map(&fkeys, &sfkeys)) != OK) + printf("Warning: orinoco couldn't observe F-key(s): %d\n",r); + + /* Try to notify INET that we are present (again). If INET cannot + * be found, assume this is the first time we started and INET is + * not yet alive. */ + r = ds_retrieve_u32("inet", &inet_proc_nr); + if (r == OK) + notify(inet_proc_nr); + else if (r != ESRCH) + printf("orinoco: ds_retrieve_u32 failed for 'inet': %d\n", r); + + while (42) { + if ((r = receive (ANY, &m)) != OK) + panic(__FILE__, "orinoco: receive failed", NO_NUM); + + switch (m.m_type) { + case DEV_PING: + notify(m.m_source); + break; + case DL_WRITEV: + or_writev (&m, FALSE, TRUE); + break; + case DL_WRITEV_S: + or_writev_s (&m, FALSE); + break; + case DL_WRITE: + or_writev (&m, FALSE, FALSE); + break; + case DL_READ: + or_readv (&m, FALSE, FALSE); + break; + case DL_READV: + or_readv (&m, FALSE, TRUE); + break; + case DL_READV_S: + or_readv_s (&m, FALSE); + break; + case DL_CONF: + or_init (&m); + break; + case DL_GETSTAT: + or_getstat (&m); + break; + case DL_GETSTAT_S: + or_getstat_s (&m); + break; + case DL_GETNAME: + or_getname(&m); + break; + case SYN_ALARM: + or_watchdog_f(NULL); + break; + case HARD_INT: + do_hard_int(); + if (int_event_check) + check_int_events(); + break ; + case FKEY_PRESSED: + or_dump(&m); + break; + case PROC_EVENT: + sig_handler(); + break; + default: + panic(__FILE__,"orinoco: illegal message:", m.m_type); + } + } +} + +/***************************************************************************** + * sig_handler * + * * + * Handles signals to the driver. * + *****************************************************************************/ +PRIVATE void sig_handler() { + sigset_t sigset; + int sig; + + if(getsigset(&sigset) != 0) return; + + if(sigismember(&sigset, SIGTERM)) { + orinoco_stop(); + } +} + + +/***************************************************************************** + * check_int_events * + * * + * If a hard interrupt message came in, call the or_check_ints for the right * + * card * + *****************************************************************************/ +static void check_int_events(void) { + int i; + t_or *orp; + + /* the HARD_INT message doesn't contain information about the port, try + * to find it */ + for (orp = or_table; + orp < or_table + OR_PORT_NR; orp++) { + if (orp->or_mode != OR_M_ENABLED) + continue; + if (!orp->or_got_int) + continue; + orp->or_got_int = 0; + assert (orp->or_flags & OR_F_ENABLED); + or_check_ints (orp); + } + +} + +/**************************************************************************** + * or_getname * + * * + * Gets the drivers name, orinoco * + ****************************************************************************/ +static void or_getname(message *mp) { + int r; + + strncpy(mp->DL_NAME, progname, sizeof(mp->DL_NAME)); + mp->DL_NAME[sizeof(mp->DL_NAME) - 1] = '\0'; + mp->m_type = DL_NAME_REPLY; + + r = send(mp->m_source, mp); + if(r != OK) { + panic(__FILE__, "or_getname: send failed", r); + } +} + +/***************************************************************************** + * do_hard_int * + * * + * Process the interrupts which the card generated * + *****************************************************************************/ +static int do_hard_int(void) { + u16_t evstat; + hermes_t *hw; + int i,s; + t_or *orp; + + for (i=0; i < OR_PORT_NR; i ++) { + /* Run interrupt handler at driver level. */ + or_handler( &or_table[i]); + + /* Reenable interrupts for this hook. */ + if ((s=sys_irqenable(&or_table[i].or_hook_id)) != OK) { + printf("orinoco: error, couldn't enable"); + printf(" interrupts: %d\n", s); + } + } +} + + + +/***************************************************************************** + * orinoco_stop * + * * + * Stops the card. The signal to the card itself is not implemented yet. * + *****************************************************************************/ +static void orinoco_stop () { + int i; + t_or *orp; + + for (i= 0, orp= &or_table[0]; ior_mode != OR_M_ENABLED) + continue; + /* TODO: send a signal to the card to shut it down */ + } + sys_exit(0); +} + +/***************************************************************************** + * or_reset * + * * + * Sometime the card gets screwed, behaving erratically. Solution: reset the * + * card. This is actually largely redoing the initialization * + *****************************************************************************/ +static void or_reset() { + static clock_t last_reset, now; + t_or *orp; + int i, j, r; + u16_t irqmask; + hermes_t *hw = &(orp->hw); + + if (OK != (r = getuptime(&now))) + panic(__FILE__, "orinoco: getuptime() failed:", r); + + if(now - last_reset < HZ * 10) { + printf("Resetting card too often. Going to reset driver\n"); + exit(1); + } + + for (i = 0, orp = or_table; orp < or_table + OR_PORT_NR; i++, orp++) { + if(orp->or_mode == OR_M_DISABLED) + printf("orinoco port %d is disabled\n", i); + + if(orp->or_mode != OR_M_ENABLED) { + continue; + } + + orp->or_need_reset = 0; + or_init_hw(orp); + + orp->rx_last = orp->rx_first = 0; + for(j = 0; j < NR_RX_BUFS; j++) { + orp->rx_length[0] = 0; + } + + if(orp->or_flags & OR_F_SEND_AVAIL) { + orp->or_tx.ret_busy = FALSE; + orp->or_send_int = TRUE; + } + } + + last_reset = now; + +} + +/***************************************************************************** + * or_dump * + * * + * Dump interesting information about the card on F-key pressed. * + * Not implemented yet * + *****************************************************************************/ +static void or_dump (message *m) { + t_or *orp; + int i, err; + u16_t evstat =0, d; + hermes_t *hw; + + for (i = 0, orp = or_table; orp < or_table + OR_PORT_NR; i++, orp++) { + if(orp->or_mode == OR_M_DISABLED) { + printf("%s is disabled\n", orp->or_name); + } + + if(orp->or_mode != OR_M_ENABLED) + continue; + + m->m_type = FKEY_CONTROL; + m->FKEY_REQUEST = FKEY_EVENTS; + if(OK!=(sendrec(TTY_PROC_NR,m)) ) + printf("Contacting the TTY failed\n"); + + if(bit_isset(m->FKEY_SFKEYS, 11)) { + print_linkstatus(orp, orp->last_linkstatus); + } + } +} + +/***************************************************************************** + * or_init * + * * + * The main initialization function, called when a DL_INIT message comes in. * + *****************************************************************************/ +static void or_init (message * mp) { + int port, err, i; + t_or *orp; + message reply; + static int first_time = 1; + hermes_t *hw; + clock_t t0,t1; + + if (first_time) { + first_time = 0; + or_pci_conf (); /* Configure PCI devices. */ + + tmra_inittimer(&or_watchdog); + /* Use a synchronous alarm instead of a watchdog timer. */ + sys_setalarm(HZ, 0); + } + + port = mp->DL_PORT; + if (port < 0 || port >= OR_PORT_NR) { + /* illegal port in message */ + reply.m_type = DL_CONF_REPLY; + reply.m3_i1 = ENXIO; + mess_reply (mp, &reply); + return; + } + + /* the port resolves to the main orinoco structure */ + orp = &or_table[port]; + /* resolving to the main hardware structure */ + hw = &(orp->hw); + + if (orp->or_mode == OR_M_DISABLED) { + /* Initialize the orp structure */ + or_init_struct (orp); + if (orp->or_mode == OR_M_DISABLED) { + reply.m_type = DL_CONF_REPLY; + reply.m3_i1 = ENXIO; + mess_reply (mp, &reply); + return; + } + if (orp->or_mode == OR_M_ENABLED) { + /* initialize card, hardware/firmware */ + orp->or_flags |= OR_F_ENABLED; + or_init_hw (orp); + } + } + + assert (orp->or_mode == OR_M_ENABLED); + assert (orp->or_flags & OR_F_ENABLED); + + /* Not supported by the driver yet, but set a couple of options: + * multicasting, promiscuity, broadcasting, depending on the users + * needs */ + orp->or_flags &= ~(OR_F_PROMISC | OR_F_MULTI | OR_F_BROAD); + if (mp->DL_MODE & DL_PROMISC_REQ) + orp->or_flags |= OR_F_PROMISC; + if (mp->DL_MODE & DL_MULTI_REQ) + orp->or_flags |= OR_F_MULTI; + if (mp->DL_MODE & DL_BROAD_REQ) + orp->or_flags |= OR_F_BROAD; + + orp->or_client = mp->m_source; + or_rec_mode (orp); + + /* reply the caller that the configuration succeeded */ + reply.m_type = DL_CONF_REPLY; + reply.m3_i1 = mp->DL_PORT; + reply.m3_i2 = OR_PORT_NR; + *(ether_addr_t *) reply.m3_ca1 = orp->or_address; + mess_reply (mp, &reply); +} + +/***************************************************************************** + * or_pci_conf * + * * + * Configure the pci related issues of the card, e.g. finding out where the * + * card is in the pci configuration, it's assigned irq, etc. This can be * + * done if the boot monitor is provided with information, or the pci bus * + * can be searched (at the end: or_probe function) * + *****************************************************************************/ +static void or_pci_conf () { + long v; + t_or *orp; + int i, h; + static char envfmt[] = "*:d.d.d"; + static char envvar[] = OR_ENVVAR "#"; + static char val[128]; + + /* extract information from the boot monitor about the pci + * configuration if provided */ + for (i = 0, orp = or_table; i < OR_PORT_NR; i++, orp++) { + strncpy (orp->or_name, OR_NAME, sizeof(OR_NAME)); + orp->or_name[sizeof(OR_NAME) - 2] = i + '0'; + orp->or_seen = FALSE; + /* whats this envvar; whats the definition;*/ + /* i guess this whole loop could be removed*/ + envvar[sizeof (OR_ENVVAR) - 1] = '0' + i; + if (0 == env_get_param(envvar, val, sizeof(val)) && + ! env_prefix(envvar, "pci")) { + env_panic(envvar); + } + v = 0; + (void) env_parse (envvar, envfmt, 1, &v, 0, 255); + orp->or_pci_bus = v; + v = 0; + (void) env_parse (envvar, envfmt, 2, &v, 0, 255); + orp->or_pci_dev = v; + v = 0; + (void) env_parse (envvar, envfmt, 3, &v, 0, 255); + orp->or_pci_func = v; + } + + /* Initialize the pci bus, bridges and cards, if not yet done */ + pci_init (); + + /* Try to find out where the card(s) are in the pci bus */ + for (h = 1; h >= 0; h--) + for (i = 0, orp = or_table; i < OR_PORT_NR; i++, orp++) { + if (((orp->or_pci_bus | orp->or_pci_dev | + orp->or_pci_func) != 0) != h) { + continue; + } + if (or_probe (orp)) + orp->or_seen = TRUE; + } +} + +/***************************************************************************** + * or_probe * + * * + * Try to find the card based on information provided by pci and get irq and * + * bar * + *****************************************************************************/ +static int or_probe (t_or * orp) { + u8_t ilr; + u32_t bar, reg, cpuspace_bar; + char *dname; + u16_t vid, did; + int i, r, devind, just_one; + + if ((orp->or_pci_bus | orp->or_pci_dev | orp->or_pci_func) != 0) { + /* The monitor has provided us with clues about where the + * device is. Try to find it at that place */ + r = pci_find_dev (orp->or_pci_bus, orp->or_pci_dev, + orp->or_pci_func, &devind); + if (r == 0) { + printf ("%s: no PCI found at %d.%d.%d\n", + orp->or_name, orp->or_pci_bus, + orp->or_pci_dev, orp->or_pci_func); + return (0); + } + /* get the information about the card, vendor id and device + * id */ + pci_ids (devind, &vid, &did); + just_one = TRUE; + } else { + /* no clue where the card is. Start looking from the + * beginning */ + r = pci_first_dev (&devind, &vid, &did); + if (r == 0) + return (0); + just_one = FALSE; + } + + while (42) { + /* loop through the pcitab to find a maching entry. The match + * being between one of the values in pcitab and the + * information provided by the pci bus */ + for (i = 0; pcitab[i].vid != 0; i++) { + if (pcitab[i].vid != vid) + continue; + if (pcitab[i].did != did) + continue; + if (pcitab[i].checkclass) { + panic(__FILE__, "or_probe:class check not implmnted", + NO_NUM); + } + /* we have found the card in the pci bus */ + break; + } + if (pcitab[i].vid != 0) + break; + + if (just_one) { + printf ("%s: wrong PCI device", orp->or_name); + printf (" (%04x/%04x) found at %d.%d.%d\n", vid, did, + orp->or_pci_bus, orp->or_pci_dev, + orp->or_pci_func); + return (0); + } + + /* if the pci device which was under consideration was not + * of the desired brand or type, get the next device */ + r = pci_next_dev (&devind, &vid, &did); + if (!r) + return (0); + } + + /* Get the name as advertised by pci */ + dname = pci_dev_name (vid, did); + if (!dname) + dname = "unknow device"; + printf ("%s: %s (%04x/%04x) at %s\n", + orp->or_name, dname, vid, did, pci_slot_name (devind)); + + pci_reserve (devind); + + orp->devind = devind; + /* Get the irq */ + ilr = pci_attr_r8 (devind, PCI_ILR); + orp->or_irq = ilr; + + /* Get the base address */ + bar = or_get_bar (devind, orp); + orp->or_base_port = bar; + + map_hw_buffer(orp); + return TRUE; +} + +/***************************************************************************** + * map_hw_buffer * + * * + * Map the memory mapped registers into user space memory * + *****************************************************************************/ +static void map_hw_buffer(t_or *orp) { + int r; + size_t o, size, reg_size; + char *buf, *abuf; + hermes_t *hw = &(orp->hw); + + /* This way, the buffer will be at least PAGE_SIZE big: see + * calculation with the offset */ + size = 2 * PAGE_SIZE; + + buf = (char *)malloc(size); + if(buf == NULL) + panic(__FILE__, "map_hw_buffer: cannot malloc size:", size); + + /* Let the mapped memory by PAGE_SIZE aligned */ + o = PAGE_SIZE - ((vir_bytes)buf % PAGE_SIZE); + abuf = buf + o; + + r = sys_vm_map(SELF, 1, (vir_bytes)abuf, + 1 * PAGE_SIZE, (phys_bytes)orp->or_base_port); + + if(r!=OK) + panic(__FILE__, "map_hw_buffer: sys_vm_map failed:", r); + + hw->locmem = abuf; +} + + + +/***************************************************************************** + * or_get_bar * + * * + * Get the base address from pci (from Base Address Register) and find out * + * whether the card is memory mapped or in I/O space. Currently, only * + * memmory mapped is supported. * + *****************************************************************************/ +static u32_t or_get_bar (int devind, t_or * orp) { + + u32_t bar, desired_bar; + int is_iospace, i; + u16_t check, check2; + hermes_t *hw = &(orp->hw); + + /* bit 1 off the PCI_BAR register indicates whether the cards registers + * are mapped in io-space or shared memory */ + is_iospace = pci_attr_r32 (devind, PCI_BAR) & 1; + + if (is_iospace) { + /* read where the base address is in I/O space */ + bar = pci_attr_r32 (devind, PCI_BAR) & 0xffffffe0; + + if ((bar & 0x3ff) >= 0x100 - 32 || bar < 0x400) + panic(__FILE__,"base address isn't properly configured", + NO_NUM); + + /* In I/O space registers are 2 bytes wide, without any spacing + * in between */ + hermes_struct_init (hw, bar, is_iospace, + HERMES_16BIT_REGSPACING); + + if (debug) { + printf ("%s: using I/O space address 0x%lx, IRQ %d\n", + orp->or_name, bar, orp->or_irq); + } + + panic(__FILE__, "Not implemente yet", NO_NUM); + /* Although we are able to find the desired bar and irq for an + * I/O spaced card, we haven't implemented the right register + * accessing functions. This wouldn't be difficult, but we were + * not able to test them. Therefore, give an alert here */ + + return bar; + } else { + /* read where the base address is in shared memory */ + bar = pci_attr_r32 (devind, PCI_BAR) & 0xfffffff0; + /* maybe some checking whether the address is legal... */ + + /* Memory mapped registers are 2 bytes wide, aligned on 4 + * bytes */ + hermes_struct_init (hw, bar, is_iospace, + HERMES_32BIT_REGSPACING); + + if (debug){ + printf ("%s: using shared memory address", + orp->or_name); + printf (" 0x%lx, IRQ %d\n", bar, orp->or_irq); + } + + return bar; + + } +} + +/***************************************************************************** + * or_init_struct * + * * + * Set the orinoco structure to default values * + *****************************************************************************/ +static void or_init_struct (t_or * orp) { + int i = 0; + static eth_stat_t empty_stat = { 0, 0, 0, 0, 0, 0 }; + + orp->or_mode = OR_M_DISABLED; + + if (orp->or_seen) + orp->or_mode = OR_M_ENABLED; + + if (orp->or_mode != OR_M_ENABLED) + return; + + orp->or_got_int = 0; + orp->or_link_up = -1; + orp->or_send_int = 0; + orp->or_clear_rx = 0; + orp->or_tx_alive = 0; + orp->or_need_reset = 0; + + orp->or_read_s = 0; + orp->or_tx_head = 0; + orp->or_tx_tail = 0; + orp->connected = 0; + + orp->or_tx.ret_busy = FALSE; + orp->or_tx.or_txfid = NO_FID; + + for(i = 0; i < NR_RX_BUFS; i++) { + orp->rxfid[i] = NO_FID; + orp->rx_length[i] = 0; + } + orp->rx_current = 0; + orp->rx_first = 0; + orp->rx_last = 0; + + orp->or_stat = empty_stat; + orp->or_flags = OR_F_EMPTY; + + /* Keep an administration in the driver whether the internal + buffer is in use. That's what ret_busy is for */ + orp->or_tx.ret_busy = FALSE; + + orp->or_nicbuf_size = IEEE802_11_FRAME_LEN + ETH_HLEN; + +} + +/***************************************************************************** + * or_init_hw * + * * + * Initialize hardware and prepare for intercepting the interrupts. At the * + * end, the card is up and running * + *****************************************************************************/ +static void or_init_hw (t_or * orp) { + int i, err, s; + hermes_t *hw = &(orp->hw); + static int first_time = TRUE; + + /* first step in starting the card */ + if (hermes_cor_reset(hw) != 0) { + printf ("%s: Failed to start the card\n", orp->or_name); + } + + /* here begins the real things, yeah! ;) */ + if (err = hermes_init (hw)) { + printf ("error value of hermes_init(): %d\n", err); + } + + /* Get the MAC address (which is a data item in the card)*/ + or_readrids (hw, orp); + + /* Write a few rids to the card, e.g. WEP key*/ + or_writerids (hw, orp); + + if (debug) { + printf ("%s: Ethernet address ", orp->or_name); + for (i = 0; i < 6; i++) { + printf ("%x%c", orp->or_address.ea_addr[i], + i < 5 ? ':' : '\n'); + } + } + + /* Prepare internal TX buffer in the card */ + err = hermes_allocate (hw, + orp->or_nicbuf_size, + &(orp->or_tx.or_txfid)); + + if (err) + printf ("%s:Error %d allocating Tx buffer\n", + orp->or_name, err); + + /* Establish event handle */ + if(first_time) { + orp->or_hook_id = orp->or_irq; + if ((s=sys_irqsetpolicy(orp->or_irq, 0, + &orp->or_hook_id)) != OK) + printf("orinoco: couldn't set IRQ policy: %d\n", s); + + if ((s=sys_irqenable(&orp->or_hook_id)) != OK) + printf("orinoco: couldn't enable interrupts: %d\n", s); + first_time = FALSE; + } + + /* Tell the card which events should raise an interrupt to the OS */ + hermes_set_irqmask (hw, ORINOCO_INTEN); + + /* Enable operation */ + err = hermes_docmd_wait (hw, HERMES_CMD_ENABLE, 0, NULL); + if (err) { + printf ("%s: Error %d enabling MAC port\n", orp->or_name, err); + } +} + + +/***************************************************************************** + * or_readrids * + * * + * Read some default rids from the card. A rid (resource identifier) * + * is a data item in the firmware, some configuration variable. * + * In our case, we are mostly interested in the MAC address for now * + *****************************************************************************/ + +static void or_readrids (hermes_t * hw, t_or * orp) { + int err, len, i; + struct hermes_idstring nickbuf; + u16_t reclen, d; + + /* Read the MAC address */ + err = hermes_read_ltv (hw, USER_BAP, HERMES_RID_CNFOWNMACADDR, + ETH_ALEN, NULL, &orp->or_address); + if (err) { + printf ("%s: failed to read MAC address!\n", orp->or_name); + return; + } + +} + +/***************************************************************************** + * or_writerids * + * * + * Write some default rids to the card. A rid (resource identifier) * + * is a data item in the firmware, some configuration variable, e.g. WEP key * + *****************************************************************************/ +static void or_writerids (hermes_t * hw, t_or * orp) { + int err; + struct hermes_idstring idbuf; + u16_t port_type, max_data_len, reclen; + static char essid[IW_ESSID_MAX_SIZE + 1]; + static char wepkey0[LARGE_KEY_LENGTH + 1]; + + /* Set the MAC port */ + port_type = 1; + err = hermes_write_wordrec (hw, USER_BAP, HERMES_RID_CNFPORTTYPE, + port_type); + if (err) { + printf ("%s: Error %d setting port type\n", orp->or_name, err); + return; + } + + if (OK != env_get_param("essid", essid, sizeof(essid))) { + essid[0] = 0; + } + + if(strlen(essid) == 0) { + printf("%s: no essid provided in boot monitor!\n", + orp->or_name); + printf("Hope you'll connect to the right network... \n"); + } + + /* Set the desired ESSID */ + idbuf.len = strlen (essid); + memcpy (&idbuf.val, essid, sizeof (idbuf.val)); + + err = hermes_write_ltv (hw, USER_BAP, HERMES_RID_CNFDESIREDSSID, + HERMES_BYTES_TO_RECLEN (strlen (essid) + 2), + &idbuf); + if (err) { + printf ("%s: Error %d setting DESIREDSSID\n", + orp->or_name, err); + return; + } + + if (OK != env_get_param("wep", wepkey0, sizeof(wepkey0))) { + wepkey0[0] = 0; + } + + switch(strlen(wepkey0)) { + case 0: + /* No key found in monitor, using no encryption */ + break; + case LARGE_KEY_LENGTH: + setup_wepkey(orp, wepkey0); + break; + default: + printf("unvalid key provided. Has to be 13 chars\n"); + break; + } +} + +/***************************************************************************** + * setup_wepkey * + * * + * If a wepkey is provided in the boot monitor, set the necessary rids so * + * that the card will decrypt received data and encrypt data to send by * + * by default with this key. * + * It appears that there is a severe bug in setting up WEP. If the driver * + * doesnt function properly, please turn WEP off. * + *****************************************************************************/ +static void setup_wepkey(t_or *orp, char *wepkey0) { + int default_key = 0, err = 0; + hermes_t *hw = &(orp->hw); + + err = hermes_write_wordrec (hw, USER_BAP, + HERMES_RID_CNFWEPDEFAULTKEYID, + default_key); + if (err) + printf ("%s: Error %d setting the default WEP-key entry\n", + orp->or_name, err); + + err = hermes_write_ltv (hw, USER_BAP, + HERMES_RID_CNFDEFAULTKEY0, + HERMES_BYTES_TO_RECLEN(LARGE_KEY_LENGTH), + wepkey0); + if (err) + printf ("%s: Error %d setting the WEP-key0\n", + orp->or_name, err); + + err = hermes_write_wordrec (hw, USER_BAP, + HERMES_RID_CNFAUTHENTICATION, + HERMES_AUTH_OPEN); + if (err) + printf ("%s: Error %d setting the authentication flag\n", + orp->or_name, err); + + err = hermes_write_wordrec (hw, USER_BAP, + HERMES_RID_CNFWEPFLAGS_INTERSIL, + HERMES_WEP_PRIVACY_INVOKED); + if (err) + printf ("%s: Error %d setting the master wep setting flag\n", + orp->or_name, err); + +} + + +/***************************************************************************** + * or_rec_mode * + * * + * Set the desired receive mode, e.g. promiscuous mode. Not implemented yet * + *****************************************************************************/ +static void or_rec_mode (t_or * orp) { + /* TODO */ +} + +/***************************************************************************** + * or_handler * + * * + * The handler which is called when the card generated an interrupt. Events * + * like EV_INFO and EV_RX have to be handled before an acknowledgement for * + * the event is returned to the card. See also the documentation * + *****************************************************************************/ +static int or_handler (t_or *orp) { + int i, err, length, nr = 0; + u16_t evstat, events, fid; + hermes_t *hw; + struct hermes_tx_descriptor desc; + + hw = &(orp->hw); + +beginning: + /* Retrieve which kind of event happened */ + evstat = hermes_read_reg (hw, HERMES_EVSTAT); + events = evstat; + + /* There are plenty of events possible. The more interesting events + are actually implemented. Whether the following events actually + raise an interrupt depends on the value of ORINOCO_INTEN. For more + information about the events, see the specification in pdf */ + + /* Occurs at each tick of the auxiliary time */ + if (events & HERMES_EV_TICK) { + events &= ~HERMES_EV_TICK; + } + /* Occurs when a wait time-out error is detected */ + if (events & HERMES_EV_WTERR) { + events &= ~HERMES_EV_WTERR; + } + + /* Occurs when an info frame is dropped because there is not enough + buffer space available */ + if (events & HERMES_EV_INFDROP) { + events &= ~(HERMES_EV_INFDROP); + } + + /* This AP-only event will be asserted at the beacon interval prior to + the DTIM interval */ + if (events & HERMES_EV_DTIM) { + events &= ~(HERMES_EV_DTIM); + } + + /* Occurs when a command execution is completed */ + if (events & HERMES_EV_CMD) { + events &= ~(HERMES_EV_CMD); + } + + /* Occurs when the asynchronous transmission process is unsuccessfully + completed */ + if (events & HERMES_EV_TXEXC) { + + /* What buffer generated the event? Represented by an fid */ + fid = hermes_read_reg(hw, HERMES_TXCOMPLFID); + if(fid == 0xFFFF) { + /* Illegal fid found */ + printf("unexpected txexc_fid interrupted\n"); + } + + orp->or_tx.ret_busy = FALSE; + + if(orp->or_flags & OR_F_SEND_AVAIL) { + orp->or_send_int = TRUE; + if (!orp->or_got_int){ + orp->or_got_int = TRUE; + int_event_check = TRUE; + } + } + + /* To detect illegal fids */ + hermes_write_reg(hw, HERMES_TXCOMPLFID, 0xFFFF); + events &= ~(HERMES_EV_TXEXC); + /* We don't do anything else yet. + * Could be used for statistics */ + } + + /* Occurs when the asynchronous transmission process is successfully + completed */ + if (events & HERMES_EV_TX) { + events &= ~(HERMES_EV_TX); + /* Which buffer was sent, represented by an fid */ + fid = hermes_read_reg (hw, HERMES_TXCOMPLFID); + if(fid == 0xFFFF) { + /* Illegal fid found */ + printf("unexpected tx_fid interrupted\n"); + } + + orp->or_tx.ret_busy = FALSE; + + if(orp->or_flags & OR_F_SEND_AVAIL) { + orp->or_send_int = TRUE; + if (!orp->or_got_int){ + orp->or_got_int = TRUE; + int_event_check = TRUE; + } + } + + /* To detect illegal fids */ + hermes_write_reg(hw, HERMES_TXCOMPLFID, 0xFFFF); + /* We don't do anything else when such event happens */ + } + + /* Occurs when an info frame is available in the card */ + if (events & HERMES_EV_INFO) { + events &= ~(HERMES_EV_INFO); + /* Process the information, inside the handler (!) */ + or_ev_info(orp); + } + + /* Occurs when a TX buffer is available again for usage */ + if (events & HERMES_EV_ALLOC) { + /* Which frame is now marked as free? */ + fid = hermes_read_reg (hw, HERMES_ALLOCFID); + if (fid == 0xFFFF){ + /* An illegal frame identifier is found. Ignore */ + printf("Allocate event on unexpected fid\n"); + return ; + } + + /* To be able to detect illegal fids */ + hermes_write_reg(hw, HERMES_ALLOCFID, 0xFFFF); + + events &= ~(HERMES_EV_ALLOC); + } + + + /* Occurs when a frame is received by the asynchronous reception + * process */ + + if (events & HERMES_EV_RX) { + orp->or_ev_rx = TRUE; + events &= ~(HERMES_EV_RX); + + /* If the last buffer is still filled with data, then we don't + * have any buffers available to store the data */ + if(orp->rx_length[orp->rx_last] != 0) { + /* indeed, we are going to overwrite information + * in a buffer */ + } + + /* Which buffer is storing the data (represented by a fid) */ + orp->rxfid[orp->rx_last] + = hermes_read_reg (hw, HERMES_RXFID); + + /* Get the packet from the card and store it in + * orp->rx_buf[orp->rx_last]. The length is returned by this + * function */ + length = or_get_recvd_packet(orp, orp->rxfid[orp->rx_last], + (orp->rx_buf[orp->rx_last])); + + if(length < 0) { + /* Error happened. */ + printf("length < 0\n"); + goto next; + } else { + orp->rx_length[orp->rx_last] = length; + } + + /* The next buffer will be used the next time, circularly */ + orp->rx_last++; + orp->rx_last %= NR_RX_BUFS; + + if (!orp->or_got_int){ + orp->or_got_int = TRUE; + } + int_event_check = TRUE; + } +next: + if (events) { + printf("Unknown event: 0x%x\n", events); + } + + /* Acknowledge to the card that the events have been processed. After + * this the card will assume we have processed any buffer which were in + * use for this event. */ + hermes_write_reg (hw, HERMES_EVACK, evstat); + + evstat = hermes_read_reg (hw, HERMES_EVSTAT); + if(evstat != 0 && !(evstat & HERMES_EV_TICK)) { + goto beginning; + } + + return (1); +} + + +/***************************************************************************** + * or_watchdog_f * + * * + * Will be called regularly to see whether the driver has crashed. If that * + * condition is detected, reset the driver and card * + *****************************************************************************/ +static void or_watchdog_f(timer_t *tp) { + int i; + t_or *orp; + + /* Use a synchronous alarm instead of a watchdog timer. */ + sys_setalarm(HZ, 0); + + for (i= 0, orp = &or_table[0]; ior_mode != OR_M_ENABLED) + continue; + + if (!(orp->or_flags & OR_F_SEND_AVAIL)) { + /* Assume that an idle system is alive */ + orp->or_tx_alive= TRUE; + continue; + } + + if (orp->connected == 0) { + orp->or_tx_alive= TRUE; + continue; + } + if (orp->or_tx_alive) { + orp->or_tx_alive= FALSE; + continue; + } + + printf("or_watchdog_f: resetting port %d\n", i); + + orp->or_need_reset= TRUE; + orp->or_got_int= TRUE; + check_int_events(); + } +} + +/***************************************************************************** + * mess_reply * + *****************************************************************************/ + +static void mess_reply (message * req, message * reply_mess) { + if (send (req->m_source, reply_mess) != 0) + panic(__FILE__, "orinoco: unable to mess_reply", NO_NUM); + +} + +/***************************************************************************** + * or_writev * + * * + * As far as we can see, this function is never called from 3.1.3. However, * + * it is still in rtl8139, so we'll keep it here as well. It's almost a copy * + * of or_writev_s. We left out the comments. For an explanation, see * + * or_writev_s * +******************************************************************************/ +static void or_writev (message * mp, int from_int, int vectored) { + int port, or_client, count, size, err, data_len, data_off, tx_head; + int o, j, n, i, s, p, cps ; + struct ethhdr *eh; + t_or *orp; + clock_t timebefore, t0; + phys_bytes phys_user, iov_src; + hermes_t *hw; + struct hermes_tx_descriptor desc; + struct header_struct hdr; + + iovec_t *iovp; + phys_bytes phys_databuf; + u16_t txfid; + static u8_t databuf[IEEE802_11_DATA_LEN + ETH_HLEN + 2 + 1]; + memset (databuf, 0, IEEE802_11_DATA_LEN + ETH_HLEN + 3); + + port = mp->DL_PORT; + count = mp->DL_COUNT; + if (port < 0 || port >= OR_PORT_NR) + panic(__FILE__, "orinoco: illegal port", NO_NUM); + + or_client = mp->DL_PROC; + orp = &or_table[port]; + orp->or_client = or_client; + hw = &(orp->hw); + + if (from_int) { + assert (orp->or_flags & OR_F_SEND_AVAIL); + orp->or_flags &= ~OR_F_SEND_AVAIL; + orp->or_send_int = FALSE; + orp->or_tx_alive = TRUE; + } + + if (orp->or_tx.ret_busy) { + assert(!(orp->or_flags & OR_F_SEND_AVAIL)); + orp->or_flags |= OR_F_SEND_AVAIL; + goto suspend_write; + } + + assert (orp->or_mode == OR_M_ENABLED); + assert (orp->or_flags & OR_F_ENABLED); + + if (vectored) { + + int iov_offset = 0; + size = 0; + o = 0; + + for (i = 0; i < count; i += IOVEC_NR, + iov_src += IOVEC_NR * sizeof (orp->or_iovec[0]), + iov_offset += IOVEC_NR * sizeof (orp->or_iovec[0])) { + + n = IOVEC_NR; + if (i + n > count) + n = count - i; + cps = sys_vircopy(or_client, D, + ((vir_bytes) mp->DL_ADDR) + iov_offset, + SELF, D, (vir_bytes) orp->or_iovec, + n * sizeof(orp->or_iovec[0])); + if (cps != OK) printf("sys_vircopy failed: %d\n", cps); + + for (j = 0, iovp = orp->or_iovec; j < n; j++, iovp++) { + s = iovp->iov_size; + if (size + s > ETH_MAX_PACK_SIZE_TAGGED) { + printf("invalid packet size\n"); + } + cps = sys_vircopy(or_client, D, iovp->iov_addr, + SELF, D, (vir_bytes) databuf + o, s); + if (cps != OK) + printf("sys_vircopy failed: %d\n",cps); + + size += s; + o += s; + } + } + if (size < ETH_MIN_PACK_SIZE) + printf("invalid packet size %d\n", size); + } else { + size = mp->DL_COUNT; + if (size < ETH_MIN_PACK_SIZE + || size > ETH_MAX_PACK_SIZE_TAGGED) + printf("invalid packet size %d\n", size); + + cps = sys_vircopy(or_client, D, (vir_bytes)mp->DL_ADDR, + SELF, D, (vir_bytes) databuf, size); + if (cps != OK) printf("sys_abscopy failed: %d\n", cps); + } + + memset (&desc, 0, sizeof (desc)); + desc.tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX; + + err = hermes_bap_pwrite (hw, USER_BAP, &desc, sizeof (desc), txfid, + 0); + if (err) { + or_reset(); + goto fail; + } + + eh = (struct ethhdr *) databuf; + if (ntohs (eh->h_proto) > 1500) { + + data_len = size - ETH_HLEN; + data_off = HERMES_802_3_OFFSET + sizeof (hdr); + + memcpy (hdr.dest, eh->h_dest, ETH_ALEN); + memcpy (hdr.src, eh->h_src, ETH_ALEN); + hdr.len = htons (data_len + ENCAPS_OVERHEAD); + + memcpy (&hdr.dsap, &encaps_hdr, sizeof (encaps_hdr)); + hdr.ethertype = eh->h_proto; + + err = hermes_bap_pwrite (hw, USER_BAP, &hdr, sizeof (hdr), + txfid, HERMES_802_3_OFFSET); + if (err) { + printf ("%s: Error %d writing packet header to BAP\n", + orp->or_name, err); + goto fail; + } + + p = ETH_HLEN; + } else { + data_len = size + ETH_HLEN; + data_off = HERMES_802_3_OFFSET; + p = 0; + } + + err = hermes_bap_pwrite (hw, USER_BAP, + (void *) &(databuf[p]), RUP_EVEN (data_len), + txfid, data_off); + if (err) { + printf ("hermes_bap_pwrite(data): error %d\n", err); + goto fail; + } + + orp->or_tx.ret_busy = TRUE; + + err = hermes_docmd_wait (hw, HERMES_CMD_TX | HERMES_CMD_RECL, + txfid, NULL); + if (err) { + orp->or_tx.ret_busy = FALSE; + printf ("hermes_docmd_wait(TX|RECL): error %d\n", err); + goto fail; + } + +fail: + orp->or_flags |= OR_F_PACK_SENT; + + if (from_int) { + return; + } + + reply (orp, OK, FALSE); + return; + +suspend_write: + orp->or_tx_mess = *mp; + reply (orp, OK, FALSE); + return; +} + + + +/***************************************************************************** + * or_writev_s * + * * + * Write data which is denoted by the message to the card and send it. * + *****************************************************************************/ +static void or_writev_s (message * mp, int from_int) { + int port, or_client, count, size, err, data_len, data_off, tx_head; + int o, j, n, i, s, p, cps ; + struct ethhdr *eh; + t_or *orp; + clock_t timebefore, t0; + phys_bytes phys_user, iov_src; + hermes_t *hw; + struct hermes_tx_descriptor desc; + int iov_offset = 0; + struct header_struct hdr; + iovec_s_t *iovp; + phys_bytes phys_databuf; + u16_t txfid; + + /* We need space for the max packet size itself, plus an ethernet + * header, plus 2 bytes so we can align the IP header on a + * 32bit boundary, plus 1 byte so we can read in odd length + * packets from the card, which has an IO granularity of 16 + * bits */ + static u8_t databuf[IEEE802_11_DATA_LEN + ETH_HLEN + 2 + 1]; + memset (databuf, 0, IEEE802_11_DATA_LEN + ETH_HLEN + 3); + + port = mp->DL_PORT; + count = mp->DL_COUNT; + if (port < 0 || port >= OR_PORT_NR) + panic(__FILE__, "orinoco: illegal port", NO_NUM); + + or_client = mp->DL_PROC; + orp = &or_table[port]; + orp->or_client = or_client; + hw = &(orp->hw); + + /* Switch off interrupts. The card is accessable via 2 BAPs, one for + * reading and one for writing. In theory these BAPs should be + * independent, but in practice, the are not. By switching off the + * interrupts of the card, the chances of one interfering with the + * other should be less */ + if (from_int){ + /* We were called with from_int, meaning that the last time we + * were called, no tx buffers were available, and we had to + * suspend. Now, we'll try again to find an empty buffer in the + * card */ + assert (orp->or_flags & OR_F_SEND_AVAIL); + orp->or_flags &= ~OR_F_SEND_AVAIL; + orp->or_send_int = FALSE; + orp->or_tx_alive = TRUE; + } + + txfid = orp->or_tx.or_txfid; + + if (orp->or_tx.ret_busy || orp->connected == 0) { + /* there is no buffer in the card available */ + assert(!(orp->or_flags & OR_F_SEND_AVAIL)); + /* Remember that there is a packet to be sent available */ + orp->or_flags |= OR_F_SEND_AVAIL; + goto suspend_write_s; + } + + assert (orp->or_mode == OR_M_ENABLED); + assert (orp->or_flags & OR_F_ENABLED); + + + /* Copy the data to be send from the vector to the databuf */ + size = 0; + o = 0; + for (i = 0; i < count; i += IOVEC_NR, + iov_src += IOVEC_NR * sizeof (orp->or_iovec_s[0]), + iov_offset += IOVEC_NR * sizeof (orp->or_iovec_s[0])) { + + n = IOVEC_NR; + if (i + n > count) + n = count - i; + + cps = sys_safecopyfrom(or_client, mp->DL_GRANT, iov_offset, + (vir_bytes) orp->or_iovec_s, + n * sizeof(orp->or_iovec_s[0]), D); + if (cps != OK) + printf("orinoco: sys_safecopyfrom failed: %d\n", cps); + + for (j = 0, iovp = orp->or_iovec_s; j < n; j++, iovp++) { + s = iovp->iov_size; + if (size + s > ETH_MAX_PACK_SIZE_TAGGED) { + printf("Orinoco: invalid pkt size\n"); + } + + cps = sys_safecopyfrom(or_client, iovp->iov_grant, 0, + (vir_bytes) databuf + o, s, D); + if (cps != OK) + printf("orinoco: sys_safecopyfrom failed:%d\n", + cps); + + size += s; + o += s; + } + } + + assert(size >= ETH_MIN_PACK_SIZE); + + memset (&desc, 0, sizeof (desc)); + /* Reclaim the tx buffer once the data is sent (OK), or it is clear + * that transmission failed (EX). Reclaiming means that we can reuse + * the buffer again for transmission */ + desc.tx_control = HERMES_TXCTRL_TX_OK | HERMES_TXCTRL_TX_EX; + /* Actually, this reclaim bit is the only thing which needs to be set + * in the descriptor */ + err = hermes_bap_pwrite (hw, USER_BAP, &desc, sizeof (desc), txfid, + 0); + if (err) { + printf("hermes_bap_pwrite() descriptor error:resetting card\n"); + /* When this happens, the card is quite confused: it will not + * recover. Reset it */ + or_reset(); + goto fail; + } + + eh = (struct ethhdr *) databuf; + /* Encapsulate Ethernet-II frames */ + if (ntohs (eh->h_proto) > 1500) { + /* Ethernet-II frame */ + data_len = size - ETH_HLEN; + data_off = HERMES_802_3_OFFSET + sizeof (hdr); + + /* 802.3 header */ + memcpy (hdr.dest, eh->h_dest, ETH_ALEN); + memcpy (hdr.src, eh->h_src, ETH_ALEN); + hdr.len = htons (data_len + ENCAPS_OVERHEAD); + + /* 802.2 header */ + memcpy (&hdr.dsap, &encaps_hdr, sizeof (encaps_hdr)); + hdr.ethertype = eh->h_proto; + + err = hermes_bap_pwrite (hw, USER_BAP, &hdr, sizeof (hdr), + txfid, HERMES_802_3_OFFSET); + if (err) { + printf ("%s: Error %d writing packet header to BAP\n", + orp->or_name, err); + goto fail; + } + + p = ETH_HLEN; + } else { + /* IEEE 802.3 frame */ + data_len = size + ETH_HLEN; + data_off = HERMES_802_3_OFFSET; + p = 0; + } + + /* Round up for odd length packets */ + err = hermes_bap_pwrite (hw, USER_BAP, + (void *) &(databuf[p]), RUP_EVEN (data_len), + txfid, data_off); + if (err) { + printf ("hermes_bap_pwrite(data): error %d\n", err); + goto fail; + } + + /* this should be before the docmd_wait. Cause otherwise the bit can + be cleared in the handler (if irq's not off) before it is set + and then 1 reset (ret_busy=false) is lost */ + orp->or_tx.ret_busy = TRUE; + + /* Send the packet which was constructed in txfid */ + err = hermes_docmd_wait (hw, HERMES_CMD_TX | HERMES_CMD_RECL, + txfid, NULL); + if (err) { + printf ("hermes_docmd_wait(TX|RECL): error %d\n", err); + /* Mark the buffer as available again */ + orp->or_tx.ret_busy = FALSE; + goto fail; + } + +fail: + /* If the interrupt handler called, don't send a reply. The reply + * will be sent after all interrupts are handled. + */ + orp->or_flags |= OR_F_PACK_SENT; + + if (from_int) { + return; + } + + reply (orp, OK, FALSE); + return; + +suspend_write_s: + orp->or_tx_mess = *mp; + + reply (orp, OK, FALSE); + return; +} + + +/***************************************************************************** + * reply * + * * + * Send a message back to the caller, informing it about the data received * + * or sent * + *****************************************************************************/ +static void reply (t_or * orp, int err, int may_block) { + message reply; + int status = 0, r; + clock_t now; + + if (orp->or_flags & OR_F_PACK_SENT) + status |= DL_PACK_SEND; + if (orp->or_flags & OR_F_PACK_RECV) + status |= DL_PACK_RECV; + + reply.m_type = DL_TASK_REPLY; + reply.DL_PORT = orp - or_table; + assert(reply.DL_PORT == 0); + reply.DL_PROC = orp->or_client; + reply.DL_STAT = status | ((u32_t) err << 16); + reply.DL_COUNT = orp->or_read_s; + + if (OK != (r = getuptime(&now))) + panic(__FILE__, "orinoco: getuptime() failed:", r); + + reply.DL_CLCK = now; + r = send (orp->or_client, &reply); + + if (r == ELOCKED && may_block) { + return; + } + + if (r < 0) + panic(__FILE__, "orinoco: send failed:", r); + + orp->or_read_s = 0; + orp->or_flags &= ~(OR_F_PACK_SENT | OR_F_PACK_RECV); +} + + + +/***************************************************************************** + * or_ev_info * + * * + * Process information which comes in from the card * + *****************************************************************************/ +static void or_ev_info (t_or * orp) { + u16_t infofid; + int err, len, type, i; + hermes_t *hw = &orp->hw; + + struct { + u16_t len; + u16_t type; + } info; + + infofid = hermes_read_reg (hw, HERMES_INFOFID); + err = hermes_bap_pread (hw, IRQ_BAP, &info, sizeof (info), infofid, + 0); + if (err) { + printf ("%s: error %d reading info frame.\n", orp->or_name, + err); + return; + } + + len = HERMES_RECLEN_TO_BYTES (info.len); + type = info.type; + + switch (type) { + case HERMES_INQ_TALLIES: + { + struct hermes_tallies_frame tallies; + + if (len > sizeof (tallies)) { + printf ("%s: Tallies frame too long ", + orp->or_name); + printf ("(%d bytes)\n", len); + len = sizeof (tallies); + } + hermes_read_words (hw, HERMES_DATA1, + (void *) &tallies, len / 2); + /* TODO: do something with the tallies structure */ + } + break; + + case HERMES_INQ_LINKSTATUS: { + u16_t newstatus; + struct hermes_linkstatus linkstatus; + + if (len != sizeof (linkstatus)) { + printf ("%s: Unexpected size for linkstatus ", + orp->or_name); + printf ("frame (%d bytes)\n", len); + } + + hermes_read_words (hw, HERMES_DATA1, + (void *) &linkstatus, len / 2); + newstatus = linkstatus.linkstatus; + + if ((newstatus == HERMES_LINKSTATUS_CONNECTED) + || (newstatus == HERMES_LINKSTATUS_AP_CHANGE) + || (newstatus == HERMES_LINKSTATUS_AP_IN_RANGE)) { + orp->connected = 1; + + if(orp->or_flags & OR_F_SEND_AVAIL) { + orp->or_send_int = TRUE; + orp->or_got_int = TRUE; + int_event_check = TRUE; + } + + + } + else if ((newstatus == + HERMES_LINKSTATUS_NOT_CONNECTED) + || (newstatus == + HERMES_LINKSTATUS_DISCONNECTED) + || (newstatus == + HERMES_LINKSTATUS_AP_OUT_OF_RANGE) + || (newstatus == + HERMES_LINKSTATUS_ASSOC_FAILED)) { + orp->connected = 0; + } + + if (newstatus != orp->last_linkstatus) + print_linkstatus(orp, newstatus); + + orp->last_linkstatus = newstatus; + } + break; + default: + printf ("%s:Unknown information frame received(type %04x).\n", + orp->or_name, type); + break; + } +} + +/***************************************************************************** + * or_print_linkstatus * + * * + * Process information which comes in from the card * + *****************************************************************************/ +static void print_linkstatus (t_or * orp, u16_t status) { + int err; + u16_t d; + char *s; + hermes_t *hw = &(orp->hw); + + switch (status) { + case HERMES_LINKSTATUS_NOT_CONNECTED: + s = "Not Connected"; + break; + case HERMES_LINKSTATUS_CONNECTED: + s = "Connected"; + break; + case HERMES_LINKSTATUS_DISCONNECTED: + s = "Disconnected"; + break; + case HERMES_LINKSTATUS_AP_CHANGE: + s = "AP Changed"; + break; + case HERMES_LINKSTATUS_AP_OUT_OF_RANGE: + s = "AP Out of Range"; + break; + case HERMES_LINKSTATUS_AP_IN_RANGE: + s = "AP In Range"; + break; + case HERMES_LINKSTATUS_ASSOC_FAILED: + s = "Association Failed"; + break; + default: + s = "UNKNOWN"; + } + + printf ("%s: link status: %s, ", orp->or_name, s); + + err = hermes_read_wordrec (hw, USER_BAP, + HERMES_RID_CURRENTCHANNEL, &d); + if (err) { + printf ("%s: Error %d \n", orp->or_name, err); + return; + } + printf("channel: %d, freq: %d MHz ", + d, (channel_frequency[d-1])); + +} + + +/***************************************************************************** + * or_check_ints * + * * + * Process events which have been postponed in the interrupt handler * + *****************************************************************************/ +static void or_check_ints (t_or * orp) { + int or_flags; + hermes_t *hw = &orp->hw; + + if (orp->or_need_reset) + or_reset(); + if ((orp->rx_first!=orp->rx_last) && (orp->or_flags & OR_F_READING)) { + orp->or_ev_rx = 0; + if (orp->or_rx_mess.m_type == DL_READV) { + or_readv (&orp->or_rx_mess, TRUE, TRUE); + } else if(orp->or_rx_mess.m_type == DL_READV_S) { + or_readv_s (&orp->or_rx_mess, TRUE); + } else { + assert(orp->or_rx_mess.m_type == DL_READ); + or_readv (&orp->or_rx_mess, TRUE, FALSE); + } + } + + if (orp->or_send_int) { + if (orp->or_tx_mess.m_type == DL_WRITEV) { + or_writev (&orp->or_tx_mess, TRUE, TRUE); + } + else if(orp->or_tx_mess.m_type == DL_WRITEV_S) { + or_writev_s (&orp->or_tx_mess, TRUE); + } else { + assert(orp->or_tx_mess.m_type == DL_WRITE); + or_writev (&orp->or_tx_mess, TRUE, FALSE); + } + } + + if (orp->or_flags & (OR_F_PACK_SENT | OR_F_PACK_RECV)) { + reply (orp, OK, TRUE); + } +} + + +/***************************************************************************** + * is_ethersnap * + * * + * is there an LLC and SNAP header in the ethernet packet? The inet task * + * isn't very interested in it... * + *****************************************************************************/ +static int is_ethersnap(struct header_struct *hdr) { + + /* We de-encapsulate all packets which, a) have SNAP headers + * (i.e. SSAP=DSAP=0xaa and CTRL=0x3 in the 802.2 LLC header + * and where b) the OUI of the SNAP header is 00:00:00 or + * 00:00:f8 - we need both because different APs appear to use + * different OUIs for some reason */ + return (memcmp(&hdr->dsap, &encaps_hdr, 5) == 0) + && ( (hdr->oui[2] == 0x00) || (hdr->oui[2] == 0xf8) ); +} + +/***************************************************************************** + * or_readv * + * * + * As far as we can see, this function is never called from 3.1.3. However, * + * it is still in rtl8139, so we'll keep it here as well. It's almost a copy * + * of or_readv_s. We left out the comments. For an explanation, see * + * or_readv_s * + *****************************************************************************/ +static void or_readv (message * mp, int from_int, int vectored) { + int i, j, n, o, s, s1, dl_port, or_client, count, size, err, yep, cps; + port_t port; + clock_t timebefore; + unsigned amount, totlen, packlen; + struct hermes_rx_descriptor desc; + phys_bytes dst_phys, iov_src; + u16_t d_start, d_end, rxfid, status; + struct header_struct hdr; + int length, offset; + u32_t l, rxstat; + struct ethhdr *eh; + struct header_struct *h; + t_or *orp; + hermes_t *hw; + iovec_t *iovp; + u8_t *databuf; + + dl_port = mp->DL_PORT; + count = mp->DL_COUNT; + if (dl_port < 0 || dl_port >= OR_PORT_NR) + panic(__FILE__, "orinoco: illegal port:", dl_port); + + orp = &or_table[dl_port]; + or_client = mp->DL_PROC; + orp->or_client = or_client; + hw = &(orp->hw); + + assert (orp->or_mode == OR_M_ENABLED); + assert (orp->or_flags & OR_F_ENABLED); + + if (!from_int && (orp->rx_first==orp->rx_last)) { + goto suspend_readv; + } + + rxfid = orp->rxfid[orp->rx_first]; + databuf = &(orp->rx_buf[orp->rx_first][0]); + length = orp->rx_length[orp->rx_first]; + + orp->rxfid[orp->rx_first] = NO_FID; + orp->rx_length[orp->rx_first] = 0; + + orp->rx_first++; + orp->rx_first %= NR_RX_BUFS; + + o = 0; + + if (vectored) { + int iov_offset = 0; + size = 0; + + for (i = 0; i < count; i += IOVEC_NR, + iov_src += IOVEC_NR * sizeof (orp->or_iovec[0]), + iov_offset += IOVEC_NR * sizeof(orp->or_iovec[0])) { + + n = IOVEC_NR; + if (i + n > count) + n = count - i; + + cps = sys_vircopy(or_client, D, + (vir_bytes) mp->DL_ADDR + iov_offset, + SELF, D, (vir_bytes) orp->or_iovec, + n * sizeof(orp->or_iovec[0])); + if (cps != OK) printf("sys_vircopy failed: %d (%d)\n", + cps, __LINE__); + + for (j = 0, iovp = orp->or_iovec; j < n; j++, iovp++) { + s = iovp->iov_size; + if (size + s > length) { + assert (length > size); + s = length - size; + } + + cps = sys_vircopy(SELF, D, + (vir_bytes) databuf + o, + or_client, D, + iovp->iov_addr, s); + if (cps != OK) + printf("sys_vircopy failed:%d (%d)\n", + cps, __LINE__); + + size += s; + if (size == length) + break; + o += s; + } + if (size == length) + break; + } + assert (size >= length); + } + + orp->or_stat.ets_packetR++; + orp->or_read_s = length; + orp->or_flags &= ~OR_F_READING; + orp->or_flags |= OR_F_PACK_RECV; + + if (!from_int) + reply (orp, OK, FALSE); + + return; + +suspend_readv : + if (from_int) { + assert (orp->or_flags & OR_F_READING); + return; + } + + orp->or_rx_mess = *mp; + assert (!(orp->or_flags & OR_F_READING)); + orp->or_flags |= OR_F_READING; + + reply (orp, OK, FALSE); +} + + +/***************************************************************************** + * or_readv_s * + * * + * Copy the data which is stored in orp->rx_buf[orp->rx_first] in the vector * + * which was given with the message *mp * + *****************************************************************************/ +static void or_readv_s (message * mp, int from_int) { + int i, j, n, o, s, s1, dl_port, or_client, count, size, err, cps; + int iov_offset = 0, length, offset; + port_t port; + clock_t timebefore; + unsigned amount, totlen, packlen; + struct hermes_rx_descriptor desc; + phys_bytes dst_phys, iov_src; + u16_t d_start, d_end, rxfid, status; + struct header_struct hdr; + u32_t l, rxstat; + struct ethhdr *eh; + struct header_struct *h; + t_or *orp; + hermes_t *hw; + + iovec_s_t *iovp; + phys_bytes databuf_phys; + + u8_t *databuf; + + dl_port = mp->DL_PORT; + count = mp->DL_COUNT; + if (dl_port < 0 || dl_port >= OR_PORT_NR) + panic(__FILE__, "orinoco: illegal port:", dl_port); + + orp = &or_table[dl_port]; + or_client = mp->DL_PROC; + orp->or_client = or_client; + hw = &(orp->hw); + + assert (orp->or_mode == OR_M_ENABLED); + assert (orp->or_flags & OR_F_ENABLED); + + if (!from_int && (orp->rx_first==orp->rx_last)) + + { + /* if we are not called from a hard int (data is not yet available) and + * there are no buffers (or->rx_buf[x]) which contain any data, we cant + * copy any data to the inet server. Goto suspend, and wait for data + * to arrive */ + goto suspend_readv_s; + } + + + + /* get the buffer which contains new data */ + rxfid = orp->rxfid[orp->rx_first]; + /* and store the pointer to this data in databuf */ + databuf = &(orp->rx_buf[orp->rx_first][0]); + length = orp->rx_length[orp->rx_first]; + + orp->rxfid[orp->rx_first] = NO_FID; + orp->rx_length[orp->rx_first] = 0; + + /* Next time, the next buffer with data will be retrieved */ + orp->rx_first++; + orp->rx_first %= NR_RX_BUFS; + + o = 0; + /* The data which we want to be copied to the vector starts at + * *databuf and will be copied to the vecor below */ + size = 0; + for (i = 0; i < count; i += IOVEC_NR, + iov_src += IOVEC_NR * sizeof (orp->or_iovec_s[0]), + iov_offset += IOVEC_NR * sizeof(orp->or_iovec_s[0])) { + n = IOVEC_NR; + if (i + n > count) + n = count - i; + + cps = sys_safecopyfrom(or_client, mp->DL_GRANT, iov_offset, + (vir_bytes)orp->or_iovec_s, + n * sizeof(orp->or_iovec_s[0]), D); + if (cps != OK) + panic(__FILE__, + "orinoco: warning, sys_safecopytp failed:", cps); + + for (j = 0, iovp = orp->or_iovec_s; j < n; j++, iovp++) { + s = iovp->iov_size; + if (size + s > length) { + assert (length > size); + s = length - size; + } + cps = sys_safecopyto(or_client, iovp->iov_grant, 0, + (vir_bytes) databuf + o, s, D); + if (cps != OK) + panic(__FILE__, + "orinoco: warning, sys_safecopy failed:", + cps); + + size += s; + if (size == length) + break; + o += s; + } + if (size == length) + break; + } + + assert(size >= length); + + orp->or_stat.ets_packetR++; +drop: + orp->or_read_s = length; + orp->or_flags &= ~OR_F_READING; + orp->or_flags |= OR_F_PACK_RECV; + + if (!from_int) { + /* There was data in the orp->rx_buf[x] which is now copied to + * the inet sever. Tell the inet server */ + reply (orp, OK, FALSE); + } + + return; +suspend_readv_s: + if (from_int) { + assert (orp->or_flags & OR_F_READING); + /* No need to store any state */ + return; + } + + /* We want to store the message, so that next time when we are called + * by hard int, we know where to copy the received data */ + orp->or_rx_mess = *mp; + assert (!(orp->or_flags & OR_F_READING)); + orp->or_flags |= OR_F_READING; + + reply (orp, OK, FALSE); + +} + + +/***************************************************************************** + * or_get_recvd_packet * + * * + * The card has received data. Retrieve the data from the card and put it * + * in a buffer in the driver (in the orp structure) * + *****************************************************************************/ +static int or_get_recvd_packet(t_or *orp, u16_t rxfid, u8_t *databuf) { + struct hermes_rx_descriptor desc; + hermes_t *hw; + struct header_struct hdr; + int err, length, offset; + struct ethhdr *eh; + u16_t status; + + memset(databuf, 0, IEEE802_11_FRAME_LEN); + + hw = &(orp->hw); + + /* Read the data from the buffer in the card which holds the data. + * First get the descriptor which will tell us whether the packet is + * healthy*/ + err = hermes_bap_pread (hw, IRQ_BAP, &desc, sizeof (desc), rxfid, 0); + if (err) { + printf("Orinoco: error %d reading Rx descriptor. " + "Frame dropped\n", err); + orp->or_stat.ets_recvErr++; + return -1; + } + + status = desc.status; + + if (status & HERMES_RXSTAT_ERR) { + if (status & HERMES_RXSTAT_UNDECRYPTABLE) { + printf("Error reading Orinoco Rx descriptor.Dropped"); + } else { + orp->or_stat.ets_CRCerr++; + printf("Orinoco: Bad CRC on Rx. Frame dropped\n"); + } + orp->or_stat.ets_recvErr++; + return -1; + } + + /* For now we ignore the 802.11 header completely, assuming + that the card's firmware has handled anything vital. The only + thing we want to know is the length of the received data */ + err = hermes_bap_pread (hw, IRQ_BAP, &hdr, sizeof (hdr), + rxfid, HERMES_802_3_OFFSET); + + if (err) { + printf("Orinoco: error %d reading frame header. " + "Frame dropped\n", err); + orp->or_stat.ets_recvErr++; + return -1; + } + + length = ntohs (hdr.len); + + /* Sanity checks */ + if (length < 3) { + /* No for even an 802.2 LLC header */ + printf("Orinoco: error in frame length: length = %d\n", + length); + /* orp->or_stat.ets_recvErr++; */ + return -1; + } + + if (length > IEEE802_11_DATA_LEN) { + printf("Orinoco: Oversized frame received (%d bytes)\n", + length); + orp->or_stat.ets_recvErr++; + return -1; + } + + length += sizeof (struct ethhdr); + offset = HERMES_802_3_OFFSET; + + /* Read the interesting parts of the data to the drivers memory. This + * would be everything from the 802.3 layer and up */ + err = hermes_bap_pread (hw, + IRQ_BAP, (void *) databuf, RUP_EVEN (length), + rxfid, offset); + + if (err) { + printf("Orinoco: error doing hermes_bap_pread()\n"); + orp->or_stat.ets_recvErr++; + return -1; + } + + /* Some types of firmware give us the SNAP and OUI headers. Remove these. + */ + if (is_ethersnap(&hdr)) { + eh = (struct ethhdr *) databuf; + length -= 8; + + + memcpy (databuf + ETH_ALEN * 2, + databuf + sizeof(struct header_struct) - 2, + length - ETH_ALEN * 2); + } + + if(length<60) length=60; + + return length; +} + +/***************************************************************************** + * or_getstat * + * * + * Return the statistics structure. The statistics aren't updated until now, * + * so this won't return much interesting yet. * + *****************************************************************************/ +static void or_getstat (message * mp) { + int r, port; + eth_stat_t stats; + t_or *orp; + + port = mp->DL_PORT; + if (port < 0 || port >= OR_PORT_NR) + panic(__FILE__, "orinoco: illegal port:", port); + orp = &or_table[port]; + orp->or_client = mp->DL_PROC; + + assert (orp->or_mode == OR_M_ENABLED); + assert (orp->or_flags & OR_F_ENABLED); + + stats = orp->or_stat; + + r = sys_datacopy(SELF, (vir_bytes)&stats, mp->DL_PROC, + (vir_bytes) mp->DL_ADDR, sizeof(stats)); + if(r != OK) { + panic(__FILE__, "or_getstat: send failed:", r); + } + + mp->m_type = DL_STAT_REPLY; + mp->DL_PORT = port; + mp->DL_STAT = OK; + + r = send(mp->m_source, mp); + if(r != OK) + panic(__FILE__, "orinoco: getstat failed:", r); + + /*reply (orp, OK, FALSE);*/ + +} + +/***************************************************************************** + * or_getstat_s * + * * + * Return the statistics structure. The statistics aren't updated until now, * + * so this won't return much interesting yet. * + *****************************************************************************/ +static void or_getstat_s (message * mp) { + int r, port; + eth_stat_t stats; + t_or *orp; + + port = mp->DL_PORT; + if (port < 0 || port >= OR_PORT_NR) + panic(__FILE__, "orinoco: illegal port:", port); + assert(port==0); + orp = &or_table[port]; + orp->or_client = mp->DL_PROC; + + assert (orp->or_mode == OR_M_ENABLED); + assert (orp->or_flags & OR_F_ENABLED); + + stats = orp->or_stat; + + r = sys_safecopyto(mp->DL_PROC, mp->DL_GRANT, 0, + (vir_bytes) &stats, sizeof(stats), D); + if(r != OK) { + panic(__FILE__, "or_getstat_s: sys_safecopyto failed:", r); + } + + mp->m_type = DL_STAT_REPLY; + mp->DL_PORT = port; + mp->DL_STAT = OK; + + r = send(mp->m_source, mp); + if(r != OK) + panic(__FILE__, "orinoco: getstat_s failed:", r); +} + diff --git a/drivers/orinoco/orinoco.h b/drivers/orinoco/orinoco.h new file mode 100755 index 000000000..45daee7e2 --- /dev/null +++ b/drivers/orinoco/orinoco.h @@ -0,0 +1,104 @@ +/* + * orinoco.h + * + * This file contains the most important structure for the driver: t_or + * and some configurable definitions + * + * Created by Stevens Le Blond + * and Michael Valkering + */ + +#include +#include + +#define NR_RX_BUFS 32 + +#define LARGE_KEY_LENGTH 13 +#define IW_ESSID_MAX_SIZE 32 +#define OR_PORT_NR 1 +#define IOVEC_NR 16 +#define OR_ENVVAR "ORETH" +#define OR_NAME "orinoco#n" + +#define IEEE802_11_HLEN 30 +#define IEEE802_11_DATA_LEN (2304) +#define IEEE802_11_FRAME_LEN (IEEE802_11_DATA_LEN + IEEE802_11_HLEN + 3) + +typedef struct s_or +{ + int or_irq; + int or_hook_id; + int or_mode; + int or_flags; + char *or_model; + int or_client; + int or_link_up; + int or_got_int; + int or_tx_alive; + int or_send_int; + int or_clear_rx; + u32_t or_base_port; + int or_need_reset; + int or_report_link; + + /* Events */ + int or_ev_rx; + int or_ev_tx; + int or_ev_info; + int or_ev_txexc; + int or_ev_alloc; + int connected; + u16_t channel_mask; + u16_t channel; + u16_t ap_density; + u16_t rts_thresh; + int bitratemode; + int last_linkstatus; + int max_data_len; + int port_type; + + /* Rx */ + phys_bytes or_rx_buf; + vir_bytes or_read_s; + u16_t rxfid[NR_RX_BUFS]; + int rx_length[NR_RX_BUFS]; + u8_t rx_buf[NR_RX_BUFS][IEEE802_11_FRAME_LEN]; + u8_t rx_offset[NR_RX_BUFS]; + int rx_first; + int rx_last; + int rx_current; + + /* Tx */ + u16_t or_nicbuf_size; + vir_bytes or_transm_s; + int or_tx_head; + int or_tx_tail; + + struct + { + int ret_busy; + u16_t or_txfid; + } or_tx; + u32_t or_ertxth; + + /* PCI related */ + int or_seen; + u8_t or_pci_bus; + u8_t or_pci_dev; + u8_t or_pci_func; + int devind; + + /* 'large' items */ + irq_hook_t or_hook; + eth_stat_t or_stat; + message or_rx_mess; + message or_tx_mess; + ether_addr_t or_address; + iovec_t or_iovec[IOVEC_NR]; + iovec_s_t or_iovec_s[IOVEC_NR]; + char or_name[sizeof (OR_NAME)]; + hermes_t hw; + char nick[IW_ESSID_MAX_SIZE + 1]; + + +} t_or; diff --git a/etc/drivers.conf b/etc/drivers.conf index 4e3c51d0e..21511aeac 100644 --- a/etc/drivers.conf +++ b/etc/drivers.conf @@ -207,3 +207,27 @@ driver printer SAFECOPYTO # 32 ; }; + +driver orinoco +{ + system + PRIVCTL # 4 + DEVIO # 21 + GETINFO # 26 + UMAP # 14 + IRQCTL # 19 + DEVIO # 21 + #SDEVIO # 22 + SETALARM # 24 + TIMES # 25 + GETINFO # 26 + SAFECOPYFROM # 31 + SAFECOPYTO # 32 + SETGRANT # 34 + VM_MAP # 30 + ; + pci device 1260/3873; + uid 0; +}; + + diff --git a/etc/usr/rc b/etc/usr/rc index f4aa98d19..e41e3d7e3 100644 --- a/etc/usr/rc +++ b/etc/usr/rc @@ -91,7 +91,7 @@ start) fi # start only network drivers that are in use - for driver in lance rtl8139 fxp dpeth dp8390 + for driver in lance rtl8139 fxp dpeth dp8390 orinoco do if grep " $driver " /etc/inet.conf > /dev/null 2>&1 then @@ -135,7 +135,8 @@ start) echo -n "Starting networking:" if grep -s 'psip0.*default' /etc/inet.conf then ifconfig -h 10.0.0.1 - else daemonize dhcpd + else sleep 5 + daemonize dhcpd fi daemonize nonamed -L if [ -f "$DAEMONS" ]