📄 eth_drv.c
字号:
bcopy(&ifr->ifr_hwaddr.sa_data, &sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN);
if ((sc->funs->control)(sc, ETH_DRV_SET_MAC_ADDRESS,
&sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN)) {
error = EINVAL;
}
break;
#ifdef SIOCGIFSTATS
case SIOCGIFSTATS:
#ifdef SIOCGIFSTATSUD
case SIOCGIFSTATSUD:
#endif
// Get interface statistics:
if ((sc->funs->control)(sc, (cmd == SIOCGIFSTATS)
? ETH_DRV_GET_IF_STATS
: ETH_DRV_GET_IF_STATS_UD,
data, 0 ) ) {
error = EINVAL;
}
break;
#endif // SIOCGIFSTATS
case SIOCSIFFLAGS:
if ((ifp->if_flags & IFF_UP) == 0 &&
(ifp->if_flags & IFF_RUNNING) != 0) {
/*
* If interface is marked down and it is running, then
* stop it.
*/
eth_drv_stop(sc);
ifp->if_flags &= ~IFF_RUNNING;
} else
if ((ifp->if_flags & IFF_UP) != 0 &&
(ifp->if_flags & IFF_RUNNING) == 0) {
/*
* If interface is marked up and it is stopped, then
* start it.
*/
eth_drv_start(sc);
} else {
/*
* Reset the interface to pick up changes in any other
* flags that affect hardware registers.
*/
eth_drv_stop(sc);
eth_drv_start(sc);
}
break;
#if 0
case SIOCADDMULTI:
case SIOCDELMULTI:
/* Update our multicast list. */
error = (cmd == SIOCADDMULTI) ?
ether_addmulti(ifr, &sc->sc_arpcom) :
ether_delmulti(ifr, &sc->sc_arpcom);
if (error == ENETRESET) {
/*
* Multicast list has changed; set the hardware filter
* accordingly.
*/
eth_drv_stop(sc); /* XXX for ds_setmcaf? */
eth_drv_start(sc);
error = 0;
}
break;
#endif
default:
error = EINVAL;
break;
}
splx(s);
return (error);
}
//
// This routine is called to start transmitting if there is data
// available.
//
static void
eth_drv_send(struct ifnet *ifp)
{
struct eth_drv_sc *sc = ifp->if_softc;
struct eth_drv_sg sg_list[MAX_ETH_DRV_SG];
int sg_len;
struct mbuf *m0, *m;
int len, total_len;
unsigned char *data;
// This is now only called from network threads, so no guarding is
// required; locking is in place via the splfoo() mechanism already.
if ((ifp->if_flags & IFF_RUNNING) != IFF_RUNNING) {
return;
}
while ((sc->funs->can_send)(sc) > 0) {
IF_DEQUEUE(&ifp->if_snd, m0);
if (m0 == 0) {
break;
}
#ifdef CYGPKG_IO_ETH_DRIVERS_SIMULATED_FAILURES
if ( simulate_fail( sc, SIMULATE_FAIL_SEND ) ) {
// must free the mbufs
m_freem(m0);
continue; // next packet to send
}
#endif
if (net_debug)
diag_printf("Sending %d bytes\n", m0->m_pkthdr.len);
/* We need to use m->m_pkthdr.len, so require the header */
if ((m0->m_flags & M_PKTHDR) == 0)
panic("eth_drv_send: no header mbuf");
#if NBPFILTER > 0
/* Tap off here if there is a BPF listener. */
if (ifp->if_bpf)
bpf_mtap(ifp->if_bpf, m0);
#endif
// Extract data pointers (don't actually move data here)
sg_len = 0; total_len = 0;
for (m = m0; m ; m = m->m_next) {
data = mtod(m, u_char *);
len = m->m_len;
total_len += len;
sg_list[sg_len].buf = (CYG_ADDRESS)data;
sg_list[sg_len].len = len;
if ( len )
sg_len++;
if (net_debug) {
diag_printf("xmit %d bytes at %x sg[%d]\n", len, data, sg_len);
if ( 1 & net_debug )
diag_dump_buf(data, len);
}
if ( MAX_ETH_DRV_SG < sg_len ) {
diag_printf("too many mbufs to tx, %d > %d\n", sg_len, MAX_ETH_DRV_SG );
sg_len = 0;
break; // drop it on the floor
}
}
// Tell hardware to send this packet
if ( sg_len )
(sc->funs->send)(sc, sg_list, sg_len, total_len, (unsigned long)m0);
}
}
//
// This function is called from the hardware driver when an output operation
// has completed - i.e. the packet has been sent.
//
static struct mbuf *mbuf_key;
static void
eth_drv_tx_done(struct eth_drv_sc *sc, CYG_ADDRESS key, int status)
{
struct ifnet *ifp = &sc->sc_arpcom.ac_if;
struct mbuf *m0 = (struct mbuf *)key;
CYGARC_HAL_SAVE_GP();
// Check for errors here (via 'status')
ifp->if_opackets++;
// Done with packet
mbuf_key = m0;
m_freem(m0);
// Start another if possible
eth_drv_send(ifp);
CYGARC_HAL_RESTORE_GP();
}
//
// This function is called from a hardware driver to indicate that an input
// packet has arrived. The routine will set up appropriate network resources
// (mbuf's) to hold the data and call back into the driver to retrieve the data.
//
static void
eth_drv_recv(struct eth_drv_sc *sc, int total_len)
{
struct ifnet *ifp = &sc->sc_arpcom.ac_if;
struct ether_header _eh, *eh=&_eh;
struct mbuf *top, **mp, *m;
int i, mlen;
unsigned char *data;
struct eth_drv_sg sg_list[MAX_ETH_DRV_SG];
int sg_len;
if ((ifp->if_flags & IFF_RUNNING) != IFF_RUNNING) {
return; // Interface not up, ignore this request
}
CYG_ASSERT( 0 != total_len, "total_len is zero!" );
CYG_ASSERT( 0 <= total_len, "total_len is negative!" );
CYG_ASSERT( sizeof( struct ether_header ) <= total_len,
"No ether header here!" );
if ( total_len < sizeof( struct ether_header ) )
// Our arithmetic below would go wrong
return;
#ifdef CYGPKG_IO_ETH_DRIVERS_SIMULATED_FAILURES
if ( simulate_fail( sc, SIMULATE_FAIL_RECV ) ) {
// there is nothing we need to do; simply do not
// unload the packet
ifp->if_ierrors++;
return;
}
#endif
CYGARC_HAL_SAVE_GP(); // This is down here to make matching restore neat
/* Pull packet off interface. */
MGETHDR(m, M_DONTWAIT, MT_DATA);
if (m == 0) {
#ifdef CYGPKG_IO_ETH_DRIVERS_WARN_NO_MBUFS
diag_printf("warning: eth_recv out of MBUFs\n");
#endif
}
// Set up buffers
// Unload ethernet header separately so IP/UDP/TCP headers are aligned
sg_list[0].buf = (CYG_ADDRESS)eh;
sg_list[0].len = sizeof(*eh);
sg_len = 1;
// Compute total length (minus ethernet header)
total_len -= sizeof(*eh);
top = 0;
mlen = MHLEN;
mp = ⊤
if (m) {
m->m_pkthdr.rcvif = ifp;
m->m_pkthdr.len = total_len;
} else {
sg_list[sg_len].buf = (CYG_ADDRESS)0;
sg_list[sg_len].len = total_len;
sg_len++;
total_len = 0;
}
while (total_len > 0) {
if (top) {
MGET(m, M_DONTWAIT, MT_DATA);
if (m == 0) {
m_freem(top);
#ifdef CYGPKG_IO_ETH_DRIVERS_WARN_NO_MBUFS
diag_printf("out of MBUFs [2]");
#endif
sg_list[sg_len].buf = (CYG_ADDRESS)0;
sg_list[sg_len].len = total_len;
sg_len++;
top = 0;
break;
}
mlen = MLEN;
}
if (total_len >= MINCLSIZE) {
MCLGET(m, M_DONTWAIT);
if ((m->m_flags & M_EXT) == 0) {
m_freem(top);
m_free(m);
#ifdef CYGPKG_IO_ETH_DRIVERS_WARN_NO_MBUFS
diag_printf("warning: eth_recv out of MBUFs\n");
#endif
sg_list[sg_len].buf = (CYG_ADDRESS)0;
sg_list[sg_len].len = total_len;
sg_len++;
top = 0;
break;
}
mlen = MCLBYTES;
}
m->m_len = mlen = min(total_len, mlen);
total_len -= mlen;
data = mtod(m, caddr_t);
sg_list[sg_len].buf = (CYG_ADDRESS)data;
sg_list[sg_len].len = mlen;
sg_len++;
*mp = m;
mp = &m->m_next;
} // endwhile
// Ask hardware to unload buffers
(sc->funs->recv)(sc, sg_list, sg_len);
#ifdef CYGPKG_IO_ETH_DRIVERS_SIMULATED_FAILURES
if ( simulate_fail( sc, SIMULATE_FAIL_CORRUPT ) ) {
// Corrupt the data
simulate_fail_corrupt_sglist( sg_list, sg_len );
}
#endif
if (net_debug) {
for (i = 0; i < sg_len; i++) {
if (sg_list[i].buf) {
diag_printf("rx %d bytes at %x sg[%d]\n", sg_list[i].len, sg_list[i].buf, i);
if ( 1 & net_debug )
diag_dump_buf((void *)sg_list[i].buf, sg_list[i].len);
}
}
}
m = top;
if (m == 0) {
ifp->if_ierrors++;
}
else {
ifp->if_ipackets++;
#if NBPFILTER > 0
#error FIXME - Need mbuf with ethernet header attached
/*
* Check if there's a BPF listener on this interface.
* If so, hand off the raw packet to bpf.
*/
if (ifp->if_bpf)
bpf_mtap(ifp->if_bpf, m);
#endif
// Push data into protocol stacks
ether_input(ifp, eh, m);
}
CYGARC_HAL_RESTORE_GP();
}
// ------------------------------------------------------------------------
// DSR to schedule network delivery thread
extern void ecos_synch_eth_drv_dsr(void); // from ecos/timeout.c in net stack
void
eth_drv_dsr(cyg_vector_t vector,
cyg_ucount32 count,
cyg_addrword_t data)
{
struct eth_drv_sc *sc = (struct eth_drv_sc *)data;
sc->state |= ETH_DRV_NEEDS_DELIVERY;
ecos_synch_eth_drv_dsr(); // [request] run delivery function for this dev
}
// This is called from the delivery thread, to do just that:
void eth_drv_run_deliveries( void )
{
cyg_netdevtab_entry_t *t;
for (t = &__NETDEVTAB__[0]; t != &__NETDEVTAB_END__; t++) {
struct eth_drv_sc *sc = (struct eth_drv_sc *)t->device_instance;
if ( ETH_DRV_NEEDS_DELIVERY & sc->state ) {
#if defined(CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT)
cyg_bool was_ctrlc_int;
#endif
sc->state &=~ETH_DRV_NEEDS_DELIVERY;
#if defined(CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT)
was_ctrlc_int = HAL_CTRLC_CHECK((*sc->funs->int_vector)(sc), (int)sc);
if (!was_ctrlc_int) // Fall through and run normal code
#endif
(*sc->funs->deliver)(sc);
}
}
}
// ------------------------------------------------------------------------
#ifdef CYGPKG_IO_PCMCIA
// Lookup a 'netdev' entry, assuming that it is an ethernet device.
cyg_netdevtab_entry_t *
eth_drv_netdev(char *name)
{
cyg_netdevtab_entry_t *t;
struct eth_drv_sc *sc;
for (t = &__NETDEVTAB__[0]; t != &__NETDEVTAB_END__; t++) {
sc = (struct eth_drv_sc *)t->device_instance;
if (strcmp(sc->dev_name, name) == 0) {
return t;
}
}
return (cyg_netdevtab_entry_t *)NULL;
}
#endif // CYGPKG_IO_PCMCIA
// EOF eth_drv.c
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -