diff --git a/firmware/SOURCES b/firmware/SOURCES index a6c9e5e..92572fd 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -303,6 +303,12 @@ usbstack/usb_charging_only.c #ifdef USB_ENABLE_HID usbstack/usb_hid.c #endif +#ifdef USB_ENABLE_AUDIO +usbstack/usb_audio.c +#endif +#ifdef USB_ENABLE_TEST +usbstack/usb_test.c +#endif #if CONFIG_USBOTG == USBOTG_M66591 drivers/m66591.c #elif CONFIG_USBOTG == USBOTG_ARC diff --git a/firmware/export/config.h b/firmware/export/config.h index e678590..b15fb95 100644 --- a/firmware/export/config.h +++ b/firmware/export/config.h @@ -900,16 +900,21 @@ Lyre prototype 1 */ #else /* BOOTLOADER */ #ifndef SIMULATOR -//#define USB_ENABLE_SERIAL +/*#define USB_ENABLE_SERIAL*/ #define USB_ENABLE_STORAGE #ifdef USB_HAS_INTERRUPT -#define USB_ENABLE_HID +/*#define USB_ENABLE_HID*/ #else #define USB_ENABLE_CHARGING_ONLY #endif #endif +#ifdef USB_HAS_ISOCHRONOUS +#define USB_ENABLE_AUDIO +//#define USB_ENABLE_TEST +#endif + #endif /* BOOTLOADER */ #endif /* HAVE_USBSTACK */ diff --git a/firmware/export/usb.h b/firmware/export/usb.h index 48400b1..dafb769 100644 --- a/firmware/export/usb.h +++ b/firmware/export/usb.h @@ -119,6 +119,12 @@ enum { #ifdef USB_ENABLE_HID USB_DRIVER_HID, #endif +#ifdef USB_ENABLE_AUDIO + USB_DRIVER_AUDIO, +#endif +#ifdef USB_ENABLE_TEST + USB_DRIVER_TEST, +#endif USB_NUM_DRIVERS }; diff --git a/firmware/export/usb_ch9.h b/firmware/export/usb_ch9.h index c11775b..d4b702f 100644 --- a/firmware/export/usb_ch9.h +++ b/firmware/export/usb_ch9.h @@ -339,6 +339,12 @@ struct usb_endpoint_descriptor { #define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */ #define USB_ENDPOINT_DIR_MASK 0x80 +#define USB_ENDPOINT_SYNCTYPE 0x0c +#define USB_ENDPOINT_SYNC_NONE (0 << 2) +#define USB_ENDPOINT_SYNC_ASYNC (1 << 2) +#define USB_ENDPOINT_SYNC_ADAPTIVE (2 << 2) +#define USB_ENDPOINT_SYNC_SYNC (3 << 2) + #define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */ #define USB_ENDPOINT_XFER_CONTROL 0 #define USB_ENDPOINT_XFER_ISOC 1 diff --git a/firmware/usb.c b/firmware/usb.c index ccf12c1..4a92ee7 100644 --- a/firmware/usb.c +++ b/firmware/usb.c @@ -292,6 +292,13 @@ static void usb_thread(void) #endif /* USB_ENABLE_CHARGING_ONLY */ #endif /* USB_ENABLE_HID */ +#ifdef USB_ENABLE_AUDIO + usb_core_enable_driver(USB_DRIVER_AUDIO, true); +#endif +#ifdef USB_ENABLE_TEST + usb_core_enable_driver(USB_DRIVER_TEST, true); +#endif + #ifdef USB_ENABLE_CHARGING_ONLY usb_core_enable_driver(USB_DRIVER_CHARGING_ONLY, true); #endif @@ -316,6 +323,12 @@ static void usb_thread(void) #ifdef USB_ENABLE_CHARGING_ONLY usb_core_enable_driver(USB_DRIVER_CHARGING_ONLY, false); #endif +#ifdef USB_ENABLE_AUDIO + usb_core_enable_driver(USB_DRIVER_AUDIO, false); +#endif +#ifdef USB_ENABLE_TEST + usb_core_enable_driver(USB_DRIVER_TEST, false); +#endif /* Check any drivers enabled at this point for exclusive storage * access requirements. */ diff --git a/firmware/usbstack/usb_audio.c b/firmware/usbstack/usb_audio.c new file mode 100644 index 0000000..ba6abc0 --- /dev/null +++ b/firmware/usbstack/usb_audio.c @@ -0,0 +1,940 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: $ + * + * Copyright (C) 20010 by Amaury Pouly + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "string.h" +#include "system.h" +#include "usb_core.h" +#include "usb_drv.h" +#include "kernel.h" +#include "sound.h" +#include "usb_class_driver.h" +#include "usb_audio_def.h" +#include "pcm_sampr.h" +#include "audio.h" +#include "pcm.h" +#include "file.h" + +#define LOGF_ENABLE +#include "logf.h" + +//#define USB_AUDIO_USE_FEEDBACK_EP + +/* Strings */ +enum +{ + USB_AUDIO_CONTROL_STRING = 0, + USB_AUDIO_STREAMING_STRING_1, + USB_AUDIO_STREAMING_STRING_2, + USB_INPUT_TERMINAL_STRING, + USB_OUTPUT_TERMINAL_STRING, + USB_FEATURE_UNIT_STRING +}; + +static const struct usb_string_descriptor __attribute__((aligned(2))) + usb_string_audio_control = +{ + 21*2+2, + USB_DT_STRING, + {'R', 'o', 'c', 'k', 'b', 'o', 'x', ' ', + 'A', 'u', 'd', 'i', 'o', ' ', + 'C', 'o', 'n', 't', 'r', 'o', 'l'} +}; + +static const struct usb_string_descriptor __attribute__((aligned(2))) + usb_string_audio_streaming_1 = +{ + 29*2+2, + USB_DT_STRING, + {'R', 'o', 'c', 'k', 'b', 'o', 'x', ' ', + 'D', 'u', 'm', 'm', 'y', ' ', + 'A', 'u', 'd', 'i', 'o', ' ', + 'S', 't', 'r', 'e', 'a', 'm', 'i', 'n', 'g'} +}; + +static const struct usb_string_descriptor __attribute__((aligned(2))) + usb_string_audio_streaming_2 = +{ + 30*2+2, + USB_DT_STRING, + {'R', 'o', 'c', 'k', 'b', 'o', 'x', ' ', + 'A', 'u', 'd', 'i', 'o', ' ', + 'S', 't', 'r', 'e', 'a', 'm', 'i', 'n', 'g', ' ', + 'O', 'u', 't', 'p', 'u', 't'} +}; + +static const struct usb_string_descriptor __attribute__((aligned(2))) + usb_string_input_terminal = +{ + 22*2+2, + USB_DT_STRING, + {'R', 'o', 'c', 'k', 'b', 'o', 'x', ' ', + 'U', 'S', 'B', ' ', + 'C', 'o', 'n', 't', 'r', 'o', 'l', 'l', 'e', 'r'} +}; + +static const struct usb_string_descriptor __attribute__((aligned(2))) + usb_string_output_terminal = +{ + 16*2+2, + USB_DT_STRING, + {'R', 'o', 'c', 'k', 'b', 'o', 'x', ' ', + 'L', 'i', 'n', 'e', '-', 'O', 'u', 't'} +}; + +static const struct usb_string_descriptor __attribute__((aligned(2))) + usb_string_feature_unit = +{ + 25*2+2, + USB_DT_STRING, + {'R', 'o', 'c', 'k', 'b', 'o', 'x', ' ', + 'M', 'a', 's', 't', 'e', 'r', ' ', + 'C', 'o', 'n', 't', 'r', 'o', 'l', 'l', 'e', 'r'} +}; + +static const struct usb_string_descriptor* const usb_strings_list[]= +{ + [USB_AUDIO_CONTROL_STRING] = &usb_string_audio_control, + [USB_AUDIO_STREAMING_STRING_1] = &usb_string_audio_streaming_1, + [USB_AUDIO_STREAMING_STRING_2] = &usb_string_audio_streaming_2, + [USB_INPUT_TERMINAL_STRING] = &usb_string_input_terminal, + [USB_OUTPUT_TERMINAL_STRING] = &usb_string_output_terminal, + [USB_FEATURE_UNIT_STRING] = &usb_string_feature_unit +}; + +#define USB_STRINGS_LIST_SIZE (sizeof(usb_strings_list)/sizeof(struct usb_string_descriptor *)) + +/* Audio Control Interface */ +static struct usb_interface_descriptor + ac_interface = +{ + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIO_CONTROL, + .bInterfaceProtocol = 0, + .iInterface = 0 +}; + +/* Audio Control Terminals/Units*/ +static struct usb_ac_header ac_header = +{ + .bLength = USB_AC_SIZEOF_HEADER(2), /* two interfaces */ + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_AC_HEADER, + .bcdADC = 0x0100, + .wTotalLength = 0, /* fill later */ + .bInCollection = 2, /* two interfaces */ + .baInterfaceNr = {0, 0}, /* fill later */ +}; + +enum +{ + AC_INPUT_TERMINAL_ID = 1, + AC_OUTPUT_TERMINAL_ID = 2, + AC_FEATURE_ID = 3 +}; + +static struct usb_ac_input_terminal ac_input = +{ + .bLength = sizeof(struct usb_ac_input_terminal), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_AC_INPUT_TERMINAL, + .bTerminalId = AC_INPUT_TERMINAL_ID, + .wTerminalType = USB_AC_TERMINAL_STREAMING, + .bAssocTerminal = 0, + .bNrChannels = 2, + .wChannelConfig = USB_AC_CHANNELS_LEFT_RIGHT_FRONT, + .iChannelNames = 0, + .iTerminal = 0, +}; + +static struct usb_ac_output_terminal ac_output = +{ + .bLength = sizeof(struct usb_ac_output_terminal), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_AC_OUTPUT_TERMINAL, + .bTerminalId = AC_OUTPUT_TERMINAL_ID, + .wTerminalType = USB_AC_OUTPUT_TERMINAL_HEADPHONES, + .bAssocTerminal = 0, + .bSourceId = AC_INPUT_TERMINAL_ID, + .iTerminal = 0, +}; + +/* Feature Unit with 0 logical channel (only master) and 2 bytes(16 bits) per control (the minimum) */ +DEFINE_USB_AC_FEATURE_UNIT(16, 0) + +static struct usb_ac_feature_unit_16_0 ac_feature = +{ + .bLength = sizeof(struct usb_ac_feature_unit_16_0), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_AC_FEATURE_UNIT, + .bUnitId = AC_FEATURE_ID, + .bSourceId = AC_INPUT_TERMINAL_ID, + .bControlSize = 2, /* by definition */ + .bmaControls = { + [0] = USB_AC_FU_MUTE | USB_AC_FU_VOLUME + }, + .iFeature = 0 +}; + +/* Audio Streaming Interface */ +/* Alternative 0: no streaming */ +static struct usb_interface_descriptor + as_interface_alt_0 = +{ + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 0, + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, + .bInterfaceProtocol = 0, + .iInterface = 0 +}; + +/* Alternative 1: output streaming */ +static struct usb_interface_descriptor + as_interface_alt_1 = +{ + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 1, +#ifdef USB_AUDIO_USE_FEEDBACK_EP + .bNumEndpoints = 2, +#else + .bNumEndpoints = 1, +#endif + .bInterfaceClass = USB_CLASS_AUDIO, + .bInterfaceSubClass = USB_SUBCLASS_AUDIO_STREAMING, + .bInterfaceProtocol = 0, + .iInterface = 0 +}; + +/* Class Specific Audio Streaming Interface */ +static struct usb_as_interface + as_cs_interface = +{ + .bLength = sizeof(struct usb_as_interface), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_AS_GENERAL, + .bTerminalLink = AC_INPUT_TERMINAL_ID, + .bDelay = 1, + .wFormatTag = USB_AS_FORMAT_TYPE_I_PCM +}; + +static struct usb_as_format_type_i_discrete + as_format_type_i = +{ + .bLength = USB_AS_SIZEOF_FORMAT_TYPE_I_DISCRETE(HW_NUM_FREQ), + .bDescriptorType = USB_DT_CS_INTERFACE, + .bDescriptorSubType = USB_AS_FORMAT_TYPE, + .bFormatType = USB_AS_FORMAT_TYPE_I, + .bNrChannels = 2, /* Stereo */ + .bSubframeSize = 2, /* 2 bytes per sample */ + .bBitResolution = 16, /* all 16-bits are used */ + .bSamFreqType = HW_NUM_FREQ, + .tSamFreq = { + [0 ... HW_NUM_FREQ-1 ] = {0}, /* filled later */ + } +}; + +static struct usb_iso_audio_endpoint_descriptor + out_iso_ep = +{ + .bLength = sizeof(struct usb_iso_audio_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_OUT, /* filled later */ + .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, + .wMaxPacketSize = 0, /* filled later */ + .bInterval = 1, /* the spec says it must be 1 */ + .bRefresh = 0, + .bSynchAddress = 0 /* filled later */ +}; + +static struct usb_as_iso_endpoint + as_out_iso_ep = +{ + .bLength = sizeof(struct usb_as_iso_endpoint), + .bDescriptorType = USB_DT_CS_ENDPOINT, + .bDescriptorSubType = USB_AS_EP_GENERAL, + .bmAttributes = USB_AS_EP_CS_SAMPLING_FREQ_CTL, + .bLockDelayUnits = 1, /* milliseconds */ + .wLockDelay = 1 /* the minimum ! */ +}; + +#ifdef USB_AUDIO_USE_FEEDBACK_EP +static struct usb_iso_audio_endpoint_descriptor + out_iso_sync_ep = +{ + .bLength = sizeof(struct usb_iso_audio_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = USB_DIR_IN, /* filled later */ + .bmAttributes = USB_ENDPOINT_XFER_ISOC, + .wMaxPacketSize = 0, /* filled later */ + .bInterval = 1, + .bRefresh = 1, /* minimum: 2ms */ + .bSynchAddress = 0 +}; +#endif + +static const struct usb_descriptor_header* const ac_cs_descriptors_list[] = +{ + (struct usb_descriptor_header *) &ac_header, + (struct usb_descriptor_header *) &ac_input, + (struct usb_descriptor_header *) &ac_output, + (struct usb_descriptor_header *) &ac_feature, +}; + +#define AC_CS_DESCRIPTORS_LIST_SIZE (sizeof(ac_cs_descriptors_list)/sizeof(ac_cs_descriptors_list[0])) + +static const struct usb_descriptor_header* const usb_descriptors_list[] = +{ + /* Audio Control */ + (struct usb_descriptor_header *) &ac_interface, + (struct usb_descriptor_header *) &ac_header, + (struct usb_descriptor_header *) &ac_input, + (struct usb_descriptor_header *) &ac_output, + (struct usb_descriptor_header *) &ac_feature, + /* Audio Streaming */ + (struct usb_descriptor_header *) &as_interface_alt_0, + (struct usb_descriptor_header *) &as_interface_alt_1, + (struct usb_descriptor_header *) &as_cs_interface, + (struct usb_descriptor_header *) &as_format_type_i, + (struct usb_descriptor_header *) &out_iso_ep, + (struct usb_descriptor_header *) &as_out_iso_ep, +#ifdef USB_AUDIO_USE_FEEDBACK_EP + (struct usb_descriptor_header *) &out_iso_sync_ep, +#endif +}; + +#define USB_DESCRIPTORS_LIST_SIZE (sizeof(usb_descriptors_list)/sizeof(usb_descriptors_list[0])) + +static int usb_interface; /* first interface */ +static int usb_string_index; /* first string index */ +static int usb_as_intf_alt; /* streaming interface alternate setting */ + +static int as_freq_idx; /* audio streaming frequency index (in hw_freq_sampr) */ + +static int out_iso_ep_adr; /* output isochronous endpoint */ +static int in_iso_ep_adr; /* input isochronous endpoint */ + +static int raw_fd; + +static unsigned char usb_buffer[128] USB_DEVBSS_ATTR; + +#define USB_AUDIO_BUFFER_SIZE 1024*1000 +struct mutex usb_audio_buffer_mutex; +static unsigned char usb_audio_buffer[USB_AUDIO_BUFFER_SIZE]; +static int usb_audio_buffer_start; +static int usb_audio_buffer_end; +static bool usb_audio_underflow; + +#define USB_AUDIO_XFER_BUFFER_SIZE 512 +static unsigned char *usb_audio_xfer_buffer; + +#ifdef USB_AUDIO_USE_FEEDBACK_EP +static unsigned char usb_audio_feedback_buffer[4] USB_DEVBSS_ATTR; +#endif + +static void encode3(uint8_t arr[3], unsigned long freq) +{ + /* ugly */ + arr[0] = freq & 0xff; + arr[1] = (freq >> 8) & 0xff; + arr[2] = (freq >> 16) & 0xff; +} + +static unsigned long decode3(uint8_t arr[3]) +{ + logf("arr=[0x%x,0x%x,0x%x]", arr[0], arr[1], arr[2]); + return arr[0] | (arr[1] << 8) | (arr[2] << 16); +} + +static void set_sampling_frequency(unsigned long f) +{ + int i = 0; + + while((i + 1) < HW_NUM_FREQ && hw_freq_sampr[i + 1] <= f) + i++; + + if((i + 1) < HW_NUM_FREQ && (hw_freq_sampr[i + 1] - f) < (f - hw_freq_sampr[i])) + i++; + as_freq_idx = i; + + logf("usbaudio: set sampling frequency to %lu Hz, best match is %lu Hz", f, hw_freq_sampr[as_freq_idx]); +} + +static unsigned long get_sampling_frequency(void) +{ + return hw_freq_sampr[as_freq_idx]; +} + +static void enqueue_iso_xfer(void) +{ + memset(usb_audio_xfer_buffer, 0, USB_AUDIO_XFER_BUFFER_SIZE); + usb_drv_recv(out_iso_ep_adr, usb_audio_xfer_buffer, USB_AUDIO_XFER_BUFFER_SIZE); +} + +#ifdef USB_AUDIO_USE_FEEDBACK_EP +static void enqueue_feedback(void) +{ + uint32_t *ptr = (uint32_t *)usb_audio_feedback_buffer; + *ptr = ((hw_freq_sampr[as_freq_idx] << 13) + 62) / 125; + + logf("usbaudio: feedback !"); + usb_drv_send_nonblocking(in_iso_ep_adr, usb_audio_feedback_buffer, sizeof(usb_audio_feedback_buffer)); +} +#endif + +void usb_audio_init(void) +{ + unsigned int i; + /* initialized tSamFreq array */ + for(i = 0; i < HW_NUM_FREQ; i++) + encode3(as_format_type_i.tSamFreq[i], hw_freq_sampr[i]); + + unsigned char * audio_buffer; + size_t bufsize; + + audio_buffer = audio_get_buffer(false, &bufsize); +#ifdef UNCACHED_ADDR + usb_audio_xfer_buffer = (void *)UNCACHED_ADDR((unsigned int)(audio_buffer+31) & 0xffffffe0); +#else + usb_audio_xfer_buffer = (void *)((unsigned int)(audio_buffer+31) & 0xffffffe0); +#endif + cpucache_invalidate(); +} + +int usb_audio_request_endpoints(struct usb_class_driver *drv) +{ + out_iso_ep_adr = usb_core_request_endpoint(USB_ENDPOINT_XFER_ISOC, USB_DIR_OUT, drv); + if(out_iso_ep_adr < 0) + { + logf("usbaudio: cannot get an out iso endpoint"); + return -1; + } + + in_iso_ep_adr = usb_core_request_endpoint(USB_ENDPOINT_XFER_ISOC, USB_DIR_IN, drv); + if(in_iso_ep_adr < 0) + { + usb_core_release_endpoint(out_iso_ep_adr); + logf("usbaudio: cannot get an out iso endpoint"); + return -1; + } + + logf("usbaudio: iso ep is 0x%x, sync ep is 0x%x", out_iso_ep_adr, in_iso_ep_adr); + + out_iso_ep.bEndpointAddress = out_iso_ep_adr; + out_iso_ep.bSynchAddress = in_iso_ep_adr; + +#ifdef USB_AUDIO_USE_FEEDBACK_EP + out_iso_sync_ep.bEndpointAddress = in_iso_ep_adr; +#endif + + return 0; +} + +int usb_audio_set_first_string_index(int string_index) +{ + usb_string_index = string_index; + + ac_interface.iInterface = string_index + USB_AUDIO_CONTROL_STRING; + as_interface_alt_0.iInterface = string_index + USB_AUDIO_STREAMING_STRING_1; + as_interface_alt_1.iInterface = string_index + USB_AUDIO_STREAMING_STRING_2; + ac_input.iTerminal = string_index + USB_INPUT_TERMINAL_STRING; + ac_output.iTerminal = string_index + USB_OUTPUT_TERMINAL_STRING; + ac_feature.iFeature = string_index + USB_FEATURE_UNIT_STRING; + + return string_index + USB_STRINGS_LIST_SIZE; +} + +const struct usb_string_descriptor *usb_audio_get_string_descriptor(int string_index) +{ + logf("usbaudio: get string %d", string_index); + if(string_index < usb_string_index || + string_index >= (int)(usb_string_index + USB_STRINGS_LIST_SIZE)) + return NULL; + else + return usb_strings_list[string_index - usb_string_index]; +} + +int usb_audio_set_first_interface(int interface) +{ + usb_interface = interface; + return interface + 2; /* Audio Control and Audio Streaming */ +} + +int usb_audio_get_config_descriptor(unsigned char *dest, int max_packet_size) +{ + unsigned int i; + unsigned char *orig_dest = dest; + + /** Configuration */ + + /* header */ + ac_header.baInterfaceNr[0] = usb_interface; + ac_header.baInterfaceNr[1] = usb_interface + 1; + + /* audio control interface */ + ac_interface.bInterfaceNumber = usb_interface; + + + /* compute total size of AC headers*/ + for(i = 0; i < AC_CS_DESCRIPTORS_LIST_SIZE; i++) + ac_header.wTotalLength += ac_cs_descriptors_list[i]->bLength; + + /* audio streaming */ + as_interface_alt_0.bInterfaceNumber = usb_interface + 1; + as_interface_alt_1.bInterfaceNumber = usb_interface + 1; + + /* endpoints */ + out_iso_ep.wMaxPacketSize = max_packet_size; +#ifdef USB_AUDIO_USE_FEEDBACK_EP + out_iso_sync_ep.wMaxPacketSize = max_packet_size; +#endif + + /** Packing */ + + for(i = 0; i < USB_DESCRIPTORS_LIST_SIZE; i++) + { + memcpy(dest, usb_descriptors_list[i], usb_descriptors_list[i]->bLength); + dest += usb_descriptors_list[i]->bLength; + } + + return dest - orig_dest; +} + +static void usb_audio_pcm_get_more(unsigned char **start, size_t *size) +{ + *start = NULL; + *size = 0; + #if 0 + size_t i; + + mutex_lock(&usb_audio_buffer_mutex); + + logf("usbaudio: get more !"); + logf("usbaudio: start=%d end=%d", usb_audio_buffer_start, usb_audio_buffer_end); + + if(usb_audio_buffer_start == usb_audio_buffer_end) + { + /* when audio underflow, the callback is not called anymore + * so it needs to be restarted */ + usb_audio_underflow = true; + logf("UNDERFLOW UNDERFLOW"); + } + + if(usb_audio_buffer_start <= usb_audio_buffer_end) + { + *start = usb_audio_buffer + usb_audio_buffer_start; + *size = usb_audio_buffer_end - usb_audio_buffer_start; + usb_audio_buffer_start = usb_audio_buffer_end; + } + else + { + *start = usb_audio_buffer + usb_audio_buffer_start; + *size = USB_AUDIO_BUFFER_SIZE - usb_audio_buffer_start; + usb_audio_buffer_start = 0; + } + + /* zero out right channel */ + /* + for(i = 0; i < (*size / 4); i++) + { + (*start)[4 * i + 0] = 0; + (*start)[4 * i + 1] = 0; + } + */ + + logf("=> start=%d end=%d", usb_audio_buffer_start, usb_audio_buffer_end); + mutex_unlock(&usb_audio_buffer_mutex); + #endif +} + +static void usb_audio_start(void) +{ + usb_audio_buffer_start = 0; + usb_audio_buffer_end = 0; + mutex_init(&usb_audio_buffer_mutex); + + audio_set_input_source(AUDIO_SRC_PLAYBACK, SRCF_PLAYBACK); + audio_set_output_source(AUDIO_SRC_PLAYBACK); + pcm_set_frequency(hw_freq_sampr[as_freq_idx]); + pcm_play_data(&usb_audio_pcm_get_more, NULL, 0); + pcm_play_stop(); +} + +static void usb_audio_stop(void) +{ + pcm_play_stop(); +} + +int usb_audio_set_interface(int intf, int alt) +{ + logf("usbaudio: use AS interface alternate %d", alt); + if(intf != (usb_interface + 1) || alt < 0 || alt > 1) + return -1; + usb_as_intf_alt = alt; + + if(usb_as_intf_alt == 1) + usb_audio_start(); + else + usb_audio_stop(); + + return 0; +} + +int usb_audio_get_interface(int intf) +{ + logf("usbaudio: get AS interface alternate: %d", usb_as_intf_alt); + if(intf != (usb_interface + 1)) + return -1; + else + return usb_as_intf_alt; +} + +static bool usb_audio_endpoint_request(struct usb_ctrlrequest* req) +{ + bool handled = false; + /* only support sampling frequency */ + if(req->wValue != (USB_AS_EP_CS_SAMPLING_FREQ_CTL << 8)) + { + logf("usbaudio: endpoint only handle sampling frequency control"); + return false; + } + + switch(req->bRequest) + { + case USB_AC_SET_CUR: + if(req->wLength != 3) + { + logf("usbaudio: bad length for SET_CUR"); + break; + } + logf("usbaudio: SET_CUR sampling freq"); + usb_drv_recv(EP_CONTROL, usb_buffer, req->wLength); + /* FIXME: do we have to wait for completion or it works out of the box here ? */ + set_sampling_frequency(decode3(usb_buffer)); + usb_drv_send(EP_CONTROL, NULL, 0); /* ack */ + handled = true; + break; + case USB_AC_GET_CUR: + if(req->wLength != 3) + { + logf("usbaudio: bad length for GET_CUR"); + break; + } + logf("usbaudio: GET_CUR sampling freq"); + encode3(usb_buffer, get_sampling_frequency()); + usb_drv_recv(EP_CONTROL, NULL, 0); /* ack */ + usb_drv_send(EP_CONTROL, usb_buffer, req->wLength); + handled = true; + break; + default: + logf("usbaudio: unhandled ep req 0x%x", req->bRequest); + } + + return handled; +} + +static bool feature_unit_set_mute(int value, uint8_t cmd) +{ + if(cmd != USB_AC_CUR_REQ) + { + logf("usbaudio: feature unit MUTE control only has a CUR setting"); + return false; + } + + if(value == 1) + { + logf("usbaudio: mute !"); + return true; + } + else if(value == 0) + { + logf("usbaudio: not muted !"); + return true; + } + else + { + logf("usbaudio: invalid value for CUR setting of feature unit (%d)", value); + return false; + } +} + +static bool feature_unit_get_mute(int *value, uint8_t cmd) +{ + if(cmd != USB_AC_CUR_REQ) + { + logf("usbaudio: feature unit MUTE control only has a CUR setting"); + return false; + } + + *value = 0; + return true; +} + +static bool feature_unit_set_volume(int value, uint8_t cmd) +{ + if(cmd != USB_AC_CUR_REQ) + { + logf("usbaudio: feature unit VOLUME control only support CUR setting"); + return false; + } + + logf("usbaudio: set volume to 0x%x", value); + return true; +} + +static bool feature_unit_get_volume(int *value, uint8_t cmd) +{ + if(cmd != USB_AC_CUR_REQ) + { + logf("usbaudio: feature unit VOLUME control only support CUR setting"); + return false; + } + + *value = 0xfe00; /* -1 dB */ + return true; +} + +static bool usb_audio_set_get_feature_unit(struct usb_ctrlrequest* req) +{ + int channel = req->bRequestType & 0xff; + int selector = req->bRequestType >> 8; + uint8_t cmd = (req->bRequest & ~USB_AC_GET_REQ); + int value = 0; + int i; + bool handled; + + /* master channel only */ + if(channel != 0) + { + logf("usbaudio: set/get on feature unit only apply to master channel (%d)", channel); + return false; + } + /* selectors */ + /* all send/received values are integers so already read data if necessary and store in it in an integer */ + if(req->bRequest & USB_AC_GET_REQ) + { + /* get */ + switch(selector) + { + case USB_AC_FU_MUTE: + handled = (req->wLength == 1) && feature_unit_get_mute(&value, cmd); + break; + case USB_AC_VOLUME_CONTROL: + handled = (req->wLength == 2) && feature_unit_get_volume(&value, cmd); + break; + default: + handled = false; + logf("usbaudio: unhandled control selector of feature unit (0x%x)", selector); + break; + } + + if(!handled) + return false; + + if(req->wLength == 0 || req->wLength > 4) + { + logf("usbaudio: get data payload size is invalid (%d)", req->wLength); + return false; + } + + for(i = 0; i < req->wLength; i++) + usb_buffer[i] = (value >> (8 * i)) & 0xff; + + /* ack */ + usb_drv_recv(EP_CONTROL, NULL, 0); + /* send */ + usb_drv_send(EP_CONTROL, usb_buffer, req->wLength); + return true; + } + else + { + /* set */ + if(req->wLength == 0 || req->wLength > 4) + { + logf("usbaudio: set data payload size is invalid (%d)", req->wLength); + return false; + } + + /* receive */ + usb_drv_recv(EP_CONTROL, usb_buffer, req->wLength); + for(i = 0; i < req->wLength; i++) + value = value | (usb_buffer[i] << (i * 8)); + + switch(selector) + { + case USB_AC_FU_MUTE: + handled = (req->wLength == 1) && feature_unit_set_mute(value, cmd); + break; + case USB_AC_VOLUME_CONTROL: + handled = (req->wLength == 2) && feature_unit_set_volume(value, cmd); + break; + default: + handled = false; + logf("usbaudio: unhandled control selector of feature unit (0x%x)", selector); + break; + } + + if(!handled) + return false; + + /* ack */ + usb_drv_send(EP_CONTROL, NULL, 0); + return true; + } +} + +static bool usb_audio_set_get_request(struct usb_ctrlrequest* req) +{ + switch(req->wIndex >> 8) + { + case AC_FEATURE_ID: + return usb_audio_set_get_feature_unit(req); + default: + logf("usbaudio: unhandled set/get on entity %d", req->wIndex >> 8); + return false; + } +} + +static bool usb_audio_interface_request(struct usb_ctrlrequest* req) +{ + switch(req->bRequest) + { + case USB_AC_SET_CUR: case USB_AC_SET_MIN: case USB_AC_SET_MAX: case USB_AC_SET_RES: + case USB_AC_SET_MEM: case USB_AC_GET_CUR: case USB_AC_GET_MIN: case USB_AC_GET_MAX: + case USB_AC_GET_RES: case USB_AC_GET_MEM: + return usb_audio_set_get_request(req); + default: + logf("usbaudio: unhandled intf req 0x%x", req->bRequest); + return false; + } +} + +bool usb_audio_control_request(struct usb_ctrlrequest* req) +{ + switch(req->bRequestType & USB_RECIP_MASK) + { + case USB_RECIP_ENDPOINT: + return usb_audio_endpoint_request(req); + case USB_RECIP_INTERFACE: + return usb_audio_interface_request(req); + default: + logf("usbaudio: unhandeld req 0x%x", req->bRequest); + return false; + } +} + +void usb_audio_init_connection(void) +{ + logf("usbaudio: init connection"); + + usb_as_intf_alt = 0; + set_sampling_frequency(HW_SAMPR_DEFAULT); + + cpu_boost(true); + + enqueue_iso_xfer(); + #ifdef USB_AUDIO_USE_FEEDBACK_EP + enqueue_feedback(); + #endif + + raw_fd = open("/usb_audio.raw", O_RDWR | O_CREAT | O_TRUNC); + if(raw_fd < 0) + logf("usbaudio: cannot open file for recording"); +} + +void usb_audio_disconnect(void) +{ + logf("usbaudio: disconnect"); + if(raw_fd >= 0) + close(raw_fd); + + cpu_boost(false); +} + +void usb_audio_transfer_complete(int ep, int dir, int status, int length) +{ + (void) ep; + (void) dir; + int actual_length; + + //logf("usbaudio: xfer %s", status < 0 ? "failure" : "completed"); + //logf("usbaudio: %d bytes transfered", length); + + if(ep == out_iso_ep_adr) + { + if(status == 0) + { + if(raw_fd >= 0) + { + if(write(raw_fd, usb_audio_xfer_buffer, length) < 0) + logf("usbaudio: write failed"); + else + logf("usbaudio: write %d bytes", length); + } + + #if 0 + mutex_lock(&usb_audio_buffer_mutex); + //logf("usbaudio: start=%d end=%d length=%d", usb_audio_buffer_start, usb_audio_buffer_end, length); + while(length > 0) + { + actual_length = MIN(length, USB_AUDIO_BUFFER_SIZE - usb_audio_buffer_end); + memcpy(usb_audio_buffer + usb_audio_buffer_end, usb_audio_xfer_buffer, actual_length); + + usb_audio_buffer_end += actual_length; + if(usb_audio_buffer_end >= USB_AUDIO_BUFFER_SIZE) + usb_audio_buffer_end = 0; + length -= actual_length; + } + //logf("=> start=%d end=%d", usb_audio_buffer_start, usb_audio_buffer_end); + mutex_unlock(&usb_audio_buffer_mutex); + + /* only start playback if there is sufficient data (to avoid repeated underflow) */ + if(usb_audio_underflow) + { + if(usb_audio_buffer_start <= usb_audio_buffer_end) + actual_length = usb_audio_buffer_end - usb_audio_buffer_start; + else + actual_length = usb_audio_buffer_end + USB_AUDIO_BUFFER_SIZE - usb_audio_buffer_start; + + if(actual_length >= 200*1024) + { + pcm_play_data(&usb_audio_pcm_get_more, NULL, 0); + usb_audio_underflow = false; + } + } + #endif + } + + enqueue_iso_xfer(); + } + #ifdef USB_AUDIO_USE_FEEDBACK_EP + else if(ep == in_iso_ep_adr) + { + enqueue_feedback(); + } + #endif +} diff --git a/firmware/usbstack/usb_audio.h b/firmware/usbstack/usb_audio.h new file mode 100644 index 0000000..e796d82 --- /dev/null +++ b/firmware/usbstack/usb_audio.h @@ -0,0 +1,38 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: $ + * + * Copyright (C) 20010 by Amaury Pouly + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef USB_AUDIO_H +#define USB_AUDIO_H + +#include "usb_ch9.h" + +int usb_audio_request_endpoints(struct usb_class_driver *); +int usb_audio_set_first_interface(int interface); +int usb_audio_get_config_descriptor(unsigned char *dest,int max_packet_size); +void usb_audio_init_connection(void); +void usb_audio_init(void); +void usb_audio_disconnect(void); +void usb_audio_transfer_complete(int ep,int dir, int status, int length); +bool usb_audio_control_request(struct usb_ctrlrequest* req, unsigned char *dest); +int usb_audio_set_first_string_index(int string_index); +const struct usb_string_descriptor *usb_audio_get_string_descriptor(int string_index); +int usb_audio_set_interface(int intf, int alt); +int usb_audio_get_interface(int intf); + +#endif + diff --git a/firmware/usbstack/usb_audio_def.h b/firmware/usbstack/usb_audio_def.h new file mode 100644 index 0000000..87bfe7e --- /dev/null +++ b/firmware/usbstack/usb_audio_def.h @@ -0,0 +1,233 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id: $ + * + * Copyright (C) 2010 by Amaury Pouly + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +/* Parts of this file are based on Frank Gevaerts work and on audio.h from linux */ + +#ifndef USB_AUDIO_DEF_H +#define USB_AUDIO_DEF_H + +#include "usb_ch9.h" + +#define USB_SUBCLASS_AUDIO_CONTROL 1 +#define USB_SUBCLASS_AUDIO_STREAMING 2 + +#define USB_AC_HEADER 1 +#define USB_AC_INPUT_TERMINAL 2 +#define USB_AC_OUTPUT_TERMINAL 3 +#define USB_AC_MIXER_UNIT 4 +#define USB_AC_SELECTOR_UNIT 5 +#define USB_AC_FEATURE_UNIT 6 +#define USB_AC_PROCESSING_UNIT 8 +#define USB_AC_EXTENSION_UNIT 9 + +#define USB_AS_GENERAL 1 +#define USB_AS_FORMAT_TYPE 2 + +#define USB_AC_CHANNEL_LEFT_FRONT 0x1 +#define USB_AC_CHANNEL_RIGHT_FRONT 0x2 + +#define USB_AC_CHANNELS_LEFT_RIGHT_FRONT (USB_AC_CHANNEL_LEFT_FRONT | USB_AC_CHANNEL_RIGHT_FRONT) + +/* usb audio data structures */ +struct usb_ac_header { + uint8_t bLength; + uint8_t bDescriptorType; /* USB_DT_CS_INTERFACE */ + uint8_t bDescriptorSubType; /* USB_AC_HEADER */ + uint16_t bcdADC; + uint16_t wTotalLength; + uint8_t bInCollection; + uint8_t baInterfaceNr[]; +} __attribute__ ((packed)); + +#define USB_AC_SIZEOF_HEADER(n) (8 + (n)) + +#define USB_AC_TERMINAL_UNDEFINED 0x100 +#define USB_AC_TERMINAL_STREAMING 0x101 +#define USB_AC_TERMINAL_VENDOR_SPEC 0x1ff + +#define USB_AC_EXT_TERMINAL_UNDEFINED 0x600 +#define USB_AC_EXT_TERMINAL_ANALOG 0x601 +#define USB_AC_EXT_TERMINAL_DIGITAL 0x602 +#define USB_AC_EXT_TERMINAL_LINE 0x603 +#define USB_AC_EXT_TERMINAL_LEGACY 0x604 +#define USB_AC_EXT_TERMINAL_SPDIF 0x605 +#define USB_AC_EXT_TERMINAL_1394_DA 0x606 +#define USB_AC_EXT_TERMINAL_1394_DV 0x607 + +#define USB_AC_EMB_MINIDISK 0x706 +#define USB_AC_EMB_RADIO_RECV 0x710 + +#define USB_AC_INPUT_TERMINAL_UNDEFINED 0x200 +#define USB_AC_INPUT_TERMINAL_MICROPHONE 0x201 +#define USB_AC_INPUT_TERMINAL_DESKTOP_MICROPHONE 0x202 +#define USB_AC_INPUT_TERMINAL_PERSONAL_MICROPHONE 0x203 +#define USB_AC_INPUT_TERMINAL_OMNI_DIR_MICROPHONE 0x204 +#define USB_AC_INPUT_TERMINAL_MICROPHONE_ARRAY 0x205 +#define USB_AC_INPUT_TERMINAL_PROC_MICROPHONE_ARRAY 0x206 + +struct usb_ac_input_terminal { + uint8_t bLength; + uint8_t bDescriptorType; /* USB_DT_CS_INTERFACE */ + uint8_t bDescriptorSubType; /* USB_AC_INPUT_TERMINAL */ + uint8_t bTerminalId; + uint16_t wTerminalType; /* USB_AC_INPUT_TERMINAL_* */ + uint8_t bAssocTerminal; + uint8_t bNrChannels; + uint16_t wChannelConfig; + uint8_t iChannelNames; + uint8_t iTerminal; +} __attribute__ ((packed)); + +#define USB_AC_OUTPUT_TERMINAL_UNDEFINED 0x300 +#define USB_AC_OUTPUT_TERMINAL_SPEAKER 0x301 +#define USB_AC_OUTPUT_TERMINAL_HEADPHONES 0x302 +#define USB_AC_OUTPUT_TERMINAL_HEAD_MOUNTED_DISPLAY_AUDIO 0x303 +#define USB_AC_OUTPUT_TERMINAL_DESKTOP_SPEAKER 0x304 +#define USB_AC_OUTPUT_TERMINAL_ROOM_SPEAKER 0x305 +#define USB_AC_OUTPUT_TERMINAL_COMMUNICATION_SPEAKER 0x306 +#define USB_AC_OUTPUT_TERMINAL_LOW_FREQ_EFFECTS_SPEAKER 0x307 + +struct usb_ac_output_terminal { + uint8_t bLength; + uint8_t bDescriptorType; /* USB_DT_CS_INTERFACE */ + uint8_t bDescriptorSubType; /* USB_AC_OUTPUT_TERMINAL */ + uint8_t bTerminalId; + uint16_t wTerminalType; /* USB_AC_OUTPUT_TERMINAL_* */ + uint8_t bAssocTerminal; + uint8_t bSourceId; + uint8_t iTerminal; +} __attribute__ ((packed)); + +#define USB_AC_FU_CONTROL_UNDEFINED 0x00 +#define USB_AC_MUTE_CONTROL 0x01 +#define USB_AC_VOLUME_CONTROL 0x02 +#define USB_AC_BASS_CONTROL 0x03 +#define USB_AC_MID_CONTROL 0x04 +#define USB_AC_TREBLE_CONTROL 0x05 +#define USB_AC_EQUALIZER_CONTROL 0x06 +#define USB_AC_AUTO_GAIN_CONTROL 0x07 +#define USB_AC_DELAY_CONTROL 0x08 +#define USB_AC_BASS_BOOST_CONTROL 0x09 +#define USB_AC_LOUDNESS_CONTROL 0x0a + +#define USB_AC_FU_MUTE (1 << (USB_AC_MUTE_CONTROL - 1)) +#define USB_AC_FU_VOLUME (1 << (USB_AC_VOLUME_CONTROL - 1)) +#define USB_AC_FU_BASS (1 << (USB_AC_BASS_CONTROL - 1)) +#define USB_AC_FU_MID (1 << (USB_AC_MID_CONTROL - 1)) +#define USB_AC_FU_TREBLE (1 << (USB_AC_TREBLE_CONTROL - 1)) +#define USB_AC_FU_EQUILIZER (1 << (USB_AC_EQUALIZER_CONTROL - 1)) +#define USB_AC_FU_AUTO_GAIN (1 << (USB_AC_AUTO_GAIN_CONTROL - 1)) +#define USB_AC_FU_DELAY (1 << (USB_AC_DELAY_CONTROL - 1)) +#define USB_AC_FU_BASS_BOOST (1 << (USB_AC_BASS_BOOST_CONTROL - 1)) +#define USB_AC_FU_LOUDNESS (1 << (USB_AC_LOUDNESS_CONTROL - 1)) + +#define DEFINE_USB_AC_FEATURE_UNIT(n,ch) \ +struct usb_ac_feature_unit_##n##_##ch { \ + uint8_t bLength; \ + uint8_t bDescriptorType; /* USB_DT_CS_INTERFACE */ \ + uint8_t bDescriptorSubType; /* USB_AC_FEATURE_UNIT */ \ + uint8_t bUnitId; \ + uint8_t bSourceId; \ + uint8_t bControlSize; \ + uint##n##_t bmaControls[ch + 1]; /* FU_* ORed*/ \ + uint8_t iFeature; \ +} __attribute__ ((packed)); + +#define USB_AC_SET_REQ 0x00 +#define USB_AC_GET_REQ 0x80 + +#define USB_AC_CUR_REQ 0x1 +#define USB_AC_MIN_REQ 0x2 +#define USB_AC_MAX_REQ 0x3 +#define USB_AC_RES_REQ 0x4 +#define USB_AC_MEM_REQ 0x5 + +#define USB_AC_SET_CUR (USB_AC_SET_REQ | USB_AC_CUR_REQ) +#define USB_AC_GET_CUR (USB_AC_GET_REQ | USB_AC_CUR_REQ) +#define USB_AC_SET_MIN (USB_AC_SET_REQ | USB_AC_MIN_REQ) +#define USB_AC_GET_MIN (USB_AC_GET_REQ | USB_AC_MIN_REQ) +#define USB_AC_SET_MAX (USB_AC_SET_REQ | USB_AC_MAX_REQ) +#define USB_AC_GET_MAX (USB_AC_GET_REQ | USB_AC_MAX_REQ) +#define USB_AC_SET_RES (USB_AC_SET_REQ | USB_AC_RES_REQ) +#define USB_AC_GET_RES (USB_AC_GET_REQ | USB_AC_RES_REQ) +#define USB_AC_SET_MEM (USB_AC_SET_REQ | USB_AC_MEM_REQ) +#define USB_AC_GET_MEM (USB_AC_GET_REQ | USB_AC_MEM_REQ) +#define USB_AC_GET_STAT 0xff + +#define USB_AS_FORMAT_TYPE_UNDEFINED 0x0 +#define USB_AS_FORMAT_TYPE_I 0x1 +#define USB_AS_FORMAT_TYPE_II 0x2 +#define USB_AS_FORMAT_TYPE_III 0x3 + +struct usb_as_interface { + uint8_t bLength; + uint8_t bDescriptorType; /* USB_DT_CS_INTERFACE */ + uint8_t bDescriptorSubType; /* USB_AS_GENERAL */ + uint8_t bTerminalLink; + uint8_t bDelay; + uint16_t wFormatTag; +} __attribute__ ((packed)); + +#define USB_AS_EP_GENERAL 0x01 + +#define USB_AS_EP_CS_SAMPLING_FREQ_CTL 0x01 +#define USB_AS_EP_CS_PITCH_CTL 0x02 + +struct usb_iso_audio_endpoint_descriptor { + uint8_t bLength; + uint8_t bDescriptorType; + + uint8_t bEndpointAddress; + uint8_t bmAttributes; + uint16_t wMaxPacketSize; + uint8_t bInterval; + uint8_t bRefresh; + uint8_t bSynchAddress; +} __attribute__ ((packed)); + +struct usb_as_iso_endpoint { + uint8_t bLength; + uint8_t bDescriptorType; /* USB_DT_CS_ENDPOINT */ + uint8_t bDescriptorSubType; /* USB_AS_EP_GENERAL */ + uint8_t bmAttributes; + uint8_t bLockDelayUnits; + uint16_t wLockDelay; +} __attribute__ ((packed)); + +#define USB_AS_FORMAT_TYPE_I_UNDEFINED 0x0 +#define USB_AS_FORMAT_TYPE_I_PCM 0x1 +#define USB_AS_FORMAT_TYPE_I_PCM8 0x2 +#define USB_AS_FORMAT_TYPE_I_IEEE_FLOAT 0x3 +#define USB_AS_FORMAT_TYPE_I_ALAW 0x4 +#define USB_AS_FORMAT_TYPE_I_MULAW 0x5 + +struct usb_as_format_type_i_discrete { + uint8_t bLength; + uint8_t bDescriptorType; /* USB_DT_CS_INTERFACE */ + uint8_t bDescriptorSubType; /* USB_AS_FORMAT_TYPE */ + uint8_t bFormatType; /* USB_AS_FORMAT_TYPE_I */ + uint8_t bNrChannels; + uint8_t bSubframeSize; + uint8_t bBitResolution; + uint8_t bSamFreqType; /* Number of discrete frequencies */ + uint8_t tSamFreq[][3]; +} __attribute__ ((packed)); + +#define USB_AS_SIZEOF_FORMAT_TYPE_I_DISCRETE(n) (8 + (n * 3)) + +#endif /* USB_AUDIO_DEF_H */ diff --git a/firmware/usbstack/usb_class_driver.h b/firmware/usbstack/usb_class_driver.h index c8d1e70..31f92d2 100644 --- a/firmware/usbstack/usb_class_driver.h +++ b/firmware/usbstack/usb_class_driver.h @@ -29,6 +29,8 @@ struct usb_class_driver { bool enabled; int first_interface; int last_interface; + int first_string_index; + int last_string_index; /* Driver api starts here */ @@ -83,6 +85,28 @@ struct usb_class_driver { Optional function */ void (*notify_hotswap)(int volume, bool inserted); #endif + + /* Tells the driver to select an alternate setting for a specific interface. + * Returns 0 on success and -1 on error. + * Mandatory function if alternate interface support is needed */ + int (*set_interface)(int interface, int alt_setting); + + /* Asks the driver what is the current alternate setting for a specific interface. + * Returns value on success and -1 on error. + * Mandatory function if alternate interface support is needed */ + int (*get_interface)(int interface); + + /* Tells the driver what its first string id number will be. The driver + returns the number of the first available string id for the next driver + (i.e. a driver with one string will return string_index+1) */ + int (*set_first_string_index)(int string_index); + + /* Asks the driver to return the string descriptor associated with the index + * and that has been *allocated* by the set_first_string_index mechanism. + * Return NULL on error + * Mandatory function if set_first_string_index is implemented and allocates + * at least one string index */ + const struct usb_string_descriptor* (*get_string_descriptor)(int string_index); }; #define PACK_DATA(dest, data) \ diff --git a/firmware/usbstack/usb_core.c b/firmware/usbstack/usb_core.c index a5865da..a5d3d74 100644 --- a/firmware/usbstack/usb_core.c +++ b/firmware/usbstack/usb_core.c @@ -22,7 +22,7 @@ #include "thread.h" #include "kernel.h" #include "string.h" -/*#define LOGF_ENABLE*/ +#define LOGF_ENABLE #include "logf.h" #include "usb.h" @@ -47,6 +47,14 @@ #include "usb_hid.h" #endif +#ifdef USB_ENABLE_AUDIO +#include "usb_audio.h" +#endif + +#ifdef USB_ENABLE_TEST +#include "usb_test.h" +#endif + /* TODO: Move target-specific stuff somewhere else (serial number reading) */ #ifdef HAVE_AS3514 @@ -161,6 +169,8 @@ static const struct usb_string_descriptor* const usb_strings[] = &usb_string_iSerial }; +#define USB_STRINGS_LIST_SIZE (sizeof(usb_strings) / sizeof(struct usb_string_descriptor *)) + static int usb_address = 0; static bool initialized = false; static enum { DEFAULT, ADDRESS, CONFIGURED } usb_state; @@ -255,6 +265,52 @@ static struct usb_class_driver drivers[USB_NUM_DRIVERS] = #endif }, #endif +#ifdef USB_ENABLE_AUDIO + [USB_DRIVER_AUDIO] = { + .enabled = false, + .needs_exclusive_storage = false, + .first_interface = 0, + .last_interface = 0, + .request_endpoints = usb_audio_request_endpoints, + .set_first_interface = usb_audio_set_first_interface, + .get_config_descriptor = usb_audio_get_config_descriptor, + .init_connection = usb_audio_init_connection, + .init = usb_audio_init, + .disconnect = usb_audio_disconnect, + .transfer_complete = usb_audio_transfer_complete, + .control_request = usb_audio_control_request, +#ifdef HAVE_HOTSWAP + .notify_hotswap = NULL, +#endif + .set_interface = usb_audio_set_interface, + .get_interface = usb_audio_get_interface, + .set_first_string_index = usb_audio_set_first_string_index, + .get_string_descriptor = usb_audio_get_string_descriptor, + }, +#endif +#ifdef USB_ENABLE_TEST + [USB_DRIVER_TEST] = { + .enabled = false, + .needs_exclusive_storage = false, + .first_interface = 0, + .last_interface = 0, + .request_endpoints = usb_test_request_endpoints, + .set_first_interface = usb_test_set_first_interface, + .get_config_descriptor = usb_test_get_config_descriptor, + .init_connection = usb_test_init_connection, + .init = usb_test_init, + .disconnect = usb_test_disconnect, + .transfer_complete = usb_test_transfer_complete, + .control_request = usb_test_control_request, +#ifdef HAVE_HOTSWAP + .notify_hotswap = NULL, +#endif + .set_interface = usb_test_set_interface, + .get_interface = usb_test_get_interface, + .set_first_string_index = usb_test_set_first_string_index, + .get_string_descriptor = usb_test_get_string_descriptor, + }, +#endif }; static void usb_core_control_request_handler(struct usb_ctrlrequest* req); @@ -483,6 +539,7 @@ static void allocate_interfaces_and_endpoints(void) { int i; int interface=0; + int string_index = sizeof(usb_strings)/sizeof(struct usb_string_descriptor *); memset(ep_data,0,sizeof(ep_data)); @@ -494,12 +551,17 @@ static void allocate_interfaces_and_endpoints(void) for(i=0;i interface) { + /* Check for SET_INTERFACE and GET_INTERFACE */ + if((req->bRequestType & USB_RECIP_MASK) == USB_RECIP_INTERFACE && + (req->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) + { + if(req->bRequest == USB_REQ_SET_INTERFACE) + { + if(drivers[i].set_interface && drivers[i].set_interface(req->wIndex, req->wValue) >= 0) + { + usb_drv_send(EP_CONTROL, NULL, 0); + handled = true; + } + break; + } + else if(req->bRequest == USB_REQ_GET_INTERFACE) + { + int alt = -1; + + if(drivers[i].get_interface) + alt = drivers[i].get_interface(req->wIndex); + + if(alt >= 0 && alt < 255) + { + response_data[0] = alt; + usb_drv_recv(EP_CONTROL, NULL, 0); + usb_drv_send(EP_CONTROL, response_data, 1); + handled = true; + } + break; + } + /* fallback */ + } + handled = drivers[i].control_request(req, response_data); - if(handled) - break; + + break; /* no other driver can handle it because it's interface specific */ } } if(!handled) { @@ -585,15 +679,30 @@ static void request_handler_device_get_descriptor(struct usb_ctrlrequest* req) } case USB_DT_STRING: - logf("STRING %d",index); - if ((unsigned)index < (sizeof(usb_strings)/ - sizeof(struct usb_string_descriptor*))) { + _logf("STRING %d",index); + if ((unsigned)index < USB_STRINGS_LIST_SIZE) { size = usb_strings[index]->bLength; ptr = usb_strings[index]; } else { - logf("bad string id %d",index); - usb_drv_stall(EP_CONTROL,true,true); + int i; + const struct usb_string_descriptor *desc = NULL; + + for(i = 0; i < USB_NUM_DRIVERS; i++) + if(drivers[i].enabled && drivers[i].first_string_index <= index && + index < drivers[i].last_string_index) + desc = drivers[i].get_string_descriptor(index); + + if(desc == NULL) + { + logf("bad string id %d",index); + usb_drv_stall(EP_CONTROL,true,true); + } + else + { + size = desc->bLength; + ptr = desc; + } } break; @@ -689,15 +798,8 @@ static void request_handler_interface_standard(struct usb_ctrlrequest* req) switch (req->bRequest) { case USB_REQ_SET_INTERFACE: - logf("usb_core: SET_INTERFACE"); - usb_drv_send(EP_CONTROL,NULL,0); - break; - case USB_REQ_GET_INTERFACE: - logf("usb_core: GET_INTERFACE"); - response_data[0]=0; - usb_drv_recv(EP_CONTROL,NULL,0); - usb_drv_send(EP_CONTROL,response_data,1); + control_request_handler_drivers(req); break; case USB_REQ_CLEAR_FEATURE: break; @@ -725,7 +827,10 @@ static void request_handler_interface(struct usb_ctrlrequest* req) control_request_handler_drivers(req); break; case USB_TYPE_VENDOR: - break; + default: + logf("usb bad req type %d", req->bRequestType & USB_TYPE_MASK); + usb_drv_stall(EP_CONTROL,true,true); + usb_core_ack_control(req); } } @@ -805,6 +910,12 @@ static void usb_core_control_request_handler(struct usb_ctrlrequest* req) allocate_interfaces_and_endpoints(); } + logf("usb req: recip=%x type=%x req=0x%x wValue=0x%x wIndex=0x%x wLength=0x%x", + req->bRequestType & USB_RECIP_MASK, + (req->bRequestType & USB_TYPE_MASK) >> 5, + req->bRequest, req->wValue, req->wIndex, req->wLength); + + switch(req->bRequestType & USB_RECIP_MASK) { case USB_RECIP_DEVICE: request_handler_device(req); diff --git a/firmware/usbstack/usb_test.c b/firmware/usbstack/usb_test.c new file mode 100644 index 0000000..88b3001 --- /dev/null +++ b/firmware/usbstack/usb_test.c @@ -0,0 +1,430 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 20010 Amaury Pouly + * + * 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 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "string.h" +#include "system.h" +#include "usb_core.h" +#include "usb_drv.h" +#include "kernel.h" +#include "usb_test.h" +#include "usb_test_def.h" +#include "usb_class_driver.h" +#define LOGF_ENABLE +#include "logf.h" +#include "stdlib.h" +#include "audio.h" +#include "crc32.h" + +#define USB_TEST_MIN_EP_INTERVAL 1 +#define USB_TEST_MAX_EP_INTERVAL 6 + +enum +{ + USB_TEST_INTERFACE_STRING = 0 +}; + +static const struct usb_string_descriptor __attribute__((aligned(2))) + usb_test_interface_string = +{ + 20*2+2, + USB_DT_STRING, + {'R', 'o', 'c', 'k', 'b', 'o', 'x', ' ', + 'T', 'e', 's', 't', ' ', + 'C', 'o', 'n', 't', 'r', 'o', 'l'} +}; + +static const struct usb_string_descriptor* const usb_strings_list[]= +{ + [USB_TEST_INTERFACE_STRING] = &usb_test_interface_string, +}; + +#define USB_STRINGS_LIST_SIZE (sizeof(usb_strings_list)/sizeof(struct usb_string_descriptor *)) + +static struct usb_interface_descriptor __attribute__((aligned(2))) + interface_descriptor = +{ + .bLength = sizeof(struct usb_interface_descriptor), + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0,/* filled later */ + .bNumEndpoints = 4, + .bInterfaceClass = USB_TEST_CLASS, + .bInterfaceSubClass = USB_TEST_SUBCLASS, + .bInterfaceProtocol = USB_TEST_PROTOCOL, + .iInterface = 0 /* filled later */ +}; + + +static struct usb_endpoint_descriptor __attribute__((aligned(2))) + endpoint_descriptor = +{ + .bLength = sizeof(struct usb_endpoint_descriptor), + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .wMaxPacketSize = 0, + .bInterval = 0 /* filled later */ +}; + +#define BUFFER_SIZE 16384 * 4 +static unsigned char *usb_buffer; + +static int ep_bulk_in, ep_bulk_out; +static int ep_iso_in, ep_iso_out; +static int usb_interface; +static int usb_string_index; +static int usb_test_cur_intf; + +static int stat_nb_bytes; +static int stat_first_tick; +static int stat_crc_errors; + +struct usb_test_data_request usb_data_req USB_DEVBSS_ATTR __attribute__((aligned(32))); +struct usb_test_iso_request usb_iso_req USB_DEVBSS_ATTR __attribute__((aligned(32))); +struct usb_test_stat_request usb_stat_req USB_DEVBSS_ATTR __attribute__((aligned(32))); + +int usb_test_set_first_string_index(int string_index) +{ + usb_string_index = string_index; + + interface_descriptor.iInterface = string_index + USB_TEST_INTERFACE_STRING; + + return string_index + USB_STRINGS_LIST_SIZE; +} + +const struct usb_string_descriptor *usb_test_get_string_descriptor(int string_index) +{ + logf("usbtest: get string %d", string_index); + if(string_index < usb_string_index || + string_index >= (int)(usb_string_index + USB_STRINGS_LIST_SIZE)) + return NULL; + else + return usb_strings_list[string_index - usb_string_index]; +} + +int usb_test_request_endpoints(struct usb_class_driver *drv) +{ + ep_bulk_in = usb_core_request_endpoint(USB_ENDPOINT_XFER_BULK, USB_DIR_IN, drv); + if (ep_bulk_in < 0) + { + logf("usbtest: cannot get bulk in ep"); + return -1; + } + + ep_bulk_out = usb_core_request_endpoint(USB_ENDPOINT_XFER_BULK, USB_DIR_OUT, drv); + if (ep_bulk_out < 0) + { + usb_core_release_endpoint(ep_bulk_in); + logf("usbtest: cannot get bulk out ep"); + return -1; + } + + ep_iso_in = usb_core_request_endpoint(USB_ENDPOINT_XFER_ISOC, USB_DIR_IN, drv); + if(ep_iso_in < 0) + { + usb_core_release_endpoint(ep_bulk_in); + usb_core_release_endpoint(ep_bulk_out); + logf("usbtest: cannot get iso in ep"); + return -1; + } + + ep_iso_out = usb_core_request_endpoint(USB_ENDPOINT_XFER_ISOC, USB_DIR_OUT, drv); + if(ep_iso_out < 0) + { + usb_core_release_endpoint(ep_bulk_in); + usb_core_release_endpoint(ep_bulk_out); + usb_core_release_endpoint(ep_iso_in); + logf("usbtest: cannot get iso out ep"); + return -1; + } + + return 0; +} + +int usb_test_set_first_interface(int interface) +{ + usb_interface = interface; + return interface + 1; +} + +int usb_test_set_interface(int intf, int alt) +{ + (void) intf; + usb_test_cur_intf = alt; + logf("usbtest: use interface %d", alt); + return 0; +} + +int usb_test_get_interface(int intf) +{ + (void) intf; + return usb_test_cur_intf; +} + +int usb_test_get_config_descriptor(unsigned char *dest, int max_packet_size) +{ + unsigned char *orig_dest = dest; + int i; + + for(i = 0; i < (USB_TEST_MAX_EP_INTERVAL - USB_TEST_MIN_EP_INTERVAL + 1); i++) + { + interface_descriptor.bInterfaceNumber = usb_interface; + interface_descriptor.bAlternateSetting = i; + PACK_DATA(dest, interface_descriptor); + + endpoint_descriptor.wMaxPacketSize = max_packet_size; + + endpoint_descriptor.bInterval = 0; + endpoint_descriptor.bEndpointAddress = ep_bulk_in; + endpoint_descriptor.bmAttributes = USB_ENDPOINT_XFER_BULK; + PACK_DATA(dest, endpoint_descriptor); + + endpoint_descriptor.bEndpointAddress = ep_bulk_out; + PACK_DATA(dest, endpoint_descriptor); + + endpoint_descriptor.bEndpointAddress = ep_iso_in; + endpoint_descriptor.bInterval = USB_TEST_MIN_EP_INTERVAL + i; + endpoint_descriptor.bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE; + PACK_DATA(dest, endpoint_descriptor); + + endpoint_descriptor.bEndpointAddress = ep_iso_out; + PACK_DATA(dest, endpoint_descriptor); + } + + return (dest - orig_dest); +} + +bool usb_test_get_status(struct usb_ctrlrequest* req, unsigned char* dest) +{ + (void) req; + + logf("usbtest: get status"); + dest[0] = USB_TEST_STATUS_OK; + usb_drv_recv(EP_CONTROL, NULL, 0); /* ack */ + usb_drv_send(EP_CONTROL, dest, 1); /* send */ + return true; +} + +void generate_data(void) +{ +} + +void process_out_data(int length) +{ + int i; + + if((usb_data_req.bReq & USB_TEST_DATA_OUT_MASK) == USB_TEST_DATA_OUT_CRC32) + { + for(i = 0; i < (length / endpoint_descriptor.wMaxPacketSize); i++) + { + int actual_length = endpoint_descriptor.wMaxPacketSize; + unsigned char *buffer = usb_buffer + i * endpoint_descriptor.wMaxPacketSize; + uint32_t expected_crc; + uint32_t actual_crc; + + if(actual_length < 4) + { + logf("usbtest: short CRC block"); + stat_crc_errors++; + return; + } + + expected_crc = *(uint32_t *)buffer; + actual_crc = crc_32(buffer + 4, actual_length - 4, 0xffffffff); + + if(actual_crc != expected_crc) + { + logf("usbtest: CRC error on %d byte block", actual_length); + stat_crc_errors++; + } + } + } +} + +bool usb_test_data_req(struct usb_ctrlrequest* req, unsigned char* dest) +{ + (void) req; + (void) dest; + usb_drv_recv(EP_CONTROL, &usb_data_req, sizeof(usb_data_req)); + usb_drv_send(EP_CONTROL, NULL, 0); /* ack */ + + logf("usbtest: data"); + logf("usbtest: magic=0x%lx", usb_data_req.dwMagic); + logf("usbtest: req.IN=%s req.OUT=%s", + (usb_data_req.bReq & USB_TEST_DATA_IN_MASK) == USB_TEST_DATA_IN_GENERATE ? "GENERATE" : "UNKNOWN", + (usb_data_req.bReq & USB_TEST_DATA_OUT_MASK) == USB_TEST_DATA_OUT_IGNORE ? "IGNORE" : + ((usb_data_req.bReq & USB_TEST_DATA_OUT_MASK) == USB_TEST_DATA_OUT_CRC32 ? "CRC32" : "UNKNOWN")); + + return true; +} + +void enqueue_xfer(void) +{ + if(usb_iso_req.dwLength == 0) + return; + + if(usb_iso_req.bReq == USB_TEST_ISO_IN) + { + generate_data(); + usb_drv_send(ep_iso_in, usb_buffer, MIN((int)usb_iso_req.dwLength, 3 * endpoint_descriptor.wMaxPacketSize)); + } + else if(usb_iso_req.bReq == USB_TEST_ISO_OUT) + { + usb_drv_recv(ep_iso_out, usb_buffer, MIN((int)usb_iso_req.dwLength, + endpoint_descriptor.wMaxPacketSize)); + } +} + +bool usb_test_iso_test(struct usb_ctrlrequest* req, unsigned char* dest) +{ + (void) req; + (void) dest; + usb_drv_recv(EP_CONTROL, &usb_iso_req, sizeof(usb_iso_req)); + usb_drv_send(EP_CONTROL, NULL, 0); /* ack */ + + logf("usbtest: iso test"); + logf("usbtest: magic=0x%lx", usb_iso_req.dwMagic); + logf("usbtest: dir=%s", usb_iso_req.bReq == USB_TEST_ISO_IN ? "IN" : + (usb_iso_req.bReq == USB_TEST_ISO_OUT ? "OUT" : "UNKNOWN")); + logf("usbtest: length=%lu", usb_iso_req.dwLength); + + enqueue_xfer(); + + return true; +} + +bool usb_test_stat_req(struct usb_ctrlrequest* req, unsigned char* dest) +{ + (void) req; + (void) dest; + usb_drv_recv(EP_CONTROL, &usb_stat_req, sizeof(usb_stat_req)); + usb_drv_send(EP_CONTROL, NULL, 0); /* ack */ + + logf("usbtest: stat"); + logf("usbtest: magic=0x%lx", usb_stat_req.dwMagic); + logf("usbtest: req=%s", usb_stat_req.bReq == USB_TEST_STAT_CLEAR ? "CLEAR" : + (usb_stat_req.bReq == USB_TEST_STAT_LOG ? "LOG" : "UNKNOWN")); + + if(usb_stat_req.bReq == USB_TEST_STAT_CLEAR) + { + stat_first_tick = current_tick; + stat_nb_bytes = 0; + stat_crc_errors = 0; + } + else if(usb_stat_req.bReq == USB_TEST_STAT_LOG) + { + uint32_t endtick = current_tick; + logf("usbtest: data transfered=%d B=%d KiB=%d MiB", stat_nb_bytes, + stat_nb_bytes / 1024, stat_nb_bytes / 1024 / 1024); + if((usb_data_req.bReq & USB_TEST_DATA_OUT_MASK) == USB_TEST_DATA_OUT_CRC32) + logf("usbtest: CRC errors=%d", stat_crc_errors); + logf("usbtest: time elapsed=%lu ms", ((endtick - stat_first_tick) * 1000) / HZ); + } + + return true; +} + +bool usb_test_cancel_req(struct usb_ctrlrequest* req, unsigned char* dest) +{ + (void) req; + (void) dest; + + logf("usbtest: cancel"); + + usb_iso_req.dwLength = 0; + + usb_drv_recv(EP_CONTROL, NULL, 0); + usb_drv_send(EP_CONTROL, NULL, 0); /* ack */ + + return true; +} + +/* called by usb_core_control_request() */ +bool usb_test_control_request(struct usb_ctrlrequest* req, unsigned char* dest) +{ + (void) req; + (void) dest; + bool handled = false; + + (void)dest; + switch (req->bRequest) + { + case USB_TEST_REQ_GET_STATUS: + return usb_test_get_status(req, dest); + case USB_TEST_REQ_DATA: + return usb_test_data_req(req, dest); + case USB_TEST_REQ_TEST_ISO: + return usb_test_iso_test(req, dest); + case USB_TEST_REQ_STAT: + return usb_test_stat_req(req, dest); + case USB_TEST_REQ_CANCEL: + return usb_test_cancel_req(req, dest); + default: + logf("usbtest: unhandeld req %d", req->bRequest); + } + return handled; +} + +void usb_test_init_connection(void) +{ + cpu_boost(true); + usb_test_cur_intf = 0; +} + +void usb_test_init(void) +{ + unsigned char * audio_buffer; + size_t bufsize; + + audio_buffer = audio_get_buffer(false,&bufsize); +#ifdef UNCACHED_ADDR + usb_buffer = (void *)UNCACHED_ADDR((unsigned int)(audio_buffer+31) & 0xffffffe0); +#else + usb_buffer = (void *)((unsigned int)(audio_buffer+31) & 0xffffffe0); +#endif + cpucache_invalidate(); +} + +void usb_test_disconnect(void) +{ + cpu_boost(false); +} + +/* called by usb_core_transfer_complete() */ +void usb_test_transfer_complete(int ep, int dir, int status, int length) +{ + logf("usbtest: xfer complete"); + logf("usbtest: ep=%d dir=%d status=%d length=%d", ep, dir, status, length); + + if(status == 0 && ep == ep_iso_out) + { + process_out_data(length); + + stat_nb_bytes += length; + + if(usb_iso_req.dwLength < (uint32_t)length) + usb_iso_req.dwLength = 0; + else + { + usb_iso_req.dwLength -= length; + enqueue_xfer(); + } + } +} diff --git a/firmware/usbstack/usb_test.h b/firmware/usbstack/usb_test.h new file mode 100644 index 0000000..781112d --- /dev/null +++ b/firmware/usbstack/usb_test.h @@ -0,0 +1,41 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 20010 by amaury Pouly + * + * 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 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef USB_TEST_H +#define USB_TEST_H + +#include "usb_ch9.h" +#include "usb_core.h" + +int usb_test_request_endpoints(struct usb_class_driver *drv); +int usb_test_set_first_interface(int interface); +int usb_test_get_config_descriptor(unsigned char *dest, int max_packet_size); +void usb_test_init_connection(void); +void usb_test_init(void); +void usb_test_disconnect(void); +void usb_test_transfer_complete(int ep, int dir, int status, int length); +bool usb_test_control_request(struct usb_ctrlrequest* req, unsigned char* dest); +int usb_test_set_first_string_index(int string_index); +const struct usb_string_descriptor *usb_test_get_string_descriptor(int string_index); +int usb_test_set_interface(int intf, int alt); +int usb_test_get_interface(int intf); + +#endif + diff --git a/firmware/usbstack/usb_test_def.h b/firmware/usbstack/usb_test_def.h new file mode 100644 index 0000000..8cfd538 --- /dev/null +++ b/firmware/usbstack/usb_test_def.h @@ -0,0 +1,74 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 20010 by amaury Pouly + * + * 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 + * of the License, or (at your option) any later version. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef USB_TEST_DEF_H +#define USB_TEST_DEF_H + +#include "usb_ch9.h" + +#define USB_TEST_CLASS USB_CLASS_APP_SPEC +#define USB_TEST_SUBCLASS 0xc0 +#define USB_TEST_PROTOCOL 0xde + +#define USB_TEST_STATUS_OK 0x00 + +#define USB_TEST_REQ_GET_STATUS 0x77 +#define USB_TEST_REQ_DATA 0x78 +#define USB_TEST_REQ_TEST_ISO 0x79 +#define USB_TEST_REQ_STAT 0x80 +#define USB_TEST_REQ_CANCEL 0x81 + +#define USB_TEST_MAGIC 0xdeadbeef + +/* IN transfers */ +#define USB_TEST_DATA_IN_MASK 0x0f +#define USB_TEST_DATA_IN_GENERATE 0x00 +/* OUT transfers */ +#define USB_TEST_DATA_OUT_MASK 0xf0 +#define USB_TEST_DATA_OUT_IGNORE 0x00 +#define USB_TEST_DATA_OUT_CRC32 0x10 + +#define USB_TEST_ISO_OUT 0x00 +#define USB_TEST_ISO_IN 0x01 + +#define USB_TEST_STAT_CLEAR 0x00 +#define USB_TEST_STAT_LOG 0x01 + +struct usb_test_data_request +{ + uint32_t dwMagic; + uint8_t bReq; +} __attribute__ ((packed)); + +struct usb_test_iso_request +{ + uint32_t dwMagic; + uint8_t bReq; + uint32_t dwLength; +} __attribute__ ((packed)); + +struct usb_test_stat_request +{ + uint32_t dwMagic; + uint8_t bReq; +} __attribute__ ((packed)); + +#endif +