1
0
mirror of git://projects.qi-hardware.com/openwrt-xburst.git synced 2025-04-21 12:27:27 +03:00

add all source code from linksys/broadcom which is free, to cvs for better maintainence inside

openwrt. this gives us the ability to better support different hardware models, without changing
any external tar-balls. only et.o and wl.o is missing and is fetched from my webserver.


git-svn-id: svn://svn.openwrt.org/openwrt/trunk/openwrt@379 3c298f89-4303-0410-b956-a3cf2f4a3e73
This commit is contained in:
wbx
2005-03-16 13:50:00 +00:00
parent c7df5a6a2c
commit 4f531230a3
85 changed files with 18591 additions and 16 deletions

View File

@@ -0,0 +1,35 @@
#
# Broadcom Home Networking Division (HND) driver configuration
#
# Copyright 2004, Broadcom Corporation
# All Rights Reserved.
#
# THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
# KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
# SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
#
mainmenu_option next_comment
comment 'Broadcom HND network devices'
tristate 'Broadcom HND network device support' CONFIG_HND $CONFIG_PCI
if [ "$CONFIG_HND" != "n" ]; then
dep_tristate ' Broadcom InsideLine HPNA support' CONFIG_IL $CONFIG_HND
if [ "$CONFIG_IL" != "n" ]; then
bool ' Broadcom BCM42xx support' CONFIG_IL_42XX
bool ' Broadcom BCM47xx support' CONFIG_IL_47XX
int ' LARQ buffer allocation (0 = tiny, 2 = huge)' CONFIG_LARQ_BUF 0
fi
dep_tristate ' Broadcom 10/100 Ethernet support' CONFIG_ET $CONFIG_HND
if [ "$CONFIG_ET" != "n" ]; then
bool ' Broadcom BCM4413 support' CONFIG_ET_4413
bool ' Broadcom BCM47xx support' CONFIG_ET_47XX
fi
dep_tristate ' Broadcom BCM43xx 802.11 Wireless support' CONFIG_WL $CONFIG_HND
if [ "$CONFIG_WL" != "n" ]; then
bool ' Access Point Mode Supported' CONFIG_WL_AP
bool ' STA Mode Supported' CONFIG_WL_STA
bool ' OID Interface Supported' CONFIG_WL_OID
fi
fi
endmenu

View File

@@ -0,0 +1,30 @@
#
# Makefile for Broadcom Home Networking Division (HND) shared driver code
#
# Copyright 2004, Broadcom Corporation
# All Rights Reserved.
#
# THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
# KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
# SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
#
# $Id$
#
O_TARGET := hnd.o
HND_OBJS := bcmutils.o hnddma.o linux_osl.o sbutils.o bcmsrom.o
ifneq ($(CONFIG_BCM947XX),y)
HND_OBJS += nvramstubs.o
endif
export-objs := shared_ksyms.o
obj-y := shared_ksyms.o $(HND_OBJS)
obj-m := $(O_TARGET)
include $(TOPDIR)/Rules.make
shared_ksyms.c: shared_ksyms.sh $(HND_OBJS)
sh -e $< $(HND_OBJS) > $@

View File

@@ -0,0 +1,711 @@
/*
* Misc useful routines to access NIC SROM
*
* Copyright 2004, Broadcom Corporation
* All Rights Reserved.
*
* THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
* KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
* SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
* $Id$
*/
#include <typedefs.h>
#include <osl.h>
#include <bcmutils.h>
#include <bcmsrom.h>
#include <bcmdevs.h>
#include <bcmendian.h>
#include <sbpcmcia.h>
#include <pcicfg.h>
#include <sbutils.h>
#include <proto/ethernet.h> /* for sprom content groking */
#define VARS_MAX 4096 /* should be reduced */
static int initvars_srom_pci(void *curmap, char **vars, int *count);
static int initvars_cis_pcmcia(void *sbh, void *curmap, void *osh, char **vars, int *count);
static int sprom_cmd_pcmcia(void *osh, uint8 cmd);
static int sprom_read_pcmcia(void *osh, uint16 addr, uint16 *data);
static int sprom_write_pcmcia(void *osh, uint16 addr, uint16 data);
static int sprom_read_pci(uint16 *sprom, uint byteoff, uint16 *buf, uint nbytes, bool check_crc);
/*
* Initialize the vars from the right source for this platform.
* Return 0 on success, nonzero on error.
*/
int
srom_var_init(void *sbh, uint bus, void *curmap, void *osh, char **vars, int *count)
{
if (vars == NULL)
return (0);
switch (bus) {
case SB_BUS:
/* These two could be asserts ... */
*vars = NULL;
*count = 0;
return(0);
case PCI_BUS:
ASSERT(curmap); /* can not be NULL */
return(initvars_srom_pci(curmap, vars, count));
case PCMCIA_BUS:
return(initvars_cis_pcmcia(sbh, curmap, osh, vars, count));
default:
ASSERT(0);
}
return (-1);
}
/* support only 16-bit word read from srom */
int
srom_read(uint bus, void *curmap, void *osh, uint byteoff, uint nbytes, uint16 *buf)
{
void *srom;
uint i, off, nw;
/* check input - 16-bit access only */
if (byteoff & 1 || nbytes & 1 || (byteoff + nbytes) > (SPROM_SIZE * 2))
return 1;
if (bus == PCI_BUS) {
if (!curmap)
return 1;
srom = (void *)((uint)curmap + PCI_BAR0_SPROM_OFFSET);
if (sprom_read_pci(srom, byteoff, buf, nbytes, FALSE))
return 1;
} else if (bus == PCMCIA_BUS) {
off = byteoff / 2;
nw = nbytes / 2;
for (i = 0; i < nw; i++) {
if (sprom_read_pcmcia(osh, (uint16)(off + i), (uint16*)(buf + i)))
return 1;
}
} else {
return 1;
}
return 0;
}
/* support only 16-bit word write into srom */
int
srom_write(uint bus, void *curmap, void *osh, uint byteoff, uint nbytes, uint16 *buf)
{
uint16 *srom;
uint i, off, nw, crc_range;
uint16 image[SPROM_SIZE], *p;
uint8 crc;
volatile uint32 val32;
/* check input - 16-bit access only */
if (byteoff & 1 || nbytes & 1 || (byteoff + nbytes) > (SPROM_SIZE * 2))
return 1;
crc_range = ((bus == PCMCIA_BUS) ? SPROM_SIZE : SPROM_CRC_RANGE) * 2;
/* if changes made inside crc cover range */
if (byteoff < crc_range) {
nw = (((byteoff + nbytes) > crc_range) ? byteoff + nbytes : crc_range) / 2;
/* read data including entire first 64 words from srom */
if (srom_read(bus, curmap, osh, 0, nw * 2, image))
return 1;
/* make changes */
bcopy((void*)buf, (void*)&image[byteoff / 2], nbytes);
/* calculate crc */
htol16_buf(image, crc_range);
crc = ~crc8((uint8 *)image, crc_range - 1, CRC8_INIT_VALUE);
ltoh16_buf(image, crc_range);
image[(crc_range / 2) - 1] = (crc << 8) | (image[(crc_range / 2) - 1] & 0xff);
p = image;
off = 0;
} else {
p = buf;
off = byteoff / 2;
nw = nbytes / 2;
}
if (bus == PCI_BUS) {
srom = (uint16*)((uint)curmap + PCI_BAR0_SPROM_OFFSET);
/* enable writes to the SPROM */
val32 = OSL_PCI_READ_CONFIG(osh, PCI_SPROM_CONTROL, sizeof(uint32));
val32 |= SPROM_WRITEEN;
OSL_PCI_WRITE_CONFIG(osh, PCI_SPROM_CONTROL, sizeof(uint32), val32);
bcm_mdelay(500);
/* write srom */
for (i = 0; i < nw; i++) {
W_REG(&srom[off + i], p[i]);
bcm_mdelay(20);
}
/* disable writes to the SPROM */
OSL_PCI_WRITE_CONFIG(osh, PCI_SPROM_CONTROL, sizeof(uint32), val32 & ~SPROM_WRITEEN);
} else if (bus == PCMCIA_BUS) {
/* enable writes to the SPROM */
if (sprom_cmd_pcmcia(osh, SROM_WEN))
return 1;
bcm_mdelay(500);
/* write srom */
for (i = 0; i < nw; i++) {
sprom_write_pcmcia(osh, (uint16)(off + i), p[i]);
bcm_mdelay(20);
}
/* disable writes to the SPROM */
if (sprom_cmd_pcmcia(osh, SROM_WDS))
return 1;
} else {
return 1;
}
bcm_mdelay(500);
return 0;
}
int
srom_parsecis(uint8 *cis, char **vars, int *count)
{
char eabuf[32];
char *vp, *base;
uint8 tup, tlen, sromrev = 1;
int i, j;
uint varsize;
bool ag_init = FALSE;
uint16 w;
ASSERT(vars);
ASSERT(count);
base = vp = MALLOC(VARS_MAX);
ASSERT(vp);
i = 0;
do {
tup = cis[i++];
tlen = cis[i++];
if ((i + tlen) >= CIS_SIZE)
break;
switch (tup) {
case CISTPL_MANFID:
vp += sprintf(vp, "manfid=%d", (cis[i + 1] << 8) + cis[i]);
vp++;
vp += sprintf(vp, "prodid=%d", (cis[i + 3] << 8) + cis[i + 2]);
vp++;
break;
case CISTPL_FUNCE:
if (cis[i] == LAN_NID) {
ASSERT(cis[i + 1] == ETHER_ADDR_LEN);
bcm_ether_ntoa((uchar*)&cis[i + 2], eabuf);
vp += sprintf(vp, "il0macaddr=%s", eabuf);
vp++;
}
break;
case CISTPL_CFTABLE:
vp += sprintf(vp, "regwindowsz=%d", (cis[i + 7] << 8) | cis[i + 6]);
vp++;
break;
case CISTPL_BRCM_HNBU:
switch (cis[i]) {
case HNBU_CHIPID:
vp += sprintf(vp, "vendid=%d", (cis[i + 2] << 8) + cis[i + 1]);
vp++;
vp += sprintf(vp, "devid=%d", (cis[i + 4] << 8) + cis[i + 3]);
vp++;
if (tlen == 7) {
vp += sprintf(vp, "chiprev=%d", (cis[i + 6] << 8) + cis[i + 5]);
vp++;
}
break;
case HNBU_BOARDREV:
vp += sprintf(vp, "boardrev=%d", cis[i + 1]);
vp++;
break;
case HNBU_AA:
vp += sprintf(vp, "aa0=%d", cis[i + 1]);
vp++;
break;
case HNBU_AG:
vp += sprintf(vp, "ag0=%d", cis[i + 1]);
vp++;
ag_init = TRUE;
break;
case HNBU_CC:
vp += sprintf(vp, "cc=%d", cis[i + 1]);
vp++;
break;
case HNBU_PAPARMS:
vp += sprintf(vp, "pa0maxpwr=%d", cis[i + tlen - 1]);
vp++;
if (tlen == 9) {
/* New version */
for (j = 0; j < 3; j++) {
vp += sprintf(vp, "pa0b%d=%d", j,
(cis[i + (j * 2) + 2] << 8) + cis[i + (j * 2) + 1]);
vp++;
}
vp += sprintf(vp, "pa0itssit=%d", cis[i + 7]);
vp++;
}
break;
case HNBU_OEM:
vp += sprintf(vp, "oem=%02x%02x%02x%02x%02x%02x%02x%02x",
cis[i + 1], cis[i + 2], cis[i + 3], cis[i + 4],
cis[i + 5], cis[i + 6], cis[i + 7], cis[i + 8]);
vp++;
break;
case HNBU_BOARDFLAGS:
w = (cis[i + 2] << 8) + cis[i + 1];
if (w == 0xffff) w = 0;
vp += sprintf(vp, "boardflags=%d", w);
vp++;
break;
case HNBU_LED:
if (cis[i + 1] != 0xff) {
vp += sprintf(vp, "wl0gpio0=%d", cis[i + 1]);
vp++;
}
if (cis[i + 2] != 0xff) {
vp += sprintf(vp, "wl0gpio1=%d", cis[i + 2]);
vp++;
}
if (cis[i + 3] != 0xff) {
vp += sprintf(vp, "wl0gpio2=%d", cis[i + 3]);
vp++;
}
if (cis[i + 4] != 0xff) {
vp += sprintf(vp, "wl0gpio3=%d", cis[i + 4]);
vp++;
}
break;
}
break;
}
i += tlen;
} while (tup != 0xff);
/* Set the srom version */
vp += sprintf(vp, "sromrev=%d", sromrev);
vp++;
/* For now just set boardflags2 to zero */
vp += sprintf(vp, "boardflags2=0");
vp++;
/* if there is no antenna gain field, set default */
if (ag_init == FALSE) {
vp += sprintf(vp, "ag0=%d", 0xff);
vp++;
}
/* final nullbyte terminator */
*vp++ = '\0';
varsize = (uint)vp - (uint)base;
ASSERT(varsize < VARS_MAX);
if (varsize == VARS_MAX) {
*vars = base;
} else {
vp = MALLOC(varsize);
ASSERT(vp);
bcopy(base, vp, varsize);
MFREE(base, VARS_MAX);
*vars = vp;
}
*count = varsize;
return (0);
}
/* set PCMCIA sprom command register */
static int
sprom_cmd_pcmcia(void *osh, uint8 cmd)
{
uint8 status;
uint wait_cnt = 1000;
/* write sprom command register */
OSL_PCMCIA_WRITE_ATTR(osh, SROM_CS, &cmd, 1);
/* wait status */
while (wait_cnt--) {
OSL_PCMCIA_READ_ATTR(osh, SROM_CS, &status, 1);
if (status & SROM_DONE)
return 0;
}
return 1;
}
/* read a word from the PCMCIA srom */
static int
sprom_read_pcmcia(void *osh, uint16 addr, uint16 *data)
{
uint8 addr_l, addr_h, data_l, data_h;
addr_l = (uint8)((addr * 2) & 0xff);
addr_h = (uint8)(((addr * 2) >> 8) & 0xff);
/* set address */
OSL_PCMCIA_WRITE_ATTR(osh, SROM_ADDRH, &addr_h, 1);
OSL_PCMCIA_WRITE_ATTR(osh, SROM_ADDRL, &addr_l, 1);
/* do read */
if (sprom_cmd_pcmcia(osh, SROM_READ))
return 1;
/* read data */
OSL_PCMCIA_READ_ATTR(osh, SROM_DATAH, &data_h, 1);
OSL_PCMCIA_READ_ATTR(osh, SROM_DATAL, &data_l, 1);
*data = (data_h << 8) | data_l;
return 0;
}
/* write a word to the PCMCIA srom */
static int
sprom_write_pcmcia(void *osh, uint16 addr, uint16 data)
{
uint8 addr_l, addr_h, data_l, data_h;
addr_l = (uint8)((addr * 2) & 0xff);
addr_h = (uint8)(((addr * 2) >> 8) & 0xff);
data_l = (uint8)(data & 0xff);
data_h = (uint8)((data >> 8) & 0xff);
/* set address */
OSL_PCMCIA_WRITE_ATTR(osh, SROM_ADDRH, &addr_h, 1);
OSL_PCMCIA_WRITE_ATTR(osh, SROM_ADDRL, &addr_l, 1);
/* write data */
OSL_PCMCIA_WRITE_ATTR(osh, SROM_DATAH, &data_h, 1);
OSL_PCMCIA_WRITE_ATTR(osh, SROM_DATAL, &data_l, 1);
/* do write */
return sprom_cmd_pcmcia(osh, SROM_WRITE);
}
/*
* Read in and validate sprom.
* Return 0 on success, nonzero on error.
*/
static int
sprom_read_pci(uint16 *sprom, uint byteoff, uint16 *buf, uint nbytes, bool check_crc)
{
int off, nw;
uint8 chk8;
int i;
off = byteoff / 2;
nw = ROUNDUP(nbytes, 2) / 2;
/* read the sprom */
for (i = 0; i < nw; i++)
buf[i] = R_REG(&sprom[off + i]);
if (check_crc) {
/* fixup the endianness so crc8 will pass */
htol16_buf(buf, nw * 2);
if ((chk8 = crc8((uchar*)buf, nbytes, CRC8_INIT_VALUE)) != CRC8_GOOD_VALUE)
return (1);
/* now correct the endianness of the byte array */
ltoh16_buf(buf, nw * 2);
}
return (0);
}
/*
* Initialize nonvolatile variable table from sprom.
* Return 0 on success, nonzero on error.
*/
static int
initvars_srom_pci(void *curmap, char **vars, int *count)
{
uint16 w, b[64];
uint8 sromrev;
struct ether_addr ea;
char eabuf[32];
uint32 bfl;
int c, woff, i;
char *vp, *base;
if (sprom_read_pci((void *)((uint)curmap + PCI_BAR0_SPROM_OFFSET), 0, b, sizeof (b), TRUE))
return (-1);
/* top word of sprom contains version and crc8 */
sromrev = b[63] & 0xff;
if ((sromrev != 1) && (sromrev != 2)) {
return (-2);
}
ASSERT(vars);
ASSERT(count);
base = vp = MALLOC(VARS_MAX);
ASSERT(vp);
vp += sprintf(vp, "sromrev=%d", sromrev);
vp++;
if (sromrev >= 2) {
/* New section takes over the 4th hardware function space */
/* Word 29 is max power 11a high/low */
w = b[29];
vp += sprintf(vp, "pa1himaxpwr=%d", w & 0xff);
vp++;
vp += sprintf(vp, "pa1lomaxpwr=%d", (w >> 8) & 0xff);
vp++;
/* Words 30-32 set the 11alow pa settings,
* 33-35 are the 11ahigh ones.
*/
for (i = 0; i < 3; i++) {
vp += sprintf(vp, "pa1lob%d=%d", i, b[30 + i]);
vp++;
vp += sprintf(vp, "pa1hib%d=%d", i, b[33 + i]);
vp++;
}
w = b[59];
if (w == 0)
vp += sprintf(vp, "ccode=");
else
vp += sprintf(vp, "ccode=%c%c", (w >> 8), (w & 0xff));
vp++;
}
/* parameter section of sprom starts at byte offset 72 */
woff = 72/2;
/* first 6 bytes are il0macaddr */
ea.octet[0] = (b[woff] >> 8) & 0xff;
ea.octet[1] = b[woff] & 0xff;
ea.octet[2] = (b[woff+1] >> 8) & 0xff;
ea.octet[3] = b[woff+1] & 0xff;
ea.octet[4] = (b[woff+2] >> 8) & 0xff;
ea.octet[5] = b[woff+2] & 0xff;
woff += ETHER_ADDR_LEN/2 ;
bcm_ether_ntoa((uchar*)&ea, eabuf);
vp += sprintf(vp, "il0macaddr=%s", eabuf);
vp++;
/* next 6 bytes are et0macaddr */
ea.octet[0] = (b[woff] >> 8) & 0xff;
ea.octet[1] = b[woff] & 0xff;
ea.octet[2] = (b[woff+1] >> 8) & 0xff;
ea.octet[3] = b[woff+1] & 0xff;
ea.octet[4] = (b[woff+2] >> 8) & 0xff;
ea.octet[5] = b[woff+2] & 0xff;
woff += ETHER_ADDR_LEN/2 ;
bcm_ether_ntoa((uchar*)&ea, eabuf);
vp += sprintf(vp, "et0macaddr=%s", eabuf);
vp++;
/* next 6 bytes are et1macaddr */
ea.octet[0] = (b[woff] >> 8) & 0xff;
ea.octet[1] = b[woff] & 0xff;
ea.octet[2] = (b[woff+1] >> 8) & 0xff;
ea.octet[3] = b[woff+1] & 0xff;
ea.octet[4] = (b[woff+2] >> 8) & 0xff;
ea.octet[5] = b[woff+2] & 0xff;
woff += ETHER_ADDR_LEN/2 ;
bcm_ether_ntoa((uchar*)&ea, eabuf);
vp += sprintf(vp, "et1macaddr=%s", eabuf);
vp++;
/*
* Enet phy settings one or two singles or a dual
* Bits 4-0 : MII address for enet0 (0x1f for not there)
* Bits 9-5 : MII address for enet1 (0x1f for not there)
* Bit 14 : Mdio for enet0
* Bit 15 : Mdio for enet1
*/
w = b[woff];
vp += sprintf(vp, "et0phyaddr=%d", (w & 0x1f));
vp++;
vp += sprintf(vp, "et1phyaddr=%d", ((w >> 5) & 0x1f));
vp++;
vp += sprintf(vp, "et0mdcport=%d", ((w >> 14) & 0x1));
vp++;
vp += sprintf(vp, "et1mdcport=%d", ((w >> 15) & 0x1));
vp++;
/* Word 46 has board rev, antennas 0/1 & Country code/control */
w = b[46];
vp += sprintf(vp, "boardrev=%d", w & 0xff);
vp++;
if (sromrev > 1)
vp += sprintf(vp, "cctl=%d", (w >> 8) & 0xf);
else
vp += sprintf(vp, "cc=%d", (w >> 8) & 0xf);
vp++;
vp += sprintf(vp, "aa0=%d", (w >> 12) & 0x3);
vp++;
vp += sprintf(vp, "aa1=%d", (w >> 14) & 0x3);
vp++;
/* Words 47-49 set the (wl) pa settings */
woff = 47;
for (i = 0; i < 3; i++) {
vp += sprintf(vp, "pa0b%d=%d", i, b[woff+i]);
vp++;
vp += sprintf(vp, "pa1b%d=%d", i, b[woff+i+6]);
vp++;
}
/*
* Words 50-51 set the customer-configured wl led behavior.
* 8 bits/gpio pin. High bit: activehi=0, activelo=1;
* LED behavior values defined in wlioctl.h .
*/
w = b[50];
if ((w != 0) && (w != 0xffff)) {
/* gpio0 */
vp += sprintf(vp, "wl0gpio0=%d", (w & 0xff));
vp++;
/* gpio1 */
vp += sprintf(vp, "wl0gpio1=%d", (w >> 8) & 0xff);
vp++;
}
w = b[51];
if ((w != 0) && (w != 0xffff)) {
/* gpio2 */
vp += sprintf(vp, "wl0gpio2=%d", w & 0xff);
vp++;
/* gpio3 */
vp += sprintf(vp, "wl0gpio3=%d", (w >> 8) & 0xff);
vp++;
}
/* Word 52 is max power 0/1 */
w = b[52];
vp += sprintf(vp, "pa0maxpwr=%d", w & 0xff);
vp++;
vp += sprintf(vp, "pa1maxpwr=%d", (w >> 8) & 0xff);
vp++;
/* Word 56 is idle tssi target 0/1 */
w = b[56];
vp += sprintf(vp, "pa0itssit=%d", w & 0xff);
vp++;
vp += sprintf(vp, "pa1itssit=%d", (w >> 8) & 0xff);
vp++;
/* Word 57 is boardflags, if not programmed make it zero */
bfl = (uint32)b[57];
if (bfl == 0xffff) bfl = 0;
if (sromrev > 1) {
/* Word 28 is boardflags2 */
bfl |= (uint32)b[28] << 16;
}
vp += sprintf(vp, "boardflags=%d", bfl);
vp++;
/* Word 58 is antenna gain 0/1 */
w = b[58];
vp += sprintf(vp, "ag0=%d", w & 0xff);
vp++;
vp += sprintf(vp, "ag1=%d", (w >> 8) & 0xff);
vp++;
if (sromrev == 1) {
/* set the oem string */
vp += sprintf(vp, "oem=%02x%02x%02x%02x%02x%02x%02x%02x",
((b[59] >> 8) & 0xff), (b[59] & 0xff),
((b[60] >> 8) & 0xff), (b[60] & 0xff),
((b[61] >> 8) & 0xff), (b[61] & 0xff),
((b[62] >> 8) & 0xff), (b[62] & 0xff));
vp++;
} else {
if (sromrev >= 1){
/* Word 60 OFDM tx power offset from CCK level */
/* OFDM Power Offset - opo */
w = b[60] & 0xff;
if (w == 0xff)
w = 16;
vp += sprintf(vp, "opo=%d", w);
vp++;
}
}
/* final nullbyte terminator */
*vp++ = '\0';
c = vp - base;
ASSERT(c <= VARS_MAX);
if (c == VARS_MAX) {
*vars = base;
} else {
vp = MALLOC(c);
ASSERT(vp);
bcopy(base, vp, c);
MFREE(base, VARS_MAX);
*vars = vp;
}
*count = c;
return (0);
}
/*
* Read the cis and call parsecis to initialize the vars.
* Return 0 on success, nonzero on error.
*/
static int
initvars_cis_pcmcia(void *sbh, void *curmap, void *osh, char **vars, int *count)
{
uint8 *cis = NULL;
int rc;
uint data_sz;
data_sz = (sb_pcmciarev(sbh) == 1) ? (SPROM_SIZE * 2) : CIS_SIZE;
if ((cis = MALLOC(data_sz)) == NULL)
return (-1);
if (sb_pcmciarev(sbh) == 1) {
if (srom_read(PCMCIA_BUS, (void *)NULL, osh, 0, data_sz, (uint16 *)cis)) {
MFREE(cis, data_sz);
return (-1);
}
/* fix up endianess for 16-bit data vs 8-bit parsing */
ltoh16_buf((uint16 *)cis, data_sz);
} else
OSL_PCMCIA_READ_ATTR(osh, 0, cis, data_sz);
rc = srom_parsecis(cis, vars, count);
MFREE(cis, data_sz);
return (rc);
}

View File

@@ -0,0 +1,803 @@
/*
* Misc useful OS-independent routines.
*
* Copyright 2004, Broadcom Corporation
* All Rights Reserved.
*
* THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
* KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
* SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
* $Id$
*/
#include <typedefs.h>
#include <osl.h>
#include <bcmutils.h>
#include <bcmendian.h>
#include <bcmnvram.h>
unsigned char bcm_ctype[] = {
_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C, /* 0-7 */
_BCM_C,_BCM_C|_BCM_S,_BCM_C|_BCM_S,_BCM_C|_BCM_S,_BCM_C|_BCM_S,_BCM_C|_BCM_S,_BCM_C,_BCM_C, /* 8-15 */
_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C, /* 16-23 */
_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C,_BCM_C, /* 24-31 */
_BCM_S|_BCM_SP,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 32-39 */
_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 40-47 */
_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D,_BCM_D, /* 48-55 */
_BCM_D,_BCM_D,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 56-63 */
_BCM_P,_BCM_U|_BCM_X,_BCM_U|_BCM_X,_BCM_U|_BCM_X,_BCM_U|_BCM_X,_BCM_U|_BCM_X,_BCM_U|_BCM_X,_BCM_U, /* 64-71 */
_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U, /* 72-79 */
_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U, /* 80-87 */
_BCM_U,_BCM_U,_BCM_U,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 88-95 */
_BCM_P,_BCM_L|_BCM_X,_BCM_L|_BCM_X,_BCM_L|_BCM_X,_BCM_L|_BCM_X,_BCM_L|_BCM_X,_BCM_L|_BCM_X,_BCM_L, /* 96-103 */
_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L, /* 104-111 */
_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L, /* 112-119 */
_BCM_L,_BCM_L,_BCM_L,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_C, /* 120-127 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 128-143 */
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 144-159 */
_BCM_S|_BCM_SP,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 160-175 */
_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P,_BCM_P, /* 176-191 */
_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U, /* 192-207 */
_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_P,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_U,_BCM_L, /* 208-223 */
_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L, /* 224-239 */
_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_P,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L,_BCM_L /* 240-255 */
};
uchar
bcm_toupper(uchar c)
{
if (bcm_islower(c))
c -= 'a'-'A';
return (c);
}
ulong
bcm_strtoul(char *cp, char **endp, uint base)
{
ulong result, value;
bool minus;
minus = FALSE;
while (bcm_isspace(*cp))
cp++;
if (cp[0] == '+')
cp++;
else if (cp[0] == '-') {
minus = TRUE;
cp++;
}
if (base == 0) {
if (cp[0] == '0') {
if ((cp[1] == 'x') || (cp[1] == 'X')) {
base = 16;
cp = &cp[2];
} else {
base = 8;
cp = &cp[1];
}
} else
base = 10;
} else if (base == 16 && (cp[0] == '0') && ((cp[1] == 'x') || (cp[1] == 'X'))) {
cp = &cp[2];
}
result = 0;
while (bcm_isxdigit(*cp) &&
(value = bcm_isdigit(*cp) ? *cp-'0' : bcm_toupper(*cp)-'A'+10) < base) {
result = result*base + value;
cp++;
}
if (minus)
result = (ulong)(result * -1);
if (endp)
*endp = (char *)cp;
return (result);
}
uint
bcm_atoi(char *s)
{
uint n;
n = 0;
while (bcm_isdigit(*s))
n = (n * 10) + *s++ - '0';
return (n);
}
void
deadbeef(char *p, uint len)
{
static uchar meat[] = { 0xde, 0xad, 0xbe, 0xef };
while (len-- > 0) {
*p = meat[((uint)p) & 3];
p++;
}
}
/* pretty hex print a contiguous buffer */
void
prhex(char *msg, uchar *buf, uint nbytes)
{
char line[256];
char* p;
uint i;
if (msg && (msg[0] != '\0'))
printf("%s: ", msg);
p = line;
for (i = 0; i < nbytes; i++) {
if (i % 16 == 0) {
p += sprintf(p, "%04d: ", i); /* line prefix */
}
p += sprintf(p, "%02x ", buf[i]);
if (i % 16 == 15) {
printf("%s\n", line); /* flush line */
p = line;
}
}
/* flush last partial line */
if (p != line)
printf("%s\n", line);
}
/* pretty hex print a pkt buffer chain */
void
prpkt(char *msg, void *drv, void *p0)
{
void *p;
if (msg && (msg[0] != '\0'))
printf("%s: ", msg);
for (p = p0; p; p = PKTNEXT(drv, p))
prhex(NULL, PKTDATA(drv, p), PKTLEN(drv, p));
}
/* copy a pkt buffer chain into a buffer */
uint
pktcopy(void *drv, void *p, uint offset, int len, uchar *buf)
{
uint n, ret = 0;
if (len < 0)
len = 4096; /* "infinite" */
/* skip 'offset' bytes */
for (; p && offset; p = PKTNEXT(drv, p)) {
if (offset < (uint)PKTLEN(drv, p))
break;
offset -= PKTLEN(drv, p);
}
if (!p)
return 0;
/* copy the data */
for (; p && len; p = PKTNEXT(drv, p)) {
n = MIN((uint)PKTLEN(drv, p) - offset, (uint)len);
bcopy(PKTDATA(drv, p) + offset, buf, n);
buf += n;
len -= n;
ret += n;
offset = 0;
}
return ret;
}
/* return total length of buffer chain */
uint
pkttotlen(void *drv, void *p)
{
uint total;
total = 0;
for (; p; p = PKTNEXT(drv, p))
total += PKTLEN(drv, p);
return (total);
}
uchar*
bcm_ether_ntoa(char *ea, char *buf)
{
sprintf(buf,"%02x:%02x:%02x:%02x:%02x:%02x",
(uchar)ea[0]&0xff, (uchar)ea[1]&0xff, (uchar)ea[2]&0xff,
(uchar)ea[3]&0xff, (uchar)ea[4]&0xff, (uchar)ea[5]&0xff);
return (buf);
}
/* parse a xx:xx:xx:xx:xx:xx format ethernet address */
int
bcm_ether_atoe(char *p, char *ea)
{
int i = 0;
for (;;) {
ea[i++] = (char) bcm_strtoul(p, &p, 16);
if (!*p++ || i == 6)
break;
}
return (i == 6);
}
/*
* Advance from the current 1-byte tag/1-byte length/variable-length value
* triple, to the next, returning a pointer to the next.
*/
bcm_tlv_t *
bcm_next_tlv(bcm_tlv_t *elt, int *buflen)
{
int len;
/* validate current elt */
if (*buflen < 2) {
return NULL;
}
len = elt->len;
/* validate remaining buflen */
if (*buflen >= (2 + len + 2)) {
elt = (bcm_tlv_t*)(elt->data + len);
*buflen -= (2 + len);
} else {
elt = NULL;
}
return elt;
}
/*
* Traverse a string of 1-byte tag/1-byte length/variable-length value
* triples, returning a pointer to the substring whose first element
* matches tag. Stop parsing when we see an element whose ID is greater
* than the target key.
*/
bcm_tlv_t *
bcm_parse_ordered_tlvs(void *buf, int buflen, uint key)
{
bcm_tlv_t *elt;
int totlen;
elt = (bcm_tlv_t*)buf;
totlen = buflen;
/* find tagged parameter */
while (totlen >= 2) {
uint id = elt->id;
int len = elt->len;
/* Punt if we start seeing IDs > than target key */
if (id > key)
return(NULL);
/* validate remaining totlen */
if ((id == key) && (totlen >= (len + 2)))
return (elt);
elt = (bcm_tlv_t*)((uint8*)elt + (len + 2));
totlen -= (len + 2);
}
return NULL;
}
/*
* Traverse a string of 1-byte tag/1-byte length/variable-length value
* triples, returning a pointer to the substring whose first element
* matches tag
*/
bcm_tlv_t *
bcm_parse_tlvs(void *buf, int buflen, uint key)
{
bcm_tlv_t *elt;
int totlen;
elt = (bcm_tlv_t*)buf;
totlen = buflen;
/* find tagged parameter */
while (totlen >= 2) {
int len = elt->len;
/* validate remaining totlen */
if ((elt->id == key) && (totlen >= (len + 2)))
return (elt);
elt = (bcm_tlv_t*)((uint8*)elt + (len + 2));
totlen -= (len + 2);
}
return NULL;
}
void
pktq_init(struct pktq *q, uint maxlen, bool priority)
{
q->head = q->tail = NULL;
q->priority = priority;
q->maxlen = maxlen;
q->len = 0;
}
bool
pktenq(struct pktq *q, void *p, bool lifo)
{
void *next, *prev;
/* Queue is full */
if (q->len >= q->maxlen)
return FALSE;
/* Queueing chains not allowed */
ASSERT(PKTLINK(p) == NULL);
/* Queue is empty */
if (q->tail == NULL) {
ASSERT(q->head == NULL);
q->head = q->tail = p;
}
/* Insert at head or tail */
else if (q->priority == FALSE) {
/* Insert at head (LIFO) */
if (lifo) {
PKTSETLINK(p, q->head);
q->head = p;
}
/* Insert at tail (FIFO) */
else {
ASSERT(PKTLINK(q->tail) == NULL);
PKTSETLINK(q->tail, p);
PKTSETLINK(p, NULL);
q->tail = p;
}
}
/* Insert by priority */
else {
ASSERT(q->head);
ASSERT(q->tail);
/* Shortcut to insertion at tail */
if (PKTPRIO(p) < PKTPRIO(q->tail) ||
(!lifo && PKTPRIO(p) <= PKTPRIO(q->tail))) {
prev = q->tail;
next = NULL;
}
/* Insert at head or in the middle */
else {
prev = NULL;
next = q->head;
}
/* Walk the queue */
for (; next; prev = next, next = PKTLINK(next)) {
/* Priority queue invariant */
ASSERT(!prev || PKTPRIO(prev) >= PKTPRIO(next));
/* Insert at head of string of packets of same priority (LIFO) */
if (lifo) {
if (PKTPRIO(p) >= PKTPRIO(next))
break;
}
/* Insert at tail of string of packets of same priority (FIFO) */
else {
if (PKTPRIO(p) > PKTPRIO(next))
break;
}
}
/* Insert at tail */
if (next == NULL) {
ASSERT(PKTLINK(q->tail) == NULL);
PKTSETLINK(q->tail, p);
PKTSETLINK(p, NULL);
q->tail = p;
}
/* Insert in the middle */
else if (prev) {
PKTSETLINK(prev, p);
PKTSETLINK(p, next);
}
/* Insert at head */
else {
PKTSETLINK(p, q->head);
q->head = p;
}
}
/* List invariants after insertion */
ASSERT(q->head);
ASSERT(PKTLINK(q->tail) == NULL);
q->len++;
return TRUE;
}
void*
pktdeq(struct pktq *q)
{
void *p;
if ((p = q->head)) {
ASSERT(q->tail);
q->head = PKTLINK(p);
PKTSETLINK(p, NULL);
q->len--;
if (q->head == NULL)
q->tail = NULL;
}
else {
ASSERT(q->tail == NULL);
}
return (p);
}
/*******************************************************************************
* crc8
*
* Computes a crc8 over the input data using the polynomial:
*
* x^8 + x^7 +x^6 + x^4 + x^2 + 1
*
* The caller provides the initial value (either CRC8_INIT_VALUE
* or the previous returned value) to allow for processing of
* discontiguous blocks of data. When generating the CRC the
* caller is responsible for complementing the final return value
* and inserting it into the byte stream. When checking, a final
* return value of CRC8_GOOD_VALUE indicates a valid CRC.
*
* Reference: Dallas Semiconductor Application Note 27
* Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms",
* ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd.,
* ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt
*
******************************************************************************/
static uint8 crc8_table[256] = {
0x00, 0xF7, 0xB9, 0x4E, 0x25, 0xD2, 0x9C, 0x6B,
0x4A, 0xBD, 0xF3, 0x04, 0x6F, 0x98, 0xD6, 0x21,
0x94, 0x63, 0x2D, 0xDA, 0xB1, 0x46, 0x08, 0xFF,
0xDE, 0x29, 0x67, 0x90, 0xFB, 0x0C, 0x42, 0xB5,
0x7F, 0x88, 0xC6, 0x31, 0x5A, 0xAD, 0xE3, 0x14,
0x35, 0xC2, 0x8C, 0x7B, 0x10, 0xE7, 0xA9, 0x5E,
0xEB, 0x1C, 0x52, 0xA5, 0xCE, 0x39, 0x77, 0x80,
0xA1, 0x56, 0x18, 0xEF, 0x84, 0x73, 0x3D, 0xCA,
0xFE, 0x09, 0x47, 0xB0, 0xDB, 0x2C, 0x62, 0x95,
0xB4, 0x43, 0x0D, 0xFA, 0x91, 0x66, 0x28, 0xDF,
0x6A, 0x9D, 0xD3, 0x24, 0x4F, 0xB8, 0xF6, 0x01,
0x20, 0xD7, 0x99, 0x6E, 0x05, 0xF2, 0xBC, 0x4B,
0x81, 0x76, 0x38, 0xCF, 0xA4, 0x53, 0x1D, 0xEA,
0xCB, 0x3C, 0x72, 0x85, 0xEE, 0x19, 0x57, 0xA0,
0x15, 0xE2, 0xAC, 0x5B, 0x30, 0xC7, 0x89, 0x7E,
0x5F, 0xA8, 0xE6, 0x11, 0x7A, 0x8D, 0xC3, 0x34,
0xAB, 0x5C, 0x12, 0xE5, 0x8E, 0x79, 0x37, 0xC0,
0xE1, 0x16, 0x58, 0xAF, 0xC4, 0x33, 0x7D, 0x8A,
0x3F, 0xC8, 0x86, 0x71, 0x1A, 0xED, 0xA3, 0x54,
0x75, 0x82, 0xCC, 0x3B, 0x50, 0xA7, 0xE9, 0x1E,
0xD4, 0x23, 0x6D, 0x9A, 0xF1, 0x06, 0x48, 0xBF,
0x9E, 0x69, 0x27, 0xD0, 0xBB, 0x4C, 0x02, 0xF5,
0x40, 0xB7, 0xF9, 0x0E, 0x65, 0x92, 0xDC, 0x2B,
0x0A, 0xFD, 0xB3, 0x44, 0x2F, 0xD8, 0x96, 0x61,
0x55, 0xA2, 0xEC, 0x1B, 0x70, 0x87, 0xC9, 0x3E,
0x1F, 0xE8, 0xA6, 0x51, 0x3A, 0xCD, 0x83, 0x74,
0xC1, 0x36, 0x78, 0x8F, 0xE4, 0x13, 0x5D, 0xAA,
0x8B, 0x7C, 0x32, 0xC5, 0xAE, 0x59, 0x17, 0xE0,
0x2A, 0xDD, 0x93, 0x64, 0x0F, 0xF8, 0xB6, 0x41,
0x60, 0x97, 0xD9, 0x2E, 0x45, 0xB2, 0xFC, 0x0B,
0xBE, 0x49, 0x07, 0xF0, 0x9B, 0x6C, 0x22, 0xD5,
0xF4, 0x03, 0x4D, 0xBA, 0xD1, 0x26, 0x68, 0x9F
};
/*
* Search the name=value vars for a specific one and return its value.
* Returns NULL if not found.
*/
char*
getvar(char *vars, char *name)
{
char *s;
int len;
len = strlen(name);
/* first look in vars[] */
for (s = vars; s && *s; ) {
if ((bcmp(s, name, len) == 0) && (s[len] == '='))
return (&s[len+1]);
while (*s++)
;
}
/* then query nvram */
return (nvram_get(name));
}
/*
* Search the vars for a specific one and return its value as
* an integer. Returns 0 if not found.
*/
int
getintvar(char *vars, char *name)
{
char *val;
if ((val = getvar(vars, name)) == NULL)
return (0);
return (bcm_strtoul(val, NULL, 0));
}
/* return pointer to location of substring 'needle' in 'haystack' */
char*
bcmstrstr(char *haystack, char *needle)
{
int len, nlen;
int i;
if ((haystack == NULL) || (needle == NULL))
return (haystack);
nlen = strlen(needle);
len = strlen(haystack) - nlen + 1;
for (i = 0; i < len; i++)
if (bcmp(needle, &haystack[i], nlen) == 0)
return (&haystack[i]);
return (NULL);
}
void
bcm_mdelay(uint ms)
{
uint i;
for (i = 0; i < ms; i++) {
OSL_DELAY(1000);
}
}
#define CRC_INNER_LOOP(n, c, x) \
(c) = ((c) >> 8) ^ crc##n##_table[((c) ^ (x)) & 0xff]
uint8
crc8(
uint8 *pdata, /* pointer to array of data to process */
uint nbytes, /* number of input data bytes to process */
uint8 crc /* either CRC8_INIT_VALUE or previous return value */
)
{
/* hard code the crc loop instead of using CRC_INNER_LOOP macro
* to avoid the undefined and unnecessary (uint8 >> 8) operation. */
while (nbytes-- > 0)
crc = crc8_table[(crc ^ *pdata++) & 0xff];
return crc;
}
/*******************************************************************************
* crc16
*
* Computes a crc16 over the input data using the polynomial:
*
* x^16 + x^12 +x^5 + 1
*
* The caller provides the initial value (either CRC16_INIT_VALUE
* or the previous returned value) to allow for processing of
* discontiguous blocks of data. When generating the CRC the
* caller is responsible for complementing the final return value
* and inserting it into the byte stream. When checking, a final
* return value of CRC16_GOOD_VALUE indicates a valid CRC.
*
* Reference: Dallas Semiconductor Application Note 27
* Williams, Ross N., "A Painless Guide to CRC Error Detection Algorithms",
* ver 3, Aug 1993, ross@guest.adelaide.edu.au, Rocksoft Pty Ltd.,
* ftp://ftp.rocksoft.com/clients/rocksoft/papers/crc_v3.txt
*
******************************************************************************/
static uint16 crc16_table[256] = {
0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E,
0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876,
0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD,
0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5,
0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C,
0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974,
0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB,
0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3,
0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A,
0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72,
0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9,
0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1,
0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738,
0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70,
0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7,
0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,
0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036,
0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E,
0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5,
0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD,
0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134,
0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C,
0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3,
0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB,
0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232,
0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A,
0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1,
0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9,
0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330,
0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78
};
uint16
crc16(
uint8 *pdata, /* pointer to array of data to process */
uint nbytes, /* number of input data bytes to process */
uint16 crc /* either CRC16_INIT_VALUE or previous return value */
)
{
while (nbytes-- > 0)
CRC_INNER_LOOP(16, crc, *pdata++);
return crc;
}
static uint32 crc32_table[256] = {
0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA,
0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3,
0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91,
0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE,
0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC,
0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5,
0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B,
0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940,
0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116,
0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F,
0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D,
0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A,
0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818,
0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01,
0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457,
0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C,
0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2,
0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB,
0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9,
0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086,
0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4,
0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD,
0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683,
0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8,
0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE,
0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7,
0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5,
0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252,
0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60,
0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79,
0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F,
0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04,
0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A,
0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713,
0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21,
0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E,
0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C,
0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45,
0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB,
0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0,
0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6,
0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF,
0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
};
uint32
crc32(
uint8 *pdata, /* pointer to array of data to process */
uint nbytes, /* number of input data bytes to process */
uint32 crc /* either CRC32_INIT_VALUE or previous return value */
)
{
uint8 *pend;
#ifdef __mips__
uint8 tmp[4];
ulong *tptr = (ulong *)tmp;
/* in case the beginning of the buffer isn't aligned */
pend = (uint8 *)((uint)(pdata + 3) & 0xfffffffc);
nbytes -= (pend - pdata);
while (pdata < pend)
CRC_INNER_LOOP(32, crc, *pdata++);
/* handle bulk of data as 32-bit words */
pend = pdata + (nbytes & 0xfffffffc);
while (pdata < pend) {
*tptr = *((ulong *)pdata)++;
CRC_INNER_LOOP(32, crc, tmp[0]);
CRC_INNER_LOOP(32, crc, tmp[1]);
CRC_INNER_LOOP(32, crc, tmp[2]);
CRC_INNER_LOOP(32, crc, tmp[3]);
}
/* 1-3 bytes at end of buffer */
pend = pdata + (nbytes & 0x03);
while (pdata < pend)
CRC_INNER_LOOP(32, crc, *pdata++);
#else
pend = pdata + nbytes;
while (pdata < pend)
CRC_INNER_LOOP(32, crc, *pdata++);
#endif
return crc;
}
#ifdef notdef
#define CLEN 1499
#define CBUFSIZ (CLEN+4)
#define CNBUFS 5
void testcrc32(void)
{
uint j,k,l;
uint8 *buf;
uint len[CNBUFS];
uint32 crcr;
uint32 crc32tv[CNBUFS] =
{0xd2cb1faa, 0xd385c8fa, 0xf5b4f3f3, 0x55789e20, 0x00343110};
ASSERT((buf = MALLOC(CBUFSIZ*CNBUFS)) != NULL);
/* step through all possible alignments */
for (l=0;l<=4;l++) {
for (j=0; j<CNBUFS; j++) {
len[j] = CLEN;
for (k=0; k<len[j]; k++)
*(buf + j*CBUFSIZ + (k+l)) = (j+k) & 0xff;
}
for (j=0; j<CNBUFS; j++) {
crcr = crc32(buf + j*CBUFSIZ + l, len[j], CRC32_INIT_VALUE);
ASSERT(crcr == crc32tv[j]);
}
}
MFREE(buf, CBUFSIZ*CNBUFS);
return;
}
#endif

View File

@@ -0,0 +1,841 @@
/*
* Generic Broadcom Home Networking Division (HND) DMA module.
* This supports the following chips: BCM42xx, 44xx, 47xx .
*
* Copyright 2004, Broadcom Corporation
* All Rights Reserved.
*
* THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
* KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
* SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
*
* $Id$
*/
#include <typedefs.h>
#include <osl.h>
#include <bcmendian.h>
#include <bcmutils.h>
struct dma_info; /* forward declaration */
#define di_t struct dma_info
#include <hnddma.h>
/* debug/trace */
#define DMA_ERROR(args)
#define DMA_TRACE(args)
/* default dma message level(if input msg_level pointer is null in dma_attach()) */
static uint dma_msg_level = 0;
#define MAXNAMEL 8
#define MAXDD (DMAMAXRINGSZ / sizeof (dmadd_t))
/* dma engine software state */
typedef struct dma_info {
hnddma_t hnddma; /* exported structure */
uint *msg_level; /* message level pointer */
char name[MAXNAMEL]; /* callers name for diag msgs */
void *drv; /* driver handle */
void *dev; /* device handle */
dmaregs_t *regs; /* dma engine registers */
dmadd_t *txd; /* pointer to chip-specific tx descriptor ring */
uint txin; /* index of next descriptor to reclaim */
uint txout; /* index of next descriptor to post */
uint txavail; /* # free tx descriptors */
void *txp[MAXDD]; /* parallel array of pointers to packets */
ulong txdpa; /* physical address of descriptor ring */
uint txdalign; /* #bytes added to alloc'd mem to align txd */
dmadd_t *rxd; /* pointer to chip-specific rx descriptor ring */
uint rxin; /* index of next descriptor to reclaim */
uint rxout; /* index of next descriptor to post */
void *rxp[MAXDD]; /* parallel array of pointers to packets */
ulong rxdpa; /* physical address of descriptor ring */
uint rxdalign; /* #bytes added to alloc'd mem to align rxd */
/* tunables */
uint ntxd; /* # tx descriptors */
uint nrxd; /* # rx descriptors */
uint rxbufsize; /* rx buffer size in bytes */
uint nrxpost; /* # rx buffers to keep posted */
uint rxoffset; /* rxcontrol offset */
uint ddoffset; /* add to get dma address of descriptor ring */
uint dataoffset; /* add to get dma address of data buffer */
} dma_info_t;
/* descriptor bumping macros */
#define TXD(x) ((x) & (di->ntxd - 1))
#define RXD(x) ((x) & (di->nrxd - 1))
#define NEXTTXD(i) TXD(i + 1)
#define PREVTXD(i) TXD(i - 1)
#define NEXTRXD(i) RXD(i + 1)
#define NTXDACTIVE(h, t) TXD(t - h)
#define NRXDACTIVE(h, t) RXD(t - h)
/* macros to convert between byte offsets and indexes */
#define B2I(bytes) ((bytes) / sizeof (dmadd_t))
#define I2B(index) ((index) * sizeof (dmadd_t))
void*
dma_attach(void *drv, void *dev, char *name, dmaregs_t *regs, uint ntxd, uint nrxd,
uint rxbufsize, uint nrxpost, uint rxoffset, uint ddoffset, uint dataoffset, uint *msg_level)
{
dma_info_t *di;
void *va;
ASSERT(ntxd <= MAXDD);
ASSERT(nrxd <= MAXDD);
/* allocate private info structure */
if ((di = MALLOC(sizeof (dma_info_t))) == NULL)
return (NULL);
bzero((char*)di, sizeof (dma_info_t));
/* set message level */
di->msg_level = msg_level ? msg_level : &dma_msg_level;
DMA_TRACE(("%s: dma_attach: drv 0x%x dev 0x%x regs 0x%x ntxd %d nrxd %d rxbufsize %d nrxpost %d rxoffset %d ddoffset 0x%x dataoffset 0x%x\n", name, (uint)drv, (uint)dev, (uint)regs, ntxd, nrxd, rxbufsize, nrxpost, rxoffset, ddoffset, dataoffset));
/* make a private copy of our callers name */
strncpy(di->name, name, MAXNAMEL);
di->name[MAXNAMEL-1] = '\0';
di->drv = drv;
di->dev = dev;
di->regs = regs;
/* allocate transmit descriptor ring */
if (ntxd) {
if ((va = DMA_ALLOC_CONSISTENT(dev, (DMAMAXRINGSZ + DMARINGALIGN), &di->txdpa)) == NULL)
goto fail;
di->txd = (dmadd_t*) ROUNDUP(va, DMARINGALIGN);
di->txdalign = ((uint)di->txd - (uint)va);
di->txdpa = di->txdpa + di->txdalign;
ASSERT(ISALIGNED(di->txd, DMARINGALIGN));
}
/* allocate receive descriptor ring */
if (nrxd) {
if ((va = DMA_ALLOC_CONSISTENT(dev, (DMAMAXRINGSZ + DMARINGALIGN), &di->rxdpa)) == NULL)
goto fail;
di->rxd = (dmadd_t*) ROUNDUP(va, DMARINGALIGN);
di->rxdalign = ((uint)di->rxd - (uint)va);
di->rxdpa = di->rxdpa + di->rxdalign;
ASSERT(ISALIGNED(di->rxd, DMARINGALIGN));
}
/* save tunables */
di->ntxd = ntxd;
di->nrxd = nrxd;
di->rxbufsize = rxbufsize;
di->nrxpost = nrxpost;
di->rxoffset = rxoffset;
di->ddoffset = ddoffset;
di->dataoffset = dataoffset;
return ((void*)di);
fail:
dma_detach((void*)di);
return (NULL);
}
/* may be called with core in reset */
void
dma_detach(dma_info_t *di)
{
if (di == NULL)
return;
DMA_TRACE(("%s: dma_detach\n", di->name));
/* shouldn't be here if descriptors are unreclaimed */
ASSERT(di->txin == di->txout);
ASSERT(di->rxin == di->rxout);
/* free dma descriptor rings */
if (di->txd)
DMA_FREE_CONSISTENT(di->dev, (void *)((uint)di->txd - di->txdalign), (DMAMAXRINGSZ + DMARINGALIGN), di->txdpa);
if (di->rxd)
DMA_FREE_CONSISTENT(di->dev, (void *)((uint)di->rxd - di->rxdalign), (DMAMAXRINGSZ + DMARINGALIGN), di->rxdpa);
/* free our private info structure */
MFREE((void*)di, sizeof (dma_info_t));
}
void
dma_txreset(dma_info_t *di)
{
uint32 status;
DMA_TRACE(("%s: dma_txreset\n", di->name));
/* suspend tx DMA first */
W_REG(&di->regs->xmtcontrol, XC_SE);
SPINWAIT((status = (R_REG(&di->regs->xmtstatus) & XS_XS_MASK)) != XS_XS_DISABLED &&
status != XS_XS_IDLE &&
status != XS_XS_STOPPED,
10000);
W_REG(&di->regs->xmtcontrol, 0);
SPINWAIT((status = (R_REG(&di->regs->xmtstatus) & XS_XS_MASK)) != XS_XS_DISABLED,
10000);
if (status != XS_XS_DISABLED) {
DMA_ERROR(("%s: dma_txreset: dma cannot be stopped\n", di->name));
}
/* wait for the last transaction to complete */
OSL_DELAY(300);
}
void
dma_rxreset(dma_info_t *di)
{
uint32 status;
DMA_TRACE(("%s: dma_rxreset\n", di->name));
W_REG(&di->regs->rcvcontrol, 0);
SPINWAIT((status = (R_REG(&di->regs->rcvstatus) & RS_RS_MASK)) != RS_RS_DISABLED,
10000);
if (status != RS_RS_DISABLED) {
DMA_ERROR(("%s: dma_rxreset: dma cannot be stopped\n", di->name));
}
}
void
dma_txinit(dma_info_t *di)
{
DMA_TRACE(("%s: dma_txinit\n", di->name));
di->txin = di->txout = 0;
di->txavail = di->ntxd - 1;
/* clear tx descriptor ring */
BZERO_SM((void*)di->txd, (di->ntxd * sizeof (dmadd_t)));
W_REG(&di->regs->xmtcontrol, XC_XE);
W_REG(&di->regs->xmtaddr, (di->txdpa + di->ddoffset));
}
bool
dma_txenabled(dma_info_t *di)
{
uint32 xc;
/* If the chip is dead, it is not enabled :-) */
xc = R_REG(&di->regs->xmtcontrol);
return ((xc != 0xffffffff) && (xc & XC_XE));
}
void
dma_txsuspend(dma_info_t *di)
{
DMA_TRACE(("%s: dma_txsuspend\n", di->name));
OR_REG(&di->regs->xmtcontrol, XC_SE);
}
void
dma_txresume(dma_info_t *di)
{
DMA_TRACE(("%s: dma_txresume\n", di->name));
AND_REG(&di->regs->xmtcontrol, ~XC_SE);
}
bool
dma_txsuspended(dma_info_t *di)
{
if (!(R_REG(&di->regs->xmtcontrol) & XC_SE))
return 0;
if ((R_REG(&di->regs->xmtstatus) & XS_XS_MASK) != XS_XS_IDLE)
return 0;
OSL_DELAY(2);
return ((R_REG(&di->regs->xmtstatus) & XS_XS_MASK) == XS_XS_IDLE);
}
bool
dma_txstopped(dma_info_t *di)
{
return ((R_REG(&di->regs->xmtstatus) & XS_XS_MASK) == XS_XS_STOPPED);
}
bool
dma_rxstopped(dma_info_t *di)
{
return ((R_REG(&di->regs->rcvstatus) & RS_RS_MASK) == RS_RS_STOPPED);
}
void
dma_fifoloopbackenable(dma_info_t *di)
{
DMA_TRACE(("%s: dma_fifoloopbackenable\n", di->name));
OR_REG(&di->regs->xmtcontrol, XC_LE);
}
void
dma_rxinit(dma_info_t *di)
{
DMA_TRACE(("%s: dma_rxinit\n", di->name));
di->rxin = di->rxout = 0;
/* clear rx descriptor ring */
BZERO_SM((void*)di->rxd, (di->nrxd * sizeof (dmadd_t)));
dma_rxenable(di);
W_REG(&di->regs->rcvaddr, (di->rxdpa + di->ddoffset));
}
void
dma_rxenable(dma_info_t *di)
{
DMA_TRACE(("%s: dma_rxenable\n", di->name));
W_REG(&di->regs->rcvcontrol, ((di->rxoffset << RC_RO_SHIFT) | RC_RE));
}
bool
dma_rxenabled(dma_info_t *di)
{
uint32 rc;
rc = R_REG(&di->regs->rcvcontrol);
return ((rc != 0xffffffff) && (rc & RC_RE));
}
/*
* The BCM47XX family supports full 32bit dma engine buffer addressing so
* dma buffers can cross 4 Kbyte page boundaries.
*/
int
dma_txfast(dma_info_t *di, void *p0, uint32 coreflags)
{
void *p, *next;
uchar *data;
uint len;
uint txout;
uint32 ctrl;
uint32 pa;
DMA_TRACE(("%s: dma_txfast\n", di->name));
txout = di->txout;
ctrl = 0;
/*
* Walk the chain of packet buffers
* allocating and initializing transmit descriptor entries.
*/
for (p = p0; p; p = next) {
data = PKTDATA(di->drv, p);
len = PKTLEN(di->drv, p);
next = PKTNEXT(di->drv, p);
/* return nonzero if out of tx descriptors */
if (NEXTTXD(txout) == di->txin)
goto outoftxd;
if (len == 0)
continue;
/* get physical address of buffer start */
pa = (uint32) DMA_MAP(di->dev, data, len, DMA_TX, p);
/* build the descriptor control value */
ctrl = len & CTRL_BC_MASK;
ctrl |= coreflags;
if (p == p0)
ctrl |= CTRL_SOF;
if (next == NULL)
ctrl |= (CTRL_IOC | CTRL_EOF);
if (txout == (di->ntxd - 1))
ctrl |= CTRL_EOT;
/* init the tx descriptor */
W_SM(&di->txd[txout].ctrl, BUS_SWAP32(ctrl));
W_SM(&di->txd[txout].addr, BUS_SWAP32(pa + di->dataoffset));
ASSERT(di->txp[txout] == NULL);
txout = NEXTTXD(txout);
}
/* if last txd eof not set, fix it */
if (!(ctrl & CTRL_EOF))
W_SM(&di->txd[PREVTXD(txout)].ctrl, BUS_SWAP32(ctrl | CTRL_IOC | CTRL_EOF));
/* save the packet */
di->txp[PREVTXD(txout)] = p0;
/* bump the tx descriptor index */
di->txout = txout;
/* kick the chip */
W_REG(&di->regs->xmtptr, I2B(txout));
/* tx flow control */
di->txavail = di->ntxd - NTXDACTIVE(di->txin, di->txout) - 1;
return (0);
outoftxd:
DMA_ERROR(("%s: dma_txfast: out of txds\n", di->name));
PKTFREE(di->drv, p0, TRUE);
di->txavail = 0;
di->hnddma.txnobuf++;
return (-1);
}
#define PAGESZ 4096
#define PAGEBASE(x) ((uint)(x) & ~4095)
/*
* Just like above except go through the extra effort of splitting
* buffers that cross 4Kbyte boundaries into multiple tx descriptors.
*/
int
dma_tx(dma_info_t *di, void *p0, uint32 coreflags)
{
void *p, *next;
uchar *data;
uint plen, len;
uchar *page, *start, *end;
uint txout;
uint32 ctrl;
uint32 pa;
DMA_TRACE(("%s: dma_tx\n", di->name));
txout = di->txout;
ctrl = 0;
/*
* Walk the chain of packet buffers
* splitting those that cross 4 Kbyte boundaries
* allocating and initializing transmit descriptor entries.
*/
for (p = p0; p; p = next) {
data = PKTDATA(di->drv, p);
plen = PKTLEN(di->drv, p);
next = PKTNEXT(di->drv, p);
if (plen == 0)
continue;
for (page = (uchar*)PAGEBASE(data);
page <= (uchar*)PAGEBASE(data + plen - 1);
page += PAGESZ) {
/* return nonzero if out of tx descriptors */
if (NEXTTXD(txout) == di->txin)
goto outoftxd;
start = (page == (uchar*)PAGEBASE(data))? data: page;
end = (page == (uchar*)PAGEBASE(data + plen))?
(data + plen): (page + PAGESZ);
len = end - start;
/* build the descriptor control value */
ctrl = len & CTRL_BC_MASK;
ctrl |= coreflags;
if ((p == p0) && (start == data))
ctrl |= CTRL_SOF;
if ((next == NULL) && (end == (data + plen)))
ctrl |= (CTRL_IOC | CTRL_EOF);
if (txout == (di->ntxd - 1))
ctrl |= CTRL_EOT;
/* get physical address of buffer start */
pa = (uint32) DMA_MAP(di->dev, start, len, DMA_TX, p);
/* init the tx descriptor */
W_SM(&di->txd[txout].ctrl, BUS_SWAP32(ctrl));
W_SM(&di->txd[txout].addr, BUS_SWAP32(pa + di->dataoffset));
ASSERT(di->txp[txout] == NULL);
txout = NEXTTXD(txout);
}
}
/* if last txd eof not set, fix it */
if (!(ctrl & CTRL_EOF))
W_SM(&di->txd[PREVTXD(txout)].ctrl, BUS_SWAP32(ctrl | CTRL_IOC | CTRL_EOF));
/* save the packet */
di->txp[PREVTXD(txout)] = p0;
/* bump the tx descriptor index */
di->txout = txout;
/* kick the chip */
W_REG(&di->regs->xmtptr, I2B(txout));
/* tx flow control */
di->txavail = di->ntxd - NTXDACTIVE(di->txin, di->txout) - 1;
return (0);
outoftxd:
DMA_ERROR(("%s: dma_tx: out of txds\n", di->name));
PKTFREE(di->drv, p0, TRUE);
di->txavail = 0;
di->hnddma.txnobuf++;
return (-1);
}
/* returns a pointer to the next frame received, or NULL if there are no more */
void*
dma_rx(dma_info_t *di)
{
void *p;
uint len;
int skiplen = 0;
while ((p = dma_getnextrxp(di, FALSE))) {
/* skip giant packets which span multiple rx descriptors */
if (skiplen > 0) {
skiplen -= di->rxbufsize;
if (skiplen < 0)
skiplen = 0;
PKTFREE(di->drv, p, FALSE);
continue;
}
len = ltoh16(*(uint16*)(PKTDATA(di->drv, p)));
DMA_TRACE(("%s: dma_rx len %d\n", di->name, len));
/* bad frame length check */
if (len > (di->rxbufsize - di->rxoffset)) {
DMA_ERROR(("%s: dma_rx: bad frame length (%d)\n", di->name, len));
if (len > 0)
skiplen = len - (di->rxbufsize - di->rxoffset);
PKTFREE(di->drv, p, FALSE);
di->hnddma.rxgiants++;
continue;
}
/* set actual length */
PKTSETLEN(di->drv, p, (di->rxoffset + len));
break;
}
return (p);
}
/* post receive buffers */
void
dma_rxfill(dma_info_t *di)
{
void *p;
uint rxin, rxout;
uint ctrl;
uint n;
uint i;
uint32 pa;
uint rxbufsize;
/*
* Determine how many receive buffers we're lacking
* from the full complement, allocate, initialize,
* and post them, then update the chip rx lastdscr.
*/
rxin = di->rxin;
rxout = di->rxout;
rxbufsize = di->rxbufsize;
n = di->nrxpost - NRXDACTIVE(rxin, rxout);
DMA_TRACE(("%s: dma_rxfill: post %d\n", di->name, n));
for (i = 0; i < n; i++) {
if ((p = PKTGET(di->drv, rxbufsize, FALSE)) == NULL) {
DMA_ERROR(("%s: dma_rxfill: out of rxbufs\n", di->name));
di->hnddma.rxnobuf++;
break;
}
*(uint32*)(OSL_UNCACHED(PKTDATA(di->drv, p))) = 0;
pa = (uint32) DMA_MAP(di->dev, PKTDATA(di->drv, p), rxbufsize, DMA_RX, p);
ASSERT(ISALIGNED(pa, 4));
/* save the free packet pointer */
ASSERT(di->rxp[rxout] == NULL);
di->rxp[rxout] = p;
/* prep the descriptor control value */
ctrl = rxbufsize;
if (rxout == (di->nrxd - 1))
ctrl |= CTRL_EOT;
/* init the rx descriptor */
W_SM(&di->rxd[rxout].ctrl, BUS_SWAP32(ctrl));
W_SM(&di->rxd[rxout].addr, BUS_SWAP32(pa + di->dataoffset));
rxout = NEXTRXD(rxout);
}
di->rxout = rxout;
/* update the chip lastdscr pointer */
W_REG(&di->regs->rcvptr, I2B(rxout));
}
void
dma_txreclaim(dma_info_t *di, bool forceall)
{
void *p;
DMA_TRACE(("%s: dma_txreclaim %s\n", di->name, forceall ? "all" : ""));
while ((p = dma_getnexttxp(di, forceall)))
PKTFREE(di->drv, p, TRUE);
}
/*
* Reclaim next completed txd (txds if using chained buffers) and
* return associated packet.
* If 'force' is true, reclaim txd(s) and return associated packet
* regardless of the value of the hardware "curr" pointer.
*/
void*
dma_getnexttxp(dma_info_t *di, bool forceall)
{
uint start, end, i;
void *txp;
DMA_TRACE(("%s: dma_getnexttxp %s\n", di->name, forceall ? "all" : ""));
txp = NULL;
start = di->txin;
if (forceall)
end = di->txout;
else
end = B2I(R_REG(&di->regs->xmtstatus) & XS_CD_MASK);
if ((start == 0) && (end > di->txout))
goto bogus;
for (i = start; i != end && !txp; i = NEXTTXD(i)) {
DMA_UNMAP(di->dev, (BUS_SWAP32(R_SM(&di->txd[i].addr)) - di->dataoffset),
(BUS_SWAP32(R_SM(&di->txd[i].ctrl)) & CTRL_BC_MASK), DMA_TX, di->txp[i]);
W_SM(&di->txd[i].addr, 0xdeadbeef);
txp = di->txp[i];
di->txp[i] = NULL;
}
di->txin = i;
/* tx flow control */
di->txavail = di->ntxd - NTXDACTIVE(di->txin, di->txout) - 1;
return (txp);
bogus:
/*
DMA_ERROR(("dma_getnexttxp: bogus curr: start %d end %d txout %d force %d\n",
start, end, di->txout, forceall));
*/
return (NULL);
}
/* like getnexttxp but no reclaim */
void*
dma_peeknexttxp(dma_info_t *di)
{
uint end, i;
end = B2I(R_REG(&di->regs->xmtstatus) & XS_CD_MASK);
for (i = di->txin; i != end; i = NEXTTXD(i))
if (di->txp[i])
return (di->txp[i]);
return (NULL);
}
void
dma_rxreclaim(dma_info_t *di)
{
void *p;
DMA_TRACE(("%s: dma_rxreclaim\n", di->name));
while ((p = dma_getnextrxp(di, TRUE)))
PKTFREE(di->drv, p, FALSE);
}
void *
dma_getnextrxp(dma_info_t *di, bool forceall)
{
uint i;
void *rxp;
/* if forcing, dma engine must be disabled */
ASSERT(!forceall || !dma_rxenabled(di));
i = di->rxin;
/* return if no packets posted */
if (i == di->rxout)
return (NULL);
/* ignore curr if forceall */
if (!forceall && (i == B2I(R_REG(&di->regs->rcvstatus) & RS_CD_MASK)))
return (NULL);
/* get the packet pointer that corresponds to the rx descriptor */
rxp = di->rxp[i];
ASSERT(rxp);
di->rxp[i] = NULL;
/* clear this packet from the descriptor ring */
DMA_UNMAP(di->dev, (BUS_SWAP32(R_SM(&di->rxd[i].addr)) - di->dataoffset),
di->rxbufsize, DMA_RX, rxp);
W_SM(&di->rxd[i].addr, 0xdeadbeef);
di->rxin = NEXTRXD(i);
return (rxp);
}
char*
dma_dumptx(dma_info_t *di, char *buf)
{
buf += sprintf(buf, "txd 0x%lx txdpa 0x%lx txp 0x%lx txin %d txout %d txavail %d\n",
(ulong)di->txd, di->txdpa, (ulong)di->txp, di->txin, di->txout, di->txavail);
buf += sprintf(buf, "xmtcontrol 0x%x xmtaddr 0x%x xmtptr 0x%x xmtstatus 0x%x\n",
R_REG(&di->regs->xmtcontrol),
R_REG(&di->regs->xmtaddr),
R_REG(&di->regs->xmtptr),
R_REG(&di->regs->xmtstatus));
return (buf);
}
char*
dma_dumprx(dma_info_t *di, char *buf)
{
buf += sprintf(buf, "rxd 0x%lx rxdpa 0x%lx rxp 0x%lx rxin %d rxout %d\n",
(ulong)di->rxd, di->rxdpa, (ulong)di->rxp, di->rxin, di->rxout);
buf += sprintf(buf, "rcvcontrol 0x%x rcvaddr 0x%x rcvptr 0x%x rcvstatus 0x%x\n",
R_REG(&di->regs->rcvcontrol),
R_REG(&di->regs->rcvaddr),
R_REG(&di->regs->rcvptr),
R_REG(&di->regs->rcvstatus));
return (buf);
}
char*
dma_dump(dma_info_t *di, char *buf)
{
buf = dma_dumptx(di, buf);
buf = dma_dumprx(di, buf);
return (buf);
}
uint
dma_getvar(dma_info_t *di, char *name)
{
if (!strcmp(name, "&txavail"))
return ((uint) &di->txavail);
else {
ASSERT(0);
}
return (0);
}
void
dma_txblock(dma_info_t *di)
{
di->txavail = 0;
}
void
dma_txunblock(dma_info_t *di)
{
di->txavail = di->ntxd - NTXDACTIVE(di->txin, di->txout) - 1;
}
uint
dma_txactive(dma_info_t *di)
{
return (NTXDACTIVE(di->txin, di->txout));
}
/*
* Rotate all active tx dma ring entries "forward" by (ActiveDescriptor - txin).
*/
void
dma_txrotate(di_t *di)
{
uint ad;
uint nactive;
uint rot;
uint old, new;
uint32 w;
uint first, last;
ASSERT(dma_txsuspended(di));
nactive = dma_txactive(di);
ad = B2I((R_REG(&di->regs->xmtstatus) & XS_AD_MASK) >> XS_AD_SHIFT);
rot = TXD(ad - di->txin);
ASSERT(rot < di->ntxd);
/* full-ring case is a lot harder - don't worry about this */
if (rot >= (di->ntxd - nactive)) {
DMA_ERROR(("%s: dma_txrotate: ring full - punt\n", di->name));
return;
}
first = di->txin;
last = PREVTXD(di->txout);
/* move entries starting at last and moving backwards to first */
for (old = last; old != PREVTXD(first); old = PREVTXD(old)) {
new = TXD(old + rot);
/*
* Move the tx dma descriptor.
* EOT is set only in the last entry in the ring.
*/
w = R_SM(&di->txd[old].ctrl) & ~CTRL_EOT;
if (new == (di->ntxd - 1))
w |= CTRL_EOT;
W_SM(&di->txd[new].ctrl, w);
W_SM(&di->txd[new].addr, R_SM(&di->txd[old].addr));
/* zap the old tx dma descriptor address field */
W_SM(&di->txd[old].addr, 0xdeadbeef);
/* move the corresponding txp[] entry */
ASSERT(di->txp[new] == NULL);
di->txp[new] = di->txp[old];
di->txp[old] = NULL;
}
/* update txin and txout */
di->txin = ad;
di->txout = TXD(di->txout + rot);
di->txavail = di->ntxd - NTXDACTIVE(di->txin, di->txout) - 1;
/* kick the chip */
W_REG(&di->regs->xmtptr, I2B(di->txout));
}

View File

@@ -0,0 +1,465 @@
/*
* Linux OS Independent Layer
*
* Copyright 2004, Broadcom Corporation
* All Rights Reserved.
*
* THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
* KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
* SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
*
* $Id$
*/
#define LINUX_OSL
#include <typedefs.h>
#include <bcmendian.h>
#include <linuxver.h>
#include <linux_osl.h>
#include <bcmutils.h>
#include <linux/delay.h>
#ifdef mips
#include <asm/paccess.h>
#endif
#include <pcicfg.h>
#define PCI_CFG_RETRY 10
void*
osl_pktget(void *drv, uint len, bool send)
{
struct sk_buff *skb;
if ((skb = dev_alloc_skb(len)) == NULL)
return (NULL);
skb_put(skb, len);
/* ensure the cookie field is cleared */
PKTSETCOOKIE(skb, NULL);
return ((void*) skb);
}
void
osl_pktfree(void *p)
{
struct sk_buff *skb, *nskb;
skb = (struct sk_buff*) p;
/* perversion: we use skb->next to chain multi-skb packets */
while (skb) {
nskb = skb->next;
skb->next = NULL;
if (skb->destructor) {
/* cannot kfree_skb() on hard IRQ (net/core/skbuff.c) if destructor exists */
dev_kfree_skb_any(skb);
} else {
/* can free immediately (even in_irq()) if destructor does not exist */
dev_kfree_skb(skb);
}
skb = nskb;
}
}
uint32
osl_pci_read_config(void *loc, uint offset, uint size)
{
struct pci_dev *pdev;
uint val;
uint retry=PCI_CFG_RETRY;
/* only 4byte access supported */
ASSERT(size == 4);
pdev = (struct pci_dev*)loc;
do {
pci_read_config_dword(pdev, offset, &val);
if (val != 0xffffffff)
break;
} while (retry--);
return (val);
}
void
osl_pci_write_config(void *loc, uint offset, uint size, uint val)
{
struct pci_dev *pdev;
uint retry=PCI_CFG_RETRY;
/* only 4byte access supported */
ASSERT(size == 4);
pdev = (struct pci_dev*)loc;
do {
pci_write_config_dword(pdev, offset, val);
if (offset!=PCI_BAR0_WIN)
break;
if (osl_pci_read_config(loc,offset,size) == val)
break;
} while (retry--);
}
static void
osl_pcmcia_attr(void *osh, uint offset, char *buf, int size, bool write)
{
}
void
osl_pcmcia_read_attr(void *osh, uint offset, void *buf, int size)
{
osl_pcmcia_attr(osh, offset, (char *) buf, size, FALSE);
}
void
osl_pcmcia_write_attr(void *osh, uint offset, void *buf, int size)
{
osl_pcmcia_attr(osh, offset, (char *) buf, size, TRUE);
}
#if defined(BINOSL)
void
osl_assert(char *exp, char *file, int line)
{
char tempbuf[255];
sprintf(tempbuf, "assertion \"%s\" failed: file \"%s\", line %d\n", exp, file, line);
panic(tempbuf);
}
void*
osl_malloc(uint size)
{
return (kmalloc(size, GFP_ATOMIC));
}
void
osl_mfree(void *addr, uint size)
{
kfree(addr);
}
uint
osl_malloced(void)
{
#ifdef MODULE
return malloced;
#else
return 0;
#endif
}
#endif /* defined(BCMDBG) || defined(BINOSL) */
/*
* BINOSL selects the slightly slower function-call-based binary compatible osl.
*/
#ifdef BINOSL
int
osl_printf(const char *format, ...)
{
va_list args;
char buf[1024];
int len;
/* sprintf into a local buffer because there *is* no "vprintk()".. */
va_start(args, format);
len = vsprintf(buf, format, args);
va_end(args);
if (len > sizeof (buf)) {
printk("osl_printf: buffer overrun\n");
return (0);
}
return (printk(buf));
}
int
osl_sprintf(char *buf, const char *format, ...)
{
va_list args;
int rc;
va_start(args, format);
rc = vsprintf(buf, format, args);
va_end(args);
return (rc);
}
int
osl_strcmp(const char *s1, const char *s2)
{
return (strcmp(s1, s2));
}
int
osl_strncmp(const char *s1, const char *s2, uint n)
{
return (strncmp(s1, s2, n));
}
int
osl_strlen(char *s)
{
return (strlen(s));
}
char*
osl_strcpy(char *d, const char *s)
{
return (strcpy(d, s));
}
char*
osl_strncpy(char *d, const char *s, uint n)
{
return (strncpy(d, s, n));
}
void
bcopy(const void *src, void *dst, int len)
{
memcpy(dst, src, len);
}
int
bcmp(const void *b1, const void *b2, int len)
{
return (memcmp(b1, b2, len));
}
void
bzero(void *b, int len)
{
memset(b, '\0', len);
}
uint32
osl_readl(volatile uint32 *r)
{
return (readl(r));
}
uint16
osl_readw(volatile uint16 *r)
{
return (readw(r));
}
uint8
osl_readb(volatile uint8 *r)
{
return (readb(r));
}
void
osl_writel(uint32 v, volatile uint32 *r)
{
writel(v, r);
}
void
osl_writew(uint16 v, volatile uint16 *r)
{
writew(v, r);
}
void
osl_writeb(uint8 v, volatile uint8 *r)
{
writeb(v, r);
}
void *
osl_uncached(void *va)
{
#ifdef mips
return ((void*)KSEG1ADDR(va));
#else
return ((void*)va);
#endif
}
uint
osl_getcycles(void)
{
uint cycles;
#if defined(mips)
cycles = read_c0_count() * 2;
#elif defined(__i386__)
rdtscl(cycles);
#else
cycles = 0;
#endif
return cycles;
}
void *
osl_reg_map(uint32 pa, uint size)
{
return (ioremap_nocache((unsigned long)pa, (unsigned long)size));
}
void
osl_reg_unmap(void *va)
{
iounmap(va);
}
int
osl_busprobe(uint32 *val, uint32 addr)
{
#ifdef mips
return get_dbe(*val, (uint32*)addr);
#else
*val = readl(addr);
return 0;
#endif
}
void*
osl_dma_alloc_consistent(void *dev, uint size, ulong *pap)
{
return (pci_alloc_consistent((struct pci_dev*)dev, size, (dma_addr_t*)pap));
}
void
osl_dma_free_consistent(void *dev, void *va, uint size, ulong pa)
{
pci_free_consistent((struct pci_dev*)dev, size, va, (dma_addr_t)pa);
}
uint
osl_dma_map(void *dev, void *va, uint size, int direction)
{
int dir;
dir = (direction == DMA_TX)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE;
return (pci_map_single(dev, va, size, dir));
}
void
osl_dma_unmap(void *dev, uint pa, uint size, int direction)
{
int dir;
dir = (direction == DMA_TX)? PCI_DMA_TODEVICE: PCI_DMA_FROMDEVICE;
pci_unmap_single(dev, (uint32)pa, size, dir);
}
void
osl_delay(uint usec)
{
udelay(usec);
}
uchar*
osl_pktdata(void *drv, void *skb)
{
return (((struct sk_buff*)skb)->data);
}
uint
osl_pktlen(void *drv, void *skb)
{
return (((struct sk_buff*)skb)->len);
}
uint
osl_pktheadroom(void *drv, void *skb)
{
return (uint) skb_headroom((struct sk_buff *) skb);
}
uint
osl_pkttailroom(void *drv, void *skb)
{
return (uint) skb_tailroom((struct sk_buff *) skb);
}
void*
osl_pktnext(void *drv, void *skb)
{
return (((struct sk_buff*)skb)->next);
}
void
osl_pktsetnext(void *skb, void *x)
{
((struct sk_buff*)skb)->next = (struct sk_buff*)x;
}
void
osl_pktsetlen(void *drv, void *skb, uint len)
{
__skb_trim((struct sk_buff*)skb, len);
}
uchar*
osl_pktpush(void *drv, void *skb, int bytes)
{
return (skb_push((struct sk_buff*)skb, bytes));
}
uchar*
osl_pktpull(void *drv, void *skb, int bytes)
{
return (skb_pull((struct sk_buff*)skb, bytes));
}
void*
osl_pktdup(void *drv, void *skb)
{
return (skb_clone((struct sk_buff*)skb, GFP_ATOMIC));
}
void*
osl_pktcookie(void *skb)
{
return ((void*)((struct sk_buff*)skb)->csum);
}
void
osl_pktsetcookie(void *skb, void *x)
{
((struct sk_buff*)skb)->csum = (uint)x;
}
void*
osl_pktlink(void *skb)
{
return (((struct sk_buff*)skb)->prev);
}
void
osl_pktsetlink(void *skb, void *x)
{
((struct sk_buff*)skb)->prev = (struct sk_buff*)x;
}
uint
osl_pktprio(void *skb)
{
return (((struct sk_buff*)skb)->priority);
}
void
osl_pktsetprio(void *skb, uint x)
{
((struct sk_buff*)skb)->priority = x;
}
#endif /* BINOSL */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,21 @@
#!/bin/sh
#
# Copyright 2004, Broadcom Corporation
# All Rights Reserved.
#
# THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
# KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
# SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
#
# $Id$
#
cat <<EOF
#include <linux/config.h>
#include <linux/module.h>
EOF
for file in $* ; do
${NM} $file | sed -ne 's/[0-9A-Fa-f]* [DT] \([^ ]*\)/extern void \1; EXPORT_SYMBOL(\1);/p'
done