📄 usbs_upd985xx.c
字号:
typedef struct ep0_impl {
usbs_control_endpoint common;
cyg_bool rx_expecting_data;
cyg_bool rx_indicator_valid;
RxMailbox rx_indicator;
cyg_bool tx_indicator_valid;
TxMailbox tx_indicator;
cyg_bool tx_needs_zero_transfer;
cyg_uint32 tx_size;
} ep0_impl;
static ep0_impl ep0 = {
common:
{
state: USBS_STATE_POWERED, // The hardware does not distinguish between detached, attached and powered.
enumeration_data: (usbs_enumeration_data*) 0,
start_fn: &usbs_upd985xx_ep0_start,
poll_fn: &usbs_upd985xx_poll,
interrupt_vector: CYGNUM_HAL_INTERRUPT_USB,
control_buffer: { 0, 0, 0, 0, 0, 0, 0, 0 },
state_change_fn: (void (*)(usbs_control_endpoint*, void*, usbs_state_change, int)) 0,
state_change_data: (void*) 0,
standard_control_fn: (usbs_control_return (*)(usbs_control_endpoint*, void*)) 0,
standard_control_data: (void*) 0,
class_control_fn: (usbs_control_return (*)(usbs_control_endpoint*, void*)) 0,
class_control_data: (void*) 0,
vendor_control_fn: (usbs_control_return (*)(usbs_control_endpoint*, void*)) 0,
vendor_control_data: (void*) 0,
reserved_control_fn: (usbs_control_return (*)(usbs_control_endpoint*, void*)) 0,
reserved_control_data: (void*) 0,
buffer: (unsigned char*) 0,
buffer_size: 0,
fill_buffer_fn: (void (*)(usbs_control_endpoint*)) 0,
fill_data: (void*) 0,
fill_index: 0,
complete_fn: (usbs_control_return (*)(usbs_control_endpoint*, int)) 0
},
rx_expecting_data: false,
rx_indicator_valid: false,
rx_indicator: { 0, (void*) 0 },
tx_indicator_valid: false,
tx_indicator: { 0 },
tx_needs_zero_transfer: 0
};
extern usbs_control_endpoint usbs_upd985xx_ep0 __attribute__((alias ("ep0")));
// Endpoint 1, isochronous transmits. This endpoint is not yet
// supported. Although the interface for bulk transmits should be
// mostly re-usable, there are some additional error conditions if
// either the host or the target fails to achieve the desired
// throughput.
// Endpoint 2, isochronous receives. Not yet supported for now, just
// like endpoint 1.
// Endpoints 3 and 5 can share some code.
#if defined(CYGPKG_DEVS_USB_UPD985XX_EP3) || defined(CYGPKG_DEVS_USB_UPD985XX_EP5)
// Endpoint 3, bulk transmits, and endpoint 5, either interrupt transmits
// or emulation of bulk transmits. The hardware does most
// of the work.
typedef struct ep35_impl {
usbs_tx_endpoint common;
cyg_bool tx_indicator_valid;
TxMailbox tx_indicator;
int send_command;
volatile cyg_uint32* cr;
TxBufferDescriptor* tx_bufdesc;
} ep35_impl;
# ifdef CYGPKG_DEVS_USB_UPD985XX_EP3
static void ep3_start_tx(usbs_tx_endpoint*);
static void ep3_set_halted(usbs_tx_endpoint*, cyg_bool);
static ep35_impl ep3 = {
common: {
start_tx_fn: &ep3_start_tx,
set_halted_fn: &ep3_set_halted,
complete_fn: (void (*)(void*, int)) 0,
complete_data: (void*) 0,
buffer: (const unsigned char*) 0,
buffer_size: 0,
halted: 0,
},
tx_indicator_valid: false,
tx_indicator: { 0 },
send_command: USBS_CMR_COMMAND_TX_EP3,
cr: EP3_CR,
tx_bufdesc: 0 // Needs run-time initialization
};
extern usbs_tx_endpoint usbs_upd985xx_ep3 __attribute__ ((alias ("ep3")));
# endif
# ifdef CYGPKG_DEVS_USB_UPD985XX_EP5
static void ep5_start_tx(usbs_tx_endpoint*);
static void ep5_set_halted(usbs_tx_endpoint*, cyg_bool);
static ep35_impl ep5 = {
common: {
start_tx_fn: &ep5_start_tx,
set_halted_fn: &ep5_set_halted,
complete_fn: (void (*)(void*, int)) 0,
complete_data: (void*) 0,
buffer: (const unsigned char*) 0,
buffer_size: 0,
halted: 0,
},
tx_indicator_valid: false,
tx_indicator: { 0 },
send_command: USBS_CMR_COMMAND_TX_EP5,
cr: EP5_CR,
tx_bufdesc: 0 // Needs run-time initialization
};
extern usbs_tx_endpoint usbs_upd985xx_ep5 __attribute__ ((alias ("ep5")));
# endif
#endif
#ifdef CYGPKG_DEVS_USB_UPD985XX_EP4
// Endpoint 4, bulk receives. Again the hardware does the hard work.
// Receive pool 2 is reserved for this endpoint.
typedef struct ep4_impl {
usbs_rx_endpoint common;
cyg_uint32 head_size;
cyg_uint32 direct_size;
cyg_uint32 tail_size;
cyg_bool rx_indicator_valid;
RxMailbox rx_indicator;
cyg_int32 tail_index;
} ep4_impl;
static void ep4_start_rx(usbs_rx_endpoint*);
static void ep4_set_halted(usbs_rx_endpoint*, cyg_bool);
static ep4_impl ep4 = {
common: {
start_rx_fn: &ep4_start_rx,
set_halted_fn: &ep4_set_halted,
complete_fn: (void (*)(void*, int)) 0,
complete_data: (void*) 0,
buffer: (unsigned char*) 0,
buffer_size: 0,
halted: 0,
},
rx_indicator_valid: false,
rx_indicator: { 0, (void*) 0 },
tail_index: -1
};
extern usbs_rx_endpoint usbs_upd985xx_ep4 __attribute__((alias ("ep4")));
#endif
// Endpoint 6, interrupt receives. Not yet implemented. There may
// be conflicts because the hardware is shared with endpoint 0.
// ----------------------------------------------------------------------------
// Mailbox support.
//
// The transmit and receive mailboxes are shared between the
// appropriate endpoints. This causes some complications if e.g.
// transmits on several endpoints complete at the same time. For
// example the tx mailbox might contain send indicators for endpoints
// 3 and 0, but the DSR code will process endpoint 0 before endpoint
// 3.
//
// This device driver works on the basis that there can be only one
// transmit and/or receive in progress for any given endpoint, so the
// relevant information can be extracted from the mailbox and put into
// the per-endpoint structures. The routines below can be used to
// move data from the mailboxes. They will be called in DSR context
// so there is no need to worry about locking.
static void
drain_tx_mailbox(void)
{
TxMailbox* tmra = IBUS_SWAPPTR(TxMailbox, *USBS_TMRA);
TxMailbox* tmwa = IBUS_SWAPPTR(TxMailbox, *USBS_TMWA);
if (tmra != tmwa) {
do {
TxMailbox mbox = *tmra;
tmra++;
if (tmra == &(uncached->tx_mboxes[TXMBOX_COUNT])) {
tmra = &(uncached->tx_mboxes[0]);
}
switch(mbox.status & TXMBOX_STATUS_EPN_MASK) {
case TXMBOX_STATUS_EPN_EP0:
CYG_ASSERT(false == ep0.tx_indicator_valid, "Only one ep0 transmit should be in progress at a time");
ep0.tx_indicator = mbox;
ep0.tx_indicator_valid = true;
break;
#ifdef CYGPKG_DEVS_USB_UPD985XX_EP3
case TXMBOX_STATUS_EPN_EP3:
CYG_ASSERT(false == ep3.tx_indicator_valid, "Only one ep3 transmit should be in progress at a time");
ep3.tx_indicator = mbox;
ep3.tx_indicator_valid = true;
break;
#endif
#ifdef CYGPKG_DEVS_USB_UPD985XX_EP5
case TXMBOX_STATUS_EPN_EP5:
CYG_ASSERT(false == ep5.tx_indicator_valid, "Only one ep5 transmit should be in progress at a time");
ep5.tx_indicator = mbox;
ep5.tx_indicator_valid = true;
break;
#endif
default:
break;
}
} while (tmra != tmwa);
*USBS_TMRA = IBUS_SWAPPTR(TxMailbox, tmra); FLUSH_IBUS();
}
}
static void
drain_rx_mailbox(void)
{
RxMailbox* rmra = IBUS_SWAPPTR(RxMailbox, *USBS_RMRA);
RxMailbox* rmwa = IBUS_SWAPPTR(RxMailbox, *USBS_RMWA);
if (rmra != rmwa) {
do {
RxMailbox mbox = *rmra;
rmra++;
if (rmra == &(uncached->rx_mboxes[RXMBOX_COUNT])) {
rmra = &(uncached->rx_mboxes[0]);
}
switch(mbox.status & RXMBOX_STATUS_EPN_MASK) {
case RXMBOX_STATUS_EPN_EP0:
// Ignore zero-byte transfers. It is not clear why
// these happen, but they have been observed.
if (0 != (mbox.status & RXMBOX_STATUS_SIZE_MASK)) {
CYG_ASSERT(false == ep0.rx_indicator_valid, "Only one ep0 receive should be in progress at a time");
ep0.rx_indicator = mbox;
ep0.rx_indicator_valid = true;
}
break;
#ifdef CYGPKG_DEVS_USB_UPD985XX_EP4
case RXMBOX_STATUS_EPN_EP4:
// If an error occurs then the hardware may report
// multiple rx completions, each with an IBUS error
// indicator. For now only the last rx indicator is
// taken into account, which means we could lose
// a successful receive that happens to be followed
// by an error.
// NOTE: any possibility of improving on this?
#if 1
CYG_ASSERT(false == ep4.rx_indicator_valid, "Only one ep4 receive should be in progress at a time");
#endif
ep4.rx_indicator = mbox;
ep4.rx_indicator_valid = true;
break;
#endif
default:
break;
}
} while (rmra != rmwa);
*USBS_RMRA = IBUS_SWAPPTR(RxMailbox, rmra); FLUSH_IBUS();
}
}
// ----------------------------------------------------------------------------
// Transmit locking.
//
// According to NEC errata U3 and U4 the hardware may exhibit
// undesirable behaviour if there are concurrent transmissions. There
// are various ways of resolving this, but the simplest is to perform
// locking in software so that at most one transmit endpoint is in use
// at any one time. This approach works fine if transmissions only
// involve one tx endpoint plus the control endpoint because the
// control endpoint generally only gets used during initialization and
// the other endpoint only gets used after initialization. If multiple
// transmit endpoints are used then locking in software becomes less
// acceptable, especially if isochronous transfers are used because
// timing is important for those.
//
// There is a theoretical problem if e.g. there is a very large bulk
// transfer on a busy bus and it is necessary to respond to a control
// message. The control reply would be delayed, possibly causing a
// violation of the USB standard and a timeout on the host.
#ifdef CYGIMP_DEVS_USB_UPD985XX_SERIALIZE_TRANSMITS
static void ep0_start_tx(void);
# if defined(CYGPKG_DEVS_USB_UPD985XX_EP3) || defined(CYGPKG_DEVS_USB_UPD985XX_EP5)
static void ep35_start_tx(ep35_impl*);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -