This is a driver for Fore/Marconi HE155 and HE622 ATM cards. It is full
busdma and has extensively been tested on i386 and sparc64.
This commit is contained in:
parent
fe5c322931
commit
c594298bee
@ -1673,12 +1673,16 @@ options MSIZE=512 # mbuf size in bytes
|
|||||||
# The `en' device provides support for Efficient Networks (ENI)
|
# The `en' device provides support for Efficient Networks (ENI)
|
||||||
# ENI-155 PCI midway cards, and the Adaptec 155Mbps PCI ATM cards (ANA-59x0).
|
# ENI-155 PCI midway cards, and the Adaptec 155Mbps PCI ATM cards (ANA-59x0).
|
||||||
#
|
#
|
||||||
|
# The `hatm' device provides support for Fore/Marconi HE155 and HE622
|
||||||
|
# ATM PCI cards.
|
||||||
|
#
|
||||||
# atm device provides generic atm functions and is required for
|
# atm device provides generic atm functions and is required for
|
||||||
# atm devices.
|
# atm devices.
|
||||||
# NATM enables the netnatm protocol family that can be used to
|
# NATM enables the netnatm protocol family that can be used to
|
||||||
# bypass TCP/IP.
|
# bypass TCP/IP.
|
||||||
#
|
#
|
||||||
# utopia provides the access to the ATM PHY chips and is required for en
|
# utopia provides the access to the ATM PHY chips and is required for en
|
||||||
|
# and hatm
|
||||||
#
|
#
|
||||||
# the current driver supports only PVC operations (no atm-arp, no multicast).
|
# the current driver supports only PVC operations (no atm-arp, no multicast).
|
||||||
# for more details, please read the original documents at
|
# for more details, please read the original documents at
|
||||||
@ -1686,6 +1690,7 @@ options MSIZE=512 # mbuf size in bytes
|
|||||||
#
|
#
|
||||||
device atm
|
device atm
|
||||||
device en
|
device en
|
||||||
|
device hatm #Fore/Marconi HE155/622
|
||||||
device utopia #ATM PHY driver
|
device utopia #ATM PHY driver
|
||||||
options NATM #native ATM
|
options NATM #native ATM
|
||||||
|
|
||||||
|
@ -408,6 +408,11 @@ dev/fxp/if_fxp.c optional fxp
|
|||||||
dev/gem/if_gem.c optional gem
|
dev/gem/if_gem.c optional gem
|
||||||
dev/gem/if_gem_pci.c optional gem pci
|
dev/gem/if_gem_pci.c optional gem pci
|
||||||
dev/gx/if_gx.c optional gx
|
dev/gx/if_gx.c optional gx
|
||||||
|
dev/hatm/if_hatm.c optional hatm pci
|
||||||
|
dev/hatm/if_hatm_intr.c optional hatm pci
|
||||||
|
dev/hatm/if_hatm_ioctl.c optional hatm pci
|
||||||
|
dev/hatm/if_hatm_rx.c optional hatm pci
|
||||||
|
dev/hatm/if_hatm_tx.c optional hatm pci
|
||||||
dev/hea/eni.c optional hea nowerror
|
dev/hea/eni.c optional hea nowerror
|
||||||
dev/hea/eni_buffer.c optional hea nowerror
|
dev/hea/eni_buffer.c optional hea nowerror
|
||||||
dev/hea/eni_globals.c optional hea
|
dev/hea/eni_globals.c optional hea
|
||||||
|
2382
sys/dev/hatm/if_hatm.c
Normal file
2382
sys/dev/hatm/if_hatm.c
Normal file
File diff suppressed because it is too large
Load Diff
681
sys/dev/hatm/if_hatm_intr.c
Normal file
681
sys/dev/hatm/if_hatm_intr.c
Normal file
@ -0,0 +1,681 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2001-2003
|
||||||
|
* Fraunhofer Institute for Open Communication Systems (FhG Fokus).
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* Author: Hartmut Brandt <harti@freebsd.org>
|
||||||
|
*
|
||||||
|
* $FreeBSD$
|
||||||
|
*
|
||||||
|
* ForeHE driver.
|
||||||
|
*
|
||||||
|
* Interrupt handler.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "opt_inet.h"
|
||||||
|
#include "opt_natm.h"
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/systm.h>
|
||||||
|
#include <sys/malloc.h>
|
||||||
|
#include <sys/kernel.h>
|
||||||
|
#include <sys/bus.h>
|
||||||
|
#include <sys/errno.h>
|
||||||
|
#include <sys/conf.h>
|
||||||
|
#include <sys/module.h>
|
||||||
|
#include <sys/queue.h>
|
||||||
|
#include <sys/syslog.h>
|
||||||
|
#include <sys/condvar.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#include <vm/uma.h>
|
||||||
|
|
||||||
|
#include <sys/sockio.h>
|
||||||
|
#include <sys/mbuf.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <net/if_media.h>
|
||||||
|
#include <net/if_atm.h>
|
||||||
|
#include <net/route.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/if_atm.h>
|
||||||
|
|
||||||
|
#include <machine/bus.h>
|
||||||
|
#include <machine/resource.h>
|
||||||
|
#include <sys/bus.h>
|
||||||
|
#include <sys/rman.h>
|
||||||
|
#include <pci/pcireg.h>
|
||||||
|
#include <pci/pcivar.h>
|
||||||
|
|
||||||
|
#include <dev/utopia/utopia.h>
|
||||||
|
#include <dev/hatm/if_hatmconf.h>
|
||||||
|
#include <dev/hatm/if_hatmreg.h>
|
||||||
|
#include <dev/hatm/if_hatmvar.h>
|
||||||
|
|
||||||
|
CTASSERT(sizeof(struct mbuf_page) == MBUF_ALLOC_SIZE);
|
||||||
|
CTASSERT(sizeof(struct mbuf0_chunk) == MBUF0_CHUNK);
|
||||||
|
CTASSERT(sizeof(struct mbuf1_chunk) == MBUF1_CHUNK);
|
||||||
|
CTASSERT(sizeof(((struct mbuf0_chunk *)NULL)->storage) >= MBUF0_SIZE);
|
||||||
|
CTASSERT(sizeof(((struct mbuf1_chunk *)NULL)->storage) >= MBUF1_SIZE);
|
||||||
|
CTASSERT(sizeof(struct tpd) <= HE_TPD_SIZE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Either the queue treshold was crossed or a TPD with the INTR bit set
|
||||||
|
* was transmitted.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
he_intr_tbrq(struct hatm_softc *sc, struct hetbrq *q, u_int group)
|
||||||
|
{
|
||||||
|
uint32_t *tailp = &sc->hsp->group[group].tbrq_tail;
|
||||||
|
u_int no;
|
||||||
|
|
||||||
|
while (q->head != (*tailp >> 2)) {
|
||||||
|
no = (q->tbrq[q->head].addr & HE_REGM_TBRQ_ADDR) >>
|
||||||
|
HE_REGS_TPD_ADDR;
|
||||||
|
hatm_tx_complete(sc, TPD_ADDR(sc, no),
|
||||||
|
(q->tbrq[q->head].addr & HE_REGM_TBRQ_FLAGS));
|
||||||
|
|
||||||
|
if (++q->head == q->size)
|
||||||
|
q->head = 0;
|
||||||
|
}
|
||||||
|
WRITE4(sc, HE_REGO_TBRQ_H(group), q->head << 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* DMA loader function for external mbuf page.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
hatm_extbuf_helper(void *arg, bus_dma_segment_t *segs, int nsegs,
|
||||||
|
int error)
|
||||||
|
{
|
||||||
|
if (error) {
|
||||||
|
printf("%s: mapping error %d\n", __func__, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
KASSERT(nsegs == 1,
|
||||||
|
("too many segments for DMA: %d", nsegs));
|
||||||
|
KASSERT(segs[0].ds_addr <= 0xffffffffLU,
|
||||||
|
("phys addr too large %lx", (u_long)segs[0].ds_addr));
|
||||||
|
|
||||||
|
*(uint32_t *)arg = segs[0].ds_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate a page of external mbuf storage for the small pools.
|
||||||
|
* Create a DMA map and load it. Put all the chunks onto the right
|
||||||
|
* free list.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
hatm_mbuf_page_alloc(struct hatm_softc *sc, u_int group)
|
||||||
|
{
|
||||||
|
struct mbuf_page *pg;
|
||||||
|
int err;
|
||||||
|
u_int i;
|
||||||
|
|
||||||
|
if (sc->mbuf_npages == HE_CONFIG_MAX_MBUF_PAGES)
|
||||||
|
return;
|
||||||
|
if ((pg = malloc(MBUF_ALLOC_SIZE, M_DEVBUF, M_NOWAIT)) == NULL)
|
||||||
|
return;
|
||||||
|
bzero(pg->hdr.card, sizeof(pg->hdr.card));
|
||||||
|
bzero(pg->hdr.used, sizeof(pg->hdr.used));
|
||||||
|
|
||||||
|
err = bus_dmamap_create(sc->mbuf_tag, 0, &pg->hdr.map);
|
||||||
|
if (err != 0) {
|
||||||
|
if_printf(&sc->ifatm.ifnet, "%s -- bus_dmamap_create: %d\n",
|
||||||
|
__func__, err);
|
||||||
|
free(pg, M_DEVBUF);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
err = bus_dmamap_load(sc->mbuf_tag, pg->hdr.map, pg, MBUF_ALLOC_SIZE,
|
||||||
|
hatm_extbuf_helper, &pg->hdr.phys, 0);
|
||||||
|
if (err != 0) {
|
||||||
|
if_printf(&sc->ifatm.ifnet, "%s -- mbuf mapping failed %d\n",
|
||||||
|
__func__, err);
|
||||||
|
bus_dmamap_destroy(sc->mbuf_tag, pg->hdr.map);
|
||||||
|
free(pg, M_DEVBUF);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sc->mbuf_pages[sc->mbuf_npages] = pg;
|
||||||
|
|
||||||
|
if (group == 0) {
|
||||||
|
struct mbuf0_chunk *c;
|
||||||
|
|
||||||
|
pg->hdr.nchunks = MBUF0_PER_PAGE;
|
||||||
|
pg->hdr.chunksize = MBUF0_CHUNK;
|
||||||
|
pg->hdr.hdroff = sizeof(c->storage);
|
||||||
|
c = (struct mbuf0_chunk *)pg;
|
||||||
|
for (i = 0; i < MBUF0_PER_PAGE; i++, c++) {
|
||||||
|
c->hdr.pageno = sc->mbuf_npages;
|
||||||
|
c->hdr.chunkno = i;
|
||||||
|
SLIST_INSERT_HEAD(&sc->mbuf0_list,
|
||||||
|
(struct mbufx_free *)c, link);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
struct mbuf1_chunk *c;
|
||||||
|
|
||||||
|
pg->hdr.nchunks = MBUF1_PER_PAGE;
|
||||||
|
pg->hdr.chunksize = MBUF1_CHUNK;
|
||||||
|
pg->hdr.hdroff = sizeof(c->storage);
|
||||||
|
c = (struct mbuf1_chunk *)pg;
|
||||||
|
for (i = 0; i < MBUF1_PER_PAGE; i++, c++) {
|
||||||
|
c->hdr.pageno = sc->mbuf_npages;
|
||||||
|
c->hdr.chunkno = i;
|
||||||
|
SLIST_INSERT_HEAD(&sc->mbuf1_list,
|
||||||
|
(struct mbufx_free *)c, link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sc->mbuf_npages++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free an mbuf and put it onto the free list.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
hatm_mbuf0_free(void *buf, void *args)
|
||||||
|
{
|
||||||
|
struct hatm_softc *sc = args;
|
||||||
|
struct mbuf0_chunk *c = buf;
|
||||||
|
|
||||||
|
mtx_lock(&sc->mbuf0_mtx);
|
||||||
|
SLIST_INSERT_HEAD(&sc->mbuf0_list, (struct mbufx_free *)c, link);
|
||||||
|
MBUF_CLR_BIT(sc->mbuf_pages[c->hdr.pageno]->hdr.used, c->hdr.chunkno);
|
||||||
|
mtx_unlock(&sc->mbuf0_mtx);
|
||||||
|
}
|
||||||
|
static void
|
||||||
|
hatm_mbuf1_free(void *buf, void *args)
|
||||||
|
{
|
||||||
|
struct hatm_softc *sc = args;
|
||||||
|
struct mbuf1_chunk *c = buf;
|
||||||
|
|
||||||
|
mtx_lock(&sc->mbuf1_mtx);
|
||||||
|
SLIST_INSERT_HEAD(&sc->mbuf1_list, (struct mbufx_free *)c, link);
|
||||||
|
MBUF_CLR_BIT(sc->mbuf_pages[c->hdr.pageno]->hdr.used, c->hdr.chunkno);
|
||||||
|
mtx_unlock(&sc->mbuf1_mtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate an external mbuf storage
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
hatm_mbuf_alloc(struct hatm_softc *sc, u_int group, struct mbuf *m,
|
||||||
|
uint32_t *phys, uint32_t *handle)
|
||||||
|
{
|
||||||
|
struct mbufx_free *cf;
|
||||||
|
struct mbuf_page *pg;
|
||||||
|
|
||||||
|
if (group == 0) {
|
||||||
|
struct mbuf0_chunk *buf0;
|
||||||
|
|
||||||
|
mtx_lock(&sc->mbuf0_mtx);
|
||||||
|
if ((cf = SLIST_FIRST(&sc->mbuf0_list)) == NULL) {
|
||||||
|
hatm_mbuf_page_alloc(sc, group);
|
||||||
|
if ((cf = SLIST_FIRST(&sc->mbuf0_list)) == NULL) {
|
||||||
|
mtx_unlock(&sc->mbuf0_mtx);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SLIST_REMOVE_HEAD(&sc->mbuf0_list, link);
|
||||||
|
buf0 = (struct mbuf0_chunk *)cf;
|
||||||
|
pg = sc->mbuf_pages[buf0->hdr.pageno];
|
||||||
|
MBUF_SET_BIT(pg->hdr.card, buf0->hdr.chunkno);
|
||||||
|
mtx_unlock(&sc->mbuf0_mtx);
|
||||||
|
|
||||||
|
m_extadd(m, (caddr_t)buf0, MBUF0_SIZE, hatm_mbuf0_free, sc,
|
||||||
|
M_PKTHDR, EXT_NET_DRV);
|
||||||
|
m->m_data += MBUF0_OFFSET;
|
||||||
|
buf0->hdr.mbuf = m;
|
||||||
|
|
||||||
|
*handle = MBUF_MAKE_HANDLE(buf0->hdr.pageno, buf0->hdr.chunkno);
|
||||||
|
|
||||||
|
} else if (group == 1) {
|
||||||
|
struct mbuf1_chunk *buf1;
|
||||||
|
|
||||||
|
mtx_lock(&sc->mbuf1_mtx);
|
||||||
|
if ((cf = SLIST_FIRST(&sc->mbuf1_list)) == NULL) {
|
||||||
|
hatm_mbuf_page_alloc(sc, group);
|
||||||
|
if ((cf = SLIST_FIRST(&sc->mbuf1_list)) == NULL) {
|
||||||
|
mtx_unlock(&sc->mbuf1_mtx);
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SLIST_REMOVE_HEAD(&sc->mbuf1_list, link);
|
||||||
|
buf1 = (struct mbuf1_chunk *)cf;
|
||||||
|
pg = sc->mbuf_pages[buf1->hdr.pageno];
|
||||||
|
MBUF_SET_BIT(pg->hdr.card, buf1->hdr.chunkno);
|
||||||
|
mtx_unlock(&sc->mbuf1_mtx);
|
||||||
|
|
||||||
|
m_extadd(m, (caddr_t)buf1, MBUF1_SIZE, hatm_mbuf1_free, sc,
|
||||||
|
M_PKTHDR, EXT_NET_DRV);
|
||||||
|
m->m_data += MBUF1_OFFSET;
|
||||||
|
buf1->hdr.mbuf = m;
|
||||||
|
|
||||||
|
*handle = MBUF_MAKE_HANDLE(buf1->hdr.pageno, buf1->hdr.chunkno);
|
||||||
|
|
||||||
|
} else
|
||||||
|
return (-1);
|
||||||
|
|
||||||
|
*phys = pg->hdr.phys + (mtod(m, char *) - (char *)pg);
|
||||||
|
bus_dmamap_sync(sc->mbuf_tag, pg->hdr.map, BUS_DMASYNC_PREREAD);
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
hatm_mbuf_helper(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
|
||||||
|
{
|
||||||
|
uint32_t *ptr = (uint32_t *)arg;
|
||||||
|
|
||||||
|
if (nsegs == 0) {
|
||||||
|
printf("%s: error=%d\n", __func__, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
KASSERT(nsegs == 1, ("too many segments for mbuf: %d", nsegs));
|
||||||
|
KASSERT(segs[0].ds_addr <= 0xffffffffLU,
|
||||||
|
("phys addr too large %lx", (u_long)segs[0].ds_addr));
|
||||||
|
|
||||||
|
*ptr = segs[0].ds_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Receive buffer pool interrupt. This means the number of entries in the
|
||||||
|
* queue has dropped below the threshold. Try to supply new buffers.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
he_intr_rbp(struct hatm_softc *sc, struct herbp *rbp, u_int large,
|
||||||
|
u_int group)
|
||||||
|
{
|
||||||
|
u_int ntail, upd;
|
||||||
|
struct mbuf *m;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
DBG(sc, INTR, ("%s buffer supply threshold crossed for group %u",
|
||||||
|
large ? "large" : "small", group));
|
||||||
|
|
||||||
|
rbp->head = (READ4(sc, HE_REGO_RBP_S(large, group)) >> HE_REGS_RBP_HEAD)
|
||||||
|
& (rbp->size - 1);
|
||||||
|
|
||||||
|
upd = 0;
|
||||||
|
for (;;) {
|
||||||
|
if ((ntail = rbp->tail + 1) == rbp->size)
|
||||||
|
ntail = 0;
|
||||||
|
if (ntail == rbp->head)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* allocate the MBUF */
|
||||||
|
if (large) {
|
||||||
|
if ((m = m_getcl(M_DONTWAIT, MT_DATA,
|
||||||
|
M_PKTHDR)) == NULL) {
|
||||||
|
if_printf(&sc->ifatm.ifnet,
|
||||||
|
"no mbuf clusters\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
m->m_data += MBUFL_OFFSET;
|
||||||
|
|
||||||
|
if (sc->lbufs[sc->lbufs_next] != NULL)
|
||||||
|
panic("hatm: lbufs full %u", sc->lbufs_next);
|
||||||
|
sc->lbufs[sc->lbufs_next] = m;
|
||||||
|
|
||||||
|
if ((error = bus_dmamap_load(sc->mbuf_tag,
|
||||||
|
sc->rmaps[sc->lbufs_next],
|
||||||
|
m->m_data, rbp->bsize, hatm_mbuf_helper,
|
||||||
|
&rbp->rbp[rbp->tail].phys, 0)) != NULL)
|
||||||
|
panic("hatm: mbuf mapping failed %d", error);
|
||||||
|
|
||||||
|
bus_dmamap_sync(sc->mbuf_tag,
|
||||||
|
sc->rmaps[sc->lbufs_next],
|
||||||
|
BUS_DMASYNC_PREREAD);
|
||||||
|
|
||||||
|
rbp->rbp[rbp->tail].handle = sc->lbufs_next |
|
||||||
|
MBUF_LARGE_FLAG;
|
||||||
|
|
||||||
|
if (++sc->lbufs_next == sc->lbufs_size)
|
||||||
|
sc->lbufs_next = 0;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
MGETHDR(m, M_DONTWAIT, MT_DATA);
|
||||||
|
if (m == NULL) {
|
||||||
|
if_printf(&sc->ifatm.ifnet, "no mbufs\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (hatm_mbuf_alloc(sc, group, m,
|
||||||
|
&rbp->rbp[rbp->tail].phys,
|
||||||
|
&rbp->rbp[rbp->tail].handle)) {
|
||||||
|
m_freem(m);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DBG(sc, DMA, ("MBUF loaded: handle=%x m=%p phys=%x",
|
||||||
|
rbp->rbp[rbp->tail].handle, m, rbp->rbp[rbp->tail].phys));
|
||||||
|
rbp->rbp[rbp->tail].handle <<= HE_REGS_RBRQ_ADDR;
|
||||||
|
|
||||||
|
rbp->tail = ntail;
|
||||||
|
upd++;
|
||||||
|
}
|
||||||
|
if (upd) {
|
||||||
|
WRITE4(sc, HE_REGO_RBP_T(large, group),
|
||||||
|
(rbp->tail << HE_REGS_RBP_TAIL));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Extract the buffer and hand it to the receive routine
|
||||||
|
*/
|
||||||
|
static struct mbuf *
|
||||||
|
hatm_rx_buffer(struct hatm_softc *sc, u_int group, u_int handle)
|
||||||
|
{
|
||||||
|
u_int pageno;
|
||||||
|
u_int chunkno;
|
||||||
|
struct mbuf *m;
|
||||||
|
|
||||||
|
if (handle & MBUF_LARGE_FLAG) {
|
||||||
|
/* large buffer - sync and unload */
|
||||||
|
handle &= ~MBUF_LARGE_FLAG;
|
||||||
|
DBG(sc, RX, ("RX large handle=%x", handle));
|
||||||
|
|
||||||
|
bus_dmamap_sync(sc->mbuf_tag, sc->rmaps[handle],
|
||||||
|
BUS_DMASYNC_POSTREAD);
|
||||||
|
bus_dmamap_unload(sc->mbuf_tag, sc->rmaps[handle]);
|
||||||
|
|
||||||
|
m = sc->lbufs[handle];
|
||||||
|
sc->lbufs[handle] = NULL;
|
||||||
|
|
||||||
|
return (m);
|
||||||
|
}
|
||||||
|
|
||||||
|
MBUF_PARSE_HANDLE(handle, pageno, chunkno);
|
||||||
|
|
||||||
|
DBG(sc, RX, ("RX group=%u handle=%x page=%u chunk=%u", group, handle,
|
||||||
|
pageno, chunkno));
|
||||||
|
|
||||||
|
if (group == 0) {
|
||||||
|
struct mbuf0_chunk *c0;
|
||||||
|
|
||||||
|
c0 = (struct mbuf0_chunk *)sc->mbuf_pages[pageno] + chunkno;
|
||||||
|
KASSERT(c0->hdr.pageno == pageno, ("pageno = %u/%u",
|
||||||
|
c0->hdr.pageno, pageno));
|
||||||
|
KASSERT(c0->hdr.chunkno == chunkno, ("chunkno = %u/%u",
|
||||||
|
c0->hdr.chunkno, chunkno));
|
||||||
|
|
||||||
|
m = c0->hdr.mbuf;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
struct mbuf1_chunk *c1;
|
||||||
|
|
||||||
|
c1 = (struct mbuf1_chunk *)sc->mbuf_pages[pageno] + chunkno;
|
||||||
|
KASSERT(c1->hdr.pageno == pageno, ("pageno = %u/%u",
|
||||||
|
c1->hdr.pageno, pageno));
|
||||||
|
KASSERT(c1->hdr.chunkno == chunkno, ("chunkno = %u/%u",
|
||||||
|
c1->hdr.chunkno, chunkno));
|
||||||
|
|
||||||
|
m = c1->hdr.mbuf;
|
||||||
|
}
|
||||||
|
MBUF_CLR_BIT(sc->mbuf_pages[pageno]->hdr.card, chunkno);
|
||||||
|
MBUF_SET_BIT(sc->mbuf_pages[pageno]->hdr.used, chunkno);
|
||||||
|
|
||||||
|
bus_dmamap_sync(sc->mbuf_tag, sc->mbuf_pages[pageno]->hdr.map,
|
||||||
|
BUS_DMASYNC_POSTREAD);
|
||||||
|
|
||||||
|
return (m);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Interrupt because of receive buffer returned.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
he_intr_rbrq(struct hatm_softc *sc, struct herbrq *rq, u_int group)
|
||||||
|
{
|
||||||
|
struct he_rbrqen *e;
|
||||||
|
uint32_t flags, tail;
|
||||||
|
u_int cid, len;
|
||||||
|
struct mbuf *m;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
tail = sc->hsp->group[group].rbrq_tail >> 3;
|
||||||
|
|
||||||
|
if (rq->head == tail)
|
||||||
|
break;
|
||||||
|
|
||||||
|
e = &rq->rbrq[rq->head];
|
||||||
|
|
||||||
|
flags = e->addr & HE_REGM_RBRQ_FLAGS;
|
||||||
|
if (!(flags & HE_REGM_RBRQ_HBUF_ERROR))
|
||||||
|
m = hatm_rx_buffer(sc, group,
|
||||||
|
(e->addr & HE_REGM_RBRQ_ADDR) >> HE_REGS_RBRQ_ADDR);
|
||||||
|
else
|
||||||
|
m = NULL;
|
||||||
|
|
||||||
|
cid = (e->len & HE_REGM_RBRQ_CID) >> HE_REGS_RBRQ_CID;
|
||||||
|
len = 4 * (e->len & HE_REGM_RBRQ_LEN);
|
||||||
|
|
||||||
|
hatm_rx(sc, cid, flags, m, len);
|
||||||
|
|
||||||
|
if (++rq->head == rq->size)
|
||||||
|
rq->head = 0;
|
||||||
|
}
|
||||||
|
WRITE4(sc, HE_REGO_RBRQ_H(group), rq->head << 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
hatm_intr(void *p)
|
||||||
|
{
|
||||||
|
struct heirq *q = p;
|
||||||
|
struct hatm_softc *sc = q->sc;
|
||||||
|
u_int status;
|
||||||
|
u_int tail;
|
||||||
|
|
||||||
|
/* if we have a stray interrupt with a non-initialized card,
|
||||||
|
* we cannot even lock before looking at the flag */
|
||||||
|
if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING))
|
||||||
|
return;
|
||||||
|
|
||||||
|
mtx_lock(&sc->mtx);
|
||||||
|
(void)READ4(sc, HE_REGO_INT_FIFO);
|
||||||
|
|
||||||
|
tail = *q->tailp;
|
||||||
|
if (q->head == tail) {
|
||||||
|
/* workaround for tail pointer not updated bug (8.1.1) */
|
||||||
|
DBG(sc, INTR, ("hatm: intr tailq not updated bug triggered"));
|
||||||
|
|
||||||
|
/* read the tail pointer from the card */
|
||||||
|
tail = READ4(sc, HE_REGO_IRQ_BASE(q->group)) &
|
||||||
|
HE_REGM_IRQ_BASE_TAIL;
|
||||||
|
BARRIER_R(sc);
|
||||||
|
|
||||||
|
sc->istats.bug_no_irq_upd++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* clear the interrupt */
|
||||||
|
WRITE4(sc, HE_REGO_INT_FIFO, HE_REGM_INT_FIFO_CLRA);
|
||||||
|
BARRIER_W(sc);
|
||||||
|
|
||||||
|
while (q->head != tail) {
|
||||||
|
status = q->irq[q->head];
|
||||||
|
q->irq[q->head] = HE_REGM_ITYPE_INVALID;
|
||||||
|
if (++q->head == (q->size - 1))
|
||||||
|
q->head = 0;
|
||||||
|
|
||||||
|
switch (status & HE_REGM_ITYPE) {
|
||||||
|
|
||||||
|
case HE_REGM_ITYPE_TBRQ:
|
||||||
|
DBG(sc, INTR, ("TBRQ treshold %u", status & HE_REGM_IGROUP));
|
||||||
|
sc->istats.itype_tbrq++;
|
||||||
|
he_intr_tbrq(sc, &sc->tbrq, status & HE_REGM_IGROUP);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HE_REGM_ITYPE_TPD:
|
||||||
|
DBG(sc, INTR, ("TPD ready %u", status & HE_REGM_IGROUP));
|
||||||
|
sc->istats.itype_tpd++;
|
||||||
|
he_intr_tbrq(sc, &sc->tbrq, status & HE_REGM_IGROUP);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HE_REGM_ITYPE_RBPS:
|
||||||
|
sc->istats.itype_rbps++;
|
||||||
|
switch (status & HE_REGM_IGROUP) {
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
he_intr_rbp(sc, &sc->rbp_s0, 0, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
he_intr_rbp(sc, &sc->rbp_s1, 0, 1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if_printf(&sc->ifatm.ifnet, "bad INTR RBPS%u\n",
|
||||||
|
status & HE_REGM_IGROUP);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HE_REGM_ITYPE_RBPL:
|
||||||
|
sc->istats.itype_rbpl++;
|
||||||
|
switch (status & HE_REGM_IGROUP) {
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
he_intr_rbp(sc, &sc->rbp_l0, 1, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if_printf(&sc->ifatm.ifnet, "bad INTR RBPL%u\n",
|
||||||
|
status & HE_REGM_IGROUP);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HE_REGM_ITYPE_RBRQ:
|
||||||
|
DBG(sc, INTR, ("INTERRUPT RBRQ %u", status & HE_REGM_IGROUP));
|
||||||
|
sc->istats.itype_rbrq++;
|
||||||
|
switch (status & HE_REGM_IGROUP) {
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
he_intr_rbrq(sc, &sc->rbrq_0, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
if (sc->rbrq_1.size > 0) {
|
||||||
|
he_intr_rbrq(sc, &sc->rbrq_1, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* FALLTHRU */
|
||||||
|
|
||||||
|
default:
|
||||||
|
if_printf(&sc->ifatm.ifnet, "bad INTR RBRQ%u\n",
|
||||||
|
status & HE_REGM_IGROUP);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HE_REGM_ITYPE_RBRQT:
|
||||||
|
DBG(sc, INTR, ("INTERRUPT RBRQT %u", status & HE_REGM_IGROUP));
|
||||||
|
sc->istats.itype_rbrqt++;
|
||||||
|
switch (status & HE_REGM_IGROUP) {
|
||||||
|
|
||||||
|
case 0:
|
||||||
|
he_intr_rbrq(sc, &sc->rbrq_0, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
if (sc->rbrq_1.size > 0) {
|
||||||
|
he_intr_rbrq(sc, &sc->rbrq_1, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* FALLTHRU */
|
||||||
|
|
||||||
|
default:
|
||||||
|
if_printf(&sc->ifatm.ifnet, "bad INTR RBRQT%u\n",
|
||||||
|
status & HE_REGM_IGROUP);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HE_REGM_ITYPE_PHYS:
|
||||||
|
sc->istats.itype_phys++;
|
||||||
|
utopia_intr(&sc->utopia);
|
||||||
|
break;
|
||||||
|
|
||||||
|
#if HE_REGM_ITYPE_UNKNOWN != HE_REGM_ITYPE_INVALID
|
||||||
|
case HE_REGM_ITYPE_UNKNOWN:
|
||||||
|
sc->istats.itype_unknown++;
|
||||||
|
if_printf(&sc->ifatm.ifnet, "bad interrupt\n");
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
case HE_REGM_ITYPE_ERR:
|
||||||
|
sc->istats.itype_err++;
|
||||||
|
switch (status) {
|
||||||
|
|
||||||
|
case HE_REGM_ITYPE_PERR:
|
||||||
|
if_printf(&sc->ifatm.ifnet, "parity error\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HE_REGM_ITYPE_ABORT:
|
||||||
|
if_printf(&sc->ifatm.ifnet, "abort interrupt "
|
||||||
|
"addr=0x%08x\n",
|
||||||
|
READ4(sc, HE_REGO_ABORT_ADDR));
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if_printf(&sc->ifatm.ifnet,
|
||||||
|
"bad interrupt type %08x\n", status);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HE_REGM_ITYPE_INVALID:
|
||||||
|
/* this is the documented fix for the ISW bug 8.1.1
|
||||||
|
* Note, that the documented fix is partly wrong:
|
||||||
|
* the ISWs should be intialized to 0xf8 not 0xff */
|
||||||
|
sc->istats.bug_bad_isw++;
|
||||||
|
DBG(sc, INTR, ("hatm: invalid ISW bug triggered"));
|
||||||
|
he_intr_tbrq(sc, &sc->tbrq, 0);
|
||||||
|
he_intr_rbp(sc, &sc->rbp_s0, 0, 0);
|
||||||
|
he_intr_rbp(sc, &sc->rbp_l0, 1, 0);
|
||||||
|
he_intr_rbp(sc, &sc->rbp_s1, 0, 1);
|
||||||
|
he_intr_rbrq(sc, &sc->rbrq_0, 0);
|
||||||
|
he_intr_rbrq(sc, &sc->rbrq_1, 1);
|
||||||
|
utopia_intr(&sc->utopia);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if_printf(&sc->ifatm.ifnet, "bad interrupt type %08x\n",
|
||||||
|
status);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* write back head to clear queue */
|
||||||
|
WRITE4(sc, HE_REGO_IRQ_HEAD(0),
|
||||||
|
((q->size - 1) << HE_REGS_IRQ_HEAD_SIZE) |
|
||||||
|
(q->thresh << HE_REGS_IRQ_HEAD_THRESH) |
|
||||||
|
(q->head << HE_REGS_IRQ_HEAD_HEAD));
|
||||||
|
BARRIER_W(sc);
|
||||||
|
|
||||||
|
/* workaround the back-to-back irq access problem (8.1.2) */
|
||||||
|
(void)READ4(sc, HE_REGO_INT_FIFO);
|
||||||
|
BARRIER_R(sc);
|
||||||
|
|
||||||
|
mtx_unlock(&sc->mtx);
|
||||||
|
}
|
486
sys/dev/hatm/if_hatm_ioctl.c
Normal file
486
sys/dev/hatm/if_hatm_ioctl.c
Normal file
@ -0,0 +1,486 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2001-2003
|
||||||
|
* Fraunhofer Institute for Open Communication Systems (FhG Fokus).
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* Author: Hartmut Brandt <harti@freebsd.org>
|
||||||
|
*
|
||||||
|
* $FreeBSD$
|
||||||
|
*
|
||||||
|
* ForeHE driver.
|
||||||
|
*
|
||||||
|
* Ioctl handler.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "opt_inet.h"
|
||||||
|
#include "opt_natm.h"
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/systm.h>
|
||||||
|
#include <sys/malloc.h>
|
||||||
|
#include <sys/kernel.h>
|
||||||
|
#include <sys/bus.h>
|
||||||
|
#include <sys/errno.h>
|
||||||
|
#include <sys/conf.h>
|
||||||
|
#include <sys/module.h>
|
||||||
|
#include <sys/queue.h>
|
||||||
|
#include <sys/syslog.h>
|
||||||
|
#include <sys/condvar.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#include <vm/uma.h>
|
||||||
|
|
||||||
|
#include <sys/sockio.h>
|
||||||
|
#include <sys/mbuf.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <net/if_media.h>
|
||||||
|
#include <net/if_atm.h>
|
||||||
|
#include <net/route.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/if_atm.h>
|
||||||
|
|
||||||
|
#include <machine/bus.h>
|
||||||
|
#include <machine/resource.h>
|
||||||
|
#include <sys/bus.h>
|
||||||
|
#include <sys/rman.h>
|
||||||
|
#include <pci/pcireg.h>
|
||||||
|
#include <pci/pcivar.h>
|
||||||
|
|
||||||
|
#include <dev/utopia/utopia.h>
|
||||||
|
#include <dev/hatm/if_hatmconf.h>
|
||||||
|
#include <dev/hatm/if_hatmreg.h>
|
||||||
|
#include <dev/hatm/if_hatmvar.h>
|
||||||
|
|
||||||
|
static u_int hatm_natm_traffic = ATMIO_TRAFFIC_UBR;
|
||||||
|
static u_int hatm_natm_pcr = 0;
|
||||||
|
|
||||||
|
static int hatm_sysctl_natm_traffic(SYSCTL_HANDLER_ARGS);
|
||||||
|
|
||||||
|
SYSCTL_DECL(_hw_atm);
|
||||||
|
|
||||||
|
SYSCTL_PROC(_hw_atm, OID_AUTO, natm_traffic, CTLTYPE_UINT | CTLFLAG_RW,
|
||||||
|
&hatm_natm_traffic, sizeof(hatm_natm_traffic), hatm_sysctl_natm_traffic,
|
||||||
|
"IU", "traffic type for NATM connections");
|
||||||
|
SYSCTL_UINT(_hw_atm, OID_AUTO, natm_pcr, CTLFLAG_RW,
|
||||||
|
&hatm_natm_pcr, 0, "PCR for NATM connections");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return a table of VCCs in a freshly allocated memory area.
|
||||||
|
* Here we have a problem: we first count, how many vccs we need
|
||||||
|
* to return. The we allocate the memory and finally fill it in.
|
||||||
|
* Because we cannot lock while calling malloc, the number of active
|
||||||
|
* vccs may change while we're in malloc. So we allocate a couple of
|
||||||
|
* vccs more and if space anyway is not enough re-iterate.
|
||||||
|
*/
|
||||||
|
static struct atmio_vcctable *
|
||||||
|
hatm_getvccs(struct hatm_softc *sc)
|
||||||
|
{
|
||||||
|
u_int cid, alloc;
|
||||||
|
size_t len;
|
||||||
|
struct atmio_vcctable *vccs;
|
||||||
|
struct atmio_vcc *v;
|
||||||
|
|
||||||
|
alloc = sc->open_vccs + 10;
|
||||||
|
vccs = NULL;
|
||||||
|
|
||||||
|
again:
|
||||||
|
len = sizeof(*vccs) + alloc * sizeof(vccs->vccs[0]);
|
||||||
|
vccs = reallocf(vccs, len, M_DEVBUF, M_WAITOK);
|
||||||
|
bzero(vccs, len);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fill in
|
||||||
|
*/
|
||||||
|
vccs->count = 0;
|
||||||
|
v = vccs->vccs;
|
||||||
|
|
||||||
|
mtx_lock(&sc->mtx);
|
||||||
|
for (cid = 0; cid < HE_MAX_VCCS; cid++)
|
||||||
|
if (sc->vccs[cid] != NULL &&
|
||||||
|
(sc->vccs[cid]->vflags & (HE_VCC_RX_OPEN |
|
||||||
|
HE_VCC_TX_OPEN))) {
|
||||||
|
if (++vccs->count == alloc) {
|
||||||
|
/*
|
||||||
|
* too many - try again
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*v++ = sc->vccs[cid]->param;
|
||||||
|
}
|
||||||
|
mtx_unlock(&sc->mtx);
|
||||||
|
|
||||||
|
if (cid == HE_MAX_VCCS)
|
||||||
|
return (vccs);
|
||||||
|
|
||||||
|
alloc *= 2;
|
||||||
|
goto again;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try to open the given VCC.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
hatm_open_vcc(struct hatm_softc *sc, struct atmio_openvcc *arg)
|
||||||
|
{
|
||||||
|
u_int cid;
|
||||||
|
struct hevcc *vcc;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
DBG(sc, VCC, ("Open VCC: %u.%u flags=%#x", arg->param.vpi,
|
||||||
|
arg->param.vci, arg->param.flags));
|
||||||
|
|
||||||
|
if ((arg->param.vpi & ~HE_VPI_MASK) ||
|
||||||
|
(arg->param.vci & ~HE_VCI_MASK) ||
|
||||||
|
(arg->param.vci == 0))
|
||||||
|
return (EINVAL);
|
||||||
|
cid = HE_CID(arg->param.vpi, arg->param.vci);
|
||||||
|
|
||||||
|
if ((arg->param.flags & ATMIO_FLAG_NOTX) &&
|
||||||
|
(arg->param.flags & ATMIO_FLAG_NORX))
|
||||||
|
return (EINVAL);
|
||||||
|
|
||||||
|
vcc = uma_zalloc(sc->vcc_zone, M_NOWAIT | M_ZERO);
|
||||||
|
if (vcc == NULL)
|
||||||
|
return (ENOMEM);
|
||||||
|
|
||||||
|
mtx_lock(&sc->mtx);
|
||||||
|
if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) {
|
||||||
|
error = EIO;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
if (sc->vccs[cid] != NULL) {
|
||||||
|
error = EBUSY;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
vcc->param = arg->param;
|
||||||
|
vcc->rxhand = arg->rxhand;
|
||||||
|
switch (vcc->param.aal) {
|
||||||
|
|
||||||
|
case ATMIO_AAL_0:
|
||||||
|
case ATMIO_AAL_5:
|
||||||
|
case ATMIO_AAL_RAW:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error = EINVAL;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
switch (vcc->param.traffic) {
|
||||||
|
|
||||||
|
case ATMIO_TRAFFIC_UBR:
|
||||||
|
case ATMIO_TRAFFIC_CBR:
|
||||||
|
case ATMIO_TRAFFIC_ABR:
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error = EINVAL;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
vcc->ntpds = 0;
|
||||||
|
vcc->chain = vcc->last = NULL;
|
||||||
|
vcc->ibytes = vcc->ipackets = 0;
|
||||||
|
vcc->obytes = vcc->opackets = 0;
|
||||||
|
|
||||||
|
if (!(vcc->param.flags & ATMIO_FLAG_NOTX) &&
|
||||||
|
(error = hatm_tx_vcc_can_open(sc, cid, vcc)) != 0)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
/* ok - go ahead */
|
||||||
|
sc->vccs[cid] = vcc;
|
||||||
|
|
||||||
|
if (!(vcc->param.flags & ATMIO_FLAG_NOTX))
|
||||||
|
hatm_tx_vcc_open(sc, cid);
|
||||||
|
if (!(vcc->param.flags & ATMIO_FLAG_NORX))
|
||||||
|
hatm_rx_vcc_open(sc, cid);
|
||||||
|
|
||||||
|
#ifdef notyet
|
||||||
|
/* inform management about non-NG and NG-PVCs */
|
||||||
|
if (!(vcc->param.flags & ATMIO_FLAG_NG) ||
|
||||||
|
(vcc->param.flags & ATMIO_FLAG_PVC))
|
||||||
|
atm_message(&sc->ifatm.ifnet, ATM_MSG_VCC_CHANGED,
|
||||||
|
(1 << 24) | (arg->vpi << 16) | arg->vci);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* don't free below */
|
||||||
|
vcc = NULL;
|
||||||
|
|
||||||
|
sc->open_vccs++;
|
||||||
|
|
||||||
|
done:
|
||||||
|
mtx_unlock(&sc->mtx);
|
||||||
|
if (vcc != NULL)
|
||||||
|
uma_zfree(sc->vcc_zone, vcc);
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Enable ioctl for NATM. Map to an open ioctl.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
hatm_open_vcc1(struct hatm_softc *sc, struct atm_pseudoioctl *ph)
|
||||||
|
{
|
||||||
|
struct atmio_openvcc *v;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
if ((v = malloc(sizeof(*v), M_TEMP, M_NOWAIT | M_ZERO)) == NULL)
|
||||||
|
return (ENOMEM);
|
||||||
|
|
||||||
|
v->param.flags = ATM_PH_FLAGS(&ph->aph) &
|
||||||
|
(ATM_PH_AAL5 | ATM_PH_LLCSNAP);
|
||||||
|
v->param.vpi = ATM_PH_VPI(&ph->aph);
|
||||||
|
v->param.vci = ATM_PH_VCI(&ph->aph);
|
||||||
|
v->param.aal = (ATM_PH_FLAGS(&ph->aph) & ATM_PH_AAL5)
|
||||||
|
? ATMIO_AAL_5 : ATMIO_AAL_0;
|
||||||
|
v->param.traffic = hatm_natm_traffic;
|
||||||
|
v->rxhand = ph->rxhand;
|
||||||
|
if ((v->param.tparam.pcr = hatm_natm_pcr) == 0 ||
|
||||||
|
hatm_natm_pcr > sc->ifatm.mib.pcr)
|
||||||
|
v->param.tparam.pcr = sc->ifatm.mib.pcr;
|
||||||
|
v->param.tparam.mcr = 0;
|
||||||
|
|
||||||
|
error = hatm_open_vcc(sc, v);
|
||||||
|
if (error == 0)
|
||||||
|
sc->vccs[HE_CID(v->param.vpi, v->param.vci)]->vflags |=
|
||||||
|
HE_VCC_ASYNC;
|
||||||
|
|
||||||
|
free(v, M_TEMP);
|
||||||
|
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* VCC has been finally closed.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
hatm_vcc_closed(struct hatm_softc *sc, u_int cid)
|
||||||
|
{
|
||||||
|
struct hevcc *vcc = sc->vccs[cid];
|
||||||
|
|
||||||
|
#ifdef notyet
|
||||||
|
/* inform management about non-NG and NG-PVCs */
|
||||||
|
if (!(vcc->param.flags & ATMIO_FLAG_NG) ||
|
||||||
|
(vcc->param.flags & ATMIO_FLAG_PVC))
|
||||||
|
atm_message(&sc->ifatm.ifnet, ATM_MSG_VCC_CHANGED,
|
||||||
|
(0 << 24) | (HE_VPI(cid) << 16) | HE_VCI(cid));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
sc->open_vccs--;
|
||||||
|
uma_zfree(sc->vcc_zone, vcc);
|
||||||
|
sc->vccs[cid] = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try to close the given VCC
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
hatm_close_vcc(struct hatm_softc *sc, struct atmio_closevcc *arg)
|
||||||
|
{
|
||||||
|
u_int cid;
|
||||||
|
struct hevcc *vcc;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
DBG(sc, VCC, ("Close VCC: %u.%u", arg->vpi, arg->vci));
|
||||||
|
|
||||||
|
if((arg->vpi & ~HE_VPI_MASK) ||
|
||||||
|
(arg->vci & ~HE_VCI_MASK) ||
|
||||||
|
(arg->vci == 0))
|
||||||
|
return (EINVAL);
|
||||||
|
cid = HE_CID(arg->vpi, arg->vci);
|
||||||
|
|
||||||
|
mtx_lock(&sc->mtx);
|
||||||
|
vcc = sc->vccs[cid];
|
||||||
|
if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) {
|
||||||
|
error = EIO;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vcc == NULL || !(vcc->vflags & HE_VCC_OPEN)) {
|
||||||
|
error = ENOENT;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vcc->vflags & HE_VCC_TX_OPEN)
|
||||||
|
hatm_tx_vcc_close(sc, cid);
|
||||||
|
if (vcc->vflags & HE_VCC_RX_OPEN)
|
||||||
|
hatm_rx_vcc_close(sc, cid);
|
||||||
|
|
||||||
|
if (vcc->vflags & HE_VCC_ASYNC)
|
||||||
|
goto done;
|
||||||
|
|
||||||
|
while ((sc->ifatm.ifnet.if_flags & IFF_RUNNING) &&
|
||||||
|
(vcc->vflags & (HE_VCC_TX_CLOSING | HE_VCC_RX_CLOSING)))
|
||||||
|
cv_wait(&sc->vcc_cv, &sc->mtx);
|
||||||
|
|
||||||
|
if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING)) {
|
||||||
|
error = EIO;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(vcc->vflags & ATMIO_FLAG_NOTX))
|
||||||
|
hatm_tx_vcc_closed(sc, cid);
|
||||||
|
|
||||||
|
hatm_vcc_closed(sc, cid);
|
||||||
|
|
||||||
|
done:
|
||||||
|
mtx_unlock(&sc->mtx);
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
hatm_close_vcc1(struct hatm_softc *sc, struct atm_pseudoioctl *ph)
|
||||||
|
{
|
||||||
|
struct atmio_closevcc v;
|
||||||
|
|
||||||
|
v.vpi = ATM_PH_VPI(&ph->aph);
|
||||||
|
v.vci = ATM_PH_VCI(&ph->aph);
|
||||||
|
|
||||||
|
return (hatm_close_vcc(sc, &v));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* IOCTL handler
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
hatm_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
|
||||||
|
{
|
||||||
|
struct ifreq *ifr = (struct ifreq *)data;
|
||||||
|
struct ifaddr *ifa = (struct ifaddr *)data;
|
||||||
|
struct hatm_softc *sc = (struct hatm_softc *)ifp->if_softc;
|
||||||
|
struct atmio_vcctable *vtab;
|
||||||
|
int error = 0;
|
||||||
|
|
||||||
|
switch (cmd) {
|
||||||
|
|
||||||
|
case SIOCSIFADDR:
|
||||||
|
mtx_lock(&sc->mtx);
|
||||||
|
ifp->if_flags |= IFF_UP;
|
||||||
|
if (!(ifp->if_flags & IFF_RUNNING))
|
||||||
|
hatm_initialize(sc);
|
||||||
|
switch (ifa->ifa_addr->sa_family) {
|
||||||
|
|
||||||
|
#ifdef INET
|
||||||
|
case AF_INET:
|
||||||
|
case AF_INET6:
|
||||||
|
ifa->ifa_rtrequest = atm_rtrequest;
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
mtx_unlock(&sc->mtx);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SIOCSIFFLAGS:
|
||||||
|
mtx_lock(&sc->mtx);
|
||||||
|
if (ifp->if_flags & IFF_UP) {
|
||||||
|
if (!(ifp->if_flags & IFF_RUNNING)) {
|
||||||
|
hatm_initialize(sc);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ifp->if_flags & IFF_RUNNING) {
|
||||||
|
hatm_stop(sc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mtx_unlock(&sc->mtx);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SIOCGIFMEDIA:
|
||||||
|
case SIOCSIFMEDIA:
|
||||||
|
error = ifmedia_ioctl(ifp, ifr, &sc->media, cmd);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SIOCSIFMTU:
|
||||||
|
/*
|
||||||
|
* Set the interface MTU.
|
||||||
|
*/
|
||||||
|
if (ifr->ifr_mtu > ATMMTU)
|
||||||
|
error = EINVAL;
|
||||||
|
else
|
||||||
|
ifp->if_mtu = ifr->ifr_mtu;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SIOCATMGVCCS:
|
||||||
|
/* return vcc table */
|
||||||
|
vtab = hatm_getvccs(sc);
|
||||||
|
if (vtab == NULL) {
|
||||||
|
error = ENOMEM;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
error = copyout(vtab, ifr->ifr_data, sizeof(*vtab) +
|
||||||
|
vtab->count * sizeof(vtab->vccs[0]));
|
||||||
|
free(vtab, M_DEVBUF);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SIOCATMENA: /* NATM internal use */
|
||||||
|
error = hatm_open_vcc1(sc, (struct atm_pseudoioctl *)data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SIOCATMDIS: /* NATM internal use */
|
||||||
|
error = hatm_close_vcc1(sc, (struct atm_pseudoioctl *)data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SIOCATMGETVCCS: /* netgraph internal use */
|
||||||
|
if ((vtab = hatm_getvccs(sc)) == NULL) {
|
||||||
|
error = ENOMEM;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*(void **)data = vtab;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SIOCATMOPENVCC: /* netgraph/harp internal use */
|
||||||
|
error = hatm_open_vcc(sc, (struct atmio_openvcc *)data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SIOCATMCLOSEVCC: /* netgraph and HARP internal use */
|
||||||
|
error = hatm_close_vcc(sc, (struct atmio_closevcc *)data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
DBG(sc, IOCTL, ("cmd=%08lx arg=%p", cmd, data));
|
||||||
|
error = EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
hatm_sysctl_natm_traffic(SYSCTL_HANDLER_ARGS)
|
||||||
|
{
|
||||||
|
int error;
|
||||||
|
int tmp;
|
||||||
|
|
||||||
|
tmp = hatm_natm_traffic;
|
||||||
|
error = sysctl_handle_int(oidp, &tmp, 0, req);
|
||||||
|
if (error != 0 || req->newptr == NULL)
|
||||||
|
return (error);
|
||||||
|
|
||||||
|
if (tmp != ATMIO_TRAFFIC_UBR && tmp != ATMIO_TRAFFIC_CBR)
|
||||||
|
return (EINVAL);
|
||||||
|
|
||||||
|
hatm_natm_traffic = tmp;
|
||||||
|
return (0);
|
||||||
|
}
|
324
sys/dev/hatm/if_hatm_rx.c
Normal file
324
sys/dev/hatm/if_hatm_rx.c
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2001-2003
|
||||||
|
* Fraunhofer Institute for Open Communication Systems (FhG Fokus).
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* Author: Hartmut Brandt <harti@freebsd.org>
|
||||||
|
*
|
||||||
|
* $FreeBSD$
|
||||||
|
*
|
||||||
|
* ForeHE driver.
|
||||||
|
*
|
||||||
|
* Receive.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "opt_inet.h"
|
||||||
|
#include "opt_natm.h"
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/systm.h>
|
||||||
|
#include <sys/kernel.h>
|
||||||
|
#include <sys/bus.h>
|
||||||
|
#include <sys/errno.h>
|
||||||
|
#include <sys/conf.h>
|
||||||
|
#include <sys/module.h>
|
||||||
|
#include <sys/queue.h>
|
||||||
|
#include <sys/syslog.h>
|
||||||
|
#include <sys/condvar.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#include <vm/uma.h>
|
||||||
|
|
||||||
|
#include <sys/sockio.h>
|
||||||
|
#include <sys/mbuf.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <net/if_media.h>
|
||||||
|
#include <net/if_atm.h>
|
||||||
|
#include <net/route.h>
|
||||||
|
#ifdef ENABLE_BPF
|
||||||
|
#include <net/bpf.h>
|
||||||
|
#endif
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/if_atm.h>
|
||||||
|
|
||||||
|
#include <machine/bus.h>
|
||||||
|
#include <machine/resource.h>
|
||||||
|
#include <sys/bus.h>
|
||||||
|
#include <sys/rman.h>
|
||||||
|
#include <pci/pcireg.h>
|
||||||
|
#include <pci/pcivar.h>
|
||||||
|
|
||||||
|
#include <dev/utopia/utopia.h>
|
||||||
|
#include <dev/hatm/if_hatmconf.h>
|
||||||
|
#include <dev/hatm/if_hatmreg.h>
|
||||||
|
#include <dev/hatm/if_hatmvar.h>
|
||||||
|
|
||||||
|
void
|
||||||
|
hatm_rx(struct hatm_softc *sc, u_int cid, u_int flags, struct mbuf *m0,
|
||||||
|
u_int len)
|
||||||
|
{
|
||||||
|
struct hevcc *vcc;
|
||||||
|
struct atm_pseudohdr aph;
|
||||||
|
struct mbuf *m, *m1;
|
||||||
|
u_int vpi, vci;
|
||||||
|
u_char *ptr;
|
||||||
|
|
||||||
|
DBG(sc, RX, ("cid=%#x flags=%#x len=%u mbuf=%p", cid, flags, len, m0));
|
||||||
|
|
||||||
|
vcc = sc->vccs[cid];
|
||||||
|
if (vcc == NULL)
|
||||||
|
goto drop;
|
||||||
|
|
||||||
|
if (flags & HE_REGM_RBRQ_CON_CLOSED) {
|
||||||
|
if (vcc->vflags & HE_VCC_RX_CLOSING) {
|
||||||
|
vcc->vflags &= ~HE_VCC_RX_CLOSING;
|
||||||
|
if (vcc->vflags & HE_VCC_ASYNC) {
|
||||||
|
if (!(vcc->vflags & HE_VCC_OPEN))
|
||||||
|
hatm_vcc_closed(sc, cid);
|
||||||
|
} else
|
||||||
|
cv_signal(&sc->vcc_cv);
|
||||||
|
}
|
||||||
|
goto drop;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(vcc->vflags & HE_VCC_RX_OPEN))
|
||||||
|
goto drop;
|
||||||
|
|
||||||
|
if (flags & HE_REGM_RBRQ_HBUF_ERROR) {
|
||||||
|
sc->istats.hbuf_error++;
|
||||||
|
if (vcc->chain != NULL) {
|
||||||
|
m_freem(vcc->chain);
|
||||||
|
vcc->chain = vcc->last = NULL;
|
||||||
|
}
|
||||||
|
goto drop;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((m0->m_len = len) == 0) {
|
||||||
|
sc->istats.empty_hbuf++;
|
||||||
|
m_free(m0);
|
||||||
|
|
||||||
|
} else if (vcc->chain == NULL) {
|
||||||
|
sc->istats.rx_seg++;
|
||||||
|
vcc->chain = vcc->last = m0;
|
||||||
|
vcc->last->m_next = NULL;
|
||||||
|
vcc->chain->m_pkthdr.len = m0->m_len;
|
||||||
|
vcc->chain->m_pkthdr.rcvif = &sc->ifatm.ifnet;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
sc->istats.rx_seg++;
|
||||||
|
vcc->last->m_next = m0;
|
||||||
|
vcc->last = m0;
|
||||||
|
vcc->last->m_next = NULL;
|
||||||
|
vcc->chain->m_pkthdr.len += m0->m_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(flags & HE_REGM_RBRQ_END_PDU))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (flags & HE_REGM_RBRQ_CRC_ERROR) {
|
||||||
|
if (vcc->chain)
|
||||||
|
m_freem(vcc->chain);
|
||||||
|
vcc->chain = vcc->last = NULL;
|
||||||
|
sc->istats.crc_error++;
|
||||||
|
sc->ifatm.ifnet.if_ierrors++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (flags & HE_REGM_RBRQ_LEN_ERROR) {
|
||||||
|
if (vcc->chain)
|
||||||
|
m_freem(vcc->chain);
|
||||||
|
vcc->chain = vcc->last = NULL;
|
||||||
|
sc->istats.len_error++;
|
||||||
|
sc->ifatm.ifnet.if_ierrors++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
{
|
||||||
|
struct mbuf *tmp;
|
||||||
|
|
||||||
|
for (tmp = vcc->chain; tmp != NULL; tmp = tmp->m_next) {
|
||||||
|
printf("mbuf %p: len=%u\n", tmp, tmp->m_len);
|
||||||
|
for (ptr = mtod(tmp, u_char *);
|
||||||
|
ptr < mtod(tmp, u_char *) + tmp->m_len; ptr++)
|
||||||
|
printf("%02x ", *ptr);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (vcc->param.aal == ATMIO_AAL_5) {
|
||||||
|
/*
|
||||||
|
* Need to remove padding and the trailer. The trailer
|
||||||
|
* may be split accross buffers according to 2.10.1.2
|
||||||
|
* Assume that mbufs sizes are even (buffer sizes and cell
|
||||||
|
* payload sizes are) and that there are no empty mbufs.
|
||||||
|
*/
|
||||||
|
m = vcc->last;
|
||||||
|
if (m->m_len == 2) {
|
||||||
|
/* Ah, oh, only part of CRC */
|
||||||
|
if (m == vcc->chain) {
|
||||||
|
/* ups */
|
||||||
|
sc->istats.short_aal5++;
|
||||||
|
m_freem(vcc->chain);
|
||||||
|
vcc->chain = vcc->last = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (m1 = vcc->chain; m1->m_next != m; m1 = m1->m_next)
|
||||||
|
;
|
||||||
|
ptr = (u_char *)m1->m_data + m1->m_len - 4;
|
||||||
|
|
||||||
|
} else if (m->m_len == 4) {
|
||||||
|
/* Ah, oh, only CRC */
|
||||||
|
if (m == vcc->chain) {
|
||||||
|
/* ups */
|
||||||
|
sc->istats.short_aal5++;
|
||||||
|
m_freem(vcc->chain);
|
||||||
|
vcc->chain = vcc->last = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (m1 = vcc->chain; m1->m_next != m; m1 = m1->m_next)
|
||||||
|
;
|
||||||
|
ptr = (u_char *)m1->m_data + m1->m_len - 2;
|
||||||
|
|
||||||
|
} else if (m->m_len >= 6) {
|
||||||
|
ptr = (u_char *)m->m_data + m->m_len - 6;
|
||||||
|
} else
|
||||||
|
panic("hatm_rx: bad mbuf len %d", m->m_len);
|
||||||
|
|
||||||
|
len = (ptr[0] << 8) + ptr[1];
|
||||||
|
if (len > (u_int)vcc->chain->m_pkthdr.len - 4) {
|
||||||
|
sc->istats.badlen_aal5++;
|
||||||
|
m_freem(vcc->chain);
|
||||||
|
vcc->chain = vcc->last = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
m_adj(vcc->chain, -(vcc->chain->m_pkthdr.len - len));
|
||||||
|
}
|
||||||
|
m = vcc->chain;
|
||||||
|
vcc->chain = vcc->last = NULL;
|
||||||
|
|
||||||
|
#ifdef ENABLE_BPF
|
||||||
|
if (!(vcc->param.flags & ATMIO_FLAG_NG) &&
|
||||||
|
(vcc->param.flags & ATM_PH_AAL5) &&
|
||||||
|
(vcc->param.flags & ATM_PH_LLCSNAP))
|
||||||
|
BPF_MTAP(&sc->ifatm.ifnet, m);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
vpi = HE_VPI(cid);
|
||||||
|
vci = HE_VCI(cid);
|
||||||
|
|
||||||
|
ATM_PH_FLAGS(&aph) = vcc->param.flags & 0xff;
|
||||||
|
ATM_PH_VPI(&aph) = vpi;
|
||||||
|
ATM_PH_SETVCI(&aph, vci);
|
||||||
|
|
||||||
|
sc->ifatm.ifnet.if_ipackets++;
|
||||||
|
/* this is in if_atmsubr.c */
|
||||||
|
/* sc->ifatm.ifnet.if_ibytes += len; */
|
||||||
|
|
||||||
|
vcc->ibytes += len;
|
||||||
|
vcc->ipackets++;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
{
|
||||||
|
struct mbuf *tmp;
|
||||||
|
|
||||||
|
for (tmp = m; tmp != NULL; tmp = tmp->m_next) {
|
||||||
|
printf("mbuf %p: len=%u\n", tmp, tmp->m_len);
|
||||||
|
for (ptr = mtod(tmp, u_char *);
|
||||||
|
ptr < mtod(tmp, u_char *) + tmp->m_len; ptr++)
|
||||||
|
printf("%02x ", *ptr);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
atm_input(&sc->ifatm.ifnet, &aph, m, vcc->rxhand);
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
drop:
|
||||||
|
if (m0 != NULL)
|
||||||
|
m_free(m0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
hatm_rx_vcc_open(struct hatm_softc *sc, u_int cid)
|
||||||
|
{
|
||||||
|
struct hevcc *vcc = sc->vccs[cid];
|
||||||
|
uint32_t rsr0, rsr1, rsr4;
|
||||||
|
|
||||||
|
rsr0 = rsr1 = rsr4 = 0;
|
||||||
|
|
||||||
|
if (vcc->param.traffic == ATMIO_TRAFFIC_ABR) {
|
||||||
|
rsr1 |= HE_REGM_RSR1_AQI;
|
||||||
|
rsr4 |= HE_REGM_RSR4_AQI;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (vcc->param.aal == ATMIO_AAL_5) {
|
||||||
|
rsr0 |= HE_REGM_RSR0_STARTPDU | HE_REGM_RSR0_AAL_5;
|
||||||
|
} else if (vcc->param.aal == ATMIO_AAL_0) {
|
||||||
|
rsr0 |= HE_REGM_RSR0_AAL_0;
|
||||||
|
} else {
|
||||||
|
if (sc->rbp_s1.size != 0) {
|
||||||
|
rsr1 |= (1 << HE_REGS_RSR1_GROUP);
|
||||||
|
rsr4 |= (1 << HE_REGS_RSR4_GROUP);
|
||||||
|
}
|
||||||
|
rsr0 |= HE_REGM_RSR0_AAL_RAW;
|
||||||
|
}
|
||||||
|
rsr0 |= HE_REGM_RSR0_OPEN;
|
||||||
|
|
||||||
|
WRITE_RSR(sc, cid, 0, 0xf, rsr0);
|
||||||
|
WRITE_RSR(sc, cid, 1, 0xf, rsr1);
|
||||||
|
WRITE_RSR(sc, cid, 4, 0xf, rsr4);
|
||||||
|
|
||||||
|
vcc->vflags |= HE_VCC_RX_OPEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Close the RX side of a VCC.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
hatm_rx_vcc_close(struct hatm_softc *sc, u_int cid)
|
||||||
|
{
|
||||||
|
struct hevcc *vcc = sc->vccs[cid];
|
||||||
|
uint32_t v;
|
||||||
|
|
||||||
|
vcc->vflags |= HE_VCC_RX_CLOSING;
|
||||||
|
WRITE_RSR(sc, cid, 0, 0xf, 0);
|
||||||
|
|
||||||
|
v = READ4(sc, HE_REGO_RCCSTAT);
|
||||||
|
while ((sc->ifatm.ifnet.if_flags & IFF_RUNNING) &&
|
||||||
|
(READ4(sc, HE_REGO_RCCSTAT) & HE_REGM_RCCSTAT_PROG))
|
||||||
|
cv_timedwait(&sc->cv_rcclose, &sc->mtx, 1);
|
||||||
|
|
||||||
|
if (!(sc->ifatm.ifnet.if_flags & IFF_RUNNING))
|
||||||
|
return;
|
||||||
|
|
||||||
|
WRITE_MBOX4(sc, HE_REGO_RCON_CLOSE, cid);
|
||||||
|
|
||||||
|
vcc->vflags |= HE_VCC_RX_CLOSING;
|
||||||
|
vcc->vflags &= ~HE_VCC_RX_OPEN;
|
||||||
|
}
|
780
sys/dev/hatm/if_hatm_tx.c
Normal file
780
sys/dev/hatm/if_hatm_tx.c
Normal file
@ -0,0 +1,780 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2001-2003
|
||||||
|
* Fraunhofer Institute for Open Communication Systems (FhG Fokus).
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* Author: Hartmut Brandt <harti@freebsd.org>
|
||||||
|
*
|
||||||
|
* $FreeBSD$
|
||||||
|
*
|
||||||
|
* ForeHE driver.
|
||||||
|
*
|
||||||
|
* Transmission.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "opt_inet.h"
|
||||||
|
#include "opt_natm.h"
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <sys/systm.h>
|
||||||
|
#include <sys/kernel.h>
|
||||||
|
#include <sys/malloc.h>
|
||||||
|
#include <sys/bus.h>
|
||||||
|
#include <sys/errno.h>
|
||||||
|
#include <sys/conf.h>
|
||||||
|
#include <sys/module.h>
|
||||||
|
#include <sys/queue.h>
|
||||||
|
#include <sys/syslog.h>
|
||||||
|
#include <sys/condvar.h>
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#include <vm/uma.h>
|
||||||
|
|
||||||
|
#include <sys/sockio.h>
|
||||||
|
#include <sys/mbuf.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
#include <net/if.h>
|
||||||
|
#include <net/if_media.h>
|
||||||
|
#include <net/if_atm.h>
|
||||||
|
#include <net/route.h>
|
||||||
|
#ifdef ENABLE_BPF
|
||||||
|
#include <net/bpf.h>
|
||||||
|
#endif
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/if_atm.h>
|
||||||
|
|
||||||
|
#include <machine/bus.h>
|
||||||
|
#include <machine/resource.h>
|
||||||
|
#include <sys/bus.h>
|
||||||
|
#include <sys/rman.h>
|
||||||
|
#include <pci/pcireg.h>
|
||||||
|
#include <pci/pcivar.h>
|
||||||
|
|
||||||
|
#include <dev/utopia/utopia.h>
|
||||||
|
#include <dev/hatm/if_hatmconf.h>
|
||||||
|
#include <dev/hatm/if_hatmreg.h>
|
||||||
|
#include <dev/hatm/if_hatmvar.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allocate a new TPD, zero the TPD part. Cannot return NULL if
|
||||||
|
* flag is 0. The TPD is removed from the free list and its used
|
||||||
|
* bit is set.
|
||||||
|
*/
|
||||||
|
static struct tpd *
|
||||||
|
hatm_alloc_tpd(struct hatm_softc *sc, u_int flags)
|
||||||
|
{
|
||||||
|
struct tpd *t;
|
||||||
|
|
||||||
|
/* if we allocate a transmit TPD check for the reserve */
|
||||||
|
if (flags & M_NOWAIT) {
|
||||||
|
if (sc->tpd_nfree <= HE_CONFIG_TPD_RESERVE)
|
||||||
|
return (NULL);
|
||||||
|
} else {
|
||||||
|
if (sc->tpd_nfree == 0)
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* make it beeing used */
|
||||||
|
t = SLIST_FIRST(&sc->tpd_free);
|
||||||
|
KASSERT(t != NULL, ("tpd botch"));
|
||||||
|
SLIST_REMOVE_HEAD(&sc->tpd_free, link);
|
||||||
|
TPD_SET_USED(sc, t->no);
|
||||||
|
sc->tpd_nfree--;
|
||||||
|
|
||||||
|
/* initialize */
|
||||||
|
t->mbuf = NULL;
|
||||||
|
t->cid = 0;
|
||||||
|
bzero(&t->tpd, sizeof(t->tpd));
|
||||||
|
t->tpd.addr = t->no << HE_REGS_TPD_ADDR;
|
||||||
|
|
||||||
|
return (t);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free a TPD. If the mbuf pointer in that TPD is not zero, it is assumed, that
|
||||||
|
* the DMA map of this TPD was used to load this mbuf. The map is unloaded
|
||||||
|
* and the mbuf is freed. The TPD is put back onto the free list and
|
||||||
|
* its used bit is cleared.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
hatm_free_tpd(struct hatm_softc *sc, struct tpd *tpd)
|
||||||
|
{
|
||||||
|
if (tpd->mbuf != NULL) {
|
||||||
|
bus_dmamap_unload(sc->tx_tag, tpd->map);
|
||||||
|
m_freem(tpd->mbuf);
|
||||||
|
tpd->mbuf = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* insert TPD into free list */
|
||||||
|
SLIST_INSERT_HEAD(&sc->tpd_free, tpd, link);
|
||||||
|
TPD_CLR_USED(sc, tpd->no);
|
||||||
|
sc->tpd_nfree++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Queue a number of TPD. If there is not enough space none of the TPDs
|
||||||
|
* is queued and an error code is returned.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
hatm_queue_tpds(struct hatm_softc *sc, u_int count, struct tpd **list,
|
||||||
|
u_int cid)
|
||||||
|
{
|
||||||
|
u_int space;
|
||||||
|
u_int i;
|
||||||
|
|
||||||
|
if (count >= sc->tpdrq.size) {
|
||||||
|
sc->istats.tdprq_full++;
|
||||||
|
return (EBUSY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sc->tpdrq.tail < sc->tpdrq.head)
|
||||||
|
space = sc->tpdrq.head - sc->tpdrq.tail;
|
||||||
|
else
|
||||||
|
space = sc->tpdrq.head - sc->tpdrq.tail + sc->tpdrq.size;
|
||||||
|
|
||||||
|
if (space <= count) {
|
||||||
|
sc->tpdrq.head =
|
||||||
|
(READ4(sc, HE_REGO_TPDRQ_H) >> HE_REGS_TPDRQ_H_H) &
|
||||||
|
(sc->tpdrq.size - 1);
|
||||||
|
|
||||||
|
if (sc->tpdrq.tail < sc->tpdrq.head)
|
||||||
|
space = sc->tpdrq.head - sc->tpdrq.tail;
|
||||||
|
else
|
||||||
|
space = sc->tpdrq.head - sc->tpdrq.tail +
|
||||||
|
sc->tpdrq.size;
|
||||||
|
|
||||||
|
if (space <= count) {
|
||||||
|
if_printf(&sc->ifatm.ifnet, "TPDRQ full\n");
|
||||||
|
sc->istats.tdprq_full++;
|
||||||
|
return (EBUSY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we are going to write to the TPD queue space */
|
||||||
|
bus_dmamap_sync(sc->tpdrq.mem.tag, sc->tpdrq.mem.map,
|
||||||
|
BUS_DMASYNC_PREWRITE);
|
||||||
|
|
||||||
|
/* put the entries into the TPD space */
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
/* we are going to 'write' the TPD to the device */
|
||||||
|
bus_dmamap_sync(sc->tpds.tag, sc->tpds.map,
|
||||||
|
BUS_DMASYNC_PREWRITE);
|
||||||
|
|
||||||
|
sc->tpdrq.tpdrq[sc->tpdrq.tail].tpd =
|
||||||
|
sc->tpds.paddr + HE_TPD_SIZE * list[i]->no;
|
||||||
|
sc->tpdrq.tpdrq[sc->tpdrq.tail].cid = cid;
|
||||||
|
|
||||||
|
if (++sc->tpdrq.tail == sc->tpdrq.size)
|
||||||
|
sc->tpdrq.tail = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* update tail pointer */
|
||||||
|
WRITE4(sc, HE_REGO_TPDRQ_T, (sc->tpdrq.tail << HE_REGS_TPDRQ_T_T));
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper struct for communication with the DMA load helper.
|
||||||
|
*/
|
||||||
|
struct load_txbuf_arg {
|
||||||
|
struct hatm_softc *sc;
|
||||||
|
struct tpd *first;
|
||||||
|
struct mbuf *mbuf;
|
||||||
|
struct hevcc *vcc;
|
||||||
|
int error;
|
||||||
|
u_int pti;
|
||||||
|
u_int vpi, vci;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Loader callback for the mbuf. This function allocates the TPDs and
|
||||||
|
* fills them. It puts the dmamap and and the mbuf pointer into the last
|
||||||
|
* TPD and then tries to queue all the TPDs. If anything fails, all TPDs
|
||||||
|
* allocated by this function are freed and the error flag is set in the
|
||||||
|
* argument structure. The first TPD must then be freed by the caller.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
hatm_load_txbuf(void *uarg, bus_dma_segment_t *segs, int nseg,
|
||||||
|
bus_size_t mapsize, int error)
|
||||||
|
{
|
||||||
|
struct load_txbuf_arg *arg = uarg;
|
||||||
|
u_int tpds_needed, i, n, tpd_cnt;
|
||||||
|
int need_intr;
|
||||||
|
struct tpd *tpd;
|
||||||
|
struct tpd *tpd_list[HE_CONFIG_MAX_TPD_PER_PACKET];
|
||||||
|
|
||||||
|
if (error != 0) {
|
||||||
|
DBG(arg->sc, DMA, ("%s -- error=%d plen=%d\n",
|
||||||
|
__func__, error, arg->mbuf->m_pkthdr.len));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ensure, we have enough TPDs (remember, we already have one) */
|
||||||
|
tpds_needed = (nseg + 2) / 3;
|
||||||
|
if (HE_CONFIG_TPD_RESERVE + tpds_needed - 1 > arg->sc->tpd_nfree) {
|
||||||
|
if_printf(&arg->sc->ifatm.ifnet, "%s -- out of TPDs (need %d, "
|
||||||
|
"have %u)\n", __func__, tpds_needed - 1,
|
||||||
|
arg->sc->tpd_nfree + 1);
|
||||||
|
arg->error = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check for the maximum number of TPDs on the connection.
|
||||||
|
*/
|
||||||
|
need_intr = 0;
|
||||||
|
if (arg->sc->max_tpd > 0) {
|
||||||
|
if (arg->vcc->ntpds + tpds_needed > arg->sc->max_tpd) {
|
||||||
|
arg->sc->istats.flow_closed++;
|
||||||
|
arg->vcc->vflags |= HE_VCC_FLOW_CTRL;
|
||||||
|
#ifdef notyet
|
||||||
|
atm_message(&arg->sc->ifatm.ifnet, ATM_MSG_FLOW_CONTROL,
|
||||||
|
(1 << 24) | (arg->vpi << 16) | arg->vci);
|
||||||
|
#endif
|
||||||
|
arg->error = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (arg->vcc->ntpds + tpds_needed >
|
||||||
|
(9 * arg->sc->max_tpd) / 10)
|
||||||
|
need_intr = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
tpd = arg->first;
|
||||||
|
tpd_cnt = 0;
|
||||||
|
tpd_list[tpd_cnt++] = tpd;
|
||||||
|
for (i = n = 0; i < nseg; i++, n++) {
|
||||||
|
if (n == 3) {
|
||||||
|
if ((tpd = hatm_alloc_tpd(arg->sc, M_NOWAIT)) == NULL)
|
||||||
|
/* may not fail (see check above) */
|
||||||
|
panic("%s: out of TPDs", __func__);
|
||||||
|
tpd->cid = arg->first->cid;
|
||||||
|
tpd->tpd.addr |= arg->pti;
|
||||||
|
tpd_list[tpd_cnt++] = tpd;
|
||||||
|
n = 0;
|
||||||
|
}
|
||||||
|
KASSERT(segs[i].ds_addr <= 0xffffffffLU,
|
||||||
|
("phys addr too large %lx", (u_long)segs[i].ds_addr));
|
||||||
|
|
||||||
|
DBG(arg->sc, DMA, ("DMA loaded: %lx/%lu",
|
||||||
|
(u_long)segs[i].ds_addr, (u_long)segs[i].ds_len));
|
||||||
|
|
||||||
|
tpd->tpd.bufs[n].addr = segs[i].ds_addr;
|
||||||
|
tpd->tpd.bufs[n].len = segs[i].ds_len;
|
||||||
|
|
||||||
|
DBG(arg->sc, TX, ("seg[%u]=tpd[%u,%u]=%x/%u", i,
|
||||||
|
tpd_cnt, n, tpd->tpd.bufs[n].addr, tpd->tpd.bufs[n].len));
|
||||||
|
|
||||||
|
if (i == nseg - 1)
|
||||||
|
tpd->tpd.bufs[n].len |= HE_REGM_TPD_LST;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Swap the MAP in the first and the last TPD and set the mbuf
|
||||||
|
* pointer into the last TPD. We use the map in the last TPD, because
|
||||||
|
* the map must stay valid until the last TPD is processed by the card.
|
||||||
|
*/
|
||||||
|
if (tpd_cnt > 1) {
|
||||||
|
bus_dmamap_t tmp;
|
||||||
|
|
||||||
|
tmp = arg->first->map;
|
||||||
|
arg->first->map = tpd_list[tpd_cnt - 1]->map;
|
||||||
|
tpd_list[tpd_cnt - 1]->map = tmp;
|
||||||
|
}
|
||||||
|
tpd_list[tpd_cnt - 1]->mbuf = arg->mbuf;
|
||||||
|
|
||||||
|
if (need_intr)
|
||||||
|
tpd_list[tpd_cnt - 1]->tpd.addr |= HE_REGM_TPD_INTR;
|
||||||
|
|
||||||
|
/* queue the TPDs */
|
||||||
|
if (hatm_queue_tpds(arg->sc, tpd_cnt, tpd_list, arg->first->cid)) {
|
||||||
|
/* free all, except the first TPD */
|
||||||
|
for (i = 1; i < tpd_cnt; i++)
|
||||||
|
hatm_free_tpd(arg->sc, tpd_list[i]);
|
||||||
|
arg->error = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
arg->vcc->ntpds += tpd_cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start output on the interface
|
||||||
|
*
|
||||||
|
* For raw aal we process only the first cell in the mbuf chain! XXX
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
hatm_start(struct ifnet *ifp)
|
||||||
|
{
|
||||||
|
struct hatm_softc *sc = (struct hatm_softc *)ifp->if_softc;
|
||||||
|
struct mbuf *m;
|
||||||
|
struct atm_pseudohdr *aph;
|
||||||
|
u_int cid;
|
||||||
|
struct tpd *tpd;
|
||||||
|
struct load_txbuf_arg arg;
|
||||||
|
u_int len;
|
||||||
|
int error;
|
||||||
|
|
||||||
|
if (!(ifp->if_flags & IFF_RUNNING))
|
||||||
|
return;
|
||||||
|
mtx_lock(&sc->mtx);
|
||||||
|
arg.sc = sc;
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
IF_DEQUEUE(&ifp->if_snd, m);
|
||||||
|
if (m == NULL)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (m->m_len < sizeof(*aph))
|
||||||
|
if ((m = m_pullup(m, sizeof(*aph))) == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
aph = mtod(m, struct atm_pseudohdr *);
|
||||||
|
arg.vci = ATM_PH_VCI(aph);
|
||||||
|
arg.vpi = ATM_PH_VPI(aph);
|
||||||
|
m_adj(m, sizeof(*aph));
|
||||||
|
|
||||||
|
if ((len = m->m_pkthdr.len) == 0) {
|
||||||
|
m_freem(m);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((arg.vpi & ~HE_VPI_MASK) || (arg.vci & ~HE_VCI_MASK) ||
|
||||||
|
(arg.vci == 0)) {
|
||||||
|
m_freem(m);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
cid = HE_CID(arg.vpi, arg.vci);
|
||||||
|
arg.vcc = sc->vccs[cid];
|
||||||
|
|
||||||
|
if (arg.vcc == NULL || !(arg.vcc->vflags & HE_VCC_OPEN)) {
|
||||||
|
m_freem(m);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg.vcc->vflags & HE_VCC_FLOW_CTRL) {
|
||||||
|
m_freem(m);
|
||||||
|
sc->istats.flow_drop++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
arg.pti = 0;
|
||||||
|
if (arg.vcc->param.aal == ATMIO_AAL_RAW) {
|
||||||
|
if (len < 52) {
|
||||||
|
m_freem(m);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (len > 52) {
|
||||||
|
m_adj(m, -((int)(len - 52)));
|
||||||
|
len = 52;
|
||||||
|
}
|
||||||
|
if (m->m_len < 4 && (m = m_pullup(m, 4)) == NULL)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* ignore header except payload type and CLP */
|
||||||
|
arg.pti = mtod(m, u_char *)[3] & 0xf;
|
||||||
|
arg.pti = ((arg.pti & 0xe) << 2) | ((arg.pti & 1) << 1);
|
||||||
|
m_adj(m, 4);
|
||||||
|
len -= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ENABLE_BPF
|
||||||
|
if (!(arg.vcc->param.flags & ATMIO_FLAG_NG) &&
|
||||||
|
(arg.vcc->param.flags & ATM_PH_AAL5) &&
|
||||||
|
(arg.vcc->param.flags & ATM_PH_LLCSNAP))
|
||||||
|
BPF_MTAP(ifp, m);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Now load a DMA map with the packet. Allocate the first
|
||||||
|
* TPD to get a map. Additional TPDs may be allocated by the
|
||||||
|
* callback. */
|
||||||
|
if ((tpd = hatm_alloc_tpd(sc, M_NOWAIT)) == NULL) {
|
||||||
|
m_freem(m);
|
||||||
|
sc->ifatm.ifnet.if_oerrors++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
tpd->cid = cid;
|
||||||
|
tpd->tpd.addr |= arg.pti;
|
||||||
|
arg.first = tpd;
|
||||||
|
arg.error = 0;
|
||||||
|
arg.mbuf = m;
|
||||||
|
|
||||||
|
error = bus_dmamap_load_mbuf(sc->tx_tag, tpd->map, m,
|
||||||
|
hatm_load_txbuf, &arg, 0);
|
||||||
|
|
||||||
|
if (error == EFBIG) {
|
||||||
|
/* try to defragment the packet */
|
||||||
|
sc->istats.defrag++;
|
||||||
|
m = m_defrag(m, M_DONTWAIT);
|
||||||
|
if (m == NULL) {
|
||||||
|
sc->ifatm.ifnet.if_oerrors++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
arg.mbuf = m;
|
||||||
|
error = bus_dmamap_load_mbuf(sc->tx_tag, tpd->map, m,
|
||||||
|
hatm_load_txbuf, &arg, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error != 0) {
|
||||||
|
if_printf(&sc->ifatm.ifnet, "mbuf loaded error=%d\n",
|
||||||
|
error);
|
||||||
|
hatm_free_tpd(sc, tpd);
|
||||||
|
sc->ifatm.ifnet.if_oerrors++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (arg.error) {
|
||||||
|
hatm_free_tpd(sc, tpd);
|
||||||
|
sc->ifatm.ifnet.if_oerrors++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
arg.vcc->opackets++;
|
||||||
|
arg.vcc->obytes += len;
|
||||||
|
sc->ifatm.ifnet.if_opackets++;
|
||||||
|
}
|
||||||
|
mtx_unlock(&sc->mtx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
hatm_tx_complete(struct hatm_softc *sc, struct tpd *tpd, uint32_t flags)
|
||||||
|
{
|
||||||
|
struct hevcc *vcc = sc->vccs[tpd->cid];
|
||||||
|
|
||||||
|
DBG(sc, TX, ("tx_complete cid=%#x flags=%#x", tpd->cid, flags));
|
||||||
|
|
||||||
|
if (vcc == NULL)
|
||||||
|
return;
|
||||||
|
if ((flags & HE_REGM_TBRQ_EOS) && (vcc->vflags & HE_VCC_TX_CLOSING)) {
|
||||||
|
vcc->vflags &= ~HE_VCC_TX_CLOSING;
|
||||||
|
if (vcc->vflags & HE_VCC_ASYNC) {
|
||||||
|
hatm_tx_vcc_closed(sc, tpd->cid);
|
||||||
|
if (!(vcc->vflags & HE_VCC_OPEN)) {
|
||||||
|
hatm_vcc_closed(sc, tpd->cid);
|
||||||
|
vcc = NULL;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
cv_signal(&sc->vcc_cv);
|
||||||
|
}
|
||||||
|
hatm_free_tpd(sc, tpd);
|
||||||
|
|
||||||
|
if (vcc == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
vcc->ntpds--;
|
||||||
|
|
||||||
|
if ((vcc->vflags & HE_VCC_FLOW_CTRL) &&
|
||||||
|
vcc->ntpds <= HE_CONFIG_TPD_FLOW_ENB) {
|
||||||
|
vcc->vflags &= ~HE_VCC_FLOW_CTRL;
|
||||||
|
#ifdef notyet
|
||||||
|
atm_message(&sc->ifatm.ifnet, ATM_MSG_FLOW_CONTROL,
|
||||||
|
(0 << 24) | (HE_VPI(tpd->cid) << 16) | HE_VCI(tpd->cid));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert CPS to Rate for a rate group
|
||||||
|
*/
|
||||||
|
static u_int
|
||||||
|
cps_to_rate(struct hatm_softc *sc, uint32_t cps)
|
||||||
|
{
|
||||||
|
u_int clk = sc->he622 ? HE_622_CLOCK : HE_155_CLOCK;
|
||||||
|
u_int period, rate;
|
||||||
|
|
||||||
|
/* how many double ticks between two cells */
|
||||||
|
period = (clk + 2 * cps - 1) / (2 * cps);
|
||||||
|
rate = hatm_cps2atmf(period);
|
||||||
|
if (hatm_atmf2cps(rate) < period)
|
||||||
|
rate++;
|
||||||
|
|
||||||
|
return (rate);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check whether the VCC is really closed on the hardware and available for
|
||||||
|
* open. Check that we have enough resources. If this function returns ok,
|
||||||
|
* a later actual open must succeed. Assume, that we are locked between this
|
||||||
|
* function and the next one, so that nothing does change. For CBR this
|
||||||
|
* assigns the rate group and set the rate group's parameter.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
hatm_tx_vcc_can_open(struct hatm_softc *sc, u_int cid, struct hevcc *vcc)
|
||||||
|
{
|
||||||
|
uint32_t v, line_rate;
|
||||||
|
u_int rc, idx, free_idx;
|
||||||
|
struct atmio_tparam *t = &vcc->param.tparam;
|
||||||
|
|
||||||
|
/* verify that connection is closed */
|
||||||
|
#if 0
|
||||||
|
v = READ_TSR(sc, cid, 4);
|
||||||
|
if(!(v & HE_REGM_TSR4_SESS_END)) {
|
||||||
|
if_printf(&sc->ifatm.ifnet, "cid=%#x not closed (TSR4)\n", cid);
|
||||||
|
return (EBUSY);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
v = READ_TSR(sc, cid, 0);
|
||||||
|
if((v & HE_REGM_TSR0_CONN_STATE) != 0) {
|
||||||
|
if_printf(&sc->ifatm.ifnet, "cid=%#x not closed (TSR0=%#x)\n",
|
||||||
|
cid, v);
|
||||||
|
return (EBUSY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check traffic parameters */
|
||||||
|
line_rate = sc->he622 ? ATM_RATE_622M : ATM_RATE_155M;
|
||||||
|
switch (vcc->param.traffic) {
|
||||||
|
|
||||||
|
case ATMIO_TRAFFIC_UBR:
|
||||||
|
if (t->pcr == 0 || t->pcr > line_rate)
|
||||||
|
t->pcr = line_rate;
|
||||||
|
if (t->mcr != 0 || t->icr != 0 || t->tbe != 0 || t->nrm != 0 ||
|
||||||
|
t->trm != 0 || t->adtf != 0 || t->rif != 0 || t->rdf != 0 ||
|
||||||
|
t->cdf != 0)
|
||||||
|
return (EINVAL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ATMIO_TRAFFIC_CBR:
|
||||||
|
/*
|
||||||
|
* Compute rate group index
|
||||||
|
*/
|
||||||
|
if (t->pcr < 10)
|
||||||
|
t->pcr = 10;
|
||||||
|
if (sc->cbr_bw + t->pcr > line_rate)
|
||||||
|
return (EINVAL);
|
||||||
|
if (t->mcr != 0 || t->icr != 0 || t->tbe != 0 || t->nrm != 0 ||
|
||||||
|
t->trm != 0 || t->adtf != 0 || t->rif != 0 || t->rdf != 0 ||
|
||||||
|
t->cdf != 0)
|
||||||
|
return (EINVAL);
|
||||||
|
|
||||||
|
rc = cps_to_rate(sc, t->pcr);
|
||||||
|
free_idx = HE_REGN_CS_STPER;
|
||||||
|
for (idx = 0; idx < HE_REGN_CS_STPER; idx++) {
|
||||||
|
if (sc->rate_ctrl[idx].refcnt == 0) {
|
||||||
|
if (free_idx == HE_REGN_CS_STPER)
|
||||||
|
free_idx = idx;
|
||||||
|
} else {
|
||||||
|
if (sc->rate_ctrl[idx].rate == rc)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (idx == HE_REGN_CS_STPER) {
|
||||||
|
if ((idx = free_idx) == HE_REGN_CS_STPER)
|
||||||
|
return (EBUSY);
|
||||||
|
sc->rate_ctrl[idx].rate = rc;
|
||||||
|
WRITE_MBOX4(sc, HE_REGO_CS_STPER(idx), rc);
|
||||||
|
}
|
||||||
|
vcc->rc = idx;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ATMIO_TRAFFIC_ABR:
|
||||||
|
if (t->pcr > line_rate)
|
||||||
|
t->pcr = line_rate;
|
||||||
|
if (t->mcr > line_rate)
|
||||||
|
t->mcr = line_rate;
|
||||||
|
if (t->icr > line_rate)
|
||||||
|
t->icr = line_rate;
|
||||||
|
if (t->tbe == 0 || t->tbe >= 1 << 24 || t->nrm > 7 ||
|
||||||
|
t->trm > 7 || t->adtf >= 1 << 10 || t->rif > 15 ||
|
||||||
|
t->rdf > 15 || t->cdf > 7)
|
||||||
|
return (EINVAL);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return (EINVAL);
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define NRM_CODE2VAL(CODE) (2 * (1 << (CODE)))
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Actually open the transmit VCC
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
hatm_tx_vcc_open(struct hatm_softc *sc, u_int cid)
|
||||||
|
{
|
||||||
|
struct hevcc *vcc = sc->vccs[cid];
|
||||||
|
uint32_t tsr0, tsr4, atmf, crm;
|
||||||
|
const struct atmio_tparam *t = &vcc->param.tparam;
|
||||||
|
|
||||||
|
if (vcc->param.aal == ATMIO_AAL_5) {
|
||||||
|
tsr0 = HE_REGM_TSR0_AAL_5 << HE_REGS_TSR0_AAL;
|
||||||
|
tsr4 = HE_REGM_TSR4_AAL_5 << HE_REGS_TSR4_AAL;
|
||||||
|
} else {
|
||||||
|
tsr0 = HE_REGM_TSR0_AAL_0 << HE_REGS_TSR0_AAL;
|
||||||
|
tsr4 = HE_REGM_TSR4_AAL_0 << HE_REGS_TSR4_AAL;
|
||||||
|
}
|
||||||
|
tsr4 |= 1;
|
||||||
|
|
||||||
|
switch (vcc->param.traffic) {
|
||||||
|
|
||||||
|
case ATMIO_TRAFFIC_UBR:
|
||||||
|
atmf = hatm_cps2atmf(t->pcr);
|
||||||
|
|
||||||
|
tsr0 |= HE_REGM_TSR0_TRAFFIC_UBR << HE_REGS_TSR0_TRAFFIC;
|
||||||
|
tsr0 |= HE_REGM_TSR0_USE_WMIN | HE_REGM_TSR0_UPDATE_GER;
|
||||||
|
|
||||||
|
WRITE_TSR(sc, cid, 0, 0xf, tsr0);
|
||||||
|
WRITE_TSR(sc, cid, 4, 0xf, tsr4);
|
||||||
|
WRITE_TSR(sc, cid, 1, 0xf, (atmf << HE_REGS_TSR1_PCR));
|
||||||
|
WRITE_TSR(sc, cid, 2, 0xf, (atmf << HE_REGS_TSR2_ACR));
|
||||||
|
WRITE_TSR(sc, cid, 9, 0xf, HE_REGM_TSR9_INIT);
|
||||||
|
WRITE_TSR(sc, cid, 3, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 5, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 6, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 7, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 8, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 10, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 11, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 12, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 13, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 14, 0xf, 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ATMIO_TRAFFIC_CBR:
|
||||||
|
atmf = hatm_cps2atmf(t->pcr);
|
||||||
|
sc->rate_ctrl[vcc->rc].refcnt++;
|
||||||
|
|
||||||
|
tsr0 |= HE_REGM_TSR0_TRAFFIC_CBR << HE_REGS_TSR0_TRAFFIC;
|
||||||
|
tsr0 |= vcc->rc;
|
||||||
|
|
||||||
|
WRITE_TSR(sc, cid, 1, 0xf, (atmf << HE_REGS_TSR1_PCR));
|
||||||
|
WRITE_TSR(sc, cid, 2, 0xf, (atmf << HE_REGS_TSR2_ACR));
|
||||||
|
WRITE_TSR(sc, cid, 3, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 5, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 6, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 7, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 8, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 10, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 11, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 12, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 13, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 14, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 4, 0xf, tsr4);
|
||||||
|
WRITE_TSR(sc, cid, 9, 0xf, HE_REGM_TSR9_INIT);
|
||||||
|
WRITE_TSR(sc, cid, 0, 0xf, tsr0);
|
||||||
|
|
||||||
|
sc->cbr_bw += t->pcr;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ATMIO_TRAFFIC_ABR:
|
||||||
|
if ((crm = t->tbe / NRM_CODE2VAL(t->nrm)) > 0xffff)
|
||||||
|
crm = 0xffff;
|
||||||
|
|
||||||
|
tsr0 |= HE_REGM_TSR0_TRAFFIC_ABR << HE_REGS_TSR0_TRAFFIC;
|
||||||
|
tsr0 |= HE_REGM_TSR0_USE_WMIN | HE_REGM_TSR0_UPDATE_GER;
|
||||||
|
|
||||||
|
WRITE_TSR(sc, cid, 0, 0xf, tsr0);
|
||||||
|
WRITE_TSR(sc, cid, 4, 0xf, tsr4);
|
||||||
|
|
||||||
|
WRITE_TSR(sc, cid, 1, 0xf,
|
||||||
|
((hatm_cps2atmf(t->pcr) << HE_REGS_TSR1_PCR) |
|
||||||
|
(hatm_cps2atmf(t->mcr) << HE_REGS_TSR1_MCR)));
|
||||||
|
WRITE_TSR(sc, cid, 2, 0xf,
|
||||||
|
(hatm_cps2atmf(t->icr) << HE_REGS_TSR2_ACR));
|
||||||
|
WRITE_TSR(sc, cid, 3, 0xf,
|
||||||
|
((NRM_CODE2VAL(t->nrm) - 1) << HE_REGS_TSR3_NRM) |
|
||||||
|
(crm << HE_REGS_TSR3_CRM));
|
||||||
|
|
||||||
|
WRITE_TSR(sc, cid, 5, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 6, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 7, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 8, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 10, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 12, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 14, 0xf, 0);
|
||||||
|
WRITE_TSR(sc, cid, 9, 0xf, HE_REGM_TSR9_INIT);
|
||||||
|
|
||||||
|
WRITE_TSR(sc, cid, 11, 0xf,
|
||||||
|
(hatm_cps2atmf(t->icr) << HE_REGS_TSR11_ICR) |
|
||||||
|
(t->trm << HE_REGS_TSR11_TRM) |
|
||||||
|
(t->nrm << HE_REGS_TSR11_NRM) |
|
||||||
|
(t->adtf << HE_REGS_TSR11_ADTF));
|
||||||
|
|
||||||
|
WRITE_TSR(sc, cid, 13, 0xf,
|
||||||
|
(t->rdf << HE_REGS_TSR13_RDF) |
|
||||||
|
(t->rif << HE_REGS_TSR13_RIF) |
|
||||||
|
(t->cdf << HE_REGS_TSR13_CDF) |
|
||||||
|
(crm << HE_REGS_TSR13_CRM));
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
vcc->vflags |= HE_VCC_TX_OPEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Close the TX side of a VCC. Set the CLOSING flag.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
hatm_tx_vcc_close(struct hatm_softc *sc, u_int cid)
|
||||||
|
{
|
||||||
|
struct hevcc *vcc = sc->vccs[cid];
|
||||||
|
struct tpd *tpd_list[1];
|
||||||
|
u_int i, pcr = 0;
|
||||||
|
|
||||||
|
WRITE_TSR(sc, cid, 4, 0x8, HE_REGM_TSR4_FLUSH);
|
||||||
|
|
||||||
|
switch (vcc->param.traffic) {
|
||||||
|
|
||||||
|
case ATMIO_TRAFFIC_CBR:
|
||||||
|
WRITE_TSR(sc, cid, 14, 0x8, HE_REGM_TSR14_CBR_DELETE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ATMIO_TRAFFIC_ABR:
|
||||||
|
WRITE_TSR(sc, cid, 14, 0x4, HE_REGM_TSR14_ABR_CLOSE);
|
||||||
|
pcr = vcc->param.tparam.pcr;
|
||||||
|
/* FALL THROUGH */
|
||||||
|
|
||||||
|
case ATMIO_TRAFFIC_UBR:
|
||||||
|
WRITE_TSR(sc, cid, 1, 0xf,
|
||||||
|
hatm_cps2atmf(HE_CONFIG_FLUSH_RATE) << HE_REGS_TSR1_MCR |
|
||||||
|
hatm_cps2atmf(pcr) << HE_REGS_TSR1_PCR);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
tpd_list[0] = hatm_alloc_tpd(sc, 0);
|
||||||
|
tpd_list[0]->tpd.addr |= HE_REGM_TPD_EOS | HE_REGM_TPD_INTR;
|
||||||
|
tpd_list[0]->cid = cid;
|
||||||
|
|
||||||
|
vcc->vflags |= HE_VCC_TX_CLOSING;
|
||||||
|
vcc->vflags &= ~HE_VCC_TX_OPEN;
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
while (hatm_queue_tpds(sc, 1, tpd_list, cid) != 0) {
|
||||||
|
if (++i == 1000)
|
||||||
|
panic("TPDRQ permanently full");
|
||||||
|
DELAY(1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
hatm_tx_vcc_closed(struct hatm_softc *sc, u_int cid)
|
||||||
|
{
|
||||||
|
if (sc->vccs[cid]->param.traffic == ATMIO_TRAFFIC_CBR) {
|
||||||
|
sc->cbr_bw -= sc->vccs[cid]->param.tparam.pcr;
|
||||||
|
sc->rate_ctrl[sc->vccs[cid]->rc].refcnt--;
|
||||||
|
}
|
||||||
|
}
|
104
sys/dev/hatm/if_hatmconf.h
Normal file
104
sys/dev/hatm/if_hatmconf.h
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2001-2003
|
||||||
|
* Fraunhofer Institute for Open Communication Systems (FhG Fokus).
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* Author: Hartmut Brandt <harti@freebsd.org>
|
||||||
|
*
|
||||||
|
* $FreeBSD$
|
||||||
|
*
|
||||||
|
* Default configuration
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* configuration */
|
||||||
|
#define HE_CONFIG_VPI_BITS 2
|
||||||
|
#define HE_CONFIG_VCI_BITS 10
|
||||||
|
|
||||||
|
/* interrupt group 0 only */
|
||||||
|
/* the size must be 1 <= size <= 1023 */
|
||||||
|
#define HE_CONFIG_IRQ0_SIZE 256
|
||||||
|
#define HE_CONFIG_IRQ0_THRESH 224 /* retrigger interrupt */
|
||||||
|
#define HE_CONFIG_IRQ0_LINE HE_REGM_IRQ_A /* routing */
|
||||||
|
|
||||||
|
/* don't change these */
|
||||||
|
#define HE_CONFIG_TXMEM (128 * 1024) /* words */
|
||||||
|
#define HE_CONFIG_RXMEM (64 * 1024) /* words */
|
||||||
|
#define HE_CONFIG_LCMEM (512 * 1024) /* words */
|
||||||
|
|
||||||
|
/* group 0 - all AALs except AAL.raw */
|
||||||
|
/* receive group 0 buffer pools (mbufs and mbufs+cluster) */
|
||||||
|
/* the size must be a power of 2: 4 <= size <= 8192 */
|
||||||
|
#define HE_CONFIG_RBPS0_SIZE 2048 /* entries per queue */
|
||||||
|
#define HE_CONFIG_RBPS0_THRESH 256 /* interrupt threshold */
|
||||||
|
#define HE_CONFIG_RBPL0_SIZE 512 /* entries per queue */
|
||||||
|
#define HE_CONFIG_RBPL0_THRESH 32 /* interrupt threshold */
|
||||||
|
|
||||||
|
/* receive group 0 buffer return queue */
|
||||||
|
/* the size must be a power of 2: 1 <= size <= 16384 */
|
||||||
|
#define HE_CONFIG_RBRQ0_SIZE 512 /* entries in queue */
|
||||||
|
#define HE_CONFIG_RBRQ0_THRESH 256 /* interrupt threshold */
|
||||||
|
#define HE_CONFIG_RBRQ0_TOUT 10 /* interrupt timeout */
|
||||||
|
#define HE_CONFIG_RBRQ0_PCNT 5 /* packet count threshold */
|
||||||
|
|
||||||
|
/* group 1 - raw cells */
|
||||||
|
/* receive group 1 small buffer pool */
|
||||||
|
/* the size must be a power of 2: 4 <= size <= 8192 */
|
||||||
|
#define HE_CONFIG_RBPS1_SIZE 1024 /* entries in queue */
|
||||||
|
#define HE_CONFIG_RBPS1_THRESH 512 /* interrupt threshold */
|
||||||
|
|
||||||
|
/* receive group 1 buffer return queue */
|
||||||
|
/* the size must be a power of 2: 1 <= size <= 16384 */
|
||||||
|
#define HE_CONFIG_RBRQ1_SIZE 512 /* entries in queue */
|
||||||
|
#define HE_CONFIG_RBRQ1_THRESH 256 /* interrupt threshold */
|
||||||
|
#define HE_CONFIG_RBRQ1_TOUT 100 /* interrupt timeout */
|
||||||
|
#define HE_CONFIG_RBRQ1_PCNT 25 /* packet count threshold */
|
||||||
|
|
||||||
|
/* there is only one TPD queue */
|
||||||
|
/* the size must be a power of 2: 1 <= size <= 4096 */
|
||||||
|
#define HE_CONFIG_TPDRQ_SIZE 2048 /* entries in queue */
|
||||||
|
|
||||||
|
/* transmit group 0 */
|
||||||
|
/* the size must be a power of 2: 1 <= size <= 16384 */
|
||||||
|
#define HE_CONFIG_TBRQ_SIZE 512 /* entries in queue */
|
||||||
|
#define HE_CONFIG_TBRQ_THRESH 400 /* interrupt threshold */
|
||||||
|
|
||||||
|
/* Maximum number of TPDs to allocate to a single VCC. This
|
||||||
|
* number should depend on the cell rate and the maximum allowed cell delay */
|
||||||
|
#define HE_CONFIG_TPD_MAXCC 2048
|
||||||
|
|
||||||
|
/* Maximum number of external mbuf pages */
|
||||||
|
#define HE_CONFIG_MAX_MBUF_PAGES 256
|
||||||
|
|
||||||
|
/* Maximum number of TPDs used for one packet */
|
||||||
|
#define HE_CONFIG_MAX_TPD_PER_PACKET \
|
||||||
|
((((HE_MAX_PDU + MCLBYTES - 1) / MCLBYTES + 2) / 3) + 2)
|
||||||
|
|
||||||
|
/* Number of TPDs to reserve for close operations */
|
||||||
|
#define HE_CONFIG_TPD_RESERVE 32
|
||||||
|
|
||||||
|
/* Number of TPDs per VCC when to re-enable flow control */
|
||||||
|
#define HE_CONFIG_TPD_FLOW_ENB 80
|
||||||
|
|
||||||
|
/* MCR for flushing CBR and ABR connections at close */
|
||||||
|
#define HE_CONFIG_FLUSH_RATE 200000
|
641
sys/dev/hatm/if_hatmreg.h
Normal file
641
sys/dev/hatm/if_hatmreg.h
Normal file
@ -0,0 +1,641 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2001-2003
|
||||||
|
* Fraunhofer Institute for Open Communication Systems (FhG Fokus).
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* Author: Hartmut Brandt <harti@freebsd.org>
|
||||||
|
*
|
||||||
|
* $FreeBSD$
|
||||||
|
*
|
||||||
|
* Fore HE driver for NATM
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* check configuration */
|
||||||
|
#if HE_CONFIG_VPI_BITS + HE_CONFIG_VCI_BITS > 12
|
||||||
|
#error "hatm: too many bits configured for VPI/VCI"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define HE_MAX_VCCS (1 << (HE_CONFIG_VPI_BITS + HE_CONFIG_VCI_BITS))
|
||||||
|
|
||||||
|
#define HE_VPI_MASK ((1 << (HE_CONFIG_VPI_BITS))-1)
|
||||||
|
#define HE_VCI_MASK ((1 << (HE_CONFIG_VCI_BITS))-1)
|
||||||
|
|
||||||
|
#define HE_VPI(CID) (((CID) >> HE_CONFIG_VCI_BITS) & HE_VPI_MASK)
|
||||||
|
#define HE_VCI(CID) ((CID) & HE_VCI_MASK)
|
||||||
|
|
||||||
|
#define HE_CID(VPI,VCI) ((((VPI) & HE_VPI_MASK) << HE_CONFIG_VCI_BITS) | \
|
||||||
|
((VCI) & HE_VCI_MASK))
|
||||||
|
|
||||||
|
|
||||||
|
/* GEN_CNTL_0 register */
|
||||||
|
#define HE_PCIR_GEN_CNTL_0 0x40
|
||||||
|
#define HE_PCIM_CTL0_64BIT (1 << 0)
|
||||||
|
#define HE_PCIM_CTL0_IGNORE_TIMEOUT (1 << 1)
|
||||||
|
#define HE_PCIM_CTL0_INIT_ENB (1 << 2)
|
||||||
|
#define HE_PCIM_CTL0_MRM (1 << 4)
|
||||||
|
#define HE_PCIM_CTL0_MRL (1 << 5)
|
||||||
|
#define HE_PCIM_CTL0_BIGENDIAN (1 << 16)
|
||||||
|
#define HE_PCIM_CTL0_INT_PROC_ENB (1 << 25)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Memory registers
|
||||||
|
*/
|
||||||
|
#define HE_REGO_FLASH 0x00000
|
||||||
|
#define HE_REGO_RESET_CNTL 0x80000
|
||||||
|
#define HE_REGM_RESET_STATE (1 << 6)
|
||||||
|
#define HE_REGO_HOST_CNTL 0x80004
|
||||||
|
#define HE_REGM_HOST_BUS64 (1 << 27)
|
||||||
|
#define HE_REGM_HOST_DESC_RD64 (1 << 26)
|
||||||
|
#define HE_REGM_HOST_DATA_RD64 (1 << 25)
|
||||||
|
#define HE_REGM_HOST_DATA_WR64 (1 << 24)
|
||||||
|
#define HE_REGM_HOST_PROM_SEL (1 << 12)
|
||||||
|
#define HE_REGM_HOST_PROM_WREN (1 << 11)
|
||||||
|
#define HE_REGM_HOST_PROM_DATA_OUT (1 << 10)
|
||||||
|
#define HE_REGS_HOST_PROM_DATA_OUT 10
|
||||||
|
#define HE_REGM_HOST_PROM_DATA_IN (1 << 9)
|
||||||
|
#define HE_REGS_HOST_PROM_DATA_IN 9
|
||||||
|
#define HE_REGM_HOST_PROM_CLOCK (1 << 8)
|
||||||
|
#define HE_REGM_HOST_PROM_BITS (0x00001f00)
|
||||||
|
#define HE_REGM_HOST_QUICK_RD (1 << 7)
|
||||||
|
#define HE_REGM_HOST_QUICK_WR (1 << 6)
|
||||||
|
#define HE_REGM_HOST_OUTFF_ENB (1 << 5)
|
||||||
|
#define HE_REGM_HOST_CMDFF_ENB (1 << 4)
|
||||||
|
#define HE_REGO_LB_SWAP 0x80008
|
||||||
|
#define HE_REGM_LBSWAP_RNUM (0xf << 27)
|
||||||
|
#define HE_REGS_LBSWAP_RNUM 27
|
||||||
|
#define HE_REGM_LBSWAP_DATA_WR_SWAP (1 << 20)
|
||||||
|
#define HE_REGM_LBSWAP_DESC_RD_SWAP (1 << 19)
|
||||||
|
#define HE_REGM_LBSWAP_DATA_RD_SWAP (1 << 18)
|
||||||
|
#define HE_REGM_LBSWAP_INTR_SWAP (1 << 17)
|
||||||
|
#define HE_REGM_LBSWAP_DESC_WR_SWAP (1 << 16)
|
||||||
|
#define HE_REGM_LBSWAP_BIG_ENDIAN (1 << 14)
|
||||||
|
#define HE_REGM_LBSWAP_XFER_SIZE (1 << 7)
|
||||||
|
|
||||||
|
#define HE_REGO_LB_MEM_ADDR 0x8000C
|
||||||
|
#define HE_REGO_LB_MEM_DATA 0x80010
|
||||||
|
#define HE_REGO_LB_MEM_ACCESS 0x80014
|
||||||
|
#define HE_REGM_LB_MEM_HNDSHK (1 << 30)
|
||||||
|
#define HE_REGM_LB_MEM_READ 0x3
|
||||||
|
#define HE_REGM_LB_MEM_WRITE 0x7
|
||||||
|
|
||||||
|
#define HE_REGO_SDRAM_CNTL 0x80018
|
||||||
|
#define HE_REGM_SDRAM_64BIT (1 << 3)
|
||||||
|
#define HE_REGO_INT_FIFO 0x8001C
|
||||||
|
#define HE_REGM_INT_FIFO_CLRA (1 << 8)
|
||||||
|
#define HE_REGM_INT_FIFO_CLRB (1 << 9)
|
||||||
|
#define HE_REGM_INT_FIFO_CLRC (1 << 10)
|
||||||
|
#define HE_REGM_INT_FIFO_CLRD (1 << 11)
|
||||||
|
#define HE_REGO_ABORT_ADDR 0x80020
|
||||||
|
|
||||||
|
#define HE_REGO_IRQ0_BASE 0x80080
|
||||||
|
#define HE_REGO_IRQ_BASE(Q) (HE_REGO_IRQ0_BASE + (Q) * 0x10 + 0x00)
|
||||||
|
#define HE_REGM_IRQ_BASE_TAIL 0x3ff
|
||||||
|
#define HE_REGO_IRQ_HEAD(Q) (HE_REGO_IRQ0_BASE + (Q) * 0x10 + 0x04)
|
||||||
|
#define HE_REGS_IRQ_HEAD_SIZE 22
|
||||||
|
#define HE_REGS_IRQ_HEAD_THRESH 12
|
||||||
|
#define HE_REGS_IRQ_HEAD_HEAD 2
|
||||||
|
#define HE_REGO_IRQ_CNTL(Q) (HE_REGO_IRQ0_BASE + (Q) * 0x10 + 0x08)
|
||||||
|
#define HE_REGM_IRQ_A (0 << 2)
|
||||||
|
#define HE_REGM_IRQ_B (1 << 2)
|
||||||
|
#define HE_REGM_IRQ_C (2 << 2)
|
||||||
|
#define HE_REGM_IRQ_D (3 << 2)
|
||||||
|
#define HE_REGO_IRQ_DATA(Q) (HE_REGO_IRQ0_BASE + (Q) * 0x10 + 0x0C)
|
||||||
|
|
||||||
|
#define HE_REGO_GRP_1_0_MAP 0x800C0
|
||||||
|
#define HE_REGO_GRP_3_2_MAP 0x800C4
|
||||||
|
#define HE_REGO_GRP_5_4_MAP 0x800C8
|
||||||
|
#define HE_REGO_GRP_7_6_MAP 0x800CC
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Receive buffer pools
|
||||||
|
*/
|
||||||
|
#define HE_REGO_G0_RBPS_S 0x80400
|
||||||
|
#define HE_REGO_G0_RBPS_T 0x80404
|
||||||
|
#define HE_REGO_G0_RBPS_QI 0x80408
|
||||||
|
#define HE_REGO_G0_RBPS_BL 0x8040C
|
||||||
|
|
||||||
|
#define HE_REGO_RBP_S(K,G) (HE_REGO_G0_RBPS_S + (K) * 0x10 + (G) * 0x20)
|
||||||
|
#define HE_REGO_RBP_T(K,G) (HE_REGO_G0_RBPS_T + (K) * 0x10 + (G) * 0x20)
|
||||||
|
#define HE_REGO_RBP_QI(K,G) (HE_REGO_G0_RBPS_QI + (K) * 0x10 + (G) * 0x20)
|
||||||
|
#define HE_REGO_RBP_BL(K,G) (HE_REGO_G0_RBPS_BL + (K) * 0x10 + (G) * 0x20)
|
||||||
|
|
||||||
|
#define HE_REGS_RBP_HEAD 3
|
||||||
|
#define HE_REGS_RBP_TAIL 3
|
||||||
|
#define HE_REGS_RBP_SIZE 14
|
||||||
|
#define HE_REGM_RBP_INTR_ENB (1 << 13)
|
||||||
|
#define HE_REGS_RBP_THRESH 0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Receive buffer return queues
|
||||||
|
*/
|
||||||
|
#define HE_REGO_G0_RBRQ_ST 0x80500
|
||||||
|
#define HE_REGO_G0_RBRQ_H 0x80504
|
||||||
|
#define HE_REGO_G0_RBRQ_Q 0x80508
|
||||||
|
#define HE_REGO_G0_RBRQ_I 0x8050C
|
||||||
|
|
||||||
|
#define HE_REGO_RBRQ_ST(G) (HE_REGO_G0_RBRQ_ST + (G) * 0x10)
|
||||||
|
#define HE_REGO_RBRQ_H(G) (HE_REGO_G0_RBRQ_H + (G) * 0x10)
|
||||||
|
#define HE_REGO_RBRQ_Q(G) (HE_REGO_G0_RBRQ_Q + (G) * 0x10)
|
||||||
|
#define HE_REGO_RBRQ_I(G) (HE_REGO_G0_RBRQ_I + (G) * 0x10)
|
||||||
|
|
||||||
|
#define HE_REGS_RBRQ_HEAD 3
|
||||||
|
#define HE_REGS_RBRQ_THRESH 13
|
||||||
|
#define HE_REGS_RBRQ_SIZE 0
|
||||||
|
#define HE_REGS_RBRQ_TIME 8
|
||||||
|
#define HE_REGS_RBRQ_COUNT 0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Intermediate queues
|
||||||
|
*/
|
||||||
|
#define HE_REGO_G0_INMQ_S 0x80580
|
||||||
|
#define HE_REGO_G0_INMQ_L 0x80584
|
||||||
|
#define HE_REGO_INMQ_S(G) (HE_REGO_G0_INMQ_S + (G) * 8)
|
||||||
|
#define HE_REGO_INMQ_L(G) (HE_REGO_G0_INMQ_L + (G) * 8)
|
||||||
|
|
||||||
|
#define HE_REGO_RHCONFIG 0x805C0
|
||||||
|
#define HE_REGM_RHCONFIG_PHYENB (1 << 10)
|
||||||
|
#define HE_REGS_RHCONFIG_OAM_GID 7
|
||||||
|
#define HE_REGS_RHCONFIG_PTMR_PRE 0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Transmit buffer return queues
|
||||||
|
*/
|
||||||
|
#define HE_REGO_TBRQ0_B_T 0x80600
|
||||||
|
#define HE_REGO_TBRQ0_H 0x80604
|
||||||
|
#define HE_REGO_TBRQ0_S 0x80608
|
||||||
|
#define HE_REGO_TBRQ0_THRESH 0x8060C
|
||||||
|
|
||||||
|
#define HE_REGO_TBRQ_B_T(G) (HE_REGO_TBRQ0_B_T + (G) * 0x10)
|
||||||
|
#define HE_REGO_TBRQ_H(G) (HE_REGO_TBRQ0_H + (G) * 0x10)
|
||||||
|
#define HE_REGO_TBRQ_S(G) (HE_REGO_TBRQ0_S + (G) * 0x10)
|
||||||
|
#define HE_REGO_TBRQ_THRESH(G) (HE_REGO_TBRQ0_THRESH + (G) * 0x10)
|
||||||
|
|
||||||
|
#define HE_REGS_TBRQ_HEAD 2
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Transmit packet descriptor ready queue
|
||||||
|
*/
|
||||||
|
#define HE_REGO_TPDRQ_H 0x80680
|
||||||
|
#define HE_REGS_TPDRQ_H_H 3
|
||||||
|
/* #define HE_REGM_TPDRQ_H_H ((HE_CONFIG_TPDRQ_SIZE - 1) << 3) */
|
||||||
|
#define HE_REGO_TPDRQ_T 0x80684
|
||||||
|
#define HE_REGS_TPDRQ_T_T 3
|
||||||
|
/* #define HE_REGM_TPDRQ_T_T ((HE_CONFIG_TPDRQ_SIZE - 1) << 3) */
|
||||||
|
#define HE_REGO_TPDRQ_S 0x80688
|
||||||
|
|
||||||
|
#define HE_REGO_UBUFF_BA 0x8068C
|
||||||
|
|
||||||
|
#define HE_REGO_RLBF0_H 0x806C0
|
||||||
|
#define HE_REGO_RLBF0_T 0x806C4
|
||||||
|
#define HE_REGO_RLBF1_H 0x806C8
|
||||||
|
#define HE_REGO_RLBF1_T 0x806CC
|
||||||
|
#define HE_REGO_RLBF_H(N) (HE_REGO_RLBF0_H + (N) * 8)
|
||||||
|
#define HE_REGO_RLBF_T(N) (HE_REGO_RLBF0_T + (N) * 8)
|
||||||
|
|
||||||
|
#define HE_REGO_RLBC_H 0x806D0
|
||||||
|
#define HE_REGO_RLBC_T 0x806D4
|
||||||
|
#define HE_REGO_RLBC_H2 0x806D8
|
||||||
|
#define HE_REGO_TLBF_H 0x806E0
|
||||||
|
#define HE_REGO_TLBF_T 0x806E4
|
||||||
|
|
||||||
|
#define HE_REGO_RLBF0_C 0x806E8
|
||||||
|
#define HE_REGO_RLBF1_C 0x806EC
|
||||||
|
#define HE_REGO_RLBF_C(N) (HE_REGO_RLBF0_C + (N) * 4)
|
||||||
|
|
||||||
|
#define HE_REGO_RXTHRSH 0x806F0
|
||||||
|
#define HE_REGO_LITHRSH 0x806F4
|
||||||
|
|
||||||
|
#define HE_REGO_LBARB 0x80700
|
||||||
|
#define HE_REGS_LBARB_SLICE 28
|
||||||
|
#define HE_REGS_LBARB_RNUM 23
|
||||||
|
#define HE_REGS_LBARB_THPRI 21
|
||||||
|
#define HE_REGS_LBARB_RHPRI 19
|
||||||
|
#define HE_REGS_LBARB_TLPRI 17
|
||||||
|
#define HE_REGS_LBARB_RLPRI 15
|
||||||
|
#define HE_REGS_LBARB_BUS_MULT 8
|
||||||
|
#define HE_REGS_LBARB_NET_PREF 0
|
||||||
|
|
||||||
|
#define HE_REGO_SDRAMCON 0x80704
|
||||||
|
#define HE_REGM_SDRAMCON_BANK (1 << 14)
|
||||||
|
#define HE_REGM_SDRAMCON_WIDE (1 << 13)
|
||||||
|
#define HE_REGM_SDRAMCON_TWRWAIT (1 << 12)
|
||||||
|
#define HE_REGM_SDRAMCON_TRPWAIT (1 << 11)
|
||||||
|
#define HE_REGM_SDRAMCON_TRASWAIT (1 << 10)
|
||||||
|
#define HE_REGS_SDRAMCON_REF 0
|
||||||
|
|
||||||
|
#define HE_REGO_RCCSTAT 0x8070C
|
||||||
|
#define HE_REGM_RCCSTAT_PROG (1 << 0)
|
||||||
|
|
||||||
|
#define HE_REGO_TCMCONFIG 0x80740
|
||||||
|
#define HE_REGS_TCMCONFIG_BANK_WAIT 6
|
||||||
|
#define HE_REGS_TCMCONFIG_RW_WAIT 2
|
||||||
|
#define HE_REGS_TCMCONFIG_TYPE 0
|
||||||
|
|
||||||
|
#define HE_REGO_TSRB_BA 0x80744
|
||||||
|
#define HE_REGO_TSRC_BA 0x80748
|
||||||
|
#define HE_REGO_TMABR_BA 0x8074C
|
||||||
|
#define HE_REGO_TPD_BA 0x80750
|
||||||
|
#define HE_REGO_TSRD_BA 0x80758
|
||||||
|
|
||||||
|
#define HE_REGO_TXCONFIG 0x80760
|
||||||
|
#define HE_REGS_TXCONFIG_THRESH 22
|
||||||
|
#define HE_REGM_TXCONFIG_UTMODE (1 << 21)
|
||||||
|
#define HE_REGS_TXCONFIG_VCI_MASK 17
|
||||||
|
#define HE_REGS_TXCONFIG_LBFREE 0
|
||||||
|
|
||||||
|
#define HE_REGO_TXAAL5_PROTO 0x80764
|
||||||
|
|
||||||
|
#define HE_REGO_RCMCONFIG 0x80780
|
||||||
|
#define HE_REGS_RCMCONFIG_BANK_WAIT 6
|
||||||
|
#define HE_REGS_RCMCONFIG_RW_WAIT 2
|
||||||
|
#define HE_REGS_RCMCONFIG_TYPE 0
|
||||||
|
|
||||||
|
#define HE_REGO_RCMRSRB_BA 0x80784
|
||||||
|
#define HE_REGO_RCMLBM_BA 0x80788
|
||||||
|
#define HE_REGO_RCMABR_BA 0x8078C
|
||||||
|
|
||||||
|
#define HE_REGO_RCCONFIG 0x807C0
|
||||||
|
#define HE_REGS_RCCONFIG_UTDELAY 11
|
||||||
|
#define HE_REGM_RCCONFIG_WRAP_MODE (1 << 10)
|
||||||
|
#define HE_REGM_RCCONFIG_UT_MODE (1 << 9)
|
||||||
|
#define HE_REGM_RCCONFIG_RXENB (1 << 8)
|
||||||
|
#define HE_REGS_RCCONFIG_VP 4
|
||||||
|
#define HE_REGS_RCCONFIG_VC 0
|
||||||
|
|
||||||
|
#define HE_REGO_MCC 0x807C4
|
||||||
|
#define HE_REGO_OEC 0x807C8
|
||||||
|
#define HE_REGO_DCC 0x807CC
|
||||||
|
#define HE_REGO_CEC 0x807D0
|
||||||
|
|
||||||
|
#define HE_REGO_HSP_BA 0x807F0
|
||||||
|
|
||||||
|
#define HE_REGO_LBCONFIG 0x807F4
|
||||||
|
|
||||||
|
#define HE_REGO_CON_DAT 0x807F8
|
||||||
|
#define HE_REGO_CON_CTL 0x807FC
|
||||||
|
#define HE_REGM_CON_MBOX (2 << 30)
|
||||||
|
#define HE_REGM_CON_TCM (1 << 30)
|
||||||
|
#define HE_REGM_CON_RCM (0 << 30)
|
||||||
|
#define HE_REGM_CON_WE (1 << 29)
|
||||||
|
#define HE_REGM_CON_STATUS (1 << 28)
|
||||||
|
#define HE_REGM_CON_DIS3 (1 << 22)
|
||||||
|
#define HE_REGM_CON_DIS2 (1 << 21)
|
||||||
|
#define HE_REGM_CON_DIS1 (1 << 20)
|
||||||
|
#define HE_REGM_CON_DIS0 (1 << 19)
|
||||||
|
#define HE_REGS_CON_DIS 19
|
||||||
|
#define HE_REGS_CON_ADDR 0
|
||||||
|
|
||||||
|
#define HE_REGO_SUNI 0x80800
|
||||||
|
#define HE_REGO_SUNI_END 0x80C00
|
||||||
|
|
||||||
|
#define HE_REGO_END 0x100000
|
||||||
|
|
||||||
|
/*
|
||||||
|
* MBOX registers
|
||||||
|
*/
|
||||||
|
#define HE_REGO_CS_STPER0 0x000
|
||||||
|
#define HE_REGO_CS_STPER(G) (HE_REGO_CS_STPER0 + (G))
|
||||||
|
#define HE_REGN_CS_STPER 32
|
||||||
|
#define HE_REGO_CS_STTIM0 0x020
|
||||||
|
#define HE_REGO_CS_STTIM(G) (HE_REGO_CS_STTIM0 + (G))
|
||||||
|
#define HE_REGO_CS_TGRLD0 0x040
|
||||||
|
#define HE_REGO_CS_TGRLD(G) (HE_REGO_CS_TGRLD0 + (G))
|
||||||
|
#define HE_REGO_CS_ERTHR0 0x50
|
||||||
|
#define HE_REGO_CS_ERTHR1 0x51
|
||||||
|
#define HE_REGO_CS_ERTHR2 0x52
|
||||||
|
#define HE_REGO_CS_ERTHR3 0x53
|
||||||
|
#define HE_REGO_CS_ERTHR4 0x54
|
||||||
|
#define HE_REGO_CS_ERCTL0 0x55
|
||||||
|
#define HE_REGO_CS_ERCTL1 0x56
|
||||||
|
#define HE_REGO_CS_ERCTL2 0x57
|
||||||
|
#define HE_REGO_CS_ERSTAT0 0x58
|
||||||
|
#define HE_REGO_CS_ERSTAT1 0x59
|
||||||
|
#define HE_REGO_CS_RTCCT 0x60
|
||||||
|
#define HE_REGO_CS_RTFWC 0x61
|
||||||
|
#define HE_REGO_CS_RTFWR 0x62
|
||||||
|
#define HE_REGO_CS_RTFTC 0x63
|
||||||
|
#define HE_REGO_CS_RTATR 0x64
|
||||||
|
#define HE_REGO_CS_TFBSET 0x70
|
||||||
|
#define HE_REGO_CS_TFBADD 0x71
|
||||||
|
#define HE_REGO_CS_TFBSUB 0x72
|
||||||
|
#define HE_REGO_CS_WCRMAX 0x73
|
||||||
|
#define HE_REGO_CS_WCRMIN 0x74
|
||||||
|
#define HE_REGO_CS_WCRINC 0x75
|
||||||
|
#define HE_REGO_CS_WCRDEC 0x76
|
||||||
|
#define HE_REGO_CS_WCRCEIL 0x77
|
||||||
|
#define HE_REGO_CS_BWDCNT 0x78
|
||||||
|
#define HE_REGO_CS_OTPPER 0x80
|
||||||
|
#define HE_REGO_CS_OTWPER 0x81
|
||||||
|
#define HE_REGO_CS_OTTLIM 0x82
|
||||||
|
#define HE_REGO_CS_OTTCNT 0x83
|
||||||
|
#define HE_REGO_CS_HGRRT0 0x90
|
||||||
|
#define HE_REGO_CS_HGRRT(G) (HE_REGO_CS_HGRRT0 + (G))
|
||||||
|
#define HE_REGO_CS_ORPTRS 0xA0
|
||||||
|
#define HE_REGO_RCON_CLOSE 0x100
|
||||||
|
#define HE_REGO_CS_END 0x101
|
||||||
|
|
||||||
|
#define HE_REGT_CS_ERTHR { \
|
||||||
|
{ /* 155 */ \
|
||||||
|
{ 0x000800ea, 0x000400ea, 0x000200ea }, /* ERTHR0 */ \
|
||||||
|
{ 0x000C3388, 0x00063388, 0x00033388 }, /* ERTHR1 */ \
|
||||||
|
{ 0x00101018, 0x00081018, 0x00041018 }, /* ERTHR2 */ \
|
||||||
|
{ 0x00181dac, 0x000c1dac, 0x00061dac }, /* ERTHR3 */ \
|
||||||
|
{ 0x0028051a, 0x0014051a, 0x000a051a }, /* ERTHR4 */ \
|
||||||
|
}, { /* 622 */ \
|
||||||
|
{ 0x000800fa, 0x000400fa, 0x000200fa }, /* ERTHR0 */ \
|
||||||
|
{ 0x000c33cb, 0x000633cb, 0x000333cb }, /* ERTHR1 */ \
|
||||||
|
{ 0x0010101b, 0x0008101b, 0x0004101b }, /* ERTHR2 */ \
|
||||||
|
{ 0x00181dac, 0x000c1dac, 0x00061dac }, /* ERTHR3 */ \
|
||||||
|
{ 0x00280600, 0x00140600, 0x000a0600 }, /* ERTHR4 */ \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HE_REGT_CS_ERCTL { \
|
||||||
|
{ 0x0235e4b1, 0x4701, 0x64b1 }, /* 155 */ \
|
||||||
|
{ 0x023de8b3, 0x1801, 0x68b3 } /* 622 */ \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HE_REGT_CS_ERSTAT { \
|
||||||
|
{ 0x1280, 0x64b1 }, /* 155 */ \
|
||||||
|
{ 0x1280, 0x68b3 }, /* 622 */ \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HE_REGT_CS_RTFWR { \
|
||||||
|
0xf424, /* 155 */ \
|
||||||
|
0x14585 /* 622 */ \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HE_REGT_CS_RTATR { \
|
||||||
|
0x4680, /* 155 */ \
|
||||||
|
0x4680 /* 622 */ \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HE_REGT_CS_BWALLOC { \
|
||||||
|
{ 0x000563b7, 0x64b1, 0x5ab1, 0xe4b1, 0xdab1, 0x64b1 }, /* 155 */\
|
||||||
|
{ 0x00159ece, 0x68b3, 0x5eb3, 0xe8b3, 0xdeb3, 0x68b3 }, /* 622 */\
|
||||||
|
}
|
||||||
|
|
||||||
|
#define HE_REGT_CS_ORCF { \
|
||||||
|
{ 0x6, 0x1e }, /* 155 */ \
|
||||||
|
{ 0x5, 0x14 } /* 622 */ \
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TSRs - NR is relative to the starting number of the block
|
||||||
|
*/
|
||||||
|
#define HE_REGO_TSRA(BASE,CID,NR) ((BASE) + ((CID) << 3) + (NR))
|
||||||
|
#define HE_REGO_TSRB(BASE,CID,NR) ((BASE) + ((CID) << 2) + (NR))
|
||||||
|
#define HE_REGO_TSRC(BASE,CID,NR) ((BASE) + ((CID) << 1) + (NR))
|
||||||
|
#define HE_REGO_TSRD(BASE,CID) ((BASE) + (CID))
|
||||||
|
|
||||||
|
#define HE_REGM_TSR0_CONN_STATE (7 << 28)
|
||||||
|
#define HE_REGS_TSR0_CONN_STATE 28
|
||||||
|
#define HE_REGM_TSR0_USE_WMIN (1 << 23)
|
||||||
|
#define HE_REGM_TSR0_GROUP (7 << 18)
|
||||||
|
#define HE_REGS_TSR0_GROUP 18
|
||||||
|
#define HE_REGM_TSR0_TRAFFIC (3 << 16)
|
||||||
|
#define HE_REGS_TSR0_TRAFFIC 16
|
||||||
|
#define HE_REGM_TSR0_TRAFFIC_CBR 0
|
||||||
|
#define HE_REGM_TSR0_TRAFFIC_UBR 1
|
||||||
|
#define HE_REGM_TSR0_TRAFFIC_ABR 2
|
||||||
|
#define HE_REGM_TSR0_PROT (1 << 15)
|
||||||
|
#define HE_REGM_TSR0_AAL (3 << 12)
|
||||||
|
#define HE_REGS_TSR0_AAL 12
|
||||||
|
#define HE_REGM_TSR0_AAL_5 0
|
||||||
|
#define HE_REGM_TSR0_AAL_0 1
|
||||||
|
#define HE_REGM_TSR0_AAL_0T 2
|
||||||
|
#define HE_REGM_TSR0_HALT_ER (1 << 11)
|
||||||
|
#define HE_REGM_TSR0_MARK_CI (1 << 10)
|
||||||
|
#define HE_REGM_TSR0_MARK_ER (1 << 9)
|
||||||
|
#define HE_REGM_TSR0_UPDATE_GER (1 << 8)
|
||||||
|
#define HE_REGM_TSR0_RC 0xff
|
||||||
|
|
||||||
|
#define HE_REGM_TSR1_PCR (0x7fff << 16)
|
||||||
|
#define HE_REGS_TSR1_PCR 16
|
||||||
|
#define HE_REGM_TSR1_MCR (0x7fff << 0)
|
||||||
|
#define HE_REGS_TSR1_MCR 0
|
||||||
|
|
||||||
|
#define HE_REGM_TSR2_ACR (0x7fff << 16)
|
||||||
|
#define HE_REGS_TSR2_ACR 16
|
||||||
|
|
||||||
|
#define HE_REGM_TSR3_NRM (0xff << 24)
|
||||||
|
#define HE_REGS_TSR3_NRM 24
|
||||||
|
#define HE_REGM_TSR3_CRM (0xff << 0)
|
||||||
|
#define HE_REGS_TSR3_CRM 0
|
||||||
|
|
||||||
|
#define HE_REGM_TSR4_FLUSH (1 << 31)
|
||||||
|
#define HE_REGM_TSR4_SESS_END (1 << 30)
|
||||||
|
#define HE_REGM_TSR4_OAM_CRC10 (1 << 28)
|
||||||
|
#define HE_REGM_TSR4_NULL_CRC10 (1 << 27)
|
||||||
|
#define HE_REGM_TSR4_PROT (1 << 26)
|
||||||
|
#define HE_REGM_TSR4_AAL (3 << 24)
|
||||||
|
#define HE_REGS_TSR4_AAL 24
|
||||||
|
#define HE_REGM_TSR4_AAL_5 0
|
||||||
|
#define HE_REGM_TSR4_AAL_0 1
|
||||||
|
#define HE_REGM_TSR4_AAL_0T 2
|
||||||
|
|
||||||
|
#define HE_REGM_TSR9_INIT 0x00100000
|
||||||
|
|
||||||
|
#define HE_REGM_TSR11_ICR (0x7fff << 16)
|
||||||
|
#define HE_REGS_TSR11_ICR 16
|
||||||
|
#define HE_REGM_TSR11_TRM (0x7 << 13)
|
||||||
|
#define HE_REGS_TSR11_TRM 13
|
||||||
|
#define HE_REGM_TSR11_NRM (0x7 << 10)
|
||||||
|
#define HE_REGS_TSR11_NRM 10
|
||||||
|
#define HE_REGM_TSR11_ADTF 0x3ff
|
||||||
|
#define HE_REGS_TSR11_ADTF 0
|
||||||
|
|
||||||
|
#define HE_REGM_TSR13_RDF (0xf << 23)
|
||||||
|
#define HE_REGS_TSR13_RDF 23
|
||||||
|
#define HE_REGM_TSR13_RIF (0xf << 19)
|
||||||
|
#define HE_REGS_TSR13_RIF 19
|
||||||
|
#define HE_REGM_TSR13_CDF (0x7 << 16)
|
||||||
|
#define HE_REGS_TSR13_CDF 16
|
||||||
|
#define HE_REGM_TSR13_CRM 0xffff
|
||||||
|
#define HE_REGS_TSR13_CRM 0
|
||||||
|
|
||||||
|
#define HE_REGM_TSR14_CBR_DELETE (1 << 31)
|
||||||
|
#define HE_REGM_TSR14_ABR_CLOSE (1 << 16)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RSRs
|
||||||
|
*/
|
||||||
|
#define HE_REGO_RSRA(BASE,CID,NR) ((BASE) + ((CID) << 3) + (NR))
|
||||||
|
#define HE_REGO_RSRB(BASE,CID,NR) ((BASE) + ((CID) << 1) + (NR))
|
||||||
|
|
||||||
|
#define HE_REGM_RSR0_PTI7 (1 << 15)
|
||||||
|
#define HE_REGM_RSR0_RM (1 << 14)
|
||||||
|
#define HE_REGM_RSR0_F5OAM (1 << 13)
|
||||||
|
#define HE_REGM_RSR0_STARTPDU (1 << 10)
|
||||||
|
#define HE_REGM_RSR0_OPEN (1 << 6)
|
||||||
|
#define HE_REGM_RSR0_PPD (1 << 5)
|
||||||
|
#define HE_REGM_RSR0_EPD (1 << 4)
|
||||||
|
#define HE_REGM_RSR0_TCPCS (1 << 3)
|
||||||
|
#define HE_REGM_RSR0_AAL 0x7
|
||||||
|
#define HE_REGM_RSR0_AAL_5 0x0
|
||||||
|
#define HE_REGM_RSR0_AAL_0 0x1
|
||||||
|
#define HE_REGM_RSR0_AAL_0T 0x2
|
||||||
|
#define HE_REGM_RSR0_AAL_RAW 0x3
|
||||||
|
#define HE_REGM_RSR0_AAL_RAWCRC10 0x4
|
||||||
|
|
||||||
|
#define HE_REGM_RSR1_AQI (1 << 20)
|
||||||
|
#define HE_REGM_RSR1_RBPL_ONLY (1 << 19)
|
||||||
|
#define HE_REGM_RSR1_GROUP (7 << 16)
|
||||||
|
#define HE_REGS_RSR1_GROUP 16
|
||||||
|
|
||||||
|
#define HE_REGM_RSR4_AQI (1 << 30)
|
||||||
|
#define HE_REGM_RSR4_GROUP (7 << 27)
|
||||||
|
#define HE_REGS_RSR4_GROUP 27
|
||||||
|
#define HE_REGM_RSR4_RBPL_ONLY (1 << 26)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Relative to RCMABR_BA
|
||||||
|
*/
|
||||||
|
#define HE_REGO_CM_GQTBL 0x000
|
||||||
|
#define HE_REGL_CM_GQTBL 0x100
|
||||||
|
#define HE_REGO_CM_RGTBL 0x100
|
||||||
|
#define HE_REGL_CM_RGTBL 0x100
|
||||||
|
#define HE_REGO_CM_TNRMTBL 0x200
|
||||||
|
#define HE_REGL_CM_TNRMTBL 0x100
|
||||||
|
#define HE_REGO_CM_ORCF 0x300
|
||||||
|
#define HE_REGL_CM_ORCF 0x100
|
||||||
|
#define HE_REGO_CM_RTGTBL 0x400
|
||||||
|
#define HE_REGL_CM_RTGTBL 0x200
|
||||||
|
#define HE_REGO_CM_IRCF 0x600
|
||||||
|
#define HE_REGL_CM_IRCF 0x200
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Interrupt Status
|
||||||
|
*/
|
||||||
|
#define HE_REGM_ITYPE 0xf8
|
||||||
|
#define HE_REGM_IGROUP 0x07
|
||||||
|
#define HE_REGM_ITYPE_TBRQ (0x0 << 3)
|
||||||
|
#define HE_REGM_ITYPE_TPD (0x1 << 3)
|
||||||
|
#define HE_REGM_ITYPE_RBPS (0x2 << 3)
|
||||||
|
#define HE_REGM_ITYPE_RBPL (0x3 << 3)
|
||||||
|
#define HE_REGM_ITYPE_RBRQ (0x4 << 3)
|
||||||
|
#define HE_REGM_ITYPE_RBRQT (0x5 << 3)
|
||||||
|
#define HE_REGM_ITYPE_PHYS (0x6 << 3)
|
||||||
|
#define HE_REGM_ITYPE_UNKNOWN 0xf8
|
||||||
|
#define HE_REGM_ITYPE_ERR 0x80
|
||||||
|
#define HE_REGM_ITYPE_PERR 0x81
|
||||||
|
#define HE_REGM_ITYPE_ABORT 0x82
|
||||||
|
#define HE_REGM_ITYPE_INVALID 0xf8
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Serial EEPROM
|
||||||
|
*/
|
||||||
|
#define HE_EEPROM_PROD_ID 0x08
|
||||||
|
#define HE_EEPROM_PROD_ID_LEN 30
|
||||||
|
#define HE_EEPROM_REV 0x26
|
||||||
|
#define HE_EEPROM_REV_LEN 4
|
||||||
|
#define HE_EEPROM_M_SN 0x3A
|
||||||
|
#define HE_EEPROM_MEDIA 0x3E
|
||||||
|
#define HE_EEPROM_MAC 0x42
|
||||||
|
|
||||||
|
#define HE_MEDIA_UTP155 0x06
|
||||||
|
#define HE_MEDIA_MMF155 0x26
|
||||||
|
#define HE_MEDIA_MMF622 0x27
|
||||||
|
#define HE_MEDIA_SMF155 0x46
|
||||||
|
#define HE_MEDIA_SMF622 0x47
|
||||||
|
|
||||||
|
#define HE_622_CLOCK 66667000
|
||||||
|
#define HE_155_CLOCK 50000000
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Statistics
|
||||||
|
*/
|
||||||
|
struct fatm_statshe {
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Queue entries
|
||||||
|
*/
|
||||||
|
/* Receive Buffer Pool Queue entry */
|
||||||
|
struct he_rbpen {
|
||||||
|
uint32_t phys; /* physical address */
|
||||||
|
uint32_t handle; /* handle or virtual address */
|
||||||
|
};
|
||||||
|
/* Receive Buffer Return Queue entry */
|
||||||
|
struct he_rbrqen {
|
||||||
|
uint32_t addr; /* handle and flags */
|
||||||
|
uint32_t len; /* length and CID */
|
||||||
|
};
|
||||||
|
#define HE_REGM_RBRQ_ADDR 0xFFFFFFC0
|
||||||
|
#define HE_REGS_RBRQ_ADDR 6
|
||||||
|
#define HE_REGM_RBRQ_FLAGS 0x0000003F
|
||||||
|
#define HE_REGM_RBRQ_HBUF_ERROR (1 << 0)
|
||||||
|
#define HE_REGM_RBRQ_CON_CLOSED (1 << 1)
|
||||||
|
#define HE_REGM_RBRQ_AAL5_PROT (1 << 2)
|
||||||
|
#define HE_REGM_RBRQ_END_PDU (1 << 3)
|
||||||
|
#define HE_REGM_RBRQ_LEN_ERROR (1 << 4)
|
||||||
|
#define HE_REGM_RBRQ_CRC_ERROR (1 << 5)
|
||||||
|
#define HE_REGM_RBRQ_CID (0x1fff << 16)
|
||||||
|
#define HE_REGS_RBRQ_CID 16
|
||||||
|
#define HE_REGM_RBRQ_LEN 0xffff
|
||||||
|
|
||||||
|
/* Transmit Packet Descriptor Ready Queue entry */
|
||||||
|
struct he_tpdrqen {
|
||||||
|
uint32_t tpd; /* physical address */
|
||||||
|
uint32_t cid; /* connection id */
|
||||||
|
};
|
||||||
|
/* Transmit buffer return queue */
|
||||||
|
struct he_tbrqen {
|
||||||
|
uint32_t addr; /* handle and flags */
|
||||||
|
};
|
||||||
|
#define HE_REGM_TBRQ_ADDR 0xffffffc0
|
||||||
|
#define HE_REGM_TBRQ_FLAGS 0x0000000a
|
||||||
|
#define HE_REGM_TBRQ_EOS 0x00000008
|
||||||
|
#define HE_REGM_TBRQ_MULT 0x00000002
|
||||||
|
|
||||||
|
struct he_tpd {
|
||||||
|
uint32_t addr; /* handle or virtual address and flags */
|
||||||
|
uint32_t res; /* reserved */
|
||||||
|
struct {
|
||||||
|
uint32_t addr; /* buffer address */
|
||||||
|
uint32_t len; /* buffer length and flags */
|
||||||
|
} bufs[3];
|
||||||
|
};
|
||||||
|
#define HE_REGM_TPD_ADDR 0xffffffC0
|
||||||
|
#define HE_REGS_TPD_ADDR 6
|
||||||
|
#define HE_REGM_TPD_INTR 0x0001
|
||||||
|
#define HE_REGM_TPD_CLP 0x0002
|
||||||
|
#define HE_REGM_TPD_EOS 0x0004
|
||||||
|
#define HE_REGM_TPD_PTI 0x0038
|
||||||
|
#define HE_REGS_TPD_PTI 3
|
||||||
|
#define HE_REGM_TPD_LST 0x80000000
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The HOST STATUS PAGE
|
||||||
|
*/
|
||||||
|
struct he_hsp {
|
||||||
|
struct {
|
||||||
|
uint32_t tbrq_tail;
|
||||||
|
uint32_t res1[15];
|
||||||
|
uint32_t rbrq_tail;
|
||||||
|
uint32_t res2[15];
|
||||||
|
} group[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
#define HE_MAX_PDU (65535)
|
619
sys/dev/hatm/if_hatmvar.h
Normal file
619
sys/dev/hatm/if_hatmvar.h
Normal file
@ -0,0 +1,619 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2001-2003
|
||||||
|
* Fraunhofer Institute for Open Communication Systems (FhG Fokus).
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions
|
||||||
|
* are met:
|
||||||
|
* 1. Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||||
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||||
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||||
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||||
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||||
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||||
|
* SUCH DAMAGE.
|
||||||
|
*
|
||||||
|
* Author: Hartmut Brandt <harti@freebsd.org>
|
||||||
|
*
|
||||||
|
* $FreeBSD$
|
||||||
|
*
|
||||||
|
* Fore HE driver for NATM
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Debug statistics of the HE driver
|
||||||
|
*/
|
||||||
|
struct istats {
|
||||||
|
uint32_t tdprq_full;
|
||||||
|
uint32_t hbuf_error;
|
||||||
|
uint32_t crc_error;
|
||||||
|
uint32_t len_error;
|
||||||
|
uint32_t flow_closed;
|
||||||
|
uint32_t flow_drop;
|
||||||
|
uint32_t tpd_no_mem;
|
||||||
|
uint32_t rx_seg;
|
||||||
|
uint32_t empty_hbuf;
|
||||||
|
uint32_t short_aal5;
|
||||||
|
uint32_t badlen_aal5;
|
||||||
|
uint32_t bug_bad_isw;
|
||||||
|
uint32_t bug_no_irq_upd;
|
||||||
|
uint32_t itype_tbrq;
|
||||||
|
uint32_t itype_tpd;
|
||||||
|
uint32_t itype_rbps;
|
||||||
|
uint32_t itype_rbpl;
|
||||||
|
uint32_t itype_rbrq;
|
||||||
|
uint32_t itype_rbrqt;
|
||||||
|
uint32_t itype_unknown;
|
||||||
|
uint32_t itype_phys;
|
||||||
|
uint32_t itype_err;
|
||||||
|
uint32_t defrag;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Card memory layout parameters */
|
||||||
|
#define HE_CONFIG_MEM_LAYOUT { \
|
||||||
|
{ /* 155 */ \
|
||||||
|
20, /* cells_per_row */ \
|
||||||
|
1024, /* bytes_per_row */ \
|
||||||
|
512, /* r0_numrows */ \
|
||||||
|
1018, /* tx_numrows */ \
|
||||||
|
512, /* r1_numrows */ \
|
||||||
|
6, /* r0_startrow */ \
|
||||||
|
2 /* cells_per_lbuf */ \
|
||||||
|
}, { /* 622 */ \
|
||||||
|
40, /* cells_per_row */ \
|
||||||
|
2048, /* bytes_per_row */ \
|
||||||
|
256, /* r0_numrows */ \
|
||||||
|
512, /* tx_numrows */ \
|
||||||
|
256, /* r1_numrows */ \
|
||||||
|
0, /* r0_startrow */ \
|
||||||
|
4 /* cells_per_lbuf */ \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
/*********************************************************************/
|
||||||
|
struct hatm_softc;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A chunk of DMA-able memory
|
||||||
|
*/
|
||||||
|
struct dmamem {
|
||||||
|
u_int size; /* in bytes */
|
||||||
|
u_int align; /* alignement */
|
||||||
|
bus_dma_tag_t tag; /* DMA tag */
|
||||||
|
void *base; /* the memory */
|
||||||
|
bus_addr_t paddr; /* physical address */
|
||||||
|
bus_dmamap_t map; /* the MAP */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RBP (Receive Buffer Pool) queue entry and queue.
|
||||||
|
*/
|
||||||
|
struct herbp {
|
||||||
|
u_int size; /* RBP number of entries (power of two) */
|
||||||
|
u_int thresh; /* interrupt treshold */
|
||||||
|
uint32_t bsize; /* buffer size in bytes */
|
||||||
|
u_int offset; /* free space at start for small bufs */
|
||||||
|
uint32_t mask; /* mask for index */
|
||||||
|
struct dmamem mem; /* the queue area */
|
||||||
|
struct he_rbpen *rbp;
|
||||||
|
uint32_t head, tail; /* head and tail */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RBRQ (Receive Buffer Return Queue) entry and queue.
|
||||||
|
*/
|
||||||
|
struct herbrq {
|
||||||
|
u_int size; /* number of entries */
|
||||||
|
u_int thresh; /* interrupt threshold */
|
||||||
|
u_int tout; /* timeout value */
|
||||||
|
u_int pcnt; /* packet count threshold */
|
||||||
|
struct dmamem mem; /* memory */
|
||||||
|
struct he_rbrqen *rbrq;
|
||||||
|
uint32_t head; /* driver end */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TPDRQ (Transmit Packet Descriptor Ready Queue) entry and queue
|
||||||
|
*/
|
||||||
|
struct hetpdrq {
|
||||||
|
u_int size; /* number of entries */
|
||||||
|
struct dmamem mem; /* memory */
|
||||||
|
struct he_tpdrqen *tpdrq;
|
||||||
|
u_int head; /* head (copy of adapter) */
|
||||||
|
u_int tail; /* written back to adapter */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TBRQ (Transmit Buffer Return Queue) entry and queue
|
||||||
|
*/
|
||||||
|
struct hetbrq {
|
||||||
|
u_int size; /* number of entries */
|
||||||
|
u_int thresh; /* interrupt threshold */
|
||||||
|
struct dmamem mem; /* memory */
|
||||||
|
struct he_tbrqen *tbrq;
|
||||||
|
u_int head; /* adapter end */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*==================================================================*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TPDs are 32 byte and must be aligned on 64 byte boundaries. That means,
|
||||||
|
* that half of the space is free. We use this space to plug in a link for
|
||||||
|
* the list of free TPDs. Note, that the m_act member of the mbufs contain
|
||||||
|
* a pointer to the dmamap.
|
||||||
|
*
|
||||||
|
* The maximum number of TDPs is the size of the common transmit packet
|
||||||
|
* descriptor ready queue plus the sizes of the transmit buffer return queues
|
||||||
|
* (currently only queue 0). We allocate and map these TPD when initializing
|
||||||
|
* the card. We also allocate on DMA map for each TPD. Only the map in the
|
||||||
|
* last TPD of a packets is used when a packet is transmitted.
|
||||||
|
* This is signalled by having the mbuf member of this TPD non-zero and
|
||||||
|
* pointing to the mbuf.
|
||||||
|
*/
|
||||||
|
#define HE_TPD_SIZE 64
|
||||||
|
struct tpd {
|
||||||
|
struct he_tpd tpd; /* at beginning */
|
||||||
|
SLIST_ENTRY(tpd) link; /* free cid list link */
|
||||||
|
struct mbuf *mbuf; /* the buf chain */
|
||||||
|
bus_dmamap_t map; /* map */
|
||||||
|
uint32_t cid; /* CID */
|
||||||
|
uint16_t no; /* number of this tpd */
|
||||||
|
};
|
||||||
|
SLIST_HEAD(tpd_list, tpd);
|
||||||
|
|
||||||
|
#define TPD_SET_USED(SC, I) do { \
|
||||||
|
(SC)->tpd_used[(I) / 8] |= (1 << ((I) % 8)); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define TPD_CLR_USED(SC, I) do { \
|
||||||
|
(SC)->tpd_used[(I) / 8] &= ~(1 << ((I) % 8)); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define TPD_TST_USED(SC, I) ((SC)->tpd_used[(I) / 8] & (1 << ((I) % 8)))
|
||||||
|
|
||||||
|
#define TPD_ADDR(SC, I) ((struct tpd *)((char *)sc->tpds.base + \
|
||||||
|
(I) * HE_TPD_SIZE))
|
||||||
|
|
||||||
|
/*==================================================================*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* External MBUFs. The card needs a lot of mbufs in the pools for high
|
||||||
|
* performance. The problem with using mbufs directly is that we would need
|
||||||
|
* a dmamap for each of the mbufs. This can exhaust iommu space on the sparc
|
||||||
|
* and it eats also a lot of processing time. So we use external mbufs
|
||||||
|
* for the small buffers and clusters for the large buffers.
|
||||||
|
* For receive group 0 we use 5 ATM cells, for group 1 one (52 byte) ATM
|
||||||
|
* cell. The mbuf storage is allocated pagewise and one dmamap is used per
|
||||||
|
* page.
|
||||||
|
*
|
||||||
|
* The handle we give to the card for the small buffers is a word combined
|
||||||
|
* of the page number and the number of the chunk in the page. This restricts
|
||||||
|
* the number of chunks per page to 256 (8 bit) and the number of pages to
|
||||||
|
* 65536 (16 bits).
|
||||||
|
*
|
||||||
|
* A chunk may be in one of three states: free, on the card and floating around
|
||||||
|
* in the system. If it is free, it is on one of the two free lists and
|
||||||
|
* start with a struct mbufx_free. Each page has a bitmap that tracks where
|
||||||
|
* its chunks are.
|
||||||
|
*
|
||||||
|
* For large buffers we use mbuf clusters. Here we have two problems: we need
|
||||||
|
* to track the buffers on the card (in the case we want to stop it) and
|
||||||
|
* we need to map the 64bit mbuf address to a 26bit handle for 64-bit machines.
|
||||||
|
* The card uses the buffers in the order we give it to the card. Therefor
|
||||||
|
* we can use a private array holding pointers to the mbufs as a circular
|
||||||
|
* queue for both tasks. This is done with the lbufs member of softc. The
|
||||||
|
* handle for these buffer is the lbufs index ored with a flag.
|
||||||
|
*/
|
||||||
|
#define MBUF0_SIZE (5 * 48) /* 240 */
|
||||||
|
#define MBUF1_SIZE (52)
|
||||||
|
|
||||||
|
#define MBUF0_CHUNK 256 /* 16 free bytes */
|
||||||
|
#define MBUF1_CHUNK 96 /* 44 free bytes */
|
||||||
|
#ifdef XXX
|
||||||
|
#define MBUF0_OFFSET (MBUF0_CHUNK - sizeof(struct mbuf_chunk_hdr) \
|
||||||
|
- MBUF0_SIZE)
|
||||||
|
#else
|
||||||
|
#define MBUF0_OFFSET 0
|
||||||
|
#endif
|
||||||
|
#define MBUF1_OFFSET (MBUF1_CHUNK - sizeof(struct mbuf_chunk_hdr) \
|
||||||
|
- MBUF1_SIZE)
|
||||||
|
#define MBUFL_OFFSET 16 /* two pointers for HARP */
|
||||||
|
|
||||||
|
#define MBUF_ALLOC_SIZE (PAGE_SIZE)
|
||||||
|
|
||||||
|
/* each allocated page has one of these structures at its very end. */
|
||||||
|
struct mbuf_page_hdr {
|
||||||
|
uint8_t card[32]; /* bitmap for on-card */
|
||||||
|
uint8_t used[32]; /* bitmap for used but not on-card */
|
||||||
|
uint16_t nchunks; /* chunks on this page */
|
||||||
|
bus_dmamap_t map; /* the DMA MAP */
|
||||||
|
uint32_t phys; /* physical base address */
|
||||||
|
uint32_t hdroff; /* chunk header offset */
|
||||||
|
uint32_t chunksize; /* chunk size */
|
||||||
|
};
|
||||||
|
struct mbuf_page {
|
||||||
|
char storage[MBUF_ALLOC_SIZE - sizeof(struct mbuf_page_hdr)];
|
||||||
|
struct mbuf_page_hdr hdr;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* numbers per page */
|
||||||
|
#define MBUF0_PER_PAGE ((MBUF_ALLOC_SIZE - sizeof(struct mbuf_page_hdr)) / \
|
||||||
|
MBUF0_CHUNK)
|
||||||
|
#define MBUF1_PER_PAGE ((MBUF_ALLOC_SIZE - sizeof(struct mbuf_page_hdr)) / \
|
||||||
|
MBUF1_CHUNK)
|
||||||
|
|
||||||
|
#define MBUF_CLR_BIT(ARRAY, BIT) ((ARRAY)[(BIT) / 8] &= ~(1 << ((BIT) % 8)))
|
||||||
|
#define MBUF_SET_BIT(ARRAY, BIT) ((ARRAY)[(BIT) / 8] |= (1 << ((BIT) % 8)))
|
||||||
|
#define MBUF_TST_BIT(ARRAY, BIT) ((ARRAY)[(BIT) / 8] & (1 << ((BIT) % 8)))
|
||||||
|
|
||||||
|
#define MBUF_MAKE_HANDLE(PAGENO, CHUNKNO) \
|
||||||
|
(((PAGENO) << 10) | (CHUNKNO))
|
||||||
|
|
||||||
|
#define MBUF_PARSE_HANDLE(HANDLE, PAGENO, CHUNKNO) do { \
|
||||||
|
(CHUNKNO) = (HANDLE) & 0x3ff; \
|
||||||
|
(PAGENO) = ((HANDLE) >> 10) & 0x3ff; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define MBUF_LARGE_FLAG (1 << 20)
|
||||||
|
|
||||||
|
/* chunks have the following structure at the end */
|
||||||
|
struct mbuf_chunk_hdr {
|
||||||
|
struct mbuf *mbuf;
|
||||||
|
uint16_t pageno;
|
||||||
|
uint16_t chunkno;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MBUFX_STORAGE_SIZE(X) (MBUF##X##_CHUNK \
|
||||||
|
- sizeof(struct mbuf_chunk_hdr))
|
||||||
|
|
||||||
|
struct mbuf0_chunk {
|
||||||
|
char storage[MBUFX_STORAGE_SIZE(0)];
|
||||||
|
struct mbuf_chunk_hdr hdr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mbuf1_chunk {
|
||||||
|
char storage[MBUFX_STORAGE_SIZE(1)];
|
||||||
|
struct mbuf_chunk_hdr hdr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mbufx_free {
|
||||||
|
SLIST_ENTRY(mbufx_free) link;
|
||||||
|
};
|
||||||
|
SLIST_HEAD(mbufx_free_list, mbufx_free);
|
||||||
|
|
||||||
|
/*==================================================================*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Interrupt queue
|
||||||
|
*/
|
||||||
|
struct heirq {
|
||||||
|
u_int size; /* number of entries */
|
||||||
|
u_int thresh; /* re-interrupt threshold */
|
||||||
|
u_int line; /* interrupt line to use */
|
||||||
|
struct dmamem mem; /* interrupt queues */
|
||||||
|
uint32_t * irq; /* interrupt queue */
|
||||||
|
uint32_t head; /* head index */
|
||||||
|
uint32_t * tailp; /* pointer to tail */
|
||||||
|
struct hatm_softc *sc; /* back pointer */
|
||||||
|
u_int group; /* interrupt group */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This structure describes all information for a VCC open on the card.
|
||||||
|
* The array of these structures is indexed by the compressed connection ID
|
||||||
|
* (CID).
|
||||||
|
*/
|
||||||
|
struct hevcc {
|
||||||
|
u_int vflags; /* private flags */
|
||||||
|
void * rxhand; /* NATM protocol block */
|
||||||
|
u_int rc; /* rate control group for CBR */
|
||||||
|
struct mbuf * chain; /* partial received PDU */
|
||||||
|
struct mbuf * last; /* last mbuf in chain */
|
||||||
|
|
||||||
|
/* from the OPEN_VCC ioctl */
|
||||||
|
struct atmio_vcc param; /* traffic parameters */
|
||||||
|
|
||||||
|
uint32_t ibytes;
|
||||||
|
uint32_t ipackets;
|
||||||
|
uint32_t obytes;
|
||||||
|
uint32_t opackets;
|
||||||
|
u_int ntpds; /* number of active TPDs */
|
||||||
|
};
|
||||||
|
#define HE_VCC_OPEN 0x000f0000
|
||||||
|
#define HE_VCC_RX_OPEN 0x00010000
|
||||||
|
#define HE_VCC_RX_CLOSING 0x00020000
|
||||||
|
#define HE_VCC_TX_OPEN 0x00040000
|
||||||
|
#define HE_VCC_TX_CLOSING 0x00080000
|
||||||
|
#define HE_VCC_FLOW_CTRL 0x00100000
|
||||||
|
#define HE_VCC_ASYNC 0x00200000
|
||||||
|
|
||||||
|
/*
|
||||||
|
* CBR rate groups
|
||||||
|
*/
|
||||||
|
struct herg {
|
||||||
|
u_int refcnt; /* how many connections reference this group */
|
||||||
|
u_int rate; /* the value */
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Softc
|
||||||
|
*/
|
||||||
|
struct hatm_softc {
|
||||||
|
struct ifatm ifatm; /* common ATM stuff */
|
||||||
|
struct mtx mtx; /* lock */
|
||||||
|
struct ifmedia media; /* media */
|
||||||
|
device_t dev; /* device */
|
||||||
|
int memid; /* resoure id for memory */
|
||||||
|
struct resource * memres; /* memory resource */
|
||||||
|
bus_space_handle_t memh; /* handle */
|
||||||
|
bus_space_tag_t memt; /* ... and tag */
|
||||||
|
bus_dma_tag_t parent_tag; /* global restriction */
|
||||||
|
struct cv vcc_cv; /* condition variable */
|
||||||
|
int irqid; /* resource id */
|
||||||
|
struct resource * irqres; /* resource */
|
||||||
|
void * ih; /* interrupt handle */
|
||||||
|
struct utopia utopia; /* utopia state */
|
||||||
|
|
||||||
|
/* rest has to be reset by stop */
|
||||||
|
int he622; /* this is a HE622 */
|
||||||
|
int pci64; /* 64bit bus */
|
||||||
|
char prod_id[HE_EEPROM_PROD_ID_LEN + 1];
|
||||||
|
char rev[HE_EEPROM_REV_LEN + 1];
|
||||||
|
struct heirq irq_0; /* interrupt queues 0 */
|
||||||
|
|
||||||
|
/* generic network controller state */
|
||||||
|
u_int cells_per_row;
|
||||||
|
u_int bytes_per_row;
|
||||||
|
u_int r0_numrows;
|
||||||
|
u_int tx_numrows;
|
||||||
|
u_int r1_numrows;
|
||||||
|
u_int r0_startrow;
|
||||||
|
u_int tx_startrow;
|
||||||
|
u_int r1_startrow;
|
||||||
|
u_int cells_per_lbuf;
|
||||||
|
u_int r0_numbuffs;
|
||||||
|
u_int r1_numbuffs;
|
||||||
|
u_int tx_numbuffs;
|
||||||
|
|
||||||
|
/* HSP */
|
||||||
|
struct he_hsp *hsp;
|
||||||
|
struct dmamem hsp_mem;
|
||||||
|
|
||||||
|
/*** TX ***/
|
||||||
|
struct hetbrq tbrq; /* TBRQ 0 */
|
||||||
|
struct hetpdrq tpdrq; /* TPDRQ */
|
||||||
|
struct tpd_list tpd_free; /* Free TPDs */
|
||||||
|
u_int tpd_nfree; /* number of free TPDs */
|
||||||
|
u_int tpd_total; /* total TPDs */
|
||||||
|
uint8_t *tpd_used; /* bitmap of used TPDs */
|
||||||
|
struct dmamem tpds; /* TPD memory */
|
||||||
|
bus_dma_tag_t tx_tag; /* DMA tag for all tx mbufs */
|
||||||
|
|
||||||
|
/*** RX ***/
|
||||||
|
/* receive/transmit groups */
|
||||||
|
struct herbp rbp_s0; /* RBPS0 */
|
||||||
|
struct herbp rbp_l0; /* RBPL0 */
|
||||||
|
struct herbp rbp_s1; /* RBPS1 */
|
||||||
|
struct herbrq rbrq_0; /* RBRQ0 */
|
||||||
|
struct herbrq rbrq_1; /* RBRQ1 */
|
||||||
|
|
||||||
|
/* list of external mbuf storage */
|
||||||
|
bus_dma_tag_t mbuf_tag;
|
||||||
|
struct mbuf_page **mbuf_pages;
|
||||||
|
u_int mbuf_npages;
|
||||||
|
struct mtx mbuf0_mtx;
|
||||||
|
struct mbufx_free_list mbuf0_list;
|
||||||
|
struct mtx mbuf1_mtx;
|
||||||
|
struct mbufx_free_list mbuf1_list;
|
||||||
|
|
||||||
|
/* mbuf cluster tracking and mapping for group 0 */
|
||||||
|
struct mbuf **lbufs; /* mbufs */
|
||||||
|
bus_dmamap_t *rmaps; /* DMA maps */
|
||||||
|
u_int lbufs_size;
|
||||||
|
u_int lbufs_next;
|
||||||
|
|
||||||
|
/* VCCs */
|
||||||
|
struct hevcc *vccs[HE_MAX_VCCS];
|
||||||
|
u_int cbr_bw; /* BW allocated to CBR */
|
||||||
|
u_int max_tpd; /* per VCC */
|
||||||
|
u_int open_vccs;
|
||||||
|
uma_zone_t vcc_zone;
|
||||||
|
|
||||||
|
/* rate groups */
|
||||||
|
struct herg rate_ctrl[HE_REGN_CS_STPER];
|
||||||
|
|
||||||
|
/* memory offsets */
|
||||||
|
u_int tsrb, tsrc, tsrd;
|
||||||
|
u_int rsrb;
|
||||||
|
|
||||||
|
struct cv cv_rcclose; /* condition variable */
|
||||||
|
uint32_t rate_grid[16][16]; /* our copy */
|
||||||
|
|
||||||
|
/* sysctl support */
|
||||||
|
struct sysctl_ctx_list sysctl_ctx;
|
||||||
|
struct sysctl_oid *sysctl_tree;
|
||||||
|
|
||||||
|
/* internal statistics */
|
||||||
|
struct istats istats;
|
||||||
|
|
||||||
|
#ifdef HATM_DEBUG
|
||||||
|
/* debugging */
|
||||||
|
u_int debug;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
#define READ4(SC,OFF) bus_space_read_4(SC->memt, SC->memh, (OFF))
|
||||||
|
#define READ2(SC,OFF) bus_space_read_2(SC->memt, SC->memh, (OFF))
|
||||||
|
#define READ1(SC,OFF) bus_space_read_1(SC->memt, SC->memh, (OFF))
|
||||||
|
|
||||||
|
#define WRITE4(SC,OFF,VAL) bus_space_write_4(SC->memt, SC->memh, (OFF), (VAL))
|
||||||
|
#define WRITE2(SC,OFF,VAL) bus_space_write_2(SC->memt, SC->memh, (OFF), (VAL))
|
||||||
|
#define WRITE1(SC,OFF,VAL) bus_space_write_1(SC->memt, SC->memh, (OFF), (VAL))
|
||||||
|
|
||||||
|
#define BARRIER_R(SC) bus_space_barrier(SC->memt, SC->memh, 0, HE_REGO_END, \
|
||||||
|
BUS_SPACE_BARRIER_READ)
|
||||||
|
#define BARRIER_W(SC) bus_space_barrier(SC->memt, SC->memh, 0, HE_REGO_END, \
|
||||||
|
BUS_SPACE_BARRIER_WRITE)
|
||||||
|
#define BARRIER_RW(SC) bus_space_barrier(SC->memt, SC->memh, 0, HE_REGO_END, \
|
||||||
|
BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)
|
||||||
|
|
||||||
|
#define READ_SUNI(SC,OFF) READ4(SC, HE_REGO_SUNI + 4 * (OFF))
|
||||||
|
#define WRITE_SUNI(SC,OFF,VAL) WRITE4(SC, HE_REGO_SUNI + 4 * (OFF), (VAL))
|
||||||
|
|
||||||
|
#define READ_LB4(SC,OFF) \
|
||||||
|
({ \
|
||||||
|
WRITE4(SC, HE_REGO_LB_MEM_ADDR, (OFF)); \
|
||||||
|
WRITE4(SC, HE_REGO_LB_MEM_ACCESS, \
|
||||||
|
(HE_REGM_LB_MEM_HNDSHK | HE_REGM_LB_MEM_READ)); \
|
||||||
|
while((READ4(SC, HE_REGO_LB_MEM_ACCESS) & HE_REGM_LB_MEM_HNDSHK))\
|
||||||
|
; \
|
||||||
|
READ4(SC, HE_REGO_LB_MEM_DATA); \
|
||||||
|
})
|
||||||
|
#define WRITE_LB4(SC,OFF,VAL) \
|
||||||
|
do { \
|
||||||
|
WRITE4(SC, HE_REGO_LB_MEM_ADDR, (OFF)); \
|
||||||
|
WRITE4(SC, HE_REGO_LB_MEM_DATA, (VAL)); \
|
||||||
|
WRITE4(SC, HE_REGO_LB_MEM_ACCESS, \
|
||||||
|
(HE_REGM_LB_MEM_HNDSHK | HE_REGM_LB_MEM_WRITE)); \
|
||||||
|
while((READ4(SC, HE_REGO_LB_MEM_ACCESS) & HE_REGM_LB_MEM_HNDSHK))\
|
||||||
|
; \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define WRITE_MEM4(SC,OFF,VAL,SPACE) \
|
||||||
|
do { \
|
||||||
|
WRITE4(SC, HE_REGO_CON_DAT, (VAL)); \
|
||||||
|
WRITE4(SC, HE_REGO_CON_CTL, \
|
||||||
|
(SPACE | HE_REGM_CON_WE | HE_REGM_CON_STATUS | (OFF))); \
|
||||||
|
while((READ4(SC, HE_REGO_CON_CTL) & HE_REGM_CON_STATUS) != 0) \
|
||||||
|
; \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define READ_MEM4(SC,OFF,SPACE) \
|
||||||
|
({ \
|
||||||
|
WRITE4(SC, HE_REGO_CON_CTL, \
|
||||||
|
(SPACE | HE_REGM_CON_STATUS | (OFF))); \
|
||||||
|
while((READ4(SC, HE_REGO_CON_CTL) & HE_REGM_CON_STATUS) != 0) \
|
||||||
|
; \
|
||||||
|
READ4(SC, HE_REGO_CON_DAT); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define WRITE_TCM4(SC,OFF,VAL) WRITE_MEM4(SC,(OFF),(VAL),HE_REGM_CON_TCM)
|
||||||
|
#define WRITE_RCM4(SC,OFF,VAL) WRITE_MEM4(SC,(OFF),(VAL),HE_REGM_CON_RCM)
|
||||||
|
#define WRITE_MBOX4(SC,OFF,VAL) WRITE_MEM4(SC,(OFF),(VAL),HE_REGM_CON_MBOX)
|
||||||
|
|
||||||
|
#define READ_TCM4(SC,OFF) READ_MEM4(SC,(OFF),HE_REGM_CON_TCM)
|
||||||
|
#define READ_RCM4(SC,OFF) READ_MEM4(SC,(OFF),HE_REGM_CON_RCM)
|
||||||
|
#define READ_MBOX4(SC,OFF) READ_MEM4(SC,(OFF),HE_REGM_CON_MBOX)
|
||||||
|
|
||||||
|
#define WRITE_TCM(SC,OFF,BYTES,VAL) \
|
||||||
|
WRITE_MEM4(SC,(OFF) | ((~(BYTES) & 0xf) << HE_REGS_CON_DIS), \
|
||||||
|
(VAL), HE_REGM_CON_TCM)
|
||||||
|
#define WRITE_RCM(SC,OFF,BYTES,VAL) \
|
||||||
|
WRITE_MEM4(SC,(OFF) | ((~(BYTES) & 0xf) << HE_REGS_CON_DIS), \
|
||||||
|
(VAL), HE_REGM_CON_RCM)
|
||||||
|
|
||||||
|
#define READ_TSR(SC,CID,NR) \
|
||||||
|
({ \
|
||||||
|
uint32_t _v; \
|
||||||
|
if((NR) <= 7) { \
|
||||||
|
_v = READ_TCM4(SC, HE_REGO_TSRA(0,CID,NR)); \
|
||||||
|
} else if((NR) <= 11) { \
|
||||||
|
_v = READ_TCM4(SC, HE_REGO_TSRB((SC)->tsrb,CID,(NR-8)));\
|
||||||
|
} else if((NR) <= 13) { \
|
||||||
|
_v = READ_TCM4(SC, HE_REGO_TSRC((SC)->tsrc,CID,(NR-12)));\
|
||||||
|
} else { \
|
||||||
|
_v = READ_TCM4(SC, HE_REGO_TSRD((SC)->tsrd,CID)); \
|
||||||
|
} \
|
||||||
|
_v; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define WRITE_TSR(SC,CID,NR,BEN,VAL) \
|
||||||
|
do { \
|
||||||
|
if((NR) <= 7) { \
|
||||||
|
WRITE_TCM(SC, HE_REGO_TSRA(0,CID,NR),BEN,VAL); \
|
||||||
|
} else if((NR) <= 11) { \
|
||||||
|
WRITE_TCM(SC, HE_REGO_TSRB((SC)->tsrb,CID,(NR-8)),BEN,VAL);\
|
||||||
|
} else if((NR) <= 13) { \
|
||||||
|
WRITE_TCM(SC, HE_REGO_TSRC((SC)->tsrc,CID,(NR-12)),BEN,VAL);\
|
||||||
|
} else { \
|
||||||
|
WRITE_TCM(SC, HE_REGO_TSRD((SC)->tsrd,CID),BEN,VAL); \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#define READ_RSR(SC,CID,NR) \
|
||||||
|
({ \
|
||||||
|
uint32_t _v; \
|
||||||
|
if((NR) <= 7) { \
|
||||||
|
_v = READ_RCM4(SC, HE_REGO_RSRA(0,CID,NR)); \
|
||||||
|
} else { \
|
||||||
|
_v = READ_RCM4(SC, HE_REGO_RSRB((SC)->rsrb,CID,(NR-8)));\
|
||||||
|
} \
|
||||||
|
_v; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define WRITE_RSR(SC,CID,NR,BEN,VAL) \
|
||||||
|
do { \
|
||||||
|
if((NR) <= 7) { \
|
||||||
|
WRITE_RCM(SC, HE_REGO_RSRA(0,CID,NR),BEN,VAL); \
|
||||||
|
} else { \
|
||||||
|
WRITE_RCM(SC, HE_REGO_RSRB((SC)->rsrb,CID,(NR-8)),BEN,VAL);\
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
#ifdef HATM_DEBUG
|
||||||
|
#define DBG(SC, FL, PRINT) do { \
|
||||||
|
if((SC)->debug & DBG_##FL) { \
|
||||||
|
if_printf(&(SC)->ifatm.ifnet, "%s: ", __func__); \
|
||||||
|
printf PRINT; \
|
||||||
|
printf("\n"); \
|
||||||
|
} \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
enum {
|
||||||
|
DBG_RX = 0x0001,
|
||||||
|
DBG_TX = 0x0002,
|
||||||
|
DBG_VCC = 0x0004,
|
||||||
|
DBG_IOCTL = 0x0008,
|
||||||
|
DBG_ATTACH = 0x0010,
|
||||||
|
DBG_INTR = 0x0020,
|
||||||
|
DBG_DMA = 0x0040,
|
||||||
|
DBG_DMAH = 0x0080,
|
||||||
|
|
||||||
|
DBG_ALL = 0x00ff
|
||||||
|
};
|
||||||
|
|
||||||
|
#else
|
||||||
|
#define DBG(SC, FL, PRINT)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
u_int hatm_cps2atmf(uint32_t);
|
||||||
|
u_int hatm_atmf2cps(uint32_t);
|
||||||
|
|
||||||
|
void hatm_intr(void *);
|
||||||
|
int hatm_ioctl(struct ifnet *, u_long, caddr_t);
|
||||||
|
void hatm_initialize(struct hatm_softc *);
|
||||||
|
void hatm_stop(struct hatm_softc *sc);
|
||||||
|
void hatm_start(struct ifnet *);
|
||||||
|
|
||||||
|
void hatm_rx(struct hatm_softc *sc, u_int cid, u_int flags, struct mbuf *m,
|
||||||
|
u_int len);
|
||||||
|
void hatm_tx_complete(struct hatm_softc *sc, struct tpd *tpd, uint32_t);
|
||||||
|
|
||||||
|
int hatm_tx_vcc_can_open(struct hatm_softc *sc, u_int cid, struct hevcc *);
|
||||||
|
void hatm_tx_vcc_open(struct hatm_softc *sc, u_int cid);
|
||||||
|
void hatm_rx_vcc_open(struct hatm_softc *sc, u_int cid);
|
||||||
|
void hatm_tx_vcc_close(struct hatm_softc *sc, u_int cid);
|
||||||
|
void hatm_rx_vcc_close(struct hatm_softc *sc, u_int cid);
|
||||||
|
void hatm_tx_vcc_closed(struct hatm_softc *sc, u_int cid);
|
||||||
|
void hatm_vcc_closed(struct hatm_softc *sc, u_int cid);
|
@ -36,6 +36,7 @@ SUBDIR= accf_data \
|
|||||||
fxp \
|
fxp \
|
||||||
geom \
|
geom \
|
||||||
gx \
|
gx \
|
||||||
|
hatm \
|
||||||
hifn \
|
hifn \
|
||||||
if_disc \
|
if_disc \
|
||||||
if_ef \
|
if_ef \
|
||||||
|
21
sys/modules/hatm/Makefile
Normal file
21
sys/modules/hatm/Makefile
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# $FreeBSD$
|
||||||
|
#
|
||||||
|
# Author: Harti Brandt <harti@freebsd.org>
|
||||||
|
#
|
||||||
|
.PATH: ${.CURDIR}/../../dev/hatm
|
||||||
|
|
||||||
|
KMOD= if_hatm
|
||||||
|
SRCS= if_hatm.c if_hatm_intr.c if_hatm_ioctl.c if_hatm_tx.c if_hatm_rx.c \
|
||||||
|
device_if.h bus_if.h pci_if.h opt_inet.h opt_natm.h
|
||||||
|
|
||||||
|
CFLAGS+= -DENABLE_BPF
|
||||||
|
# CFLAGS+= -DHATM_DEBUG -DINVARIANT_SUPPORT -DINVARIANTS -g
|
||||||
|
# LDFLAGS+= -g
|
||||||
|
|
||||||
|
opt_inet.h:
|
||||||
|
echo "#define INET 1" > opt_inet.h
|
||||||
|
|
||||||
|
opt_natm.h:
|
||||||
|
echo "#define NATM 1" > opt_natm.h
|
||||||
|
|
||||||
|
.include <bsd.kmod.mk>
|
Loading…
x
Reference in New Issue
Block a user