- Rewritten TX to use only two pointers to track producer/consumer.

- Added polling(4) support!
- Bugfix: don't forget to set IFF_OACTIVE when TX list is full.
- Minor: tidy up vr_encap().
This commit is contained in:
ru 2004-04-05 17:39:57 +00:00
parent 687078ddd6
commit ab092d4c0c
4 changed files with 332 additions and 264 deletions

View File

@ -137,7 +137,6 @@ static int vr_encap (struct vr_softc *, struct vr_chain *,
static void vr_rxeof (struct vr_softc *);
static void vr_rxeoc (struct vr_softc *);
static void vr_txeof (struct vr_softc *);
static void vr_txeoc (struct vr_softc *);
static void vr_tick (void *);
static void vr_intr (void *);
static void vr_start (struct ifnet *);
@ -952,8 +951,7 @@ vr_list_tx_init(sc)
&cd->vr_tx_chain[i + 1];
}
cd->vr_tx_free = &cd->vr_tx_chain[0];
cd->vr_tx_tail = cd->vr_tx_head = NULL;
cd->vr_tx_cons = cd->vr_tx_prod = &cd->vr_tx_chain[0];
return(0);
}
@ -1048,7 +1046,7 @@ static void
vr_rxeof(sc)
struct vr_softc *sc;
{
struct mbuf *m;
struct mbuf *m, *m0;
struct ifnet *ifp;
struct vr_chain_onefrag *cur_rx;
int total_len = 0;
@ -1060,8 +1058,14 @@ vr_rxeof(sc)
while(!((rxstat = sc->vr_cdata.vr_rx_head->vr_ptr->vr_status) &
VR_RXSTAT_OWN)) {
struct mbuf *m0 = NULL;
#ifdef DEVICE_POLLING
if (ifp->if_flags & IFF_POLLING) {
if (sc->rxcycles <= 0)
break;
sc->rxcycles--;
}
#endif /* DEVICE_POLLING */
m0 = NULL;
cur_rx = sc->vr_cdata.vr_rx_head;
sc->vr_cdata.vr_rx_head = cur_rx->vr_nextdesc;
m = cur_rx->vr_mbuf;
@ -1173,22 +1177,15 @@ vr_txeof(sc)
ifp = &sc->arpcom.ac_if;
/* Reset the timeout timer; if_txeoc will clear it. */
ifp->if_timer = 5;
/* Sanity check. */
if (sc->vr_cdata.vr_tx_head == NULL)
return;
/*
* Go through our tx list and free mbufs for those
* frames that have been transmitted.
*/
while(sc->vr_cdata.vr_tx_head->vr_mbuf != NULL) {
cur_tx = sc->vr_cdata.vr_tx_cons;
while (cur_tx->vr_mbuf != NULL) {
u_int32_t txstat;
int i;
cur_tx = sc->vr_cdata.vr_tx_head;
txstat = cur_tx->vr_ptr->vr_status;
if ((txstat & VR_TXSTAT_ABRT) ||
@ -1221,41 +1218,15 @@ vr_txeof(sc)
ifp->if_collisions +=(txstat & VR_TXSTAT_COLLCNT) >> 3;
ifp->if_opackets++;
if (cur_tx->vr_mbuf != NULL) {
m_freem(cur_tx->vr_mbuf);
cur_tx->vr_mbuf = NULL;
}
if (sc->vr_cdata.vr_tx_head == sc->vr_cdata.vr_tx_tail) {
sc->vr_cdata.vr_tx_head = NULL;
sc->vr_cdata.vr_tx_tail = NULL;
break;
}
sc->vr_cdata.vr_tx_head = cur_tx->vr_nextdesc;
}
return;
}
/*
* TX 'end of channel' interrupt handler.
*/
static void
vr_txeoc(sc)
struct vr_softc *sc;
{
struct ifnet *ifp;
ifp = &sc->arpcom.ac_if;
if (sc->vr_cdata.vr_tx_head == NULL) {
m_freem(cur_tx->vr_mbuf);
cur_tx->vr_mbuf = NULL;
ifp->if_flags &= ~IFF_OACTIVE;
sc->vr_cdata.vr_tx_tail = NULL;
ifp->if_timer = 0;
}
return;
cur_tx = cur_tx->vr_nextdesc;
}
sc->vr_cdata.vr_tx_cons = cur_tx;
if (cur_tx->vr_mbuf == NULL)
ifp->if_timer = 0;
}
static void
@ -1285,6 +1256,78 @@ vr_tick(xsc)
return;
}
#ifdef DEVICE_POLLING
static poll_handler_t vr_poll;
static void
vr_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
{
struct vr_softc *sc = ifp->if_softc;
VR_LOCK(sc);
if (cmd == POLL_DEREGISTER) { /* final call, enable interrupts */
CSR_WRITE_2(sc, VR_IMR, VR_INTRS);
goto done;
}
sc->rxcycles = count;
vr_rxeof(sc);
vr_txeof(sc);
if (ifp->if_snd.ifq_head != NULL)
vr_start(ifp);
if (cmd == POLL_AND_CHECK_STATUS) { /* also check status register */
u_int16_t status;
status = CSR_READ_2(sc, VR_ISR);
if (status)
CSR_WRITE_2(sc, VR_ISR, status);
if ((status & VR_INTRS) == 0)
goto done;
if (status & VR_ISR_RX_DROPPED) {
printf("vr%d: rx packet lost\n", sc->vr_unit);
ifp->if_ierrors++;
}
if ((status & VR_ISR_RX_ERR) || (status & VR_ISR_RX_NOBUF) ||
(status & VR_ISR_RX_NOBUF) || (status & VR_ISR_RX_OFLOW)) {
printf("vr%d: receive error (%04x)",
sc->vr_unit, status);
if (status & VR_ISR_RX_NOBUF)
printf(" no buffers");
if (status & VR_ISR_RX_OFLOW)
printf(" overflow");
if (status & VR_ISR_RX_DROPPED)
printf(" packet lost");
printf("\n");
vr_rxeoc(sc);
}
if ((status & VR_ISR_BUSERR) || (status & VR_ISR_TX_UNDERRUN)) {
vr_reset(sc);
vr_init(sc);
goto done;
}
if ((status & VR_ISR_UDFI) ||
(status & VR_ISR_TX_ABRT2) ||
(status & VR_ISR_TX_ABRT)) {
ifp->if_oerrors++;
if (sc->vr_cdata.vr_tx_cons->vr_mbuf != NULL) {
VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON);
VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_GO);
}
}
}
done:
VR_UNLOCK(sc);
}
#endif /* DEVICE_POLLING */
static void
vr_intr(arg)
void *arg;
@ -1297,6 +1340,16 @@ vr_intr(arg)
VR_LOCK(sc);
ifp = &sc->arpcom.ac_if;
#ifdef DEVICE_POLLING
if (ifp->if_flags & IFF_POLLING)
goto done;
if (ether_poll_register(vr_poll, ifp)) { /* ok, disable interrupts */
CSR_WRITE_2(sc, VR_IMR, 0x0000);
vr_poll(ifp, 0, 1);
goto done;
}
#endif /* DEVICE_POLLING */
/* Supress unwanted interrupts. */
if (!(ifp->if_flags & IFF_UP)) {
vr_stop(sc);
@ -1351,12 +1404,11 @@ vr_intr(arg)
(status & VR_ISR_TX_ABRT2) ||
(status & VR_ISR_TX_ABRT)) {
ifp->if_oerrors++;
if (sc->vr_cdata.vr_tx_head != NULL) {
if (sc->vr_cdata.vr_tx_cons->vr_mbuf != NULL) {
VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON);
VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_GO);
}
} else
vr_txeoc(sc);
}
}
}
@ -1368,6 +1420,9 @@ vr_intr(arg)
vr_start(ifp);
}
#ifdef DEVICE_POLLING
done:
#endif /* DEVICE_POLLING */
VR_UNLOCK(sc);
return;
@ -1383,49 +1438,38 @@ vr_encap(sc, c, m_head)
struct vr_chain *c;
struct mbuf *m_head;
{
int frag = 0;
struct vr_desc *f = NULL;
int total_len;
struct mbuf *m;
m = m_head;
total_len = 0;
/*
* The VIA Rhine wants packet buffers to be longword
* aligned, but very often our mbufs aren't. Rather than
* waste time trying to decide when to copy and when not
* to copy, just do it all the time.
*/
if (m != NULL) {
struct mbuf *m_new = NULL;
m_new = m_defrag(m_head, M_DONTWAIT);
if (m_new == NULL) {
return(1);
}
m_head = m_new;
/*
* The Rhine chip doesn't auto-pad, so we have to make
* sure to pad short frames out to the minimum frame length
* ourselves.
*/
if (m_head->m_len < VR_MIN_FRAMELEN) {
m_new->m_pkthdr.len += VR_MIN_FRAMELEN - m_new->m_len;
m_new->m_len = m_new->m_pkthdr.len;
}
f = c->vr_ptr;
f->vr_data = vtophys(mtod(m_new, caddr_t));
f->vr_ctl = total_len = m_new->m_len;
f->vr_ctl |= VR_TXCTL_TLINK|VR_TXCTL_FIRSTFRAG;
f->vr_status = 0;
frag = 1;
m = m_defrag(m_head, M_DONTWAIT);
if (m == NULL) {
return(1);
}
c->vr_mbuf = m_head;
c->vr_ptr->vr_ctl |= VR_TXCTL_LASTFRAG|VR_TXCTL_FINT;
c->vr_ptr->vr_next = vtophys(c->vr_nextdesc->vr_ptr);
/*
* The Rhine chip doesn't auto-pad, so we have to make
* sure to pad short frames out to the minimum frame length
* ourselves.
*/
if (m->m_len < VR_MIN_FRAMELEN) {
m->m_pkthdr.len += VR_MIN_FRAMELEN - m->m_len;
m->m_len = m->m_pkthdr.len;
}
c->vr_mbuf = m;
f = c->vr_ptr;
f->vr_data = vtophys(mtod(m, caddr_t));
f->vr_ctl = m->m_len;
f->vr_ctl |= VR_TXCTL_TLINK|VR_TXCTL_FIRSTFRAG;
f->vr_status = 0;
f->vr_ctl |= VR_TXCTL_LASTFRAG|VR_TXCTL_FINT;
f->vr_next = vtophys(c->vr_nextdesc->vr_ptr);
return(0);
}
@ -1442,45 +1486,30 @@ vr_start(ifp)
struct ifnet *ifp;
{
struct vr_softc *sc;
struct mbuf *m_head = NULL;
struct vr_chain *cur_tx = NULL, *start_tx, *prev_tx;
struct mbuf *m_head;
struct vr_chain *cur_tx;
if (ifp->if_flags & IFF_OACTIVE)
return;
sc = ifp->if_softc;
VR_LOCK(sc);
/*
* Check for an available queue slot. If there are none,
* punt.
*/
if (sc->vr_cdata.vr_tx_free->vr_mbuf != NULL) {
VR_UNLOCK(sc);
return;
}
start_tx = sc->vr_cdata.vr_tx_free;
while(sc->vr_cdata.vr_tx_free->vr_mbuf == NULL) {
cur_tx = sc->vr_cdata.vr_tx_prod;
while (cur_tx->vr_mbuf == NULL) {
IF_DEQUEUE(&ifp->if_snd, m_head);
if (m_head == NULL)
break;
/* Pick a descriptor off the free list. */
prev_tx = cur_tx;
cur_tx = sc->vr_cdata.vr_tx_free;
sc->vr_cdata.vr_tx_free = cur_tx->vr_nextdesc;
/* Pack the data into the descriptor. */
if (vr_encap(sc, cur_tx, m_head)) {
/* Rollback, send what we were able to encap. */
IF_PREPEND(&ifp->if_snd, m_head);
sc->vr_cdata.vr_tx_free = cur_tx;
cur_tx = prev_tx;
break;
}
if (cur_tx != start_tx)
VR_TXOWN(cur_tx) = VR_TXSTAT_OWN;
VR_TXOWN(cur_tx) = VR_TXSTAT_OWN;
/*
* If there's a BPF listener, bounce a copy of this frame
@ -1488,29 +1517,21 @@ vr_start(ifp)
*/
BPF_MTAP(ifp, cur_tx->vr_mbuf);
VR_TXOWN(cur_tx) = VR_TXSTAT_OWN;
cur_tx = cur_tx->vr_nextdesc;
}
if (cur_tx != sc->vr_cdata.vr_tx_prod || cur_tx->vr_mbuf != NULL) {
sc->vr_cdata.vr_tx_prod = cur_tx;
/* Tell the chip to start transmitting. */
VR_SETBIT16(sc, VR_COMMAND, /*VR_CMD_TX_ON|*/VR_CMD_TX_GO);
/* Set a timeout in case the chip goes out to lunch. */
ifp->if_timer = 5;
if (cur_tx->vr_mbuf != NULL)
ifp->if_flags |= IFF_OACTIVE;
}
/*
* If there are no frames queued, bail.
*/
if (cur_tx == NULL) {
VR_UNLOCK(sc);
return;
}
sc->vr_cdata.vr_tx_tail = cur_tx;
if (sc->vr_cdata.vr_tx_head == NULL)
sc->vr_cdata.vr_tx_head = start_tx;
/* Tell the chip to start transmitting. */
VR_SETBIT16(sc, VR_COMMAND, /*VR_CMD_TX_ON|*/VR_CMD_TX_GO);
/*
* Set a timeout in case the chip goes out to lunch.
*/
ifp->if_timer = 5;
VR_UNLOCK(sc);
return;
@ -1604,10 +1625,18 @@ vr_init(xsc)
CSR_WRITE_4(sc, VR_TXADDR, vtophys(&sc->vr_ldata->vr_tx_list[0]));
CSR_WRITE_2(sc, VR_ISR, 0xFFFF);
#ifdef DEVICE_POLLING
/*
* Disable interrupts if we are polling.
*/
if (ifp->if_flags & IFF_POLLING)
CSR_WRITE_2(sc, VR_IMR, 0);
else
#endif /* DEVICE_POLLING */
/*
* Enable interrupts.
*/
CSR_WRITE_2(sc, VR_ISR, 0xFFFF);
CSR_WRITE_2(sc, VR_IMR, VR_INTRS);
mii_mediachg(mii);
@ -1743,6 +1772,10 @@ vr_stop(sc)
ifp->if_timer = 0;
untimeout(vr_tick, sc, sc->vr_stat_ch);
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
#ifdef DEVICE_POLLING
ether_poll_deregister(ifp);
#endif /* DEVICE_POLLING */
VR_SETBIT16(sc, VR_COMMAND, VR_CMD_STOP);
VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_RX_ON|VR_CMD_TX_ON));
@ -1775,7 +1808,6 @@ vr_stop(sc)
bzero((char *)&sc->vr_ldata->vr_tx_list,
sizeof(sc->vr_ldata->vr_tx_list));
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
VR_UNLOCK(sc);
return;

View File

@ -420,9 +420,8 @@ struct vr_chain_data {
struct vr_chain_onefrag *vr_rx_head;
struct vr_chain *vr_tx_head;
struct vr_chain *vr_tx_tail;
struct vr_chain *vr_tx_free;
struct vr_chain *vr_tx_cons;
struct vr_chain *vr_tx_prod;
};
struct vr_type {
@ -469,6 +468,9 @@ struct vr_softc {
struct vr_chain_data vr_cdata;
struct callout_handle vr_stat_ch;
struct mtx vr_mtx;
#ifdef DEVICE_POLLING
int rxcycles;
#endif
};
#define VR_F_RESTART 0x01 /* Restart unit on next tick */

View File

@ -137,7 +137,6 @@ static int vr_encap (struct vr_softc *, struct vr_chain *,
static void vr_rxeof (struct vr_softc *);
static void vr_rxeoc (struct vr_softc *);
static void vr_txeof (struct vr_softc *);
static void vr_txeoc (struct vr_softc *);
static void vr_tick (void *);
static void vr_intr (void *);
static void vr_start (struct ifnet *);
@ -952,8 +951,7 @@ vr_list_tx_init(sc)
&cd->vr_tx_chain[i + 1];
}
cd->vr_tx_free = &cd->vr_tx_chain[0];
cd->vr_tx_tail = cd->vr_tx_head = NULL;
cd->vr_tx_cons = cd->vr_tx_prod = &cd->vr_tx_chain[0];
return(0);
}
@ -1048,7 +1046,7 @@ static void
vr_rxeof(sc)
struct vr_softc *sc;
{
struct mbuf *m;
struct mbuf *m, *m0;
struct ifnet *ifp;
struct vr_chain_onefrag *cur_rx;
int total_len = 0;
@ -1060,8 +1058,14 @@ vr_rxeof(sc)
while(!((rxstat = sc->vr_cdata.vr_rx_head->vr_ptr->vr_status) &
VR_RXSTAT_OWN)) {
struct mbuf *m0 = NULL;
#ifdef DEVICE_POLLING
if (ifp->if_flags & IFF_POLLING) {
if (sc->rxcycles <= 0)
break;
sc->rxcycles--;
}
#endif /* DEVICE_POLLING */
m0 = NULL;
cur_rx = sc->vr_cdata.vr_rx_head;
sc->vr_cdata.vr_rx_head = cur_rx->vr_nextdesc;
m = cur_rx->vr_mbuf;
@ -1173,22 +1177,15 @@ vr_txeof(sc)
ifp = &sc->arpcom.ac_if;
/* Reset the timeout timer; if_txeoc will clear it. */
ifp->if_timer = 5;
/* Sanity check. */
if (sc->vr_cdata.vr_tx_head == NULL)
return;
/*
* Go through our tx list and free mbufs for those
* frames that have been transmitted.
*/
while(sc->vr_cdata.vr_tx_head->vr_mbuf != NULL) {
cur_tx = sc->vr_cdata.vr_tx_cons;
while (cur_tx->vr_mbuf != NULL) {
u_int32_t txstat;
int i;
cur_tx = sc->vr_cdata.vr_tx_head;
txstat = cur_tx->vr_ptr->vr_status;
if ((txstat & VR_TXSTAT_ABRT) ||
@ -1221,41 +1218,15 @@ vr_txeof(sc)
ifp->if_collisions +=(txstat & VR_TXSTAT_COLLCNT) >> 3;
ifp->if_opackets++;
if (cur_tx->vr_mbuf != NULL) {
m_freem(cur_tx->vr_mbuf);
cur_tx->vr_mbuf = NULL;
}
if (sc->vr_cdata.vr_tx_head == sc->vr_cdata.vr_tx_tail) {
sc->vr_cdata.vr_tx_head = NULL;
sc->vr_cdata.vr_tx_tail = NULL;
break;
}
sc->vr_cdata.vr_tx_head = cur_tx->vr_nextdesc;
}
return;
}
/*
* TX 'end of channel' interrupt handler.
*/
static void
vr_txeoc(sc)
struct vr_softc *sc;
{
struct ifnet *ifp;
ifp = &sc->arpcom.ac_if;
if (sc->vr_cdata.vr_tx_head == NULL) {
m_freem(cur_tx->vr_mbuf);
cur_tx->vr_mbuf = NULL;
ifp->if_flags &= ~IFF_OACTIVE;
sc->vr_cdata.vr_tx_tail = NULL;
ifp->if_timer = 0;
}
return;
cur_tx = cur_tx->vr_nextdesc;
}
sc->vr_cdata.vr_tx_cons = cur_tx;
if (cur_tx->vr_mbuf == NULL)
ifp->if_timer = 0;
}
static void
@ -1285,6 +1256,78 @@ vr_tick(xsc)
return;
}
#ifdef DEVICE_POLLING
static poll_handler_t vr_poll;
static void
vr_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
{
struct vr_softc *sc = ifp->if_softc;
VR_LOCK(sc);
if (cmd == POLL_DEREGISTER) { /* final call, enable interrupts */
CSR_WRITE_2(sc, VR_IMR, VR_INTRS);
goto done;
}
sc->rxcycles = count;
vr_rxeof(sc);
vr_txeof(sc);
if (ifp->if_snd.ifq_head != NULL)
vr_start(ifp);
if (cmd == POLL_AND_CHECK_STATUS) { /* also check status register */
u_int16_t status;
status = CSR_READ_2(sc, VR_ISR);
if (status)
CSR_WRITE_2(sc, VR_ISR, status);
if ((status & VR_INTRS) == 0)
goto done;
if (status & VR_ISR_RX_DROPPED) {
printf("vr%d: rx packet lost\n", sc->vr_unit);
ifp->if_ierrors++;
}
if ((status & VR_ISR_RX_ERR) || (status & VR_ISR_RX_NOBUF) ||
(status & VR_ISR_RX_NOBUF) || (status & VR_ISR_RX_OFLOW)) {
printf("vr%d: receive error (%04x)",
sc->vr_unit, status);
if (status & VR_ISR_RX_NOBUF)
printf(" no buffers");
if (status & VR_ISR_RX_OFLOW)
printf(" overflow");
if (status & VR_ISR_RX_DROPPED)
printf(" packet lost");
printf("\n");
vr_rxeoc(sc);
}
if ((status & VR_ISR_BUSERR) || (status & VR_ISR_TX_UNDERRUN)) {
vr_reset(sc);
vr_init(sc);
goto done;
}
if ((status & VR_ISR_UDFI) ||
(status & VR_ISR_TX_ABRT2) ||
(status & VR_ISR_TX_ABRT)) {
ifp->if_oerrors++;
if (sc->vr_cdata.vr_tx_cons->vr_mbuf != NULL) {
VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON);
VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_GO);
}
}
}
done:
VR_UNLOCK(sc);
}
#endif /* DEVICE_POLLING */
static void
vr_intr(arg)
void *arg;
@ -1297,6 +1340,16 @@ vr_intr(arg)
VR_LOCK(sc);
ifp = &sc->arpcom.ac_if;
#ifdef DEVICE_POLLING
if (ifp->if_flags & IFF_POLLING)
goto done;
if (ether_poll_register(vr_poll, ifp)) { /* ok, disable interrupts */
CSR_WRITE_2(sc, VR_IMR, 0x0000);
vr_poll(ifp, 0, 1);
goto done;
}
#endif /* DEVICE_POLLING */
/* Supress unwanted interrupts. */
if (!(ifp->if_flags & IFF_UP)) {
vr_stop(sc);
@ -1351,12 +1404,11 @@ vr_intr(arg)
(status & VR_ISR_TX_ABRT2) ||
(status & VR_ISR_TX_ABRT)) {
ifp->if_oerrors++;
if (sc->vr_cdata.vr_tx_head != NULL) {
if (sc->vr_cdata.vr_tx_cons->vr_mbuf != NULL) {
VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_ON);
VR_SETBIT16(sc, VR_COMMAND, VR_CMD_TX_GO);
}
} else
vr_txeoc(sc);
}
}
}
@ -1368,6 +1420,9 @@ vr_intr(arg)
vr_start(ifp);
}
#ifdef DEVICE_POLLING
done:
#endif /* DEVICE_POLLING */
VR_UNLOCK(sc);
return;
@ -1383,49 +1438,38 @@ vr_encap(sc, c, m_head)
struct vr_chain *c;
struct mbuf *m_head;
{
int frag = 0;
struct vr_desc *f = NULL;
int total_len;
struct mbuf *m;
m = m_head;
total_len = 0;
/*
* The VIA Rhine wants packet buffers to be longword
* aligned, but very often our mbufs aren't. Rather than
* waste time trying to decide when to copy and when not
* to copy, just do it all the time.
*/
if (m != NULL) {
struct mbuf *m_new = NULL;
m_new = m_defrag(m_head, M_DONTWAIT);
if (m_new == NULL) {
return(1);
}
m_head = m_new;
/*
* The Rhine chip doesn't auto-pad, so we have to make
* sure to pad short frames out to the minimum frame length
* ourselves.
*/
if (m_head->m_len < VR_MIN_FRAMELEN) {
m_new->m_pkthdr.len += VR_MIN_FRAMELEN - m_new->m_len;
m_new->m_len = m_new->m_pkthdr.len;
}
f = c->vr_ptr;
f->vr_data = vtophys(mtod(m_new, caddr_t));
f->vr_ctl = total_len = m_new->m_len;
f->vr_ctl |= VR_TXCTL_TLINK|VR_TXCTL_FIRSTFRAG;
f->vr_status = 0;
frag = 1;
m = m_defrag(m_head, M_DONTWAIT);
if (m == NULL) {
return(1);
}
c->vr_mbuf = m_head;
c->vr_ptr->vr_ctl |= VR_TXCTL_LASTFRAG|VR_TXCTL_FINT;
c->vr_ptr->vr_next = vtophys(c->vr_nextdesc->vr_ptr);
/*
* The Rhine chip doesn't auto-pad, so we have to make
* sure to pad short frames out to the minimum frame length
* ourselves.
*/
if (m->m_len < VR_MIN_FRAMELEN) {
m->m_pkthdr.len += VR_MIN_FRAMELEN - m->m_len;
m->m_len = m->m_pkthdr.len;
}
c->vr_mbuf = m;
f = c->vr_ptr;
f->vr_data = vtophys(mtod(m, caddr_t));
f->vr_ctl = m->m_len;
f->vr_ctl |= VR_TXCTL_TLINK|VR_TXCTL_FIRSTFRAG;
f->vr_status = 0;
f->vr_ctl |= VR_TXCTL_LASTFRAG|VR_TXCTL_FINT;
f->vr_next = vtophys(c->vr_nextdesc->vr_ptr);
return(0);
}
@ -1442,45 +1486,30 @@ vr_start(ifp)
struct ifnet *ifp;
{
struct vr_softc *sc;
struct mbuf *m_head = NULL;
struct vr_chain *cur_tx = NULL, *start_tx, *prev_tx;
struct mbuf *m_head;
struct vr_chain *cur_tx;
if (ifp->if_flags & IFF_OACTIVE)
return;
sc = ifp->if_softc;
VR_LOCK(sc);
/*
* Check for an available queue slot. If there are none,
* punt.
*/
if (sc->vr_cdata.vr_tx_free->vr_mbuf != NULL) {
VR_UNLOCK(sc);
return;
}
start_tx = sc->vr_cdata.vr_tx_free;
while(sc->vr_cdata.vr_tx_free->vr_mbuf == NULL) {
cur_tx = sc->vr_cdata.vr_tx_prod;
while (cur_tx->vr_mbuf == NULL) {
IF_DEQUEUE(&ifp->if_snd, m_head);
if (m_head == NULL)
break;
/* Pick a descriptor off the free list. */
prev_tx = cur_tx;
cur_tx = sc->vr_cdata.vr_tx_free;
sc->vr_cdata.vr_tx_free = cur_tx->vr_nextdesc;
/* Pack the data into the descriptor. */
if (vr_encap(sc, cur_tx, m_head)) {
/* Rollback, send what we were able to encap. */
IF_PREPEND(&ifp->if_snd, m_head);
sc->vr_cdata.vr_tx_free = cur_tx;
cur_tx = prev_tx;
break;
}
if (cur_tx != start_tx)
VR_TXOWN(cur_tx) = VR_TXSTAT_OWN;
VR_TXOWN(cur_tx) = VR_TXSTAT_OWN;
/*
* If there's a BPF listener, bounce a copy of this frame
@ -1488,29 +1517,21 @@ vr_start(ifp)
*/
BPF_MTAP(ifp, cur_tx->vr_mbuf);
VR_TXOWN(cur_tx) = VR_TXSTAT_OWN;
cur_tx = cur_tx->vr_nextdesc;
}
if (cur_tx != sc->vr_cdata.vr_tx_prod || cur_tx->vr_mbuf != NULL) {
sc->vr_cdata.vr_tx_prod = cur_tx;
/* Tell the chip to start transmitting. */
VR_SETBIT16(sc, VR_COMMAND, /*VR_CMD_TX_ON|*/VR_CMD_TX_GO);
/* Set a timeout in case the chip goes out to lunch. */
ifp->if_timer = 5;
if (cur_tx->vr_mbuf != NULL)
ifp->if_flags |= IFF_OACTIVE;
}
/*
* If there are no frames queued, bail.
*/
if (cur_tx == NULL) {
VR_UNLOCK(sc);
return;
}
sc->vr_cdata.vr_tx_tail = cur_tx;
if (sc->vr_cdata.vr_tx_head == NULL)
sc->vr_cdata.vr_tx_head = start_tx;
/* Tell the chip to start transmitting. */
VR_SETBIT16(sc, VR_COMMAND, /*VR_CMD_TX_ON|*/VR_CMD_TX_GO);
/*
* Set a timeout in case the chip goes out to lunch.
*/
ifp->if_timer = 5;
VR_UNLOCK(sc);
return;
@ -1604,10 +1625,18 @@ vr_init(xsc)
CSR_WRITE_4(sc, VR_TXADDR, vtophys(&sc->vr_ldata->vr_tx_list[0]));
CSR_WRITE_2(sc, VR_ISR, 0xFFFF);
#ifdef DEVICE_POLLING
/*
* Disable interrupts if we are polling.
*/
if (ifp->if_flags & IFF_POLLING)
CSR_WRITE_2(sc, VR_IMR, 0);
else
#endif /* DEVICE_POLLING */
/*
* Enable interrupts.
*/
CSR_WRITE_2(sc, VR_ISR, 0xFFFF);
CSR_WRITE_2(sc, VR_IMR, VR_INTRS);
mii_mediachg(mii);
@ -1743,6 +1772,10 @@ vr_stop(sc)
ifp->if_timer = 0;
untimeout(vr_tick, sc, sc->vr_stat_ch);
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
#ifdef DEVICE_POLLING
ether_poll_deregister(ifp);
#endif /* DEVICE_POLLING */
VR_SETBIT16(sc, VR_COMMAND, VR_CMD_STOP);
VR_CLRBIT16(sc, VR_COMMAND, (VR_CMD_RX_ON|VR_CMD_TX_ON));
@ -1775,7 +1808,6 @@ vr_stop(sc)
bzero((char *)&sc->vr_ldata->vr_tx_list,
sizeof(sc->vr_ldata->vr_tx_list));
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
VR_UNLOCK(sc);
return;

View File

@ -420,9 +420,8 @@ struct vr_chain_data {
struct vr_chain_onefrag *vr_rx_head;
struct vr_chain *vr_tx_head;
struct vr_chain *vr_tx_tail;
struct vr_chain *vr_tx_free;
struct vr_chain *vr_tx_cons;
struct vr_chain *vr_tx_prod;
};
struct vr_type {
@ -469,6 +468,9 @@ struct vr_softc {
struct vr_chain_data vr_cdata;
struct callout_handle vr_stat_ch;
struct mtx vr_mtx;
#ifdef DEVICE_POLLING
int rxcycles;
#endif
};
#define VR_F_RESTART 0x01 /* Restart unit on next tick */