⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 usbs_upd985xx.c

📁 开放源码实时操作系统源码.
💻 C
📖 第 1 页 / 共 5 页
字号:
# 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 + -