📄 eth_drv.c
字号:
START_CONSOLE();
diag_printf("too many mbufs to tx, %d > %d, need %d\n",
sg_len, MAX_ETH_DRV_SG, needed );
END_CONSOLE();
#endif
sg_len = 0;
break; // drop it on the floor
}
}
#ifdef _LOCK_WITH_ROM_MONITOR
// Firm lock on this portion of the driver. Since we are about to
// start messing with the actual hardware, it is imperative that the
// current thread not loose control of the CPU at this time. Otherwise,
// the hardware could be left in an unusable state. This caution is
// only warranted if there is a possibility of some other thread trying
// to use the hardware simultaneously. The network stack would prevent
// this implicitly since all accesses are controlled by the "splX()"
// locks, but if there is a ROM monitor, such as RedBoot, also using
// the hardware, all bets are off.
// Note: these operations can be avoided if it were well known that
// RedBoot was not using the network hardware for diagnostic I/O. This
// can be inferred by checking which I/O channel RedBoot is currently
// hooked to.
debug_chan = CYGACC_CALL_IF_SET_DEBUG_COMM(CYGNUM_CALL_IF_SET_COMM_ID_QUERY_CURRENT);
if (debug_chan == RedBoot_TCP_CHANNEL) {
need_lock = true;
#ifdef _LOCK_USING_INTERRUPTS
HAL_DISABLE_INTERRUPTS(ints);
#endif
cyg_drv_dsr_lock();
}
#endif // _LOCK_WITH_ROM_MONITOR
// Tell hardware to send this packet
if ( sg_len )
(sc->funs->send)(sc, sg_list, sg_len, total_len, (unsigned long)m0);
#ifdef _LOCK_WITH_ROM_MONITOR
// Unlock the driver & hardware. It can once again be safely shared.
if (need_lock) {
cyg_drv_dsr_unlock();
#ifdef _LOCK_USING_INTERRUPTS
HAL_RESTORE_INTERRUPTS(ints);
#endif
}
#endif // _LOCK_WITH_ROM_MONITOR
#undef _LOCK_WITH_ROM_MONITOR
}
}
//
// 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
// Guard against a NULL return - can be caused by race conditions in
// the driver, this is the neatest fixup:
if (m0) {
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 mlen;
caddr_t data;
#if MAX_ETH_DRV_SG > 64
static // Avoid large stack requirements
#endif
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;
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
START_CONSOLE();
diag_printf("warning: eth_recv out of MBUFs\n");
#ifdef CYGDBG_NET_SHOW_MBUFS
cyg_net_show_mbufs();
#endif
END_CONSOLE();
#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
START_CONSOLE();
diag_printf("out of MBUFs [2]");
#ifdef CYGDBG_NET_SHOW_MBUFS
cyg_net_show_mbufs();
#endif
END_CONSOLE();
#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
START_CONSOLE();
diag_printf("warning: eth_recv out of MBUFs\n");
#ifdef CYGDBG_NET_SHOW_MBUFS
cyg_net_show_mbufs();
#endif
END_CONSOLE();
#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_RECV ) ) {
// toss the packet - note that some hardware gets
// fussy if the packet isn't "unloaded", thus we
// have to wait until now to throw it away
if (top) {
m_free(top);
}
ifp->if_ierrors++;
return;
}
if ( simulate_fail( sc, SIMULATE_FAIL_CORRUPT ) ) {
// Corrupt the data
simulate_fail_corrupt_sglist( sg_list, sg_len );
}
#endif
#ifdef CYGDBG_IO_ETH_DRIVERS_DEBUG
if (cyg_io_eth_net_debug) {
int i;
START_CONSOLE();
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 ( cyg_io_eth_net_debug > 1 )
diag_dump_buf((void *)sg_list[i].buf, sg_list[i].len);
}
}
END_CONSOLE();
}
#endif
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;
#ifdef CYGDBG_USE_ASSERTS
// then check that this really is a "sc"
{
cyg_netdevtab_entry_t *t;
for (t = &__NETDEVTAB__[0]; t != &__NETDEVTAB_END__; t++)
if ( ((struct eth_drv_sc *)t->device_instance) == sc )
break; // found it
CYG_ASSERT( t != &__NETDEVTAB_END__, "eth_drv_dsr: Failed to find sc in NETDEVTAB" );
}
#endif // Checking code
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);
}
}
}
// This is called from the delivery thread, to unstick devices if there is
// no network activity.
#ifdef CYGPKG_NET_FAST_THREAD_TICKLE_DEVS
void eth_drv_tickle_devices( 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_STATE_ACTIVE & sc->state ) {
struct ifnet *ifp = &sc->sc_arpcom.ac_if;
// Try to dequeue a packet for this interface, if we can. This
// will call can_send() for active interfaces. It is calls to
// this function from tx_done() which normally provide
// continuous transmissions; otherwise we do not get control.
// This call fixes that.
if (!IF_IS_EMPTY(&ifp->if_snd)) {
eth_drv_send(ifp);
}
}
}
}
#endif // CYGPKG_NET_FAST_THREAD_TICKLE_DEVS
// ------------------------------------------------------------------------
#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 src/net/eth_drv.c
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -