usbs_d12.c
来自「开放源码实时操作系统源码.」· C语言 代码 · 共 1,974 行 · 第 1/5 页
C
1,974 行
// See if caller requested a read smaller than the endp. Read &
// throw away extra
if (ep->common.buffer_size < ep_size) {
byte tmp_buf[D12_MAX_PACKET_SIZE];
if (endp == D12_MAIN_ENDP)
n = d12_read_main_endp_buf(D12_BASE_ADDR, tmp_buf, &done);
else
n = d12_read_endp_buf(D12_BASE_ADDR, RX_ENDP_INDEX[endp], tmp_buf);
if (n > ep->common.buffer_size) {
n = ep->received = ep->common.buffer_size;
ret = -ENOMEM;
TRACE_D12("\tEP%d: *** Rx Buffer too small. Data Lost ***\n", endp);
}
else
ret = ep->received = n;
memcpy(ep->common.buffer, tmp_buf, n);
buf_remaining = ep->common.buffer_size - n;
}
TRACE_D12("\tEP%d: Rx Complete. Buffer (nearly) full. [%d]\n",
endp, buf_remaining);
usbs_d12_ep_rx_complete(ep, ret);
return;
}
// ----- Read the data from the chip -----
if (endp == D12_MAIN_ENDP)
n = d12_read_main_endp_buf(D12_BASE_ADDR,
ep->common.buffer + ep->received, &done);
else {
n = d12_read_endp_buf(D12_BASE_ADDR, RX_ENDP_INDEX[endp],
ep->common.buffer + ep->received);
done = (n < RX_ENDP_SIZE[endp]);
}
ep->received += n;
buf_remaining = ep->common.buffer_size - ep->received;
done = done || (buf_remaining < ep_size);
TRACE_D12("EP%d: Received %d bytes.\n", endp, n);
TRACE_BUF("\t", ep->common.buffer + ep->received-n, n);
// ----- If we're done, complete the receive -----
if (done) {
TRACE_D12("\tEP%d Rx Complete (%d) %p\n", endp,
ep->received, ep->common.complete_fn);
usbs_d12_ep_rx_complete(ep, ep->received);
}
}
// --------------------------------------------------------------------------
// Stalls/unstalls the specified endpoint.
static void
usbs_d12_stall_rx_ep(rx_endpoint *ep, cyg_bool halt)
{
ep->common.halted = halt;
d12_stall_endp(D12_BASE_ADDR, RX_ENDP_INDEX[ep->endp], halt);
}
// --------------------------------------------------------------------------
// Handler for an Rx endpoint full interrupt. It clears the interrupt on the
// D12 by reading the endpoint's status register then calls the routine to
// read the data into the buffer.
//
// Called from the DSR context only.
//
static void
usbs_d12_ep_rx_intr(rx_endpoint *ep)
{
d12_read_last_trans_status(D12_BASE_ADDR, RX_ENDP_INDEX[ep->endp]);
usbs_d12_ep_rx(ep);
}
#endif
// --------------------------------------------------------------------------
// Common Tx Endpoint 1 & 2
// --------------------------------------------------------------------------
#if defined(_TX_EP1) || defined(_TX_EP2)
// Clears out the endpoint data structure before/after a tx is complete.
static void usbs_d12_clear_tx_ep(tx_endpoint *ep)
{
ep->common.buffer = 0;
ep->common.buffer_size = 0;
ep->common.complete_fn = 0;
ep->common.complete_data = 0;
ep->transmitted = 0;
ep->tx_empty = false;
}
// --------------------------------------------------------------------------
// This is called when a transmit is completed. It resets the endpoint vars
// and calls the registered completion function, if any.
//
// CONTEXT:
// Called from either the DSR or the app thread that started tx.
static void usbs_d12_ep_tx_complete(tx_endpoint *ep, int result)
{
completion_fn fn = ep->common.complete_fn;
void *data = ep->common.complete_data;
usbs_d12_clear_tx_ep(ep);
if (fn)
(*fn)(data, result);
}
// --------------------------------------------------------------------------
// The routine writes data to the chip and updates the endpoint's counters.
// It gets called at the start of a transfer operation to prime the device
// and then gets called each time the chip finishes sending a packet to the
// host and is ready for more data. If the amount of data remaining is
// smaller than can fit in the chip's endpoint buffer, then this is the last
// packet to send, so we call the completion function.
//
// CONTEXT:
// Called from either the DSR or the app thread that started the tx
// In either case, it's assumed the chip is locked.
static void
usbs_d12_ep_tx(tx_endpoint *ep)
{
int n, nRemaining;
// ----- Already done. Intermittent interrupt, so get out -----
if (!ep->common.buffer)
return;
// ----- See how many bytes remaining in buffer -----
nRemaining = ep->common.buffer_size - ep->transmitted;
TRACE_D12("EP%d: Tx %p, %d Done, %d Remaining\n", ep->endp,
ep->common.buffer, ep->transmitted, nRemaining);
// ----- If prev packet was last, signal that we're done -----
if (nRemaining == 0 && !ep->tx_empty) {
TRACE_D12("\tEP%d: Tx complete (%d) %p\n", ep->endp,
ep->transmitted, ep->common.complete_fn);
usbs_d12_ep_tx_complete(ep, ep->transmitted);
return;
}
// ----- Write the next packet to chip -----
if (nRemaining < TX_ENDP_SIZE[ep->endp]) {
n = nRemaining;
ep->tx_empty = false;
}
else
n = TX_ENDP_SIZE[ep->endp];
TRACE_D12("EP%d: Writing %d bytes. %s\n", ep->endp,
n, (n == 0) ? "DONE" : "");
TRACE_BUF("\t", ep->common.buffer + ep->transmitted, n);
d12_write_endp_buf(D12_BASE_ADDR, TX_ENDP_INDEX[ep->endp],
ep->common.buffer + ep->transmitted, (uint8) n);
ep->transmitted += n;
// ----- If empty packet, complete now -----
if (n == 0) {
TRACE_D12("\tEP%d: Tx complete (%d) %p\n", ep->endp,
ep->transmitted, ep->common.complete_fn);
usbs_d12_ep_tx_complete(ep, ep->transmitted);
return;
}
}
// --------------------------------------------------------------------------
// Stalls/unstalls the specified tx endpoint.
static void
usbs_d12_stall_tx_ep(tx_endpoint *ep, cyg_bool halt)
{
ep->common.halted = halt;
d12_stall_endp(D12_BASE_ADDR, TX_ENDP_INDEX[ep->endp], halt);
}
// --------------------------------------------------------------------------
// Handler for when the chip's tx RAM for an endoint has just been emptied
// (sent to the host) and the chip is ready for more data.
// We read the endpoint's last trans status register to clear the interrupt
// on the D12, then call the tx function to send the next packet or
// complete the transfer.
static void
usbs_d12_ep_tx_intr(tx_endpoint *ep)
{
d12_read_last_trans_status(D12_BASE_ADDR, TX_ENDP_INDEX[ep->endp]);
usbs_d12_ep_tx(ep);
}
#endif // defined(_TX_EP1) || defined(_TX_EP2)
// --------------------------------------------------------------------------
// Application Program Interface (API)
// --------------------------------------------------------------------------
#if defined(_RX_EP1) || defined(_RX_EP2)
// Starts a receive operation on the specified endpoint. If the buffer size
// is zero the completion function is called immediately. The routine checks
// if tehre is data in the chip's endpoint buffer, and if so it will call
// ep_rx() to start reading the data out of the chip.
//
// If the endpoint is currently stalled, a read size of zero can be used to
// block the calling thread until the stall is cleared. If the read size is
// non-zero and the endpoint is stalled the completion function is called
// immediately with an error result.
static void
usbs_d12_api_start_rx_ep(usbs_rx_endpoint *ep)
{
rx_endpoint *epx = (rx_endpoint *) ep;
if (ep->halted) {
if (ep->buffer_size != 0)
usbs_d12_ep_rx_complete(epx, -EAGAIN);
}
else if (ep->buffer_size == 0) {
usbs_d12_ep_rx_complete(epx, 0);
}
else {
TRACE_D12("EP%d: Starting Rx, %p, %d\n", epx->endp, ep->buffer,
ep->buffer_size);
usbs_d12_lock();
epx->received = 0;
if (d12_data_available(D12_BASE_ADDR, RX_ENDP_INDEX[epx->endp]))
usbs_d12_ep_rx(epx);
usbs_d12_unlock();
}
}
// --------------------------------------------------------------------------
// Halts/unhalts one of the generic rx (OUT) endpoints.
//
static void usbs_d12_api_stall_rx_ep(usbs_rx_endpoint *ep, cyg_bool halt)
{
usbs_d12_lock();
usbs_d12_stall_rx_ep((rx_endpoint*) ep, halt);
usbs_d12_unlock();
}
#endif // defined(_RX_EP1) || defined(_RX_EP2)
// --------------------------------------------------------------------------
// Tx API
// --------------------------------------------------------------------------
#if defined(_TX_EP1) || defined(_TX_EP2)
// This starts a transmit on one of the data endpoints. If the endpoint is
// stalled a buffer size of zero can be used to block until the stall is
// cleared. Any other size on a stalled endpoint will result in an error
// callback immediately. The first packet is sent to the chip immediately,
// in the application context. If the chip's buffer can contain the whole
// transfer, the completion function will be called immediately, again,
// still in the application context.
//
// If an empty packet is requested we send one from here and call the
// completion function. This should not cause an intr on the D12.
//
// CONTEXT:
// Called from an application thread
static void usbs_d12_api_start_tx_ep(usbs_tx_endpoint *ep)
{
tx_endpoint *epx = (tx_endpoint*) ep;
if (ep->halted) {
if (ep->buffer_size != 0)
usbs_d12_ep_tx_complete(epx, -EAGAIN);
}
else if (ep->buffer_size == 0) {
usbs_d12_lock();
d12_write_endp_buf(D12_BASE_ADDR, TX_ENDP_INDEX[epx->endp], 0, 0);
usbs_d12_ep_tx_complete(epx, 0);
usbs_d12_unlock();
}
else {
TRACE_D12("EP%d: Starting Tx, %p, %d\n", epx->endp, ep->buffer,
ep->buffer_size);
usbs_d12_lock();
epx->tx_empty = true;
epx->transmitted = 0;
usbs_d12_ep_tx(epx);
usbs_d12_unlock();
}
}
// --------------------------------------------------------------------------
// Halts/unhalts one of the generic endpoints.
static void
usbs_d12_api_stall_tx_ep(usbs_tx_endpoint *ep, cyg_bool halt)
{
usbs_d12_lock();
usbs_d12_stall_tx_ep((tx_endpoint*) ep, halt);
usbs_d12_unlock();
}
#endif // defined(_TX_ENDP1) || defined(_TX_EP2)
// --------------------------------------------------------------------------
// DSR
// --------------------------------------------------------------------------
// The DSR for the D12 chip. This is normally called in the DSR context when
// the D12 has raised its interrupt flag indicating that it needs to be
// serviced. The interrupt register contains bit flags that are OR'ed togther
// indicating what items need to be serviced. There are flags for the
// following:
// - The endpoints (one bit for each)
// - Bus Reset
// - Suspend Change
// - DMA (terminal count)
//
// Care must be taken in that the D12's interrupt output is level-sensitive
// (in that the interrupt sources are OR'ed together and not all cleared
// atomically in a single operation). Platforms (such as the PC) may be
// expecting edge-triggered interrupts, so we must work around that.
// So, we loop on the interrupt register. Even though, in each loop, we
// perform all of the required operations to clear the interrupts, a new
// one may have arrived before we finished clearing the previous ones.
// So we read the intr reg again. Once the intr reg gives a zero reading
// we know that the D12 has dropped its IRQ line.
//
// Note, if we're configured to use a thread, this routine is called from
// within a thread context (not a DSR context).
//
static void
usbs_d12_dsr(cyg_vector_t vector, cyg_ucount32 count,
cyg_addrword_t data)
{
uint16 status;
bool suspended;
CYG_ASSERT(vector == CYGNUM_DEVS_USB_D12_INT,
"DSR should only be invoked for D12 interrupts");
while ((status = d12_read_intr_reg(D12_BASE_ADDR)) != 0) {
TRACE_D12("Intr Status: 0x%04X\n", (unsigned) status);
if (status & D12_INTR_BUS_RESET) {
TRACE_D12("\n>>> Bus Reset <<<\n");
usbs_d12_reset();
}
else {
// ----- Suspend Change -----
suspended = (bool) (ep0.common.state & USBS_STATE_SUSPENDED);
if (status & D12_INTR_SUSPEND_CHANGE) {
if (!suspended && (status & ~D12_INTR_SUSPEND_CHANGE) == 0)
usbs_d12_suspend(true);
}
else if (suspended)
usbs_d12_suspend(false);
// ----- Bulk Endpoints -----
#ifdef _TX_EP2
if (status & D12_INTR_TX_ENDP2)
usbs_d12_ep_tx_intr(&tx_ep2);
#endif
#ifdef _RX_EP2
if (status & D12_INTR_RX_EN
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?