📄 usbs_upd985xx.c
字号:
# endif
static cyg_bool tx_in_progress = false;
static cyg_bool ep0_tx_pending = false;
# ifdef CYGPKG_DEVS_USB_UPD985XX_EP3
static cyg_bool ep3_tx_pending = false;
# endif
# ifdef CYGPKG_DEVS_USB_UPD985XX_EP5
static cyg_bool ep5_tx_pending = false;
# endif
// Invoked from ep?_start_tx(). Scheduling may or may not be locked.
static cyg_bool
tx_try_lock(cyg_bool* which)
{
cyg_bool result;
cyg_drv_dsr_lock();
if (tx_in_progress) {
result = false;
*which = true;
} else {
result = true;
tx_in_progress = true;
}
cyg_drv_dsr_unlock();
return result;
}
// Invoked only from dsr context.
static void
tx_unlock(void)
{
tx_in_progress = false;
if (ep0_tx_pending) {
ep0_tx_pending = false;
ep0_start_tx();
return;
}
# ifdef CYGPKG_DEVS_USB_UPD985XX_EP3
if (ep3_tx_pending) {
ep3_tx_pending = false;
ep35_start_tx(&ep3);
return;
}
# endif
# ifdef CYGPKG_DEVS_USB_UPD985XX_EP5
if (ep5_tx_pending) {
ep5_tx_pending = false;
ep35_start_tx(&ep5);
return;
}
# endif
}
# define TX_TRY_LOCK(_x_) tx_try_lock(_x_)
# define TX_UNLOCK() tx_unlock()
#else
# define TX_TRY_LOCK(_x_) 1
# define TX_UNLOCK() CYG_EMPTY_STATEMENT
#endif
// ----------------------------------------------------------------------------
// Endpoint 0
//
// As usual, control messages are more complicated than the rest.
//
// 1) during initialization a receive is initiated into the common
// eight-byte buffer, used for the standard header of the control
// packet. Until that header has been received and analysed,
// there is no way of knowing whether or not the host will be
// sending any more data.
//
// 2) the control packet may indicate that the host will be sending
// more data. A higher-level handler for the control message should
// have provided a suitable buffer, so a receive can be started
// into that buffer. A flag indicates whether we are currently
// receiving a new control packet or additional data.
//
// 3) the host may decide to cancel that extra data and send a new
// control message instead. There is a flag to indicate that
// the transfer included a SETUP token.
//
// 4) transmits only happen when the control packet involves returning
// data. Unfortunately there is a problem in that, with eCos, the
// return data will generally not be in a single contiguous buffer.
// Discontinuous data could be handled by having a separate buffer
// descriptor for each bit of data, but it is not known in advance
// how many buffer descriptors might be needed so allocating
// those statically presents a problem as well. Instead a single
// static buffer is used, and data from higher-level code is copied
// there. This introduces a new problem: how big should that buffer
// be? A configuration option is used for that.
//
// If endpoint 6 is in use as well then things get more complicated
// because a single receive pool will be shared between endpoints 0
// and 6, and when adding a buffer to a pool there is no way of
// specifying the endpoint. Hence it will be necessary to receive
// into a static buffer and then copy into either an endpoint 0 or
// and endpoint 6 buffer.
// Fill the transmit buffer by repeatedly invoking the refill function
// and copying into the ep0 tx buffer. The relevant fields in the
// ep0 structure are cleared immediately and the completion function
// is called, even though the data has not actually gone out. That avoids
// a possible race condition where the host sends a new control packet
// immediately, before the transmit-complete has been processed
// (unlikely in practice, not least because ep0_tx_dsr() will get called
// before ep0_rx_dsr()).
static int
ep0_fill_txbuffer(void)
{
int filled = 0;
while (filled < CYGNUM_DEVS_USB_UPD985XX_EP0_TXBUFSIZE) {
if (0 != ep0.common.buffer_size) {
if ((filled + ep0.common.buffer_size) < CYGNUM_DEVS_USB_UPD985XX_EP0_TXBUFSIZE) {
memcpy(&(uncached->ep0_tx_buffer[filled]), ep0.common.buffer, ep0.common.buffer_size);
filled += ep0.common.buffer_size;
ep0.common.buffer_size = 0;
} else {
break;
}
} else if ((void (*)(usbs_control_endpoint*))0 != ep0.common.fill_buffer_fn) {
(*ep0.common.fill_buffer_fn)(&ep0.common);
} else {
break;
}
}
CYG_ASSERT((0 == ep0.common.buffer_size) && ((void (*)(usbs_control_endpoint*))0 == ep0.common.fill_buffer_fn), \
"Endpoint 0 transmit buffer overflow");
if ((usbs_control_return (*)(usbs_control_endpoint*, int))0 != ep0.common.complete_fn) {
(*ep0.common.complete_fn)(&ep0.common, 0);
}
ep0.common.buffer = (unsigned char*) 0;
ep0.common.buffer_size = 0;
ep0.common.fill_buffer_fn = 0;
ep0.common.complete_fn = 0;
return filled;
}
// Start a new receive operation on endpoint 0. This needs to happen
// from a number of places, including from initialization.
//
// IMHO the hardware is somewhat overengineered here. All that is
// needed is to receive a single eight-byte control packet, or a
// small amount of additional control data. That could be achieved
// by using a single buffer descriptor in the uncached structure,
// plus a suitably-sized static uncached ep0_rx_buffer.
//
// But no, buffer descriptors must be linked and new buffers must
// be added to the end. When a control packet arrives, the
// receive pool continues to point at the old buffer descriptor.
// So we need two buffer descriptors plus two links, switching
// between them as appropriate.
//
// It is not at all clear what would happen if another packet
// started to happen while things were being updated. There is
// also potential confusion between endpoint 0 and endpoint 6
// receives.
static void
ep0_start_rx(cyg_uint32 size)
{
// The buffer descriptor to be added. This will be either
// ep0_rxbufdescs[0] or ep0_rxbufdescs[2];
RxBufferDescriptor* desc = &(uncached->ep0_rx_bufdescs[0]);
CYG_ASSERTC(size > 0);
// Block interrupts for the duration. This does not prevent
// problems if the hardware sees another packet and starts
// doing things, but should prevent some software race
// conditions.
cyg_drv_isr_lock();
// We are about to start a new rx operation, so the
// current indicator may get invalidated.
ep0.rx_indicator_valid = false;
// Start by looking at the current pool0 status. There are
// three possibilities: during init or after reset, the pool
// will be empty; otherwise the pool should point at either
// rx_bufdescs[0] or rx_bufdescs[2], corresponding to the
// last received packet.
if (0 == (*USBS_RP0IR & USBS_RPxIR_RNOD_MASK)) {
// Nothing currently in the pool. Use ep0_rx_bufdescs[0],
// and no need to update a link.
} else if (desc == *USBS_RP0AR) {
// The pool already points at bufdescs[0], switch to bufdescs[2],
// and link from bufdescs[1].
desc = &(uncached->ep0_rx_bufdescs[2]);
uncached->ep0_rx_bufdescs[1].buffer = (void*) desc;
} else {
// The pool should point at bufdescs[2], stick with bufdescs[0]
CYG_ASSERT(&(uncached->ep0_rx_bufdescs[2]) == *USBS_RP0AR, "Endpoint 0 rx buffer confusion");
uncached->ep0_rx_bufdescs[3].buffer = (void*) desc;
}
// Now fill in the buffer directory being added
desc[0].control = RXBUFDESC_CTRL_LAST | RXBUFDESC_CTRL_BUFDESC_BUFDESC | size;
desc[0].buffer = (void*) uncached->ep0_rx_buffer;
desc[1].control = RXBUFDESC_CTRL_BUFDESC_LINK;
desc[1].buffer = 0;
while (0 != (*USBS_CMR & IBUS_SWAP32(USBS_CMR_BUSY))) {
// Do nothing: this situation should be short-lived.
}
*USBS_CA = IBUS_SWAPPTR(void, desc); FLUSH_IBUS();
*USBS_CMR = IBUS_SWAP32(USBS_CMR_COMMAND_ADD_POOL0 | 1); FLUSH_IBUS();
cyg_drv_isr_unlock();
}
// Ditto for transmits. The data is assumed to be in
// uncached->ep0_tx_buffer already. A size of 0 indicates
// a need to send a terminating packet explicitly.
static void
ep0_start_tx(void)
{
if (!TX_TRY_LOCK(&ep0_tx_pending)) {
return;
}
uncached->ep0_tx_bufdesc.buffer = uncached->ep0_tx_buffer;
uncached->ep0_tx_bufdesc.control = TXBUFDESC_CTRL_LAST | TXBUFDESC_CTRL_BUFDESC_BUFDESC | ep0.tx_size;
cyg_drv_isr_lock();
while (0 != (*USBS_CMR & IBUS_SWAP32(USBS_CMR_BUSY))) {
// Do nothing: this situation should be short-lived.
}
*USBS_CA = IBUS_SWAPPTR(void, &(uncached->ep0_tx_bufdesc)); FLUSH_IBUS();
*USBS_CMR = IBUS_SWAP32(USBS_CMR_COMMAND_TX_EP0 | ep0.tx_size); FLUSH_IBUS();
cyg_drv_isr_unlock();
}
// An endpoint 0 transmission has completed. Usually the only action
// that is needed is to drain the tx mailbox entry, otherwise it is
// possible that we could end up with ep0 transmits using up all
// available slots. The endpoint 0 hardware requires no further
// attention, and as far as higher-level code is concerned the
// transmission completed a long time ago when ep0_fill_txbuffer()
// called the completion function.
//
// There is one special case. If the host asked for e.g. a string
// descriptor and asked for 255 bytes, but the string was only
// e.g. 32 bytes, then there is a problem. With a default value
// for CYGNUM_DEVS_USB_UPD985XX_EP0_PKTSIZE, the data will be
// transferred as four 8-byte packets, but it is necessary to
// terminate the transfer with a 0-byte packet. Endpoint 0 always
// operates in NZLP mode so the hardware will never generate
// this last packet. Instead it is necessary to set up an
// additional transfer of zero bytes. That could be done at the
// same time as the main data transfer, but then it would be
// necessary to poll the hardware and wait until it has finished
// processing that initial transfer.
static void
ep0_tx_dsr(void)
{
if (!ep0.tx_indicator_valid) {
drain_tx_mailbox();
if (!ep0.tx_indicator_valid) {
// A transmit interrupt when there does not appear to be
// any data?
CYG_FAIL("EP0 tx DSR invoked when there is no valid tx indicator");
return;
}
}
// There is not actually anything worth looking at in the status.
ep0.tx_indicator_valid = false;
if (ep0.tx_needs_zero_transfer) {
ep0.tx_needs_zero_transfer = false;
uncached->ep0_tx_bufdesc.buffer = uncached->ep0_tx_buffer;
uncached->ep0_tx_bufdesc.control = TXBUFDESC_CTRL_LAST | TXBUFDESC_CTRL_BUFDESC_BUFDESC | 0;
cyg_drv_isr_lock();
while (0 != (*USBS_CMR & IBUS_SWAP32(USBS_CMR_BUSY))) {
// Do nothing: this situation should be short-lived.
}
*USBS_CA = IBUS_SWAPPTR(void, &(uncached->ep0_tx_bufdesc)); FLUSH_IBUS();
*USBS_CMR = IBUS_SWAP32(USBS_CMR_COMMAND_TX_EP0 | 0); FLUSH_IBUS();
cyg_drv_isr_unlock();
} else {
TX_UNLOCK();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -