void udd_test_mode_packet(void) { irqflags_t flags; const uint8_t test_packet[] = { // 00000000 * 9 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 01010101 * 8 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, // 01110111 * 8 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, // 0, {111111S * 15}, 111111 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // S, 111111S, {0111111S * 7} 0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, // 00111111, {S0111111 * 9}, S0 0xFC, 0x7E, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0x7E }; // Reconfigure control endpoint to bulk IN endpoint udd_disable_endpoint(0); udd_configure_endpoint(0, USB_EP_TYPE_BULK, 1, 64, AVR32_USBC_UECFG0_EPBK_SINGLE); udd_enable_hs_test_mode(); udd_enable_hs_test_mode_packet(); // Send packet on endpoint 0 udd_udesc_set_buf0_addr(0, (uint8_t *) test_packet); flags = cpu_irq_save(); udd_enable_in_send_interrupt(0); cpu_irq_restore(flags); udd_ack_in_send(0); }
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_send_zlp_in(void) { irqflags_t flags; udd_ep_control_state = UDD_EPCTRL_HANDSHAKE_WAIT_IN_ZLP; // Validate and send empty IN packet on control endpoint flags = cpu_irq_save(); // Send ZLP on IN endpoint udd_ack_in_send(0); udd_enable_in_send_interrupt(0); // To detect a protocol error, enable nak interrupt on data OUT phase udd_ack_nak_out(0); udd_enable_nak_out_interrupt(0); cpu_irq_restore(flags); }
void udd_test_mode_packet(void) { uint8_t i; uint8_t *ptr_dest; const uint8_t *ptr_src; irqflags_t flags; const uint8_t test_packet[] = { // 00000000 * 9 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 01010101 * 8 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, // 01110111 * 8 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, // 0, {111111S * 15}, 111111 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // S, 111111S, {0111111S * 7} 0x7F, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, // 00111111, {S0111111 * 9}, S0 0xFC, 0x7E, 0xBF, 0xDF, 0xEF, 0xF7, 0xFB, 0xFD, 0x7E }; // Reconfigure control endpoint to bulk IN endpoint udd_disable_endpoint(0); udd_configure_endpoint(0, USB_EP_TYPE_BULK, 1, 64, AVR32_USBB_UECFG0_EPBK_SINGLE); udd_allocate_memory(0); udd_enable_endpoint(0); udd_enable_hs_test_mode(); udd_enable_hs_test_mode_packet(); // Send packet on endpoint 0 ptr_dest = (uint8_t *) & udd_get_endpoint_fifo_access(0, 8); ptr_src = test_packet; for (i = 0; i < sizeof(test_packet); i++) { *ptr_dest++ = *ptr_src++; } flags = cpu_irq_save(); udd_enable_in_send_interrupt(0); cpu_irq_restore(flags); udd_ack_in_send(0); }
/** * \brief Waits a DATA IN packets on control endpoint * * \param payload RAM buffer to store data received * \param size Size of RAM buffer waiting */ static void main_usb_send_in(uint8_t *payload, uint8_t size) { uint8_t *ptr_dest; do { while (!Is_udd_in_send(0)); // Fill buffer of endpoint control ptr_dest = (uint8_t *) & udd_get_endpoint_fifo_access(0, 8); // Write quickly the IN data for (uint8_t i = 0; (i<udd_get_endpoint_size(0)) && size; i++) { *ptr_dest++ = *payload++; size--; } // Validate and send the data available in the control endpoint buffer udd_ack_in_send(0); } while (size); while (!Is_udd_in_send(0)); }
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_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_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; }
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; }
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); }