bool udd_ep_run(udd_ep_id_t ep, bool b_shortpacket, uint8_t * buf, iram_size_t buf_size, udd_callback_trans_t callback) { udd_ep_id_t ep_num; udd_ep_job_t *ptr_job; irqflags_t flags; ep_num = ep & USB_EP_ADDR_MASK; if (USB_DEVICE_MAX_EP < ep_num) { return false; } if ((!Is_udd_endpoint_enabled(ep_num)) || Is_udd_endpoint_stall_requested(ep_num)) { return false; // Endpoint is halted } // Get job about endpoint ptr_job = &udd_ep_job[ep_num - 1]; flags = cpu_irq_save(); if (ptr_job->busy == true) { cpu_irq_restore(flags); return false; // Job already on going } ptr_job->busy = true; cpu_irq_restore(flags); // No job running. Let's setup a new one. // ptr_job->buf = buf; ptr_job->buf_size = buf_size; ptr_job->nb_trans = 0; ptr_job->call_trans = callback; ptr_job->b_shortpacket = b_shortpacket; ptr_job->b_use_out_cache_buffer = false; if ( (USB_EP_DIR_IN != (ep & USB_EP_DIR_IN)) && (AVR32_USBC_PTYPE_ISOCHRONOUS == udd_get_endpoint_type(ep_num)) && (0 != (buf_size % udd_get_endpoint_size(ep_num)))) { // The user must use a buffer size modulo endpoint size // for an isochronous IN endpoint ptr_job->busy = false; return false; } // Initialize value to simulate a empty transfer udd_udesc_rst_buf0_ctn(ep_num); udd_udesc_rst_buf0_size(ep_num); // Request next transfer udd_ep_trans_done(ep); return true; }
static void udd_ep_trans_done(udd_ep_id_t ep) { uint32_t udd_dma_ctrl = 0; udd_ep_job_t *ptr_job; iram_size_t next_trans; irqflags_t flags; // Get job corresponding at endpoint ptr_job = &udd_ep_job[ep - 1]; if (!ptr_job->busy) { return; // No job is running, then ignore it (system error) } if (ptr_job->nb_trans != ptr_job->buf_size) { // Need to send or receive other data next_trans = ptr_job->buf_size - ptr_job->nb_trans; if (UDD_ENDPOINT_MAX_TRANS < next_trans) { // The USB hardware support a maximum // transfer size of UDD_ENDPOINT_MAX_TRANS Bytes next_trans = UDD_ENDPOINT_MAX_TRANS; // Set 0 to transfer the maximum udd_dma_ctrl = (0 << AVR32_USBB_UDDMA1_CONTROL_CH_BYTE_LENGTH_OFFSET) & AVR32_USBB_UDDMA1_CONTROL_CH_BYTE_LENGTH_MASK; } else { udd_dma_ctrl = (next_trans << AVR32_USBB_UDDMA1_CONTROL_CH_BYTE_LENGTH_OFFSET) & AVR32_USBB_UDDMA1_CONTROL_CH_BYTE_LENGTH_MASK; } if (Is_udd_endpoint_in(ep)) { if (0 != next_trans % udd_get_endpoint_size(ep)) { // Enable short packet option // else the DMA transfer is accepted // and interrupt DMA valid but nothing is sent. udd_dma_ctrl |= AVR32_USBB_UDDMA1_CONTROL_DMAEND_EN_MASK; // No need to request another ZLP ptr_job->b_shortpacket = false; } } else { if ((USB_EP_TYPE_ISOCHRONOUS != udd_get_endpoint_type(ep)) || (next_trans <= udd_get_endpoint_size(ep))) { // Enable short packet reception udd_dma_ctrl |= AVR32_USBB_UDDMA1_CONTROL_EOT_IRQ_EN_MASK | AVR32_USBB_UDDMA1_CONTROL_BUFF_CLOSE_IN_EN_MASK; } } // Start USB DMA to fill or read fifo of the selected endpoint udd_endpoint_dma_set_addr(ep, (U32) &ptr_job->buf[ptr_job->nb_trans]); udd_dma_ctrl |= AVR32_USBB_UDDMA1_CONTROL_EOBUFF_IRQ_EN_MASK | AVR32_USBB_UDDMA1_CONTROL_CH_EN_MASK; // Disable IRQs to have a short sequence // between read of EOT_STA and DMA enable flags = cpu_irq_save(); if ( !(udd_endpoint_dma_get_status(ep) & AVR32_USBB_UDDMA1_STATUS_EOT_STA_MASK)) { udd_endpoint_dma_set_control(ep, udd_dma_ctrl); ptr_job->nb_trans += next_trans; udd_enable_endpoint_dma_interrupt(ep); cpu_irq_restore(flags); return; } cpu_irq_restore(flags); // Here a ZLP has been received // and the DMA transfer must be not started. // It is the end of transfer ptr_job->buf_size = ptr_job->nb_trans; } if (Is_udd_endpoint_in(ep)) { if (ptr_job->b_shortpacket) { // Need to send a ZLP (No possible with USB DMA) // enable interrupt to wait a free bank to sent ZLP udd_ack_in_send(ep); if (Is_udd_write_enabled(ep)) { // Force interrupt in case of ep already free udd_raise_in_send(ep); } udd_enable_in_send_interrupt(ep); udd_enable_endpoint_interrupt(ep); return; } } // Call callback to signal end of transfer udd_ep_finish_job(ptr_job, false, ep); }
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++) { // Check RXRDY and TXEMPTY event for none DMA endpoints if (!Is_udd_endpoint_interrupt_enabled(ep)) { continue; } // Get job corresponding at endpoint ptr_job = &udd_ep_job[ep - 1]; // RXOUT: Full packet received if (Is_udd_any_bank_received(ep)) { udd_ep_out_received(ep); return true; } // TXIN: packet sent if (Is_udd_in_sent(ep)) { ptr_job->bank--; // Stall when all banks free if (ptr_job->b_stall_requested) { if (ptr_job->bank) { // Send remaining udd_set_transmit_ready(ep); udd_ack_in_sent(ep); } else { // Ack last packet udd_ack_in_sent(ep); // Enable stall udd_enable_stall_handshake(ep); // Halt executed ptr_job->b_stall_requested = false; } return true; } // Finish Job when buffer end if (ptr_job->b_buf_end) { ptr_job->b_buf_end = false; ptr_job->buf_size = ptr_job->buf_cnt; // buf_size is passed to callback as XFR count udd_ep_finish_job(ptr_job, UDD_EP_TRANSFER_OK, ep); } if (ptr_job->buf_cnt >= ptr_job->buf_size && !ptr_job->b_shortpacket && ptr_job->bank == 0) { // All transfer done, including ZLP irqflags_t flags = cpu_irq_save(); udd_disable_endpoint_interrupt(ep); cpu_irq_restore(flags); // Ack last packet udd_ack_in_sent(ep); return true; } else if (udd_get_endpoint_bank_max_nbr(ep) > 1 && ptr_job->bank > 0) { // Already banks buffered, transmit while loading udd_set_transmit_ready(ep); udd_ack_in_sent(ep); udd_ep_in_sent(ep, false); } else if (udd_get_endpoint_bank_max_nbr(ep) > 1) { // Still bank free, load and transmit if (!udd_ep_in_sent(ep, true)) { ptr_job->b_buf_end = false; ptr_job->buf_size = ptr_job->buf_cnt; // buf_size is passed to callback as XFR count udd_ep_finish_job(ptr_job, UDD_EP_TRANSFER_OK, ep); } udd_ack_in_sent(ep); udd_ep_in_sent(ep, false); } else { // Single bank transfer, ack when ready udd_ep_in_sent(ep, true); udd_ack_in_sent(ep); } return true; } // Stall sent/CRC error if (Is_udd_stall(ep)) { udd_ack_stall(ep); if (udd_get_endpoint_type(ep) == UDP_CSR_EPTYPE_ISO_OUT || udd_get_endpoint_type(ep) == UDP_CSR_EPTYPE_ISO_IN) { } return true; } } return false; }
bool udd_ep_run(udd_ep_id_t ep, bool b_shortpacket, uint8_t * buf, iram_size_t buf_size, udd_callback_trans_t callback) { uint16_t ep_size, trans_size, short_packet; bool b_dir_in; udd_ep_job_t *ptr_job; irqflags_t flags; b_dir_in = (USB_EP_DIR_IN == (ep & USB_EP_DIR_IN)); ep &= USB_EP_ADDR_MASK; if (USB_DEVICE_MAX_EP < ep) return false; // Get job about endpoint ptr_job = &udd_ep_job[ep - 1]; if ((!Is_udd_endpoint_enabled(ep)) || Is_udd_endpoint_stall_requested(ep)) return false; // Endpoint is halted flags = cpu_irq_save(); if (ptr_job->busy == true) { cpu_irq_restore(flags); return false; // Job already on going } ptr_job->busy = true; cpu_irq_restore(flags); // No job running. Let's setup a new one. // // The USB hardware support a maximum transfer size of 0x7FFF Bytes ep_size = udd_get_endpoint_size(ep); if (0x7FFF < buf_size) { trans_size = 0x7FFF - (0x7FFF % ep_size); short_packet = 0; } else { trans_size = buf_size; short_packet = trans_size % ep_size; } if (b_dir_in) { // Need ZLP, if requested and last packet is not a short packet udd_udesc_set_buf0_autozlp(ep, b_shortpacket); udd_udesc_set_buf0_ctn(ep, trans_size); udd_udesc_rst_buf0_size(ep); // Link the user buffer directly on USB hardware DMA udd_udesc_set_buf0_addr(ep, buf); } else { udd_udesc_rst_buf0_ctn(ep); ptr_job->nb_trans = 0; if (trans_size < ep_size) { // The user buffer is smaller than endpoint size if (AVR32_USBC_PTYPE_ISOCHRONOUS == udd_get_endpoint_type(ep)) { ptr_job->busy = false; return false; // The user must use a buffer corresponding at isochrnous endpoint size } // Use the cache buffer for Bulk or Interrupt size endpoint ptr_job->b_use_out_cache_buffer = true; udd_udesc_set_buf0_addr(ep, udd_ep_out_cache_buffer[ep - 1]); udd_udesc_set_buf0_size(ep, ep_size); } else { // Link the user buffer directly on USB hardware DMA ptr_job->b_use_out_cache_buffer = false; udd_udesc_set_buf0_addr(ep, buf); udd_udesc_set_buf0_size(ep, trans_size - short_packet); } } // Update Job information ptr_job->buf = buf; ptr_job->buf_size = trans_size; ptr_job->call_trans = callback; ptr_job->busy = true; // Start transfer udd_disable_busy_bank0(ep); // Enable interrupt flags = cpu_irq_save(); if (b_dir_in) { udd_ack_fifocon(ep); udd_ack_in_send(ep); udd_enable_in_send_interrupt(ep); } else { udd_enable_out_received_interrupt(ep); } udd_enable_endpoint_interrupt(ep); cpu_irq_restore(flags); return true; }