/**************************************************************************
Prism2 NIC driver for Etherboot
Written by Michael Brown of Fen Systems Ltd
$Id: prism2.c 80479 2012-08-30 16:24:44Z klaus $
***************************************************************************/
/*
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2, or (at
* your option) any later version.
*/
/*
* Oracle GPL Disclaimer: For the avoidance of doubt, except that if any license choice
* other than GPL or LGPL is available it will apply instead, Oracle elects to use only
* the General Public License version 2 (GPLv2) at this time for any software where
* a choice of GPL license versions is made available with the language indicating
* that GPLv2 or any later version may be used, or where a choice of which version
* of the GPL is applied is otherwise unspecified.
*/
#include <etherboot.h>
#include <nic.h>
#include <ipxe/ethernet.h>
/*
* Hard-coded SSID
* Leave blank in order to connect to any available SSID
*/
/*
* Maximum number of info packets to wait for on a join attempt.
* Some APs (including the Linksys WAP11) will send a "you are disconnected" packet
* before sending the "you are connected" packet, if the card has previously been
* attached to the AP.
*
* 2 is probably a sensible value, but YMMV.
*/
/*
* Type of Prism2 interface to support
* If not already defined, select PLX
*/
#ifndef WLAN_HOSTIF
#endif
/*
* Include wlan_compat, p80211 and hfa384x header files from Linux Prism2 driver
* We need to hack some defines in order to avoid compiling kernel-specific routines
*/
#define __LINUX_WLAN__
#define __I386__
#include "wlan_compat.h"
#include "p80211hdr.h"
#include "hfa384x.h"
/*
* A few hacks to make the coding environment more Linux-like. This makes it somewhat
* quicker to convert code from the Linux Prism2 driver.
*/
#include <errno.h>
#define __le16_to_cpu(x) (x)
#define __le32_to_cpu(x) (x)
#define __cpu_to_le16(x) (x)
#define __cpu_to_le32(x) (x)
/*
* PLX9052 PCI register offsets
* Taken from PLX9052 datasheet available from http://www.plxtech.com/download/9052/databook/9052db-20.pdf
*/
/*
* PCMCIA CIS types
* Taken from cistpl.h in pcmcia-cs
*/
/*
* Prism2 constants
* Taken from prism2sta.c in linux-wlan-ng
*/
/* NIC specific static variables */
/* The hfa384x_t structure is used extensively in the Linux driver but is ifdef'd out in our include since __KERNEL__ is not defined.
* This is a dummy version that contains only the fields we are interested in.
*/
typedef struct hfa384x
{
void *membase;
} hfa384x_t;
/* The global instance of the hardware (i.e. where we store iobase and membase, in the absence of anywhere better to put them */
0, 0, 0, 0, 0, 0, 0, {0,0,0,0,0,0}
};
/*
* 802.11 headers in addition to those in hfa384x_tx_frame_t (LLC and SNAP)
* Taken from p80211conv.h
*/
typedef struct wlan_llc
{
} wlan_llc_t;
typedef struct wlan_snap
{
} wlan_snap_t;
typedef struct wlan_80211hdr
{
/*
* Function prototypes
*/
/*
* Hardware-level hfa384x functions
* These are based on the ones in hfa384x.h (which are ifdef'd out since __KERNEL__ is not defined).
* Basically, these functions are the result of hand-evaluating all the ifdefs and defines in the hfa384x.h versions.
*/
/* Retrieve the value of one of the MAC registers. */
{
#if (WLAN_HOSTIF == WLAN_PLX)
#endif
}
/* Set the value of one of the MAC registers. */
{
#if (WLAN_HOSTIF == WLAN_PLX)
#endif
return;
}
/*
* Noswap versions
* Etherboot is i386 only, so swap and noswap are the same...
*/
{
}
{
}
/*
* Low-level hfa384x functions
* These are based on the ones in hfa384x.c, modified to work in the Etherboot environment.
*/
/*
* hfa384x_docmd_wait
*
* Waits for availability of the Command register, then
* issues the given command. Then polls the Evstat register
* waiting for command completion.
* Arguments:
* hw device structure
* cmd Command in host order
* parm0 Parameter0 in host order
* parm1 Parameter1 in host order
* parm2 Parameter2 in host order
* Returns:
* 0 success
* >0 command indicated error, Status and Resp0-2 are
* in hw structure.
*/
{
/* wait for the busy bit to clear */
counter = 0;
counter++;
udelay(10);
}
if (HFA384x_CMD_ISBUSY(reg)) {
return -ETIMEDOUT;
}
/* busy bit clear, write command */
/* Now wait for completion */
counter = 0;
/* Initialization is the problem. It takes about
100ms. "normal" commands are typically is about
200-400 us (I've never seen less than 200). Longer
is better so that we're not hammering the bus. */
counter++;
udelay(200);
}
if ( ! HFA384x_EVSTAT_ISCMD(reg) ) {
return -ETIMEDOUT;
}
/* Read status and response */
}
/*
* Prepare BAP for access. Assigns FID and RID, sets offset register
* and waits for BAP to become available.
*
* Arguments:
* hw device structure
* id FID or RID, destined for the select register (host order)
* Returns:
* 0 success
*/
{
int result = 0;
UINT16 i;
/* Validate offset, buf, and len */
} else {
udelay(10);
/* Wait for offset[busy] to clear (see BAP_TIMEOUT) */
i = 0;
do {
if ( i > 0 ) udelay(2);
i++;
if ( i >= BAP_TIMEOUT ) {
/* failure */
} else if ( HFA384x_OFFSET_ISERR(reg) ){
/* failure */
}
}
return result;
}
/*
* Copy data from BAP to memory.
*
* Arguments:
* hw device structure
* id FID or RID, destined for the select register (host order)
* buf ptr to array of bytes
* len length of data to transfer in bytes
* Returns:
* 0 success
*/
{
int result = 0;
UINT16 i;
/* Prepare BAP */
if ( result == 0 ) {
/* Read even(len) buf contents from data reg */
}
/* If len odd, handle last byte */
if ( len % 2 ){
}
}
if (result) {
}
return result;
}
/*
* Copy data from memory to BAP.
*
* Arguments:
* hw device structure
* id FID or RID, destined for the select register (host order)
* buf ptr to array of bytes
* len length of data to transfer in bytes
* Returns:
* 0 success
*/
{
int result = 0;
UINT16 i;
/* Prepare BAP */
if ( result == 0 ) {
/* Write even(len) buf contents to data reg */
}
/* If len odd, handle last byte */
if ( len % 2 ){
if ( result == 0 ) {
}
}
}
if (result) {
}
return result;
}
/*
*
* Arguments:
* hw device structure
* write [0|1] copy the record buffer to the given
* configuration record. (host order)
*
* Returns:
* 0 success
*/
{
return hfa384x_docmd_wait(hw, HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_ACCESS) | HFA384x_CMD_WRITE_SET(write), rid, 0, 0);
}
/*
*
* Arguments:
* hw device structure
* buf host side record buffer. Upon return it will
* contain the body portion of the record (minus the
* RID and len).
* len buffer length (in bytes, should match record length)
*
* Returns:
* 0 success
*/
{
int result = 0;
/* Request read of RID */
if ( result ) {
printf("Call to hfa384x_cmd_access failed\n");
return -1;
}
/* Copy out record length */
if ( result ) {
return -1;
}
/* Validate the record length */
printf ( "RID len mismatch, rid=%#hx hlen=%d fwlen=%d\n", rid, len, (hfa384x2host_16(rec.reclen)-1)*2);
return -1;
}
/* Copy out record data */
return result;
}
/*
* and convert it to host order.
*
* Arguments:
* hw device structure
* val ptr to 16/32 bit buffer to receive value (in host order)
*
* Returns:
* 0 success
*/
#if 0 /* Not actually used anywhere */
{
int result = 0;
if ( result == 0 ) {
}
return result;
}
#endif
#if 0 /* Not actually used anywhere */
{
int result = 0;
if ( result == 0 ) {
}
return result;
}
#endif
/*
*
* Arguments:
* hw device structure
* buf host side record buffer
* len buffer length (in bytes)
*
* Returns:
* 0 success
*/
{
int result = 0;
/* write the record header */
if ( result ) {
printf("Failure writing record header\n");
return -1;
}
/* write the record data (if there is any) */
if ( len > 0 ) {
if ( result ) {
printf("Failure writing record data\n");
return -1;
}
}
/* Trigger setting of record */
return result;
}
/*
*
* Arguments:
* hw device structure
* val 16/32 bit value to store (in host order)
*
* Returns:
* 0 success
*/
{
}
#if 0 /* Not actually used anywhere */
{
}
#endif
/*
* Wait for an event, with specified checking interval and timeout.
* Automatically acknolwedges events.
*
* Arguments:
* hw device structure
* event_mask EVSTAT register mask of events to wait for
* event_ack EVACK register set of events to be acknowledged if they happen (can be
* used to acknowledge "ignorable" events in addition to the "main" event)
* wait Time (in us) to wait between each poll of the register
* timeout Maximum number of polls before timing out
* descr Descriptive text string of what is being waited for
* (will be printed out if a timeout happens)
*
* Returns:
* value of EVSTAT register, or 0 on failure
*/
static int hfa384x_wait_for_event(hfa384x_t *hw, UINT16 event_mask, UINT16 event_ack, int wait, int timeout, const char *descr)
{
int count = 0;
do {
count++;
return 0; /* Return failure */
}
/* Acknowledge all events that we were waiting on */
return reg;
}
/**************************************************************************
POLL - Wait for a frame
***************************************************************************/
{
/* Check for received packet */
if ( ! HFA384x_EVSTAT_ISRX(reg) ) {
/* No packet received - return 0 */
return 0;
}
if ( ! retrieve ) return 1;
/* Acknowledge RX event */
/* Get RX FID */
/* Get the descriptor (including headers) */
if ( result ) {
return 0; /* fail */
}
/* Byte order convert once up front. */
/* Fill in nic->packetlen */
/* Fill in nic->packet */
/*
* NOTE: Packets as received have an 8-byte header (LLC+SNAP(?)) terminating with the packet type.
* Etherboot expects a 14-byte header terminating with the packet type (it ignores the rest of the
* header), so we use a quick hack to achieve this.
*/
if ( result ) {
return 0; /* fail */
}
}
return 1; /* Packet successfully received */
}
/**************************************************************************
TRANSMIT - Transmit a frame
***************************************************************************/
static void prism2_transmit(
const char *d, /* Destination */
unsigned int t, /* Type */
unsigned int s, /* size */
const char *p) /* Packet */
{
int result;
// Request FID allocation
result = hfa384x_docmd_wait(hw, HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_ALLOC), HFA384x_DRVR_TXBUF_MAX, 0, 0);
if (result != 0) {
printf("hfa384x: Tx FID allocate command failed: Aborting transmit..\n");
return;
}
if ( !hfa384x_wait_for_event(hw, HFA384x_EVSTAT_ALLOC, HFA384x_EVACK_INFO, 10, 50, "Tx FID to be allocated\n" ) ) return;
/* Build Tx frame structure */
WLAN_SET_FC_TODS(1) );
/* Set up SNAP header */
/* Let OUI default to RFC1042 (0x000000) */
/* Copy txdesc, p80211hdr and payload parts to FID */
if ( result ) return; /* fail */
if ( result ) return; /* fail */
if ( result ) return; /* fail */
/* Issue Tx command */
if ( result != 0 ) {
return;
}
/* Wait for transmit completion (or exception) */
200, 500, "Tx to complete\n" );
if ( !result ) return; /* timeout failure */
if ( HFA384x_EVSTAT_ISTXEXC(result) ) {
if ( result ) return; /* fail */
return; /* fail */
}
}
/**************************************************************************
DISABLE - Turn off ethernet interface
***************************************************************************/
/* put the card in its initial state */
}
/**************************************************************************
IRQ - Enable, Disable, or Force interrupts
***************************************************************************/
{
switch ( action ) {
case DISABLE :
break;
case ENABLE :
break;
case FORCE :
break;
}
}
/**************************************************************************
Operations table
***************************************************************************/
.connect = dummy_connect,
.poll = prism2_poll,
.irq = prism2_irq,
};
/**************************************************************************
PROBE - Look for an adapter, this routine's visible to the outside
You should omit the last argument struct pci_device * for a non-PCI NIC
***************************************************************************/
int result;
int info_count = 0;
/* Initialize card */
/* Retrieve MAC address (and fill out nic->node_addr) */
hfa384x_drvr_getconfig ( hw, HFA384x_RID_CNFOWNMACADDR, nic->node_addr, HFA384x_RID_CNFOWNMACADDR_LEN );
/* Prepare card for autojoin */
/* This procedure is reverse-engineered from a register-level trace of the Linux driver's join process */
/* Set SSID */
result = hfa384x_drvr_setconfig(hw, HFA384x_RID_CNFDESIREDSSID, ssid, HFA384x_RID_CNFDESIREDSSID_LEN); /* Set the SSID */
/* Enable card */
result = hfa384x_docmd_wait(hw, HFA384x_CMD_CMDCODE_SET(HFA384x_CMDCODE_ENABLE) | HFA384x_CMD_MACPORT_SET(0), 0,0,0);
do {
/* Increment info_count, abort if too many attempts.
* See comment next to definition of MAX_JOIN_INFO_COUNT for explanation.
*/
info_count++;
if ( info_count > MAX_JOIN_INFO_COUNT ) {
printf ( "Too many failed attempts - aborting\n" );
return 0;
}
/* Wait for info frame to indicate link status */
if ( sizeof(hardcoded_ssid) == 1 ) {
/* Empty SSID => join to any SSID */
} else {
}
printf("done\n");
/* Retrieve the length */
if ( result ) return 0; /* fail */
/* Retrieve the rest */
if ( result ) return 0; /* fail */
/* Not a Link Status info frame: die */
return 0;
}
/* Link not connected - retry */
}
/* Retrieve BSSID and print Connected message */
/* point to NIC specific routines */
return 1;
}