static bool udd_ep_interrupt(void) { udd_ep_id_t ep; udd_ep_job_t *ptr_job; // For each endpoint different of control endpoint (0) for (ep = 1; ep <= USB_DEVICE_MAX_EP; ep++) { // Get job corresponding at endpoint ptr_job = &udd_ep_job[ep - 1]; // Check DMA event if (Is_udd_endpoint_dma_interrupt_enabled(ep) && Is_udd_endpoint_dma_interrupt(ep)) { uint32_t nb_remaining; if( udd_endpoint_dma_get_status(ep) & AVR32_USBB_UDDMA1_STATUS_CH_EN_MASK) { return true; // Ignore EOT_STA interrupt } udd_disable_endpoint_dma_interrupt(ep); // Save number of data no transfered nb_remaining = (udd_endpoint_dma_get_status(ep) & AVR32_USBB_UDDMA1_STATUS_CH_BYTE_CNT_MASK) >> AVR32_USBB_UDDMA1_STATUS_CH_BYTE_CNT_OFFSET; if (nb_remaining) { // Transfer no complete (short packet or ZLP) then: // Update number of data transfered ptr_job->nb_trans -= nb_remaining; // Set transfer complete to stop the transfer ptr_job->buf_size = ptr_job->nb_trans; } udd_ep_trans_done(ep); return true; } // Check empty bank interrupt event if (Is_udd_endpoint_interrupt_enabled(ep)) { if (Is_udd_in_send_interrupt_enabled(ep) && Is_udd_in_send(ep)) { udd_disable_in_send_interrupt(ep); // One bank is free then send a ZLP udd_ack_in_send(ep); udd_ack_fifocon(ep); udd_ep_finish_job(ptr_job, false, ep); return true; } if (Is_udd_bank_interrupt_enabled(ep) && (0 == udd_nb_busy_bank(ep))) { // End of background transfer on IN endpoint udd_disable_bank_interrupt(ep); udd_disable_endpoint_interrupt(ep); Assert(ptr_job->stall_requested); // A stall has been requested during background transfer ptr_job->stall_requested = false; udd_disable_endpoint_bank_autoswitch(ep); udd_enable_stall_handshake(ep); udd_reset_data_toggle(ep); return true; } } }
static void udd_ctrl_init(void) { // In case of abort of IN Data Phase: // No need to abort IN transfer (rise TXINI), // because it is automatically done by hardware when a Setup packet is received. // But the interrupt must be disabled to don't generate interrupt TXINI // after SETUP reception. udd_disable_in_send_interrupt(0); // In case of OUT ZLP event is no processed before Setup event occurs udd_ack_out_received(0); udd_g_ctrlreq.callback = NULL; udd_g_ctrlreq.over_under_run = NULL; udd_g_ctrlreq.payload_size = 0; udd_ep_control_state = UDD_EPCTRL_SETUP; }
static void udd_ctrl_in_sent(void) { static bool b_shortpacket = false; uint16_t nb_remain; uint8_t i; uint8_t *ptr_dest, *ptr_src; irqflags_t flags; flags = cpu_irq_save(); udd_disable_in_send_interrupt(0); cpu_irq_restore(flags); if (UDD_EPCTRL_HANDSHAKE_WAIT_IN_ZLP == udd_ep_control_state) { // ZLP on IN is sent, then valid end of setup request udd_ctrl_endofrequest(); // Reinitializes control endpoint management udd_ctrl_init(); return; } Assert(udd_ep_control_state == UDD_EPCTRL_DATA_IN); nb_remain = udd_g_ctrlreq.payload_size - udd_ctrl_payload_nb_trans; if (0 == nb_remain) { // All content of current buffer payload are sent // Update number of total data sending by previous payload buffer udd_ctrl_prev_payload_nb_trans += udd_ctrl_payload_nb_trans; if ((udd_g_ctrlreq.req.wLength == udd_ctrl_prev_payload_nb_trans) || b_shortpacket) { // All data requested are transfered or a short packet has been sent // then it is the end of data phase. // Generate an OUT ZLP for handshake phase. udd_ctrl_send_zlp_out(); return; } // Need of new buffer because the data phase is not complete if ((!udd_g_ctrlreq.over_under_run) || (!udd_g_ctrlreq.over_under_run())) { // Underrun then send zlp on IN // Here nb_remain=0 and allows to send a IN ZLP } else { // A new payload buffer is given udd_ctrl_payload_nb_trans = 0; nb_remain = udd_g_ctrlreq.payload_size; } } // Continue transfer and send next data if (nb_remain >= USB_DEVICE_EP_CTRL_SIZE) { nb_remain = USB_DEVICE_EP_CTRL_SIZE; b_shortpacket = false; } else { b_shortpacket = true; } // Fill buffer of endpoint control ptr_dest = (uint8_t *) & udd_get_endpoint_fifo_access(0, 8); ptr_src = udd_g_ctrlreq.payload + udd_ctrl_payload_nb_trans; //** Critical section // Only in case of DATA IN phase abort without USB Reset signal after. // The IN data don't must be written in endpoint 0 DPRAM during // a next setup reception in same endpoint 0 DPRAM. // Thereby, an OUT ZLP reception must check before IN data write // and if no OUT ZLP is received the data must be written quickly (800us) // before an eventually ZLP OUT and SETUP reception flags = cpu_irq_save(); if (Is_udd_out_received(0)) { // IN DATA phase aborted by OUT ZLP cpu_irq_restore(flags); udd_ep_control_state = UDD_EPCTRL_HANDSHAKE_WAIT_OUT_ZLP; return; // Exit of IN DATA phase } // Write quickly the IN data for (i = 0; i < nb_remain; i++) { *ptr_dest++ = *ptr_src++; } udd_ctrl_payload_nb_trans += nb_remain; // Validate and send the data available in the control endpoint buffer udd_ack_in_send(0); udd_enable_in_send_interrupt(0); // In case of abort of DATA IN phase, no need to enable nak OUT interrupt // because OUT endpoint is already free and ZLP OUT accepted. cpu_irq_restore(flags); }
static void udd_ctrl_in_sent(void) { uint16_t nb_remain; irqflags_t flags; flags = cpu_irq_save(); udd_disable_in_send_interrupt(0); cpu_irq_restore(flags); if (UDD_EPCTRL_HANDSHAKE_WAIT_IN_ZLP == udd_ep_control_state) { // ZLP on IN is sent, then valid end of setup request udd_ctrl_endofrequest(); // Reinitializes control endpoint management udd_ctrl_init(); return; } Assert(udd_ep_control_state == UDD_EPCTRL_DATA_IN); nb_remain = udd_g_ctrlreq.payload_size - udd_ctrl_payload_nb_trans; if (0 == nb_remain) { // All content of current buffer payload are sent if (!udd_ctrl_payload_need_in_zlp) { // It is the end of data phase, because the last data packet is a short packet // then generate an OUT ZLP for handshake phase. udd_ctrl_send_zlp_out(); return; } if ((udd_g_ctrlreq.req.wLength > (udd_ctrl_prev_payload_nb_trans + udd_g_ctrlreq. payload_size)) || (!udd_g_ctrlreq.over_under_run) || (!udd_g_ctrlreq.over_under_run())) { // Underrun or data packet complette than send zlp on IN (note don't change DataToggle) udd_ctrl_payload_need_in_zlp = false; // nb_remain==0 allows to send a IN ZLP } else { // A new payload buffer is given // Update number of total data sending by previous playlaod buffer udd_ctrl_prev_payload_nb_trans += udd_ctrl_payload_nb_trans; // Update maangement of current playoad transfer udd_ctrl_payload_nb_trans = 0; nb_remain = udd_g_ctrlreq.payload_size; // Compute if an IN ZLP must be send after IN data udd_ctrl_payload_need_in_zlp = ((udd_g_ctrlreq.payload_size % USB_DEVICE_EP_CTRL_SIZE) == 0); } } // Continue transfer and send next data if (nb_remain > USB_DEVICE_EP_CTRL_SIZE) { nb_remain = USB_DEVICE_EP_CTRL_SIZE; } //** Critical section // Only in case of DATA IN phase abort without USB Reset signal after. // The IN data don't must be writed in endpoint 0 DPRAM during // a next setup reception in same endpoint 0 DPRAM. // Thereby, an OUT ZLP reception must check before IN data write // and if no OUT ZLP is recevied the data must be written quickly (800us) // before an eventually ZLP OUT and SETUP reception flags = cpu_irq_save(); if (Is_udd_out_received(0)) { // IN DATA phase aborted by OUT ZLP cpu_irq_restore(flags); udd_ep_control_state = UDD_EPCTRL_HANDSHAKE_WAIT_OUT_ZLP; return; // Exit of IN DATA phase } // Write quickly the IN data memcpy(udd_ctrl_buffer, udd_g_ctrlreq.payload + udd_ctrl_payload_nb_trans, nb_remain); udd_ctrl_payload_nb_trans += nb_remain; udd_udesc_set_buf0_ctn(0, nb_remain); // Validate and send the data available in the control endpoint buffer udd_ack_in_send(0); udd_enable_in_send_interrupt(0); // In case of abort of DATA IN phase, no need to enable nak OUT interrupt // because OUT endpoint is already free and ZLP OUT accepted. cpu_irq_restore(flags); }