📄 usbsethdrv.c
字号:
} else if (real_size > CYGNUM_USBS_ETH_MAX_FRAME_SIZE) {
INCR_STAT(eth->rx_too_long_frames);
} else {
// The packet appears to be valid. Inform higher level
// code and mark the buffer as in use.
resubmit = false;
eth->rx_buffer_full = true;
eth->rx_active = false;
eth_drv_dsr(0, 0, (cyg_addrword_t) sc);
}
}
if (resubmit) {
eth->rx_active = true;
usbs_eth_start_rx(eth, eth->rx_bufptr, &usbs_ethdrv_recv_callback, callback_data);
}
}
// Another callback, used to wait while an endpoint is halted.
static void
usbs_ethdrv_halted_callback(void* callback_data, int size)
{
struct eth_drv_sc* sc = (struct eth_drv_sc*) callback_data;
usbs_ethdrv_recv_callback((usbs_eth*) sc->driver_private, callback_data, 0);
}
// Start a receive operation. It is not possible to abort an existing
// rx operation, so a valid sequence of events is: start, rx ongoing,
// stop, restart. The rx_active field is used to keep track of whether
// or not there is still a receive in progress. The receive callback
// will just discard incoming data if the eCos stack is not currently
// running.
static void
usbs_ethdrv_start_recv(struct eth_drv_sc* sc, usbs_eth* eth)
{
cyg_drv_dsr_lock();
if (!eth->rx_active) {
eth->rx_active = true;
usbs_eth_start_rx(eth, eth->rx_bufptr, &usbs_ethdrv_recv_callback, (void*) sc);
}
cyg_drv_dsr_unlock();
}
// This is invoked from the delivery thread when a valid buffer
// has been received. The buffer should be scattered into the
// supplied list, then another receive should be started.
static void
usbs_ethdrv_recv(struct eth_drv_sc* sc,
struct eth_drv_sg* sg_list, int sg_len)
{
usbs_eth* eth = (usbs_eth*)(sc->driver_private);
CYG_ASSERT( eth->rx_buffer_full, "This function should only be called when there is a buffer available");
(void) scatter(eth->rx_bufptr, sg_list, sg_len);
eth->rx_buffer_full = false;
eth->rx_active = true;
usbs_eth_start_rx(eth, eth->rx_bufptr, &usbs_ethdrv_recv_callback, (void*) sc);
}
// ----------------------------------------------------------------------------
// Now for the transmit process.
//
// When an application thread writes down a socket the data gets moved
// into mbufs, and then passed to the appropriate device driver - which
// may or may not be able to process it immediately. There is also a
// timeout thread within the TCP/IP to handle retransmits etc.
//
// The stack will start by calling usbs_ethdrv_can_send() to determine
// whether or not the driver can accept the packet. For the purposes
// of the USB-ethernet driver this is true provided both host
// and target are up and there is a spare buffer available.
//
// If the usbs_eth_can_send() returns true then there will be a call
// to usbs_ethdrv_send(). This gathers the data into a single
// buffer. If there is no transmit in progress yet then one is started.
//
// At some point the packet will have been transmitted and a callback
// gets invoked. This needs to call eth_drv_dsr(), waking up the
// delivery thread. The deliver() function can then check which
// transmissions have completed and inform the higher level code
// via sc->funs->eth_drv->tx_done(). The buffer can be re-used at
// that point.
static void
usbs_ethdrv_send_callback(usbs_eth* eth, void* callback_data, int size)
{
struct eth_drv_sc* sc = (struct eth_drv_sc*) callback_data;
CYG_ASSERT( eth == (usbs_eth*)(sc->driver_private), "USB and TCP/IP worlds need to be consistent");
INCR_STAT(eth->interrupts);
// There are a variety of possible error codes. -EAGAIN indicates
// that the endpoint is stalled. -EPIPE indicates that the
// connection to the host has been lost. These are not really
// particularly interesting. Whatever happens the buffer
// must be cleared and higher-level code informed so that
// the mbufs can be released.
if (size > 0) {
INCR_STAT(eth->tx_count);
}
eth->tx_done = true;
eth_drv_dsr(0, 0, (cyg_addrword_t) sc);
}
// Is it possible to send an ethernet frame? This requires
// an empty buffer, i.e. there should be no existing
// transmit in progress. It also requires that the host
// is connected and that the endpoint is not currently halted.
static int
usbs_ethdrv_can_send(struct eth_drv_sc* sc)
{
usbs_eth* eth = (usbs_eth*)(sc->driver_private);
return eth->host_up && !eth->tx_buffer_full && !eth->tx_endpoint->halted;
}
// Actually start a packet transmission. This means collecting
// all the data into a single buffer and then invoking the
// lower-level code. The latter may discard the packet immediately
// if the MAC is not appropriate: it would be more efficient to
// catch that here, especially for large packets, but the check
// has to happen inside the lower-level code anyway in case
// that is being invoked directly rather than via the driver.
//
// There is a possible recursion problem,
// send->start_tx->tx_done->can_send->send, which is guarded
// against using the tx_in_send flag.
static void
usbs_ethdrv_send(struct eth_drv_sc* sc,
struct eth_drv_sg* sgl_list, int sg_len, int total_len,
unsigned long key)
{
usbs_eth* eth = (usbs_eth*)(sc->driver_private);
CYG_ASSERT( 0 == eth->tx_in_send, "send() should not be invoked recursively");
CYG_ASSERT( total_len <= CYGNUM_USBS_ETH_MAX_FRAME_SIZE, "ethernet maximum frame size should be observed");
CYG_ASSERT( CYGNUM_USBS_ETH_MIN_FRAME_SIZE <= total_len, "ethernet minimum frame size should be observed");
eth->tx_in_send = true;
CYG_ASSERT( !eth->tx_buffer_full, "the transmit buffer should be empty");
gather(eth->tx_buffer, CYGNUM_USBS_ETH_MAX_FRAME_SIZE, sgl_list, sg_len);
eth->tx_buffer_full = true;
eth->tx_done = false;
eth->tx_key = key;
usbs_eth_start_tx(eth, eth->tx_buffer, &usbs_ethdrv_send_callback, (void*) sc);
eth->tx_in_send = false;
}
// ----------------------------------------------------------------------------
// Deliver needs to take into account both receive and transmit buffers.
static void
usbs_ethdrv_deliver(struct eth_drv_sc* sc)
{
usbs_eth* eth = (usbs_eth*)(sc->driver_private);
if (eth->rx_buffer_full) {
int size = eth->rx_bufptr[0] + (eth->rx_bufptr[1] << 8);
(*sc->funs->eth_drv->recv)(sc, size);
}
if (eth->tx_done) {
unsigned long key = eth->tx_key;
eth->tx_buffer_full = false;
eth->tx_done = false;
(*sc->funs->eth_drv->tx_done)(sc, key, 1);
}
}
// ----------------------------------------------------------------------------
// usbs_ethdrv_start()
//
// This gets called by the TCP/IP stack later on during
// initialization, when the stack is ready to send and receive
// packets. It may get called multiple times while the stack
// is running, with different flags values.
//
// As far as transmits are concerned, nothing needs to be done. If no
// transmit is in progress then everything is fine anyway. If a
// transmit is already in progress then it must be allowed to complete
// via the usual route. Receives should however be restarted, the
// start function has appropriate safeguards.
//
// Invoked in: thread context only
// ----------------------------------------------------------------------------
static void
usbs_ethdrv_start(struct eth_drv_sc* sc, unsigned char* enaddr, int flags)
{
usbs_eth* eth = (usbs_eth*)(sc->driver_private);
if (!eth->ecos_up) {
eth->ecos_up = true;
usbs_ethdrv_start_recv(sc, eth);
}
}
// ----------------------------------------------------------------------------
// usbs_ethdrv_stop()
//
// Similarly this gets called by the TCP/IP stack to bring the network
// interface down. Nothing should happen for any packets currently
// being transmitted or received, that would cause confusion everywhere.
// The receive callback checks the ecos_up flag and does the right
// thing. The TCP/IP stack should not call can_send() after taking
// the interface down so no new transmits will be initiated.
//
// Invoked in: thread context only
// ----------------------------------------------------------------------------
static void
usbs_ethdrv_stop(struct eth_drv_sc* sc)
{
usbs_eth* eth = (usbs_eth*)(sc->driver_private);
eth->ecos_up = false;
}
// ----------------------------------------------------------------------------
// usbs_eth_ioctl()
//
// The operations to worry about here are:
//
// SET_MAC_ADDRESS,via the SIOCSIFHWADDR ioctl
//
// GET_IF_STATS and GET_IF_STATS_UD, to report gathered statistics.
//
// Invoked in: thread context only
// ----------------------------------------------------------------------------
static int
usbs_ethdrv_ioctl(struct eth_drv_sc* sc, unsigned long key, void* data, int data_length)
{
usbs_eth* eth = (usbs_eth*)(sc->driver_private);
int result = EINVAL;
switch(key) {
case ETH_DRV_SET_MAC_ADDRESS:
{
if (6 == data_length) {
memcpy(eth->ecos_MAC, data, 6);
result = 0;
}
}
break;
#if defined(CYGFUN_USBS_ETHDRV_STATISTICS) && defined(ETH_DRV_GET_IF_STATS_UD)
case ETH_DRV_GET_IF_STATS_UD:
case ETH_DRV_GET_IF_STATS:
{
static unsigned char my_chipset[] = { 0, 0 };
struct ether_drv_stats *p = (struct ether_drv_stats*) data;
int i;
strcpy(p->description, CYGDAT_USBS_ETHDRV_DESCRIPTION);
for ( i = 0; i < SNMP_CHIPSET_LEN; i++ ) {
if ( 0 == (p->snmp_chipset[i] = my_chipset[i]) ) {
break;
}
}
p->duplex = 3; // 3 == duplex
p->operational = (eth->host_up && eth->ecos_up) ? 3 : 2; // 3 == up, 2 == down
p->speed = 10 * 1000000;
p->supports_dot3 = 1;
p->rx_too_long_frames = eth->rx_too_long_frames;
p->rx_short_frames = eth->rx_short_frames;
p->interrupts = eth->interrupts;
p->rx_count = eth->rx_count;
p->tx_count = eth->tx_count;
p->tx_queue_len = 1;
result=0;
}
break;
#endif
default:
break;
}
return result;
}
// ----------------------------------------------------------------------------
// usbs_ethdrv_poll()
//
// On real ethernet hardware this is used by RedBoot once the
// application has started running, so that the network device can be
// used for debugging purposes as well as for the application's own
// needs. The lower-level USB device may supply a poll function as well.
// ----------------------------------------------------------------------------
static void
usbs_ethdrv_poll(struct eth_drv_sc* sc)
{
usbs_eth* eth = (usbs_eth*)(sc->driver_private);
(*eth->control_endpoint->poll_fn)(eth->control_endpoint);
}
// ----------------------------------------------------------------------------
// usbs_ethdrv_intvector()
//
// See usbs_eth_poll().
// ----------------------------------------------------------------------------
static int
usbs_ethdrv_intvector(struct eth_drv_sc* sc)
{
usbs_eth* eth = (usbs_eth*)(sc->driver_private);
return eth->control_endpoint->interrupt_vector;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -