📄 usbs_sa11x0.c
字号:
// 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 + -