diff --git a/firmware/hackrf_usb/Makefile b/firmware/hackrf_usb/Makefile index 3d2b4d53..20a97295 100644 --- a/firmware/hackrf_usb/Makefile +++ b/firmware/hackrf_usb/Makefile @@ -27,6 +27,7 @@ SRC = $(BINARY).c \ usb_request.c \ usb_standard_request.c \ usb_descriptor.c \ + usb_queue.c \ ../common/fault_handler.c \ ../common/hackrf_core.c \ ../common/sgpio.c \ diff --git a/firmware/hackrf_usb/hackrf_usb.c b/firmware/hackrf_usb/hackrf_usb.c index eff351a4..c9fbebad 100644 --- a/firmware/hackrf_usb/hackrf_usb.c +++ b/firmware/hackrf_usb/hackrf_usb.c @@ -39,6 +39,7 @@ #include "usb.h" #include "usb_type.h" +#include "usb_queue.h" #include "usb_request.h" #include "usb_descriptor.h" #include "usb_standard_request.h" @@ -49,9 +50,6 @@ uint8_t* const usb_bulk_buffer = (uint8_t*)0x20004000; static volatile uint32_t usb_bulk_buffer_offset = 0; static const uint32_t usb_bulk_buffer_mask = 32768 - 1; -usb_transfer_descriptor_t usb_td_bulk[2] ATTR_ALIGNED(64); -const uint_fast8_t usb_td_bulk_count = sizeof(usb_td_bulk) / sizeof(usb_td_bulk[0]); - /* TODO remove this big buffer and use streaming for CPLD */ #define CPLD_XSVF_MAX_LEN (65536) uint8_t cpld_xsvf_buffer[CPLD_XSVF_MAX_LEN]; @@ -181,50 +179,6 @@ bool set_freq(uint32_t freq_mhz, uint32_t freq_hz) return success; } -static void usb_init_buffers_bulk() { - usb_td_bulk[0].next_dtd_pointer = USB_TD_NEXT_DTD_POINTER_TERMINATE; - usb_td_bulk[0].total_bytes - = USB_TD_DTD_TOKEN_TOTAL_BYTES(16384) - | USB_TD_DTD_TOKEN_MULTO(0) - ; - usb_td_bulk[0].buffer_pointer_page[0] = (uint32_t)&usb_bulk_buffer[0x0000]; - usb_td_bulk[0].buffer_pointer_page[1] = (uint32_t)&usb_bulk_buffer[0x1000]; - usb_td_bulk[0].buffer_pointer_page[2] = (uint32_t)&usb_bulk_buffer[0x2000]; - usb_td_bulk[0].buffer_pointer_page[3] = (uint32_t)&usb_bulk_buffer[0x3000]; - usb_td_bulk[0].buffer_pointer_page[4] = (uint32_t)&usb_bulk_buffer[0x4000]; - - usb_td_bulk[1].next_dtd_pointer = USB_TD_NEXT_DTD_POINTER_TERMINATE; - usb_td_bulk[1].total_bytes - = USB_TD_DTD_TOKEN_TOTAL_BYTES(16384) - | USB_TD_DTD_TOKEN_MULTO(0) - ; - usb_td_bulk[1].buffer_pointer_page[0] = (uint32_t)&usb_bulk_buffer[0x4000]; - usb_td_bulk[1].buffer_pointer_page[1] = (uint32_t)&usb_bulk_buffer[0x5000]; - usb_td_bulk[1].buffer_pointer_page[2] = (uint32_t)&usb_bulk_buffer[0x6000]; - usb_td_bulk[1].buffer_pointer_page[3] = (uint32_t)&usb_bulk_buffer[0x7000]; - usb_td_bulk[1].buffer_pointer_page[4] = (uint32_t)&usb_bulk_buffer[0x8000]; -} - -void usb_endpoint_schedule_no_int( - const usb_endpoint_t* const endpoint, - usb_transfer_descriptor_t* const td -) { - // Ensure that endpoint is ready to be primed. - // It may have been flushed due to an aborted transaction. - // TODO: This should be preceded by a flush? - while( usb_endpoint_is_ready(endpoint) ); - - // Configure a transfer. - td->total_bytes = - USB_TD_DTD_TOKEN_TOTAL_BYTES(16384) - /*| USB_TD_DTD_TOKEN_IOC*/ - | USB_TD_DTD_TOKEN_MULTO(0) - | USB_TD_DTD_TOKEN_STATUS_ACTIVE - ; - - usb_endpoint_prime(endpoint, td); -} - usb_configuration_t usb_configuration_high_speed = { .number = 1, .speed = USB_SPEED_HIGH, @@ -280,7 +234,7 @@ usb_endpoint_t usb_endpoint_bulk_in = { .in = &usb_endpoint_bulk_in, .out = 0, .setup_complete = 0, - .transfer_complete = 0, + .transfer_complete = usb_queue_transfer_complete }; usb_endpoint_t usb_endpoint_bulk_out = { @@ -289,7 +243,7 @@ usb_endpoint_t usb_endpoint_bulk_out = { .in = 0, .out = &usb_endpoint_bulk_out, .setup_complete = 0, - .transfer_complete = 0, + .transfer_complete = usb_queue_transfer_complete }; void baseband_streaming_disable() { @@ -306,8 +260,6 @@ void set_transceiver_mode(const transceiver_mode_t new_transceiver_mode) { transceiver_mode = new_transceiver_mode; - usb_init_buffers_bulk(); - if( transceiver_mode == TRANSCEIVER_MODE_RX ) { gpio_clear(PORT_LED1_3, PIN_LED3); gpio_set(PORT_LED1_3, PIN_LED2); @@ -354,7 +306,7 @@ usb_request_status_t usb_vendor_request_set_transceiver_mode( case TRANSCEIVER_MODE_RX: case TRANSCEIVER_MODE_TX: set_transceiver_mode(endpoint->setup.value); - usb_endpoint_schedule_ack(endpoint->in); + usb_transfer_schedule_ack(endpoint->in); return USB_REQUEST_STATUS_OK; default: return USB_REQUEST_STATUS_STALL; @@ -372,7 +324,7 @@ usb_request_status_t usb_vendor_request_write_max2837( if( endpoint->setup.index < MAX2837_NUM_REGS ) { if( endpoint->setup.value < MAX2837_DATA_REGS_MAX_VALUE ) { max2837_reg_write(endpoint->setup.index, endpoint->setup.value); - usb_endpoint_schedule_ack(endpoint->in); + usb_transfer_schedule_ack(endpoint->in); return USB_REQUEST_STATUS_OK; } } @@ -391,8 +343,8 @@ usb_request_status_t usb_vendor_request_read_max2837( const uint16_t value = max2837_reg_read(endpoint->setup.index); endpoint->buffer[0] = value & 0xff; endpoint->buffer[1] = value >> 8; - usb_endpoint_schedule(endpoint->in, &endpoint->buffer, 2); - usb_endpoint_schedule_ack(endpoint->out); + usb_transfer_schedule(endpoint->in, &endpoint->buffer, 2, NULL); + usb_transfer_schedule_ack(endpoint->out); return USB_REQUEST_STATUS_OK; } return USB_REQUEST_STATUS_STALL; @@ -409,7 +361,7 @@ usb_request_status_t usb_vendor_request_write_si5351c( if( endpoint->setup.index < 256 ) { if( endpoint->setup.value < 256 ) { si5351c_write_single(endpoint->setup.index, endpoint->setup.value); - usb_endpoint_schedule_ack(endpoint->in); + usb_transfer_schedule_ack(endpoint->in); return USB_REQUEST_STATUS_OK; } } @@ -427,8 +379,8 @@ usb_request_status_t usb_vendor_request_read_si5351c( if( endpoint->setup.index < 256 ) { const uint8_t value = si5351c_read_single(endpoint->setup.index); endpoint->buffer[0] = value; - usb_endpoint_schedule(endpoint->in, &endpoint->buffer, 1); - usb_endpoint_schedule_ack(endpoint->out); + usb_transfer_schedule(endpoint->in, &endpoint->buffer, 1, NULL); + usb_transfer_schedule_ack(endpoint->out); return USB_REQUEST_STATUS_OK; } return USB_REQUEST_STATUS_STALL; @@ -444,7 +396,7 @@ usb_request_status_t usb_vendor_request_set_baseband_filter_bandwidth( if( stage == USB_TRANSFER_STAGE_SETUP ) { const uint32_t bandwidth = (endpoint->setup.index << 16) | endpoint->setup.value; if( baseband_filter_bandwidth_set(bandwidth) ) { - usb_endpoint_schedule_ack(endpoint->in); + usb_transfer_schedule_ack(endpoint->in); return USB_REQUEST_STATUS_OK; } return USB_REQUEST_STATUS_STALL; @@ -462,7 +414,7 @@ usb_request_status_t usb_vendor_request_write_rffc5071( if( endpoint->setup.index < RFFC5071_NUM_REGS ) { rffc5071_reg_write(endpoint->setup.index, endpoint->setup.value); - usb_endpoint_schedule_ack(endpoint->in); + usb_transfer_schedule_ack(endpoint->in); return USB_REQUEST_STATUS_OK; } return USB_REQUEST_STATUS_STALL; @@ -483,8 +435,8 @@ usb_request_status_t usb_vendor_request_read_rffc5071( value = rffc5071_reg_read(endpoint->setup.index); endpoint->buffer[0] = value & 0xff; endpoint->buffer[1] = value >> 8; - usb_endpoint_schedule(endpoint->in, &endpoint->buffer, 2); - usb_endpoint_schedule_ack(endpoint->out); + usb_transfer_schedule(endpoint->in, &endpoint->buffer, 2, NULL); + usb_transfer_schedule_ack(endpoint->out); return USB_REQUEST_STATUS_OK; } return USB_REQUEST_STATUS_STALL; @@ -502,7 +454,7 @@ usb_request_status_t usb_vendor_request_erase_spiflash( w25q80bv_setup(); /* only chip erase is implemented */ w25q80bv_chip_erase(); - usb_endpoint_schedule_ack(endpoint->in); + usb_transfer_schedule_ack(endpoint->in); //FIXME probably should undo w25q80bv_setup() } return USB_REQUEST_STATUS_OK; @@ -523,7 +475,7 @@ usb_request_status_t usb_vendor_request_write_spiflash( || ((addr + len) > W25Q80BV_NUM_BYTES)) { return USB_REQUEST_STATUS_STALL; } else { - usb_endpoint_schedule(endpoint->out, &spiflash_buffer[0], len); + usb_transfer_schedule(endpoint->out, &spiflash_buffer[0], len, NULL); w25q80bv_setup(); return USB_REQUEST_STATUS_OK; } @@ -536,7 +488,7 @@ usb_request_status_t usb_vendor_request_write_spiflash( return USB_REQUEST_STATUS_STALL; } else { w25q80bv_program(addr, len, &spiflash_buffer[0]); - usb_endpoint_schedule_ack(endpoint->in); + usb_transfer_schedule_ack(endpoint->in); //FIXME probably should undo w25q80bv_setup() return USB_REQUEST_STATUS_OK; } @@ -567,7 +519,7 @@ usb_request_status_t usb_vendor_request_read_spiflash( { spiflash_buffer[i] = u8_addr_pt[i]; } - usb_endpoint_schedule(endpoint->in, &spiflash_buffer[0], len); + usb_transfer_schedule(endpoint->in, &spiflash_buffer[0], len, NULL); return USB_REQUEST_STATUS_OK; } } else if (stage == USB_TRANSFER_STAGE_DATA) @@ -581,7 +533,7 @@ usb_request_status_t usb_vendor_request_read_spiflash( return USB_REQUEST_STATUS_STALL; } else { - usb_endpoint_schedule_ack(endpoint->out); + usb_transfer_schedule_ack(endpoint->out); return USB_REQUEST_STATUS_OK; } } else @@ -605,7 +557,7 @@ usb_request_status_t usb_vendor_request_write_cpld( // len is limited to 64KB 16bits no overflow can happen total_len = endpoint->setup.value; len = endpoint->setup.length; - usb_endpoint_schedule(endpoint->out, &cpld_xsvf_buffer[write_cpld_idx], len); + usb_transfer_schedule(endpoint->out, &cpld_xsvf_buffer[write_cpld_idx], len, NULL); return USB_REQUEST_STATUS_OK; } else if (stage == USB_TRANSFER_STAGE_DATA) { @@ -621,7 +573,7 @@ usb_request_status_t usb_vendor_request_write_cpld( // TO FIX ACK shall be not delayed so much as cpld prog can take up to 5s. if(error == 0) { - usb_endpoint_schedule_ack(endpoint->in); + usb_transfer_schedule_ack(endpoint->in); /* blink LED1, LED2, and LED3 on success */ while (1) @@ -643,7 +595,7 @@ usb_request_status_t usb_vendor_request_write_cpld( } }else { - usb_endpoint_schedule_ack(endpoint->in); + usb_transfer_schedule_ack(endpoint->in); return USB_REQUEST_STATUS_OK; } } else @@ -657,8 +609,8 @@ usb_request_status_t usb_vendor_request_read_board_id( { if (stage == USB_TRANSFER_STAGE_SETUP) { endpoint->buffer[0] = BOARD_ID; - usb_endpoint_schedule(endpoint->in, &endpoint->buffer, 1); - usb_endpoint_schedule_ack(endpoint->out); + usb_transfer_schedule(endpoint->in, &endpoint->buffer, 1, NULL); + usb_transfer_schedule_ack(endpoint->out); } return USB_REQUEST_STATUS_OK; } @@ -670,8 +622,8 @@ usb_request_status_t usb_vendor_request_read_version_string( if (stage == USB_TRANSFER_STAGE_SETUP) { length = (uint8_t)strlen(version_string); - usb_endpoint_schedule(endpoint->in, version_string, length); - usb_endpoint_schedule_ack(endpoint->out); + usb_transfer_schedule(endpoint->in, version_string, length, NULL); + usb_transfer_schedule_ack(endpoint->out); } return USB_REQUEST_STATUS_OK; } @@ -682,13 +634,13 @@ usb_request_status_t usb_vendor_request_set_freq( { if (stage == USB_TRANSFER_STAGE_SETUP) { - usb_endpoint_schedule(endpoint->out, &set_freq_params, sizeof(set_freq_params_t)); + usb_transfer_schedule(endpoint->out, &set_freq_params, sizeof(set_freq_params_t), NULL); return USB_REQUEST_STATUS_OK; } else if (stage == USB_TRANSFER_STAGE_DATA) { if( set_freq(set_freq_params.freq_mhz, set_freq_params.freq_hz) ) { - usb_endpoint_schedule_ack(endpoint->in); + usb_transfer_schedule_ack(endpoint->in); return USB_REQUEST_STATUS_OK; } return USB_REQUEST_STATUS_STALL; @@ -704,13 +656,13 @@ usb_request_status_t usb_vendor_request_set_sample_rate_frac( { if (stage == USB_TRANSFER_STAGE_SETUP) { - usb_endpoint_schedule(endpoint->out, &set_sample_r_params, sizeof(set_sample_r_params_t)); + usb_transfer_schedule(endpoint->out, &set_sample_r_params, sizeof(set_sample_r_params_t), NULL); return USB_REQUEST_STATUS_OK; } else if (stage == USB_TRANSFER_STAGE_DATA) { if( sample_rate_frac_set(set_sample_r_params.freq_hz * 2, set_sample_r_params.divider ) ) { - usb_endpoint_schedule_ack(endpoint->in); + usb_transfer_schedule_ack(endpoint->in); return USB_REQUEST_STATUS_OK; } return USB_REQUEST_STATUS_STALL; @@ -728,12 +680,12 @@ usb_request_status_t usb_vendor_request_set_amp_enable( case 0: switchctrl |= SWITCHCTRL_AMP_BYPASS; update_switches(); - usb_endpoint_schedule_ack(endpoint->in); + usb_transfer_schedule_ack(endpoint->in); return USB_REQUEST_STATUS_OK; case 1: switchctrl &= ~SWITCHCTRL_AMP_BYPASS; update_switches(); - usb_endpoint_schedule_ack(endpoint->in); + usb_transfer_schedule_ack(endpoint->in); return USB_REQUEST_STATUS_OK; default: return USB_REQUEST_STATUS_STALL; @@ -778,8 +730,8 @@ usb_request_status_t usb_vendor_request_read_partid_serialno( read_partid_serialno.serial_no[3] = iap_cmd_res.status_res.iap_result[3]; length = (uint8_t)sizeof(read_partid_serialno_t); - usb_endpoint_schedule(endpoint->in, &read_partid_serialno, length); - usb_endpoint_schedule_ack(endpoint->out); + usb_transfer_schedule(endpoint->in, &read_partid_serialno, length, NULL); + usb_transfer_schedule_ack(endpoint->out); } return USB_REQUEST_STATUS_OK; } @@ -790,8 +742,8 @@ usb_request_status_t usb_vendor_request_set_lna_gain( if( stage == USB_TRANSFER_STAGE_SETUP ) { const uint8_t value = max2837_set_lna_gain(endpoint->setup.index); endpoint->buffer[0] = value; - usb_endpoint_schedule(endpoint->in, &endpoint->buffer, 1); - usb_endpoint_schedule_ack(endpoint->out); + usb_transfer_schedule(endpoint->in, &endpoint->buffer, 1, NULL); + usb_transfer_schedule_ack(endpoint->out); return USB_REQUEST_STATUS_OK; } return USB_REQUEST_STATUS_OK; @@ -803,8 +755,8 @@ usb_request_status_t usb_vendor_request_set_vga_gain( if( stage == USB_TRANSFER_STAGE_SETUP ) { const uint8_t value = max2837_set_vga_gain(endpoint->setup.index); endpoint->buffer[0] = value; - usb_endpoint_schedule(endpoint->in, &endpoint->buffer, 1); - usb_endpoint_schedule_ack(endpoint->out); + usb_transfer_schedule(endpoint->in, &endpoint->buffer, 1, NULL); + usb_transfer_schedule_ack(endpoint->out); return USB_REQUEST_STATUS_OK; } return USB_REQUEST_STATUS_OK; @@ -816,8 +768,8 @@ usb_request_status_t usb_vendor_request_set_txvga_gain( if( stage == USB_TRANSFER_STAGE_SETUP ) { const uint8_t value = max2837_set_txvga_gain(endpoint->setup.index); endpoint->buffer[0] = value; - usb_endpoint_schedule(endpoint->in, &endpoint->buffer, 1); - usb_endpoint_schedule_ack(endpoint->out); + usb_transfer_schedule(endpoint->in, &endpoint->buffer, 1, NULL); + usb_transfer_schedule_ack(endpoint->out); return USB_REQUEST_STATUS_OK; } return USB_REQUEST_STATUS_OK; @@ -829,7 +781,7 @@ usb_request_status_t usb_vendor_request_set_if_freq( if( stage == USB_TRANSFER_STAGE_SETUP ) { MAX2837_FREQ_NOMINAL_HZ = (uint32_t)endpoint->setup.index * 1000 * 1000; set_freq(freq_mhz_cache, freq_hz_cache); - usb_endpoint_schedule_ack(endpoint->in); + usb_transfer_schedule_ack(endpoint->in); } return USB_REQUEST_STATUS_OK; } @@ -971,7 +923,8 @@ int main(void) { enable_1v8_power(); cpu_clock_init(); - usb_set_configuration_changed_cb(usb_configuration_changed); + usb_queue_init(); + usb_set_configuration_changed_cb(usb_configuration_changed); usb_peripheral_reset(); usb_device_init(0, &usb_device); @@ -1002,21 +955,29 @@ int main(void) { while( usb_bulk_buffer_offset < 16384 ); // Set up IN transfer of buffer 0. - usb_endpoint_schedule_no_int( - (transceiver_mode == TRANSCEIVER_MODE_RX) - ? &usb_endpoint_bulk_in : &usb_endpoint_bulk_out, - &usb_td_bulk[0] - ); + if (transceiver_mode != TRANSCEIVER_MODE_OFF) { + usb_transfer_schedule( + (transceiver_mode == TRANSCEIVER_MODE_RX) + ? &usb_endpoint_bulk_in : &usb_endpoint_bulk_out, + &usb_bulk_buffer[0x0000], + 0x4000, + NULL + ); + } // Wait until buffer 1 is transmitted/received. while( usb_bulk_buffer_offset >= 16384 ); // Set up IN transfer of buffer 1. - usb_endpoint_schedule_no_int( - (transceiver_mode == TRANSCEIVER_MODE_RX) - ? &usb_endpoint_bulk_in : &usb_endpoint_bulk_out, - &usb_td_bulk[1] - ); + if (transceiver_mode != TRANSCEIVER_MODE_OFF) { + usb_transfer_schedule( + (transceiver_mode == TRANSCEIVER_MODE_RX) + ? &usb_endpoint_bulk_in : &usb_endpoint_bulk_out, + &usb_bulk_buffer[0x4000], + 0x4000, + NULL + ); + } } return 0; diff --git a/firmware/hackrf_usb/usb.c b/firmware/hackrf_usb/usb.c index 8396e1bf..8a02eb0e 100644 --- a/firmware/hackrf_usb/usb.c +++ b/firmware/hackrf_usb/usb.c @@ -24,6 +24,7 @@ #include "usb.h" #include "usb_type.h" +#include "usb_queue.h" #include "usb_standard_request.h" #include @@ -34,10 +35,8 @@ usb_device_t* usb_device_usb0 = 0; usb_queue_head_t usb_qh[12] ATTR_ALIGNED(2048); -usb_transfer_descriptor_t usb_td[12] ATTR_ALIGNED(64); #define USB_QH_INDEX(endpoint_address) (((endpoint_address & 0xF) * 2) + ((endpoint_address >> 7) & 1)) -#define USB_TD_INDEX(endpoint_address) (((endpoint_address & 0xF) * 2) + ((endpoint_address >> 7) & 1)) usb_queue_head_t* usb_queue_head( const uint_fast8_t endpoint_address @@ -45,12 +44,6 @@ usb_queue_head_t* usb_queue_head( return &usb_qh[USB_QH_INDEX(endpoint_address)]; } -usb_transfer_descriptor_t* usb_transfer_descriptor( - const uint_fast8_t endpoint_address -) { - return &usb_td[USB_TD_INDEX(endpoint_address)]; -} - static usb_endpoint_t* usb_endpoint_from_address( const uint_fast8_t endpoint_address ) { @@ -98,25 +91,25 @@ static void usb_clear_all_pending_interrupts() { static void usb_wait_for_endpoint_priming_to_finish(const uint32_t mask) { // Wait until controller has parsed new transfer descriptors and prepared // receive buffers. - while( USB0_ENDPTPRIME & mask ); + while( USB0_ENDPTPRIME & mask ); } static void usb_flush_endpoints(const uint32_t mask) { // Clear any primed buffers. If a packet is in progress, that transfer // will continue until completion. - USB0_ENDPTFLUSH = mask; + USB0_ENDPTFLUSH = mask; } static void usb_wait_for_endpoint_flushing_to_finish(const uint32_t mask) { // Wait until controller has flushed all endpoints / cleared any primed // buffers. - while( USB0_ENDPTFLUSH & mask ); + while( USB0_ENDPTFLUSH & mask ); } static void usb_flush_primed_endpoints(const uint32_t mask) { - usb_wait_for_endpoint_priming_to_finish(mask); + usb_wait_for_endpoint_priming_to_finish(mask); usb_flush_endpoints(mask); - usb_wait_for_endpoint_flushing_to_finish(mask); + usb_wait_for_endpoint_flushing_to_finish(mask); } static void usb_flush_all_primed_endpoints() { @@ -171,6 +164,7 @@ void usb_endpoint_disable( } else { USB0_ENDPTCTRL(endpoint_number) &= ~(USB0_ENDPTCTRL_RXE); } + usb_queue_flush_endpoint(endpoint); usb_endpoint_clear_pending_interrupts(endpoint); usb_endpoint_flush(endpoint); } @@ -195,7 +189,7 @@ void usb_endpoint_prime( USB0_ENDPTPRIME = USB0_ENDPTPRIME_PERB(1 << endpoint_number); } } -/* + static bool usb_endpoint_is_priming( const usb_endpoint_t* const endpoint ) { @@ -206,11 +200,57 @@ static bool usb_endpoint_is_priming( return USB0_ENDPTPRIME & USB0_ENDPTPRIME_PERB(1 << endpoint_number); } } -*/ + +// Schedule an already filled-in transfer descriptor for execution on +// the given endpoint, waiting until the endpoint has finished. +void usb_endpoint_schedule_wait( + const usb_endpoint_t* const endpoint, + usb_transfer_descriptor_t* const td +) { + // Ensure that endpoint is ready to be primed. + // It may have been flushed due to an aborted transaction. + // TODO: This should be preceded by a flush? + while( usb_endpoint_is_ready(endpoint) ); + + td->next_dtd_pointer = USB_TD_NEXT_DTD_POINTER_TERMINATE; + + usb_endpoint_prime(endpoint, td); +} + +// Schedule an already filled-in transfer descriptor for execution on +// the given endpoint, appending to the end of the endpoint's queue if +// there are pending TDs. Note that this requires that one knows the +// tail of the endpoint's TD queue. Moreover, the user is responsible +// for setting the TERMINATE bit of next_dtd_pointer if needed. +void usb_endpoint_schedule_append( + const usb_endpoint_t* const endpoint, + usb_transfer_descriptor_t* const tail_td, + usb_transfer_descriptor_t* const new_td +) { + bool done; + + tail_td->next_dtd_pointer = new_td; + + if (usb_endpoint_is_priming(endpoint)) { + return; + } + + do { + USB0_USBCMD_D |= USB0_USBCMD_D_ATDTW; + done = usb_endpoint_is_ready(endpoint); + } while (!(USB0_USBCMD_D & USB0_USBCMD_D_ATDTW)); + + USB0_USBCMD_D &= ~USB0_USBCMD_D_ATDTW; + if(!done) { + usb_endpoint_prime(endpoint, new_td); + } +} + void usb_endpoint_flush( const usb_endpoint_t* const endpoint ) { const uint_fast8_t endpoint_number = usb_endpoint_number(endpoint->address); + usb_queue_flush_endpoint(endpoint); if( usb_endpoint_is_in(endpoint->address) ) { usb_flush_primed_endpoints(USB0_ENDPTFLUSH_FETB(1 << endpoint_number)); } else { @@ -510,41 +550,6 @@ void usb_endpoint_init( usb_endpoint_enable(endpoint); } -void usb_endpoint_schedule( - const usb_endpoint_t* const endpoint, - void* const data, - const uint32_t maximum_length -) { - usb_transfer_descriptor_t* const td = usb_transfer_descriptor(endpoint->address); - - // Ensure that endpoint is ready to be primed. - // It may have been flushed due to an aborted transaction. - // TODO: This should be preceded by a flush? - while( usb_endpoint_is_ready(endpoint) ); - - // Configure a transfer. - td->next_dtd_pointer = USB_TD_NEXT_DTD_POINTER_TERMINATE; - td->total_bytes = - USB_TD_DTD_TOKEN_TOTAL_BYTES(maximum_length) - | USB_TD_DTD_TOKEN_IOC - | USB_TD_DTD_TOKEN_MULTO(0) - | USB_TD_DTD_TOKEN_STATUS_ACTIVE - ; - td->buffer_pointer_page[0] = (uint32_t)data; - td->buffer_pointer_page[1] = ((uint32_t)data + 0x1000) & 0xfffff000; - td->buffer_pointer_page[2] = ((uint32_t)data + 0x2000) & 0xfffff000; - td->buffer_pointer_page[3] = ((uint32_t)data + 0x3000) & 0xfffff000; - td->buffer_pointer_page[4] = ((uint32_t)data + 0x4000) & 0xfffff000; - - usb_endpoint_prime(endpoint, td); -} - -void usb_endpoint_schedule_ack( - const usb_endpoint_t* const endpoint -) { - usb_endpoint_schedule(endpoint, 0, 0); -} - static void usb_check_for_setup_events() { const uint32_t endptsetupstat = usb_get_endpoint_setup_status(); if( endptsetupstat ) { diff --git a/firmware/hackrf_usb/usb.h b/firmware/hackrf_usb/usb.h index 4b1d50f4..a6fa6a92 100644 --- a/firmware/hackrf_usb/usb.h +++ b/firmware/hackrf_usb/usb.h @@ -61,16 +61,6 @@ void usb_endpoint_init( const usb_endpoint_t* const endpoint ); -void usb_endpoint_schedule( - const usb_endpoint_t* const endpoint, - void* const data, - const uint32_t maximum_length -); - -void usb_endpoint_schedule_ack( - const usb_endpoint_t* const endpoint -); - void usb_endpoint_stall( const usb_endpoint_t* const endpoint ); @@ -92,4 +82,15 @@ void usb_endpoint_prime( usb_transfer_descriptor_t* const first_td ); +void usb_endpoint_schedule_wait( + const usb_endpoint_t* const endpoint, + usb_transfer_descriptor_t* const td +); + +void usb_endpoint_schedule_append( + const usb_endpoint_t* const endpoint, + usb_transfer_descriptor_t* const tail_td, + usb_transfer_descriptor_t* const new_td +); + #endif//__USB_H__ diff --git a/firmware/hackrf_usb/usb_queue.c b/firmware/hackrf_usb/usb_queue.c new file mode 100644 index 00000000..afb6c7e8 --- /dev/null +++ b/firmware/hackrf_usb/usb_queue.c @@ -0,0 +1,200 @@ +/* + * Copyright 2012 Jared Boone + * Copyright 2013 Ben Gamari + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include + +#include +#include + +#include "usb.h" +#include "usb_queue.h" + +struct _usb_transfer_t { + struct _usb_transfer_t* next; + usb_transfer_descriptor_t td ATTR_ALIGNED(64); + unsigned int maximum_length; + usb_endpoint_t* endpoint; + transfer_completion_cb completion_cb; +}; + +usb_transfer_t transfer_pool[8]; +const unsigned int transfer_pool_size = sizeof(transfer_pool) / sizeof(usb_transfer_t); + +// Available transfer list +usb_transfer_t* volatile free_transfers; + +#define USB_ENDPOINT_INDEX(endpoint_address) (((endpoint_address & 0xF) * 2) + ((endpoint_address >> 7) & 1)) + +// Pending transfer heads +usb_transfer_t* volatile endpoint_transfers[12] = {}; + +void usb_queue_init() { + usb_transfer_t* t = &transfer_pool[0]; + free_transfers = t; + for (unsigned int i=0; i < transfer_pool_size - 1; i++, t++) { + t->next = t+1; + } + t->next = NULL; +} + +/* Allocate a transfer */ +static usb_transfer_t* allocate_transfer() +{ + bool aborted; + usb_transfer_t* transfer; + while (free_transfers == NULL); + do { + transfer = (void *) __ldrex((uint32_t *) &free_transfers); + aborted = __strex((uint32_t) transfer->next, (uint32_t *) &free_transfers); + } while (aborted); + transfer->next = NULL; + return transfer; +} + +/* Place a transfer in the free list */ +static void free_transfer(usb_transfer_t* const transfer) +{ + bool aborted; + do { + transfer->next = (void *) __ldrex((uint32_t *) &free_transfers); + aborted = __strex((uint32_t) transfer, (uint32_t *) &free_transfers); + } while (aborted); +} + +/* Add a transfer to the end of an endpoint's queue */ +static void endpoint_add_transfer( + const usb_endpoint_t* const endpoint, + usb_transfer_t* const transfer +) { + uint_fast8_t index = USB_ENDPOINT_INDEX(endpoint->address); + transfer->next = NULL; + if (endpoint_transfers[index] != NULL) { + usb_transfer_t* t = endpoint_transfers[index]; + while (t->next != NULL) t = t->next; + t->next = transfer; + } else { + endpoint_transfers[index] = transfer; + } +} + +void usb_queue_flush_endpoint(const usb_endpoint_t* const endpoint) +{ + uint_fast8_t index = USB_ENDPOINT_INDEX(endpoint->address); + cm_disable_interrupts(); + while (endpoint_transfers[index]) { + usb_transfer_t * transfer = endpoint_transfers[index]; + endpoint_transfers[index] = transfer->next; + free_transfer(transfer); + } + cm_enable_interrupts(); +} + +void usb_transfer_schedule( + const usb_endpoint_t* const endpoint, + void* const data, + const uint32_t maximum_length, + const transfer_completion_cb completion_cb +) { + usb_transfer_t* const transfer = allocate_transfer(); + assert(transfer != NULL); + usb_transfer_descriptor_t* const td = &transfer->td; + uint_fast8_t index = USB_ENDPOINT_INDEX(endpoint->address); + + // Configure the transfer descriptor + td->next_dtd_pointer = USB_TD_NEXT_DTD_POINTER_TERMINATE; + td->total_bytes = + USB_TD_DTD_TOKEN_TOTAL_BYTES(maximum_length) + | USB_TD_DTD_TOKEN_IOC + | USB_TD_DTD_TOKEN_MULTO(0) + | USB_TD_DTD_TOKEN_STATUS_ACTIVE + ; + td->buffer_pointer_page[0] = (uint32_t)data; + td->buffer_pointer_page[1] = ((uint32_t)data + 0x1000) & 0xfffff000; + td->buffer_pointer_page[2] = ((uint32_t)data + 0x2000) & 0xfffff000; + td->buffer_pointer_page[3] = ((uint32_t)data + 0x3000) & 0xfffff000; + td->buffer_pointer_page[4] = ((uint32_t)data + 0x4000) & 0xfffff000; + + // Fill in transfer fields + transfer->maximum_length = maximum_length; + transfer->completion_cb = completion_cb; + transfer->endpoint = (usb_endpoint_t*) endpoint; + + cm_disable_interrupts(); + usb_transfer_t* tail = endpoint_transfers[index]; + endpoint_add_transfer(endpoint, transfer); + if (tail == NULL) { + // The queue is currently empty, we need to re-prime + usb_endpoint_schedule_wait(endpoint, &transfer->td); + } else { + // The queue is currently running, try to append + for (; tail->next != NULL; tail = tail->next); + usb_endpoint_schedule_append(endpoint, &tail->td, &transfer->td); + } + cm_enable_interrupts(); +} + +void usb_transfer_schedule_ack( + const usb_endpoint_t* const endpoint +) { + usb_transfer_schedule(endpoint, 0, 0, NULL); +} + +/* Called when an endpoint might have completed a transfer */ +void usb_queue_transfer_complete(usb_endpoint_t* const endpoint) +{ + uint_fast8_t index = USB_ENDPOINT_INDEX(endpoint->address); + usb_transfer_t* transfer = endpoint_transfers[index]; + + while (transfer != NULL) { + uint8_t status = transfer->td.total_bytes; + + // Check for failures + if ( status & USB_TD_DTD_TOKEN_STATUS_HALTED + || status & USB_TD_DTD_TOKEN_STATUS_BUFFER_ERROR + || status & USB_TD_DTD_TOKEN_STATUS_TRANSACTION_ERROR) { + // TODO: Uh oh, do something useful here + while (1); + } + + // Still not finished + if (status & USB_TD_DTD_TOKEN_STATUS_ACTIVE) + break; + + // Advance the head. We need to do this before invoking the completion + // callback as it might attempt to schedule a new transfer + endpoint_transfers[index] = transfer->next; + usb_transfer_t* next = transfer->next; + + // Invoke completion callback + unsigned int total_bytes = (transfer->td.total_bytes & USB_TD_DTD_TOKEN_TOTAL_BYTES_MASK) >> USB_TD_DTD_TOKEN_TOTAL_BYTES_SHIFT; + unsigned int transferred = transfer->maximum_length - total_bytes; + if (transfer->completion_cb) + transfer->completion_cb(transfer, transferred); + + // Advance head and free transfer + free_transfer(transfer); + transfer = next; + } +} diff --git a/firmware/hackrf_usb/usb_queue.h b/firmware/hackrf_usb/usb_queue.h new file mode 100644 index 00000000..8c776d3e --- /dev/null +++ b/firmware/hackrf_usb/usb_queue.h @@ -0,0 +1,53 @@ +/* + * Copyright 2012 Jared Boone + * Copyright 2013 Ben Gamari + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __USB_QUEUE_H__ +#define __USB_QUEUE_H__ + +#include + +#include "usb_type.h" + +typedef struct _usb_transfer_t usb_transfer_t; + +typedef void (*transfer_completion_cb)(usb_transfer_t*, unsigned int); + +void usb_queue_flush_endpoint(const usb_endpoint_t* const endpoint); + +void usb_transfer_schedule( + const usb_endpoint_t* const endpoint, + void* const data, + const uint32_t maximum_length, + const transfer_completion_cb completion_cb +); + +void usb_transfer_schedule_ack( + const usb_endpoint_t* const endpoint +); + +void usb_queue_init(void); + +void usb_queue_transfer_complete( + usb_endpoint_t* const endpoint +); + +#endif//__USB_QUEUE_H__ diff --git a/firmware/hackrf_usb/usb_request.c b/firmware/hackrf_usb/usb_request.c index 25132fd4..f9226c7f 100644 --- a/firmware/hackrf_usb/usb_request.c +++ b/firmware/hackrf_usb/usb_request.c @@ -21,6 +21,7 @@ #include "usb.h" #include "usb_request.h" +#include "usb_queue.h" #include @@ -75,6 +76,7 @@ void usb_control_out_complete( } else { usb_request(endpoint, USB_TRANSFER_STAGE_DATA); } + usb_queue_transfer_complete(endpoint); } void usb_control_in_complete( @@ -87,5 +89,6 @@ void usb_control_in_complete( } else { usb_request(endpoint, USB_TRANSFER_STAGE_STATUS); } + usb_queue_transfer_complete(endpoint); } diff --git a/firmware/hackrf_usb/usb_standard_request.c b/firmware/hackrf_usb/usb_standard_request.c index cfb58ee0..98e50142 100644 --- a/firmware/hackrf_usb/usb_standard_request.c +++ b/firmware/hackrf_usb/usb_standard_request.c @@ -27,6 +27,7 @@ #include "usb.h" #include "usb_type.h" #include "usb_descriptor.h" +#include "usb_queue.h" const uint8_t* usb_endpoint_descriptor( const usb_endpoint_t* const endpoint @@ -99,9 +100,11 @@ bool usb_set_configuration( if( new_configuration != device->configuration ) { // Configuration changed. device->configuration = new_configuration; - if (usb_configuration_changed_cb) - usb_configuration_changed_cb(device); } + + if (usb_configuration_changed_cb) + usb_configuration_changed_cb(device); + return true; } @@ -114,12 +117,13 @@ static usb_request_status_t usb_send_descriptor( if( descriptor_data[1] == USB_DESCRIPTOR_TYPE_CONFIGURATION ) { descriptor_length = (descriptor_data[3] << 8) | descriptor_data[2]; } - usb_endpoint_schedule( + usb_transfer_schedule( endpoint->in, descriptor_data, - (setup_length > descriptor_length) ? descriptor_length : setup_length + (setup_length > descriptor_length) ? descriptor_length : setup_length, + NULL ); - usb_endpoint_schedule_ack(endpoint->out); + usb_transfer_schedule_ack(endpoint->out); return USB_REQUEST_STATUS_OK; } @@ -195,7 +199,7 @@ static usb_request_status_t usb_standard_request_set_address_setup( usb_endpoint_t* const endpoint ) { usb_set_address_deferred(endpoint->device, endpoint->setup.value_l); - usb_endpoint_schedule_ack(endpoint->in); + usb_transfer_schedule_ack(endpoint->in); return USB_REQUEST_STATUS_OK; } @@ -231,7 +235,7 @@ static usb_request_status_t usb_standard_request_set_configuration_setup( // TODO: Should this be done immediately? usb_set_address_immediate(endpoint->device, 0); } - usb_endpoint_schedule_ack(endpoint->in); + usb_transfer_schedule_ack(endpoint->in); return USB_REQUEST_STATUS_OK; } else { return USB_REQUEST_STATUS_STALL; @@ -265,8 +269,8 @@ static usb_request_status_t usb_standard_request_get_configuration_setup( if( endpoint->device->configuration ) { endpoint->buffer[0] = endpoint->device->configuration->number; } - usb_endpoint_schedule(endpoint->in, &endpoint->buffer, 1); - usb_endpoint_schedule_ack(endpoint->out); + usb_transfer_schedule(endpoint->in, &endpoint->buffer, 1, NULL); + usb_transfer_schedule_ack(endpoint->out); return USB_REQUEST_STATUS_OK; } else { return USB_REQUEST_STATUS_STALL; diff --git a/host/libhackrf/src/hackrf.c b/host/libhackrf/src/hackrf.c index 7e36dc55..3fd8051c 100644 --- a/host/libhackrf/src/hackrf.c +++ b/host/libhackrf/src/hackrf.c @@ -1141,14 +1141,13 @@ int ADDCALL hackrf_start_rx(hackrf_device* device, hackrf_sample_block_cb_fn cal int ADDCALL hackrf_stop_rx(hackrf_device* device) { - int result1, result2; - result1 = kill_transfer_thread(device); - result2 = hackrf_set_transceiver_mode(device, HACKRF_TRANSCEIVER_MODE_OFF); - if (result2 != HACKRF_SUCCESS) + int result; + result = hackrf_set_transceiver_mode(device, HACKRF_TRANSCEIVER_MODE_OFF); + if (result != HACKRF_SUCCESS) { return result2; } - return result1; + return kill_transfer_thread(device); } int ADDCALL hackrf_start_tx(hackrf_device* device, hackrf_sample_block_cb_fn callback, void* tx_ctx)