usbs_d12.c
来自「开放源码实时操作系统源码.」· C语言 代码 · 共 1,974 行 · 第 1/5 页
C
1,974 行
return;
}
// ----- If prev packet was last, signal that we're done -----
if (nRemaining == 0 && !ep0.tx_empty) {
TRACE_D12("\tEP0: Tx Complete (%d) %p\n", ep0.transmitted,
ep0.common.complete_fn);
usbs_d12_ep0_complete(0);
return;
}
// ----- Load the next tx packet onto the chip -----
if (nRemaining < D12_ENDP0_SIZE) {
n = (uint8) nRemaining;
ep0.tx_empty = false;
}
else
n = D12_ENDP0_SIZE;
d12_write_endp_buf(D12_BASE_ADDR, D12_TX_ENDP0,
&ep0_tx_buffer[ep0.transmitted], n);
TRACE_D12("EP0: Wrote %u bytes\n", (unsigned) n);
TRACE_BUF0("\t", &ep0_tx_buffer[ep0.transmitted], n);
ep0.transmitted += n;
// ----- If empty packet, D12 won't interrupt, so end now ----- */
if (n == 0) {
TRACE_D12("\tEP0: Tx Complete (%d) %p\n", ep0.transmitted,
ep0.common.complete_fn);
usbs_d12_ep0_complete(0);
}
}
// --------------------------------------------------------------------------
// This function is called when a packet has been successfully sent on the
// primary control endpoint (ep0). It indicates that the chip is ready for
// another packet. We read the LastTransStatus for the endpoint to clear
// the interrupt bit, then call ep0_tx() to continue the transfer.
static void
usbs_d12_ep0_tx_intr(void)
{
d12_read_last_trans_status(D12_BASE_ADDR, D12_TX_ENDP0);
usbs_d12_ep0_tx();
}
// --------------------------------------------------------------------------
// Try to handle standard requests. This is a three step process:
// 1. If it's something we should handle internally we take care of it.
// Currently we can handle SET_ADDRESS requests, and a few others.
// 2. If the upper level code has installed a standard control handler
// we let that function have a crack at it.
// 3. If neither of those handle the packet we let
// usbs_handle_standard_control() have a last try at it.
//
// Locally:
// SET_ADDRESS: The host is demanding that we change our USB address.
// This is done by updating the Address/Enable register on the D12.
// Note, however that the USB protocol requires us to ack at the old
// address, change address, and then accept the next control message
// at the new address. The D12 address reg is buffered to do this
// automatically for us. The updated address on the chip won't take
// affect until after the empty ack is sent. Nice.
//
static usbs_control_return
usbs_d12_handle_std_req(usb_devreq *req)
{
usbs_control_return result = USBS_CONTROL_RETURN_UNKNOWN;
int recipient = req->type & USB_DEVREQ_RECIPIENT_MASK;
if (req->request == USB_DEVREQ_SET_ADDRESS) {
TRACE_D12("Setting Addr: %u\n", (unsigned) req->value_lo);
d12_set_addr_enable(D12_BASE_ADDR, req->value_lo, true);
result = USBS_CONTROL_RETURN_HANDLED;
}
else if (req->request == USB_DEVREQ_GET_STATUS) {
if (recipient == USB_DEVREQ_RECIPIENT_DEVICE) {
const usbs_enumeration_data *enum_data = ep0.common.enumeration_data;
if (enum_data && enum_data->device.number_configurations == 1 &&
enum_data->configurations) {
ep0.common.control_buffer[0] =
(enum_data->configurations[0].attributes
& USB_CONFIGURATION_DESCRIPTOR_ATTR_SELF_POWERED) ? 1 : 0;
ep0.common.control_buffer[0] |=
(enum_data->configurations[0].attributes
& USB_CONFIGURATION_DESCRIPTOR_ATTR_REMOTE_WAKEUP) ? 2 : 0;
ep0.common.control_buffer[1] = 0;
result = USBS_CONTROL_RETURN_HANDLED;
}
}
else if (recipient == USB_DEVREQ_RECIPIENT_ENDPOINT) {
bool halted = false;
result = USBS_CONTROL_RETURN_HANDLED;
switch (req->index_lo) {
#if defined(_RX_EP1)
case 0x01 : halted = rx_ep1.common.halted; break;
#endif
#if defined(_TX_EP1)
case 0x81 : halted = tx_ep1.common.halted; break;
#endif
#if defined(_RX_EP2)
case 0x02 : halted = rx_ep2.common.halted; break;
#endif
#if defined(_TX_EP2)
case 0x82 : halted = tx_ep2.common.halted; break;
#endif
default:
result = USBS_CONTROL_RETURN_STALL;
}
TRACE_D12("Get Status: Endp [0x%02X] %s\n", (unsigned) req->index_lo,
halted ? "Halt" : "Unhalt");
if (result == USBS_CONTROL_RETURN_HANDLED) {
ep0.common.control_buffer[0] = (halted) ? 1 : 0;
ep0.common.control_buffer[1] = 0;
}
}
if (result == USBS_CONTROL_RETURN_HANDLED) {
ep0.common.buffer = ep0.common.control_buffer;
ep0.common.buffer_size = 2;
ep0.common.fill_buffer_fn = 0;
ep0.common.complete_fn = 0;
}
}
else if ((req->request == USB_DEVREQ_SET_FEATURE ||
req->request == USB_DEVREQ_CLEAR_FEATURE) &&
recipient == USB_DEVREQ_RECIPIENT_ENDPOINT) {
bool halt = (req->request == USB_DEVREQ_SET_FEATURE);
result = USBS_CONTROL_RETURN_HANDLED;
TRACE_D12("Endpoint [0x%02X] %s\n", (unsigned) req->index_lo,
halt ? "Halt" : "Unhalt");
switch (req->index_lo) {
#if defined(_RX_EP1)
case 0x01 : usbs_d12_stall_rx_ep(&rx_ep1, halt); break;
#endif
#if defined(_TX_EP1)
case 0x81 : usbs_d12_stall_tx_ep(&tx_ep1, halt); break;
#endif
#if defined(_RX_EP2)
case 0x02 : usbs_d12_stall_rx_ep(&rx_ep2, halt); break;
#endif
#if defined(_TX_EP2)
case 0x82 : usbs_d12_stall_tx_ep(&tx_ep2, halt); break;
#endif
default:
result = USBS_CONTROL_RETURN_STALL;
}
}
else if (ep0.common.standard_control_fn != 0) {
result = (*ep0.common.standard_control_fn)
(&ep0.common,
ep0.common.standard_control_data);
}
if (result == USBS_CONTROL_RETURN_UNKNOWN)
result = usbs_handle_standard_control(&ep0.common);
return result;
}
// --------------------------------------------------------------------------
// Handler for the receipt of a setup (dev request) packet from the host.
// We examine the packet to determine what function(s) should get a crack
// at trying to handle it, then pass control to the proper function. If
// the function handles the message we either ACK (len==0) or prepare for
// an IN or OUT data phase. If no one handled the message, we stall the
// control endpoint.
static void
usbs_d12_ep0_setup_packet(usb_devreq* req)
{
int len, dir, protocol, recipient;
usbs_control_return result = USBS_CONTROL_RETURN_UNKNOWN;
// ----- See who should take the request -----
len = make_word(req->length_hi, req->length_lo);
dir = req->type & USB_DEVREQ_DIRECTION_MASK;
protocol = req->type & USB_DEVREQ_TYPE_MASK;
recipient = req->type & USB_DEVREQ_RECIPIENT_MASK;
TRACE_BUF0("DevReq: ", ep0.common.control_buffer, sizeof(usb_devreq));
if (protocol == USB_DEVREQ_TYPE_STANDARD)
result = usbs_d12_handle_std_req(req);
else {
// Pass on non-standard requests to registered handlers
usbs_control_return (*callback_fn)(usbs_control_endpoint*, void*);
void *callback_arg;
if (protocol == USB_DEVREQ_TYPE_CLASS) {
callback_fn = ep0.common.class_control_fn;
callback_arg = ep0.common.class_control_data;
}
else if (protocol == USB_DEVREQ_TYPE_VENDOR) {
callback_fn = ep0.common.vendor_control_fn;
callback_arg = ep0.common.vendor_control_data;
}
else {
callback_fn = ep0.common.reserved_control_fn;
callback_arg = ep0.common.reserved_control_data;
}
result = (callback_fn) ? (*callback_fn)(&ep0.common, callback_arg)
: USBS_CONTROL_RETURN_STALL;
}
// ----- If handled prep/handle data phase, otherwise stall -----
if (result == USBS_CONTROL_RETURN_HANDLED) {
if (len == 0) {
TRACE_D12("\tCtrl ACK\n");
d12_write_endp_buf(D12_BASE_ADDR, D12_TX_ENDP0, 0, 0);
}
else {
// Set EP0 state to IN or OUT mode for data phase
ep0.transmitted = 0;
ep0.length = len;
if (dir == USB_DEVREQ_DIRECTION_OUT) {
// Wait for the next packet from the host.
ep0.ep_state = ENDP_STATE_OUT;
CYG_ASSERT(ep0.common.buffer != 0,
"A rx buffer should have been provided for EP0");
CYG_ASSERT(ep0.common.complete_fn != 0,
"A completion function should be provided for EP0 OUT control messages");
}
else {
ep0.tx_empty = true;
ep0.ep_state = ENDP_STATE_IN;
ep0_fill_tx_buffer();
usbs_d12_ep0_tx();
}
}
}
else {
TRACE_D12("\t*** Unhandled Device Request ***\n");
// The request wasn't handled, so stall control endpoint
d12_stall_ctrl_endp(D12_BASE_ADDR, true);
}
}
// --------------------------------------------------------------------------
// This is called when the chip indicates that a packet has been received
// on control endpoint 0. If it's a setup packet, we handle it accordingly,
// otherwise it's a data packet coming in on ep0.
//
static void
usbs_d12_ep0_rx_intr(void)
{
byte byStat = d12_read_last_trans_status(D12_BASE_ADDR, D12_RX_ENDP0);
TRACE_D12("\tEP0 Status: 0x%02X\n", (unsigned) byStat);
if (byStat & D12_LAST_TRANS_SETUP_PACKET) {
usb_devreq *req = (usb_devreq *) ep0.common.control_buffer;
if (!d12_read_setup_packet(D12_BASE_ADDR, (byte*) req)) {
TRACE_D12("ep0_rx_dsr: Error reading setup packet\n");
d12_stall_ctrl_endp(D12_BASE_ADDR, true);
}
else
usbs_d12_ep0_setup_packet(req);
}
else {
if (ep0.common.buffer) {
uint8 n = d12_read_endp_buf(D12_BASE_ADDR, D12_RX_ENDP0,
ep0.common.buffer + ep0.transmitted);
ep0.transmitted += n;
TRACE_D12("EP0: Received %d bytes\n", (unsigned) n);
if (n < D12_ENDP0_SIZE ||
ep0.common.buffer_size - ep0.transmitted < D12_ENDP0_SIZE) {
TRACE_D12("\tEP0: Rx Complete (%d) %p\n",
ep0.transmitted, ep0.common.complete_fn);
if (usbs_d12_ep0_complete(0) == USBS_CONTROL_RETURN_HANDLED)
d12_write_endp_buf(D12_BASE_ADDR, D12_TX_ENDP0, 0, 0);
else
d12_stall_ctrl_endp(D12_BASE_ADDR, true);
}
}
else {
TRACE_D12("EP0: No Rx buffer. Discarding packet\n");
d12_read_endp_buf(D12_BASE_ADDR, D12_RX_ENDP0, NULL);
}
}
}
// --------------------------------------------------------------------------
// Handler for when the device is put into or taken out of suspend mode.
// It updates the state variable in the control endpoint and calls the
// registered state change function, if any.
// TODO: Put the chip into low power mode??? Stop clocks, etc???
static void
usbs_d12_suspend(bool suspended)
{
int old_state = ep0.common.state;
usbs_state_change state_change;
if (suspended) {
ep0.common.state |= USBS_STATE_SUSPENDED;
state_change = USBS_STATE_CHANGE_SUSPENDED;
}
else {
ep0.common.state &= USBS_STATE_MASK;
state_change = USBS_STATE_CHANGE_RESUMED;
}
if (ep0.common.state_change_fn) {
(*ep0.common.state_change_fn)(&ep0.common, ep0.common.state_change_data,
state_change, old_state);
}
}
// --------------------------------------------------------------------------
// Common Rx Endpoint 1 & 2
// --------------------------------------------------------------------------
#if defined(_RX_EP1) || defined(_RX_EP2)
static void usbs_d12_clear_rx_ep(rx_endpoint *ep)
{
ep->common.buffer = 0;
ep->common.buffer_size = 0;
ep->common.complete_fn = 0;
ep->common.complete_data = 0;
ep->received = 0;
}
// --------------------------------------------------------------------------
// This is called when an rx operation is completed. It resets the endpoint
// vars and calls the registered completion function.
//
static void
usbs_d12_ep_rx_complete(rx_endpoint *ep, int result)
{
completion_fn fn = ep->common.complete_fn;
void *data = ep->common.complete_data;
usbs_d12_clear_rx_ep(ep);
if (fn)
(*fn)(data, result);
}
// --------------------------------------------------------------------------
// This routine is called when an rx buffer in the chip is full and ready to
// be read. If there's an endpoint buffer available and room to hold the data
// we read it in, otherwise we call the completion function, but leave the
// data in the chip. The hardware will automatically NAK packages from the
// host until the app calls another start read to continue receiving data.
//
// CONTEXT:
// Called from either the DSR or application thread, via start rx.
// In either case, it's assumed that the chip is locked.
//
static void
usbs_d12_ep_rx(rx_endpoint *ep)
{
int n, ep_size, buf_remaining, endp = ep->endp;
bool done;
// The main endp is double buffered and we need to be prepared
// to read both simultaneously.
ep_size = (endp == D12_MAIN_ENDP) ? (2 * D12_MAIN_ENDP_SIZE)
: RX_ENDP_SIZE[endp];
buf_remaining = ep->common.buffer_size - ep->received;
// ----- If no space left in buffer, call completion fn -----
if (!ep->common.buffer || buf_remaining < ep_size) {
int ret = ep->received;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?