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

📄 usbs_sa11x0.c

📁 基于ecos的redboot
💻 C
📖 第 1 页 / 共 5 页
字号:
// following sequence of events:
//
// 1) the host transmits a special setup token, indicating the start
//    of a control operation and possibly cancelling any existing control
//    operation that may be in progress. USB peripherals cannot NAK this
//    even if they are busy.
//
// 2) the setup operation is followed by an eight-byte packet from the host
//    that describes the specific control operation. This fits into the
//    SA11X0's eight-byte control fifo. There will be an endpoint 0
//    interrupt with the out-packet-ready bit set. If the setup token
//    was sent while a previous control operation was also in progress
//    then the setup-end bit will be set as well.
//
// 3) the eight-byte packet is described in section 9.3 of the USB spec.
//    The first byte holds three fields, with the top bit indicating the
//    direction of subsequent data transfer. There are also two bytes
//    specifying the size of the subsequent transfer. Obviously the
//    packet also contains information such as the request type.
//
//    If the specified size is zero then the endpoint will remain in
//    its idle state. Otherwise the endpoint will switch to either
//    IN or OUT state, depending on the direction of subsequent
//    transfers.
// 
// 4) some standard control operations can be handled by the code
//    here. Set-address involves poking the address register and
//    a change of state. Set-feature and clear-feature on the
//    data endpoints can be used in conjunction with endpoint-halt.
//    Get-status on the data endpoints tests the halt condition.
//    It is also possible for the hardware-specific code to
//    implement set-feature, clear-feature and get-status
//    for the device as a whole since the SA11x0 always has to
//    be self-powered and is incapable of initiating a remote
//    wakeup.
//
//    Other standard control operations will be handled by the
//    application-specific installed handler, if any, or by the
//    default handler usbs_handle_standard_control(). Class-specific
//    and vendor-specific functions require appropriate handlers to be
//    installed as well, If a particular request is not recognized
//    then a stall condition should be raised. This will not prevent
//    subsequent control operations, just the current one.
//
//    Data transfers on endpoint 0 involve at most eight bytes at
//    a time. More data will only be accepted if the out-packet-ready
//    bit has been cleared via the serviced-opr bit, with the
//    hardware nak'ing OUT requests. To send data back to the host
//    the FIFO should be filled and then the in-packet-ready bit
//    should be set.
//
// It looks like processing all control packets at DSR level should be
// sufficient. During the data phase the hardware will NAK IN and
// OUT requests if the fifo is still empty/full, so timing is not
// an issue. Timing after receipt of the initial control message
// may be more important, e.g. the 50ms upper limit on processing
// the set-address control message, but this should still be ok.
// This decision may have to be re-examined in the light of
// experience.

// Init may get called during system startup or following a reset.
// During startup no work is needed since the hardware will
// have been reset and everything should be fine. After a reset
// the hardware will also be ok but there may be state information
// in ep0 that needs to be reset.
static void
usbs_sa11x0_ep0_init(void)
{
    if ((EP0_STATE_IDLE != ep0.ep_state) &&
        ((usbs_control_return (*)(usbs_control_endpoint*, int)) 0 != ep0.common.complete_fn)) {
        (*ep0.common.complete_fn)(&ep0.common, -EPIPE);
    }
    ep0.common.state            = USBS_STATE_POWERED;
    memset(ep0.common.control_buffer, 0, 8);
    ep0.common.buffer           = (unsigned char*) 0;
    ep0.common.buffer_size      = 0;
    ep0.common.fill_buffer_fn   = (void (*)(usbs_control_endpoint*)) 0;
    ep0.common.fill_data        = (void*) 0;
    ep0.common.fill_index       = 0;
    ep0.common.complete_fn      = (usbs_control_return (*)(usbs_control_endpoint*, int)) 0;
    ep0.ep_state                = EP0_STATE_IDLE;
    ep0.length                  = 0;
    ep0.transmitted             = 0;
}

// The start function is called by higher-level code when things have
// been set up, i.e. the enumeration data is available, appropriate
// handlers have been installed for the different types of control
// messages, and communication with the host is allowed to start. The
// next event that should happen is a reset operation from the host,
// so all other interrupts should be blocked. However it is likely
// that the hardware will detect a suspend state before the reset
// arrives, and hence the reset will act as a resume as well as a
// reset.
static void
usbs_sa11x0_ep0_start(usbs_control_endpoint* endpoint)
{
    CYG_ASSERT( endpoint == &ep0.common, "USB startup involves the wrong endpoint");
    
    // Activate the hardware. Write a 0 to the enable/disable bit 0.
    // Bit 1 is read-only. The other bits are set to 1 to disable
    // the corresponding interrupt source.
    usbs_sa11x0_poke(USBS_CONTROL, CONTROL_ALL_INTR, CONTROL_ALL_INTR, 0);

    // If there is additional platform-specific initialization to
    // perform, do it now. This macro can come from the platform HAL.
#ifdef SA11X0_USB_PLATFORM_INIT
    SA11X0_USB_PLATFORM_INIT;
#endif
        
    // Clear any pending interrupts. There should not be any, but just
    // in case. Note: passing 0x00FF as the should_be_clear argument
    // is a race condition, an external event can happen at any time,
    // so we may loop unnecessarily and lose an interrupt. However
    // the initial reset should last for 10ms.
    usbs_sa11x0_poke(USBS_STATUS, 0x00FF, 0x00, 0x00FF);

    // The only interrupt really of interest right now is reset, but
    // it is likely to be preceded by a resume. 
    usbs_sa11x0_poke(USBS_CONTROL,
                     CONTROL_INTR_ENABLE(CONTROL_RESET_INTR | CONTROL_RESUME_INTR),
                     0,
                     CONTROL_INTR_CLEAR(CONTROL_RESET_INTR | CONTROL_RESUME_INTR));
}


// Filling the fifo with a reply to the host. This can be called
// immediately at the end of a control message, to prepare for
// the next IN token. It will also get called after each subsequent
// IN operation when the fifo has been emptied.
//
// Experiments have indicated serious problems with the control fifo:
// some writes to the fifo just get lost completely. The failure rate
// is sufficiently high that more often than not the host will be
// unable to read all the enumeration data. However, the write-count
// register appears to give a valid indication of the current fifo
// contents. This means the code can retry stuffing a particular byte
// into the fifo until the write-count goes up.

static void
usbs_sa11x0_ep0_fill_fifo(void)
{
    cyg_bool ok     = true;
    int filled      = 0;
    int max;
    int fifo_count  = *EP0_WRITE_COUNT;
    int bits_to_set = 0;
    
    // The host can interrupt the current control message at any time
    // with a new one. In practice this is unlikely, things could get
    // rather confused on the host side. However if a control message
    // has been received then the fifo should obviously not be filled.
    // A new control message is indicated by the SETUP_END bit.
    //
    // The hardware design means that there is a race condition: the
    // new control message can come in at any time, even in the middle
    // of filling the fifo. Checking the SETUP_END more often would
    // reduce the probability of things getting messed up, but not
    // eliminate it.
    //
    // There is a check for SETUP_END at the start of the DSR, so
    // the setting of this bit should have resulted in another ISR
    // and another DSR being scheduled. Hence there is no need for
    // special action here.
    if (0 != (*EP0_CONTROL & EP0_SETUP_END)) {
        DBG(("EP0_fill_fifo(), interrupted by SETUP_END\n"));
        return;
    }

    // There should never be any data in the fifo. Any such data could
    // be the remnant of a previous transfer to the host, but that
    // should all have gone out already. Alternatively it could be
    // incoming data, but that means a new control message.
    if (0 != fifo_count) {
        DBG(("EP0_fill_fifo(), fifo already contains %d bytes", fifo_count));
        return;
    }

    // The IN_READY bit should never be set on entry. It can only get
    // set by a previous call to fill_fifo(), and the data should
    // have gone out before we get back here.
    if (0 != (*EP0_CONTROL & EP0_IN_READY)) {
        DBG(("EP0 fill_fifo(), in-packet-ready bit already set, state %x\n", *EP0_CONTROL));
        return;
    }

    // Now put up to another eight bytes into the fifo.
    max = ((ep0.length - ep0.transmitted) > EP0_FIFO_SIZE) ? EP0_FIFO_SIZE : (ep0.length - ep0.transmitted);
    while (ok && (filled < max)) {
        if (0 != ep0.common.buffer_size) {
            int         datum;
            int         retries, checks;
            cyg_bool    written;

            datum       = *ep0.common.buffer++;
            ep0.common.buffer_size--;
            written     = false;
            
            for (retries = 0; ok && !written && (retries < MAX_RETRIES); retries++) {
                if (filled != *EP0_WRITE_COUNT) {
                    DBG(("EP0 fill_fifo, inconsistency, written %d but write count %d\n", filled, *EP0_WRITE_COUNT));
                    ok = false;
                }
                *EP0_DATA = datum;
                // The write-count may take a few cycles to settle down.
                for (checks = 0; !written && (checks < MAX_CHECKS); checks++) {
                    if (filled < *EP0_WRITE_COUNT) {
                        filled++;
                        written = true;
                        // DBG(("Transferred %d byte (%x) after %d checks, %d retries\n", filled - 1, datum, checks, retries));
                    }
                }
            }
        } else if ((void (*)(usbs_control_endpoint*))0 != ep0.common.fill_buffer_fn) {
            (*ep0.common.fill_buffer_fn)(&ep0.common);
        } else {
            break;
        }
    }

    // At this point either it has proved impossible to fill the fifo,
    // e.g. because of a new control message, or up to another eight
    // bytes have been sent. 
    if (!ok) {
        if (0 == (EP0_SETUP_END & *EP0_CONTROL)) {
            // There is something seriously wrong.
            DBG(("ep0_fill_fifo(), failed, only filled %d bytes, status %x\n", filled, *EP0_CONTROL));
            usbs_sa11x0_poke(EP0_CONTROL, EP0_FORCE_STALL, EP0_FORCE_STALL, 0);
        }
        return;
    }

    // The following conditions are possible:
    // 1) amount transferred == amount requested, transfer complete.
    // 2) amount transferred < amount requested, this fill involved
    //    <eight bytes, transfer complete by definition of the protocol.
    // 3) amount transferred < amount requested but exactly eight
    //    bytes were sent this time. It will be necessary to send
    //    another packet of zero bytes to complete the transfer.
    ep0.transmitted += filled;
    if ((ep0.transmitted == ep0.length) || (filled < EP0_FIFO_SIZE)) {

        ep0.ep_state    = EP0_STATE_IDLE;
        if ((usbs_control_return (*)(usbs_control_endpoint*, int))0 != ep0.common.complete_fn) {
            (void) (*ep0.common.complete_fn)(&ep0.common, 0);
        }
        ep0.common.buffer               = (unsigned char*) 0;
        ep0.common.buffer_size          = 0;
        ep0.common.fill_buffer_fn       = (void (*)(usbs_control_endpoint*)) 0;

        // This informs the hardware that the control message has been
        // handled.
        bits_to_set = EP0_DATA_END;
    }
    
    // This allows another IN operation to empty the fifo.
    bits_to_set |= EP0_IN_READY;
    usbs_sa11x0_poke(EP0_CONTROL, bits_to_set, bits_to_set, 0);
}

// Another utility function to empty the fifo. This involves similar
// hardware problems to writing, it is possible to read a byte without
// changing the fifo state so that next time the same byte would be
// read again. Again there is a possible race condition if another
// control message arrives while emptying the fifo.
static int
usbs_sa11x0_ep0_empty_fifo(unsigned char* buf)
{
    int         count   = *EP0_WRITE_COUNT & 0x00FF;
    int         emptied = 0;
    cyg_bool    ok      = true;

    CYG_ASSERT( (count >= 0) & (count <= 8), "EP0 write count must be in range");

    while (ok && (emptied < count)) {
        int      retries, checks;
        cyg_bool read   = false;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -