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++) { // 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; } }
static void udd_ep_finish_job(udd_ep_job_t * ptr_job, bool b_abort, uint8_t ep_num) { if (ptr_job->busy == false) { return; // No on-going job } ptr_job->busy = false; if (NULL == ptr_job->call_trans) { return; // No callback linked to job } if (Is_udd_endpoint_in(ep_num)) { ep_num |= USB_EP_DIR_IN; } ptr_job->call_trans((b_abort) ? UDD_EP_TRANSFER_ABORT : UDD_EP_TRANSFER_OK, ptr_job->buf_size, ep_num); }
static bool udd_ep_interrupt(void) { udd_ep_id_t ep, ep_addr; // For each endpoint different of control endpoint (0) for (ep = 1; ep <= USB_DEVICE_MAX_EP; ep++) { if (!Is_udd_endpoint_interrupt_enabled(ep) || !Is_udd_endpoint_interrupt(ep)) { continue; } ep_addr = Is_udd_endpoint_in(ep) ? (ep | USB_EP_DIR_IN) : ep; udd_ep_trans_done(ep_addr); return true; } return false; }
bool udd_ep_alloc(udd_ep_id_t ep, uint8_t bmAttributes, uint16_t MaxEndpointSize) { bool b_dir_in; uint16_t ep_allocated; uint8_t bank, i; b_dir_in = ep & USB_EP_DIR_IN; ep = ep & USB_EP_ADDR_MASK; if (ep > USB_DEVICE_MAX_EP) { return false; } if (Is_udd_endpoint_enabled(ep)) { return false; } // Bank choice switch(bmAttributes&USB_EP_TYPE_MASK) { case USB_EP_TYPE_ISOCHRONOUS: bank = UDD_ISOCHRONOUS_NB_BANK(ep); break; case USB_EP_TYPE_INTERRUPT: bank = UDD_INTERRUPT_NB_BANK(ep); break; case USB_EP_TYPE_BULK: bank = UDD_BULK_NB_BANK(ep); break; default: Assert(false); return false; } switch(bank) { case 1: bank = AVR32_USBB_UECFG0_EPBK_SINGLE; break; case 2: bank = AVR32_USBB_UECFG0_EPBK_DOUBLE; break; case 3: bank = AVR32_USBB_UECFG0_EPBK_TRIPLE; break; default: Assert(false); return false; } // Check if endpoint size is 8,16,32,64,128,256,512 or 1023 Assert(MaxEndpointSize < 1024); Assert((MaxEndpointSize == 1023) || !(MaxEndpointSize & (MaxEndpointSize - 1))); Assert(MaxEndpointSize >= 8); // Set configuration of new endpoint udd_configure_endpoint(ep, bmAttributes, (b_dir_in ? 1 : 0), MaxEndpointSize, bank); ep_allocated = 1 << ep; // Unalloc endpoints superior for (i = USB_DEVICE_MAX_EP; i > ep; i--) { if (Is_udd_endpoint_enabled(i)) { ep_allocated |= 1 << i; udd_disable_endpoint(i); udd_unallocate_memory(i); } } // Realloc/Enable endpoints for (i = ep; i <= USB_DEVICE_MAX_EP; i++) { if (ep_allocated & (1 << i)) { udd_ep_job_t *ptr_job = &udd_ep_job[i - 1]; bool b_restart = ptr_job->busy; ptr_job->busy = false; udd_allocate_memory(i); udd_enable_endpoint(i); if (!Is_udd_endpoint_configured(i)) { if (NULL == ptr_job->call_trans) { return false; } if (Is_udd_endpoint_in(i)) { i |= USB_EP_DIR_IN; } ptr_job->call_trans(UDD_EP_TRANSFER_ABORT, ptr_job->buf_size, i); return false; } udd_enable_endpoint_bank_autoswitch(i); if (b_restart) { // Re-run the job udd_ep_run(i, ptr_job->b_shortpacket, ptr_job->buf, ptr_job->buf_size, ptr_job->call_trans); } } } 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 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); }
bool udd_ep_alloc(udd_ep_id_t ep, uint8_t bmAttributes, uint16_t MaxEndpointSize) { bool b_dir_in; uint16_t ep_allocated; uint8_t nb_bank, bank, i; b_dir_in = ep & USB_EP_DIR_IN; ep = ep & USB_EP_ADDR_MASK; if (ep > USB_DEVICE_MAX_EP) { return false; } if (Is_udd_endpoint_enabled(ep)) { return false; } dbg_print("alloc(%x, %d) ", ep, MaxEndpointSize); // Bank choice switch (bmAttributes & USB_EP_TYPE_MASK) { case USB_EP_TYPE_ISOCHRONOUS: nb_bank = UDD_ISOCHRONOUS_NB_BANK(ep); break; case USB_EP_TYPE_INTERRUPT: nb_bank = UDD_INTERRUPT_NB_BANK(ep); break; case USB_EP_TYPE_BULK: nb_bank = UDD_BULK_NB_BANK(ep); break; default: Assert(false); return false; } switch (nb_bank) { case 1: bank = UOTGHS_DEVEPTCFG_EPBK_1_BANK >> UOTGHS_DEVEPTCFG_EPBK_Pos; break; case 2: bank = UOTGHS_DEVEPTCFG_EPBK_2_BANK >> UOTGHS_DEVEPTCFG_EPBK_Pos; break; case 3: bank = UOTGHS_DEVEPTCFG_EPBK_3_BANK >> UOTGHS_DEVEPTCFG_EPBK_Pos; break; default: Assert(false); return false; } // Check if endpoint size is 8,16,32,64,128,256,512 or 1023 Assert(MaxEndpointSize < 1024); Assert((MaxEndpointSize == 1023) || !(MaxEndpointSize & (MaxEndpointSize - 1))); Assert(MaxEndpointSize >= 8); // Set configuration of new endpoint udd_configure_endpoint(ep, bmAttributes, (b_dir_in ? 1 : 0), MaxEndpointSize, bank); ep_allocated = 1 << ep; // Unalloc endpoints superior for (i = USB_DEVICE_MAX_EP; i > ep; i--) { if (Is_udd_endpoint_enabled(i)) { ep_allocated |= 1 << i; udd_disable_endpoint(i); udd_unallocate_memory(i); } } // Realloc/Enable endpoints for (i = ep; i <= USB_DEVICE_MAX_EP; i++) { if (ep_allocated & (1 << i)) { udd_ep_job_t *ptr_job = &udd_ep_job[i - 1]; bool b_restart = ptr_job->busy; // Restart running job because // memory window slides up and its data is lost ptr_job->busy = false; // Re-allocate memory udd_allocate_memory(i); udd_enable_endpoint(i); if (!Is_udd_endpoint_configured(i)) { dbg_print("ErrRealloc%d ", i); if (NULL == ptr_job->call_trans) { return false; } if (Is_udd_endpoint_in(i)) { i |= USB_EP_DIR_IN; } ptr_job->call_trans(UDD_EP_TRANSFER_ABORT, ptr_job->buf_cnt, i); return false; } udd_enable_endpoint_bank_autoswitch(i); if (b_restart) { // Re-run the job remaining part ptr_job->buf_cnt -= ptr_job->buf_load; b_restart = udd_ep_run(Is_udd_endpoint_in(i) ? (i | USB_EP_DIR_IN) : i, ptr_job->b_shortpacket, &ptr_job->buf[ptr_job->buf_cnt], ptr_job->buf_size - ptr_job->buf_cnt, ptr_job->call_trans); if (!b_restart) { dbg_print("ErrReRun%d ", i); return false; } } } } return true; }