📄 usbs_sa11x0.c
字号:
//// 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 voidusbs_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 voidusbs_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 voidusbs_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 intusbs_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; for (retries = 0; !read && (retries < MAX_RETRIES); retries++) { if ((count - emptied) != *EP0_WRITE_COUNT) { DBG(("EP0_empty_fifo, inconsistency, read %d bytes of %d, but fifo count %d\n", emptied, count, *EP0_WRITE_COUNT)); ok = false; } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -