void udd_ep_abort(udd_ep_id_t ep) { bool b_dir_in = ep & USB_EP_DIR_IN; irqflags_t flags; ep &= USB_EP_ADDR_MASK; if (USB_DEVICE_MAX_EP < ep) return; // Disable interrupts flags = cpu_irq_save(); udd_disable_endpoint_interrupt(ep); cpu_irq_restore(flags); // Clear pending statuses if (b_dir_in) { // Kill banks if (Is_udd_transmit_ready(ep)) { udd_kill_data_in_fifo(ep, udd_get_endpoint_bank_max_nbr(ep)>1); } udd_ack_in_sent(ep); // Reset number of buffered banks udd_ep_job[ep - 1].bank = 0; } else { // Clear all pending banks statuses while(Is_udd_any_bank_received(ep)) { udd_ep_ack_out_received(ep); } } // Reset FIFO and data toggle udd_reset_endpoint(ep); // Abort job udd_ep_abort_job(ep); }
void udd_ep_abort(udd_ep_id_t ep) { irqflags_t flags; udd_ep_job_t *ptr_job; ep &= USB_EP_ADDR_MASK; // Disable interrupt of endpoint flags = cpu_irq_save(); udd_disable_endpoint_interrupt(ep); cpu_irq_restore(flags); // Stop transfer udd_enable_busy_bank0(ep); // Job complete then call callback ptr_job = &udd_ep_job[ep - 1]; if (!ptr_job->busy) { return; } ptr_job->busy = false; if (NULL != ptr_job->call_trans) { if (Is_udd_endpoint_in(ep)) { ep |= USB_EP_DIR_IN; } // It can be a Transfer or stall callback ptr_job->call_trans(UDD_EP_TRANSFER_ABORT, ptr_job->nb_trans, 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++) { // 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 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 DMA event if (Is_udd_endpoint_dma_interrupt_enabled(ep) && Is_udd_endpoint_dma_interrupt(ep)) { uint32_t nb_remaining; 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; // Get job corresponding at endpoint ptr_job = &udd_ep_job[ep - 1]; // Update number of data transfered ptr_job->buf_size -= nb_remaining; if (!Is_udd_endpoint_in(ep)) { // Disable autoswitch bank on OUT udd_disable_endpoint_bank_autoswitch(ep); } else { // Wait end of background transfer on IN endpoint before disabled autoswitch bank udd_enable_endpoint_interrupt(ep); udd_enable_bank_interrupt(ep); } // Call callback to signal end of transfer udd_ep_finish_job(&udd_ep_job[ep - 1], false); return true; } // Check empty bank interrupt event if (Is_udd_endpoint_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); // If no new transfer running then disable autoswitch bank if (!udd_ep_job[ep - 1].busy) { udd_disable_endpoint_bank_autoswitch(ep); } // If a stall has been requested during backgound transfer then execute it if (udd_ep_job[ep - 1].stall_requested) { udd_ep_job[ep - 1].stall_requested = false; udd_enable_stall_handshake(ep); udd_reset_data_toggle(ep); } return true; } }
void udd_ep_abort(udd_ep_id_t ep) { uint8_t index = ep & USB_EP_ADDR_MASK; // Stop DMA transfer udd_disable_endpoint_dma_interrupt(index); udd_endpoint_dma_set_control(index, 0); // Kill banks udd_disable_endpoint_interrupt(index); while (udd_nb_busy_bank(index)) { udd_kill_last_in_bank(index); while(Is_udd_killing_last_in_bank(index)); } udd_ep_abort_job(ep); }
bool udd_ep_clear_halt(udd_ep_id_t ep) { bool b_stall_cleared = false; udd_ep_job_t *ptr_job; ep &= USB_EP_ADDR_MASK; if (USB_DEVICE_MAX_EP < ep) return false; ptr_job = &udd_ep_job[ep - 1]; if (ptr_job->stall_requested) { // Endpoint stall has been requested but not done // Remove stall request ptr_job->stall_requested = false; udd_disable_bank_interrupt(ep); udd_disable_endpoint_interrupt(ep); b_stall_cleared = true; } if (Is_udd_endpoint_stall_requested(ep)) { if (Is_udd_stall(ep)) { udd_ack_stall(ep); // A packet has been stalled // then reset datatoggle udd_reset_data_toggle(ep); } // Disable stall udd_disable_stall_handshake(ep); udd_enable_endpoint_bank_autoswitch(ep); b_stall_cleared = true; } if (b_stall_cleared) { // If a job is register on clear halt action // then execute callback if (ptr_job->busy == true) { ptr_job->busy = false; ptr_job->call_nohalt(); } } return true; }
static void udd_ep_out_received(udd_ep_id_t ep) { udd_ep_job_t *ptr_job = &udd_ep_job[ep - 1]; uint32_t nb_data = 0, i; uint32_t nb_remain = ptr_job->buf_size - ptr_job->buf_cnt; uint32_t pkt_size = ptr_job->size; uint8_t *ptr_dst = &ptr_job->buf[ptr_job->buf_cnt]; bool b_full = false, b_short; // Read byte count nb_data = udd_byte_count(ep); b_short = (nb_data < pkt_size); // Copy data if there is if (nb_data > 0) { if (nb_data >= nb_remain) { nb_data = nb_remain; b_full = true; } // Modify job information ptr_job->buf_cnt += nb_data; // Copy FIFO (DPRAM) to buffer for (i = 0; i < nb_data; i++) { *ptr_dst++ = udd_endpoint_fifo_read(ep); } } // Clear FIFO Status udd_ep_ack_out_received(ep); // Finish job on error or short packet if ((b_full || b_short) && !Is_udd_endpoint_stall_requested(ep)) { udd_disable_endpoint_interrupt(ep); 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); } }
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; }
static void udd_ep_trans_done(udd_ep_id_t ep) { udd_ep_job_t *ptr_job; uint16_t ep_size, nb_trans; uint16_t next_trans; udd_ep_id_t ep_num; irqflags_t flags; ep_num = ep & USB_EP_ADDR_MASK; ep_size = udd_get_endpoint_size(ep_num); // Get job corresponding at endpoint ptr_job = &udd_ep_job[ep_num - 1]; // Disable interrupt of endpoint flags = cpu_irq_save(); udd_disable_endpoint_interrupt(ep_num); cpu_irq_restore(flags); if (!ptr_job->busy) { return; // No job is running, then ignore it (system error) } if (USB_EP_DIR_IN == (ep & USB_EP_DIR_IN)) { // Transfer complete on IN nb_trans = udd_udesc_get_buf0_size(ep_num); // Lock emission of new IN packet udd_enable_busy_bank0(ep_num); // Ack interrupt udd_ack_in_send(ep_num); if (0 == nb_trans) { if (0 == udd_nb_busy_bank(ep_num)) { // All byte are transfered than take nb byte requested nb_trans = udd_udesc_get_buf0_ctn(ep_num); } } // Update number of data transfered ptr_job->nb_trans += nb_trans; // Need to send other data if ((ptr_job->nb_trans != ptr_job->buf_size) || ptr_job->b_shortpacket) { 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 - (UDD_ENDPOINT_MAX_TRANS % ep_size); udd_udesc_set_buf0_autozlp(ep_num, false); } else { // Need ZLP, if requested and last packet is not a short packet udd_udesc_set_buf0_autozlp(ep_num, ptr_job->b_shortpacket); ptr_job->b_shortpacket = false; // No need to request another ZLP } udd_udesc_set_buf0_ctn(ep_num, next_trans); udd_udesc_rst_buf0_size(ep_num); // Link the user buffer directly on USB hardware DMA udd_udesc_set_buf0_addr(ep_num, &ptr_job->buf[ptr_job->nb_trans]); // Start transfer udd_ack_fifocon(ep_num); udd_disable_busy_bank0(ep_num); // Enable interrupt flags = cpu_irq_save(); udd_enable_in_send_interrupt(ep_num); udd_enable_endpoint_interrupt(ep_num); cpu_irq_restore(flags); return; } } else { // Transfer complete on OUT nb_trans = udd_udesc_get_buf0_ctn(ep_num); // Lock reception of new OUT packet udd_enable_busy_bank0(ep_num); // Ack interrupt udd_ack_out_received(ep_num); udd_ack_fifocon(ep_num); // Can be necessary to copy data receive from cache buffer to user buffer if (ptr_job->b_use_out_cache_buffer) { memcpy(&ptr_job->buf[ptr_job->nb_trans], udd_ep_out_cache_buffer[ep_num - 1], ptr_job->buf_size % ep_size); } // Update number of data transfered ptr_job->nb_trans += nb_trans; if (ptr_job->nb_trans > ptr_job->buf_size) { ptr_job->nb_trans = ptr_job->buf_size; } // If all previous data requested are received and user buffer not full // then need to receive other data if ((nb_trans == udd_udesc_get_buf0_size(ep_num)) && (ptr_job->nb_trans != ptr_job->buf_size)) { 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 - (UDD_ENDPOINT_MAX_TRANS % ep_size); } else { next_trans -= next_trans % ep_size; } udd_udesc_rst_buf0_ctn(ep_num); if (next_trans < ep_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_num, udd_ep_out_cache_buffer[ep_num-1]); udd_udesc_set_buf0_size(ep_num, ep_size); } else { // Link the user buffer directly on USB hardware DMA udd_udesc_set_buf0_addr(ep_num, &ptr_job->buf[ptr_job->nb_trans]); udd_udesc_set_buf0_size(ep_num, next_trans); } // Start transfer udd_disable_busy_bank0(ep_num); // Enable interrupt flags = cpu_irq_save(); udd_enable_out_received_interrupt(ep_num); udd_enable_endpoint_interrupt(ep_num); cpu_irq_restore(flags); return; } } // Job complete then call callback ptr_job->busy = false; if (NULL != ptr_job->call_trans) { ptr_job->call_trans(UDD_EP_TRANSFER_OK, ptr_job->nb_trans, ep); } return; }
static void udd_ep_finish_job(udd_ep_id_t ep, bool b_abort) { udd_ep_job_t *ptr_job; uint16_t ep_size; irqflags_t flags; // Get job corresponding at endpoint ptr_job = &udd_ep_job[ep - 1]; // Test if a pending transfer is running. If not, disabled interrupt. if (!ptr_job->busy) { flags = cpu_irq_save(); udd_disable_endpoint_interrupt(ep); cpu_irq_restore(flags); return; } if (Is_udd_endpoint_in(ep)) { // Update number of data transfered ptr_job->nb_trans = udd_udesc_get_buf0_size(ep); if (0 == ptr_job->nb_trans) { if (0 == udd_nb_busy_bank(ep)) { // All byte are transfered than take nb byte requested ptr_job->nb_trans = udd_udesc_get_buf0_ctn(ep); } } } else { // Transfer complete on OUT ep_size = udd_format_endpoint_size(ep); if (ptr_job->b_use_out_cache_buffer) { // Copy data receiv from cache buffer to user buffer memcpy(&ptr_job->buf[ptr_job->nb_trans], udd_ep_out_cache_buffer[ep - 1], ptr_job->buf_size % ep_size); ptr_job->nb_trans += udd_udesc_get_buf0_ctn(ep); } else { ptr_job->nb_trans = udd_udesc_get_buf0_ctn(ep); // If all previous data requested are received // and user buffer not full if ((ptr_job->nb_trans == udd_udesc_get_buf0_size(ep)) && (ptr_job->nb_trans != ptr_job->buf_size)) { // Use the cache buffer to receiv last data // which can be more larger than user buffer remaining ptr_job->b_use_out_cache_buffer = true; udd_udesc_rst_buf0_ctn(ep); udd_udesc_set_buf0_addr(ep, udd_ep_out_cache_buffer[ep - 1]); udd_udesc_set_buf0_size(ep, ep_size); // Free buffer to accept another data to reception udd_ack_out_received(ep); udd_ack_fifocon(ep); return; } } // Free buffer but not accept another data to reception udd_ack_out_received(ep); udd_enable_busy_bank0(ep); udd_ack_fifocon(ep); } // Call callback to signal end of transfer flags = cpu_irq_save(); udd_disable_endpoint_interrupt(ep); cpu_irq_restore(flags); ptr_job->busy = false; if (NULL == ptr_job->call_trans) return; // No callback linked to job ptr_job->call_trans((b_abort) ? UDD_EP_TRANSFER_ABORT : UDD_EP_TRANSFER_OK, ptr_job->nb_trans); }