📄 usbs_upd985xx.c
字号:
result = false; *which = true; } else { result = true; tx_in_progress = true; } cyg_drv_dsr_unlock(); return result;}// Invoked only from dsr context.static voidtx_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 intep0_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 voidep0_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 voidep0_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 voidep0_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(); }}// An endpoint 0 receive has completed. This could be a new control// message. Or it could be the data for a previous control message. Or// it could be a new control message when expecting the data from// a previous one. The ep0.rx_expecting_data field indicates// whether or not a new control message is expected.//// At times an interrupt triggers and there is an rx indication for a// zero-byte transfer. Such a transfer may be followed immediately by// a real transfer. It is not understood why the zero-byte transfer// occurs. They are ignored by the drain_rx_mailbox() code, to make// sure that there is at most one valid rx indicator at a time.static voidep0_rx_dsr(void){ // Start by checking the rx indicator to make sure that a packet // really has been received. if (!ep0.rx_indicator_valid) { drain_rx_mailbox(); if (!ep0.rx_indicator_valid) { // Do not assert, in case of a spurious interrupt for a // zero-byte transfer.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -