Add USB Audio 1.0 support

Original commit credit to Amaury Pouly, Moshe Piekarski
Pushed across the finish line by Dana Conrad

To enable, see setting under General Settings --> System --> USB-DAC.
On devices with few endpoints, this may not work while HID and/or
mass storage is enabled.

Adds new dedicated mixer channel.

setting usb-dac can have values:
- never (0)
- always (1)
- while_charge_only (2)
- while_mass_storage (3)

Relevant devices are DWC2 and ARC usb controller devices. That being:
x1000 Native targets (m3k, erosqnative, q1, others...?),
sansac200, creativezenxfi2, vibe500, ipodmini2g,
ipod4g, creativezenxfi, creativezenxfi3, sansaview, ipodcolor,
creativezenxfistyle, samsungypz5, sansafuzeplus, iriverh10_5gb,
tatungtpj1022, gigabeats, faketarget, samsungyh820, gogearhdd1630, samsungyh925, ipodmini1g, ipodvideo, creativezenmozaic, sonynwze370, creativezen, gogearsa9200, gogearhdd6330, sonynwze360, sansae200, mrobe100, iriverh10, creativezenv, ipodnano1g, samsungyh920

USB Driver-wise, it should be noted that this patch requires some
slight changes:
- proper blocking on control OUT transfers, to make sure the data is
  received *before* using it, the usb_core should probably use that too
- drivers can now support interface alternate settings
- drivers can be notified of completion by a new fast handler, which
  is called directly from the driver; this is is necessary for
  isochronous transfers because going through the usb queue is way too
  slow

Designware changes:

- enable for USBOTG_DESIGNWARE
- set maxpacketsize to 1023 for ISO endpoints

Change-Id: I570871884a4e4820b4312b203b07701f06ecacc6
This commit is contained in:
Dana Conrad 2022-09-09 15:36:27 -05:00 committed by Solomon Peachy
parent af42428037
commit 9ce66e088e
20 changed files with 1867 additions and 35 deletions

View file

@ -116,6 +116,9 @@
#ifdef HAVE_USBSTACK
#include "usb_core.h"
#ifdef USB_ENABLE_AUDIO
#include "../usbstack/usb_audio.h"
#endif
#endif
#include "talk.h"
@ -2509,18 +2512,56 @@ static bool dbg_talk(void)
}
#ifdef HAVE_USBSTACK
#if defined(ROCKBOX_HAS_LOGF) && defined(USB_ENABLE_SERIAL)
static bool toggle_usb_serial(void)
#if (defined(ROCKBOX_HAS_LOGF) && defined(USB_ENABLE_SERIAL))
static bool toggle_usb_core_driver(int driver, char *msg)
{
bool enabled = !usb_core_driver_enabled(USB_DRIVER_SERIAL);
bool enabled = !usb_core_driver_enabled(driver);
usb_core_enable_driver(USB_DRIVER_SERIAL, enabled);
splashf(HZ, "USB Serial %sabled", enabled ? "en" : "dis");
usb_core_enable_driver(driver,enabled);
splashf(HZ, "%s %s", msg, enabled ? "enabled" : "disabled");
return false;
}
#ifdef USB_ENABLE_SERIAL
static bool toggle_usb_serial(void)
{
return toggle_usb_core_driver(USB_DRIVER_SERIAL, "USB Serial");
}
#endif /* USB_ENABLE_SERIAL */
#endif
#endif
#ifdef USB_ENABLE_AUDIO
static int dbg_usb_audio_cb(int action, struct gui_synclist *lists)
{
(void)lists;
simplelist_reset_lines();
simplelist_addline("%sabled", usb_core_driver_enabled(USB_DRIVER_AUDIO)?"En":"Dis");
simplelist_addline("%sPlaying", usb_audio_get_playing()?"":"Not ");
simplelist_addline("iface: %d alt: %d", usb_audio_get_main_intf(), usb_audio_get_alt_intf());
simplelist_addline("out ep: 0x%X in ep: 0x%X", usb_audio_get_out_ep(), usb_audio_get_in_ep());
simplelist_addline("Volume: %d", usb_audio_get_cur_volume());
simplelist_addline("Playback Frequency: %lu", usb_audio_get_playback_sampling_frequency());
simplelist_addline("Buffers filled: %d", usb_audio_get_prebuffering());
simplelist_addline("%s", usb_audio_get_underflow()?"UNDERFLOW!":" ");
simplelist_addline("%s", usb_audio_get_overflow()?"OVERFLOW!":" ");
simplelist_addline("%s", usb_audio_get_alloc_failed()?"ALLOC FAILED!":" ");
if (action == ACTION_NONE)
{
action = ACTION_REDRAW;
}
return action;
}
static bool dbg_usb_audio(void)
{
struct simplelist_info info;
simplelist_info_init(&info, "USB Audio", 0, NULL);
info.scroll_all = true;
info.action_callback = dbg_usb_audio_cb;
return simplelist_show_list(&info);
}
#endif /* USB_ENABLE_AUDIO */
#endif /* HAVE_USBSTACK */
#if CONFIG_USBOTG == USBOTG_ISP1583
extern int dbg_usb_num_items(void);
@ -2847,6 +2888,9 @@ static const struct {
#if defined(ROCKBOX_HAS_LOGF) && defined(USB_ENABLE_SERIAL)
{"USB Serial driver (logf)", toggle_usb_serial },
#endif
#if defined(USB_ENABLE_AUDIO)
{"USB-DAC", dbg_usb_audio},
#endif
#endif /* HAVE_USBSTACK */
#ifdef CPU_BOOST_LOGGING
{"Show cpu_boost log",cpu_boost_log},

View file

@ -10370,6 +10370,62 @@
usb_hid: "Mouse"
</voice>
</phrase>
<phrase>
id: LANG_USB_DAC
desc: in settings_menu
user: core
<source>
*: "USB-DAC"
</source>
<dest>
*: "USB-DAC"
</dest>
<voice>
*: "USB-DAC"
</voice>
</phrase>
<phrase>
id: LANG_WHILE_USB_CHARGE_ONLY
desc: in settings_menu
user: core
<source>
*: "While In USB Charge-Only Mode"
</source>
<dest>
*: "While In USB Charge-Only Mode"
</dest>
<voice>
*: "While In USB Charge-Only Mode"
</voice>
</phrase>
<phrase>
id: LANG_WHILE_MASS_STORAGE_USB_ONLY
desc: in settings_menu
user: core
<source>
*: "While In USB Mass-Storage Mode"
</source>
<dest>
*: "While In USB Mass-Storage Mode"
</dest>
<voice>
*: "While In USB Mass-Storage Mode"
</voice>
</phrase>
<phrase>
id: LANG_USB_DAC_ACTIVE
desc: for splash
user: core
<source>
*: "USB-DAC Active"
</source>
<dest>
*: "USB-DAC Active"
</dest>
<voice>
*: "USB-DAC Active"
</voice>
</phrase>
<phrase>
id: LANG_SCROLLBAR_WIDTH
desc: in Settings -> General -> Display -> Status-/Scrollbar

View file

@ -358,6 +358,9 @@ MENUITEM_SETTING(lineout_onoff, &global_settings.lineout_active, NULL);
MENUITEM_SETTING(usb_hid, &global_settings.usb_hid, NULL);
MENUITEM_SETTING(usb_keypad_mode, &global_settings.usb_keypad_mode, NULL);
#endif
#ifdef USB_ENABLE_AUDIO
MENUITEM_SETTING(usb_audio, &global_settings.usb_audio, NULL);
#endif
#if defined(USB_ENABLE_STORAGE) && defined(HAVE_MULTIDRIVE)
MENUITEM_SETTING(usb_skip_first_drive, &global_settings.usb_skip_first_drive, NULL);
#endif
@ -454,6 +457,9 @@ MAKE_MENU(system_menu, ID2P(LANG_SYSTEM),
&usb_hid,
&usb_keypad_mode,
#endif
#ifdef USB_ENABLE_AUDIO
&usb_audio,
#endif
#if defined(USB_ENABLE_STORAGE) && defined(HAVE_MULTIDRIVE)
&usb_skip_first_drive,
#endif

View file

@ -823,6 +823,10 @@ struct user_settings
int usb_keypad_mode;
#endif
#ifdef USB_ENABLE_AUDIO
int usb_audio;
#endif
#if defined(USB_ENABLE_STORAGE) && defined(HAVE_MULTIDRIVE)
bool usb_skip_first_drive;
#endif

View file

@ -2315,6 +2315,11 @@ const struct settings_list settings[] = {
), /* CHOICE_SETTING( usb_keypad_mode ) */
#endif
#ifdef USB_ENABLE_AUDIO
CHOICE_SETTING(0, usb_audio, LANG_USB_DAC, 0, "usb-dac", "never,always,while_charge_only,while_mass_storage", usb_set_audio, 4,
ID2P(LANG_NEVER), ID2P(LANG_ALWAYS), ID2P(LANG_WHILE_USB_CHARGE_ONLY), ID2P(LANG_WHILE_MASS_STORAGE_USB_ONLY)),
#endif
#if defined(USB_ENABLE_STORAGE) && defined(HAVE_MULTIDRIVE)
OFFON_SETTING(0, usb_skip_first_drive, LANG_USB_SKIP_FIRST_DRIVE, false, "usb skip first drive", usb_set_skip_first_drive),
#endif

View file

@ -935,6 +935,9 @@ usbstack/usb_storage.c
#ifdef USB_ENABLE_SERIAL
usbstack/usb_serial.c
#endif
#ifdef USB_ENABLE_AUDIO
usbstack/usb_audio.c
#endif
#ifdef USB_ENABLE_CHARGING_ONLY
usbstack/usb_charging_only.c
#endif

View file

@ -231,7 +231,7 @@ static void usb_dw_set_stall(int epnum, enum usb_dw_epdir epdir, int stall)
else
{
DWC_EPCTL(epnum, epdir) &= ~STALL;
DWC_EPCTL(epnum, epdir) |= SD0PID;
DWC_EPCTL(epnum, epdir) |= SETD0PIDEF;
}
}
@ -668,7 +668,7 @@ static void usb_dw_unconfigure_ep(int epnum, enum usb_dw_epdir epdir)
static int usb_dw_configure_ep(int epnum,
enum usb_dw_epdir epdir, int type, int maxpktsize)
{
uint32_t epctl = SD0PID|EPTYP(type)|USBAEP|maxpktsize;
uint32_t epctl = SETD0PIDEF|EPTYP(type)|USBAEP|maxpktsize;
if (epdir == USB_DW_EPDIR_IN)
{
@ -1259,7 +1259,7 @@ static void usb_dw_irq(void)
* FIFO and raises StatusRecvd | XferCompl.
*
* We do not need or want this -- we've already handled
* the data phase by this point -- but EP0 is stoppped
* the data phase by this point -- but EP0 is stopped
* as a side effect of XferCompl, so we need to restart
* it to keep receiving packets. */
usb_dw_ep0_recv();
@ -1528,7 +1528,7 @@ void usb_drv_cancel_all_transfers()
{
//usb_dw_flush_endpoint(ep, dir);
usb_dw_abort_endpoint(ep, dir);
DWC_EPCTL(ep, dir) |= SD0PID;
DWC_EPCTL(ep, dir) |= SETD0PIDEF;
}
usb_dw_target_enable_irq();
}
@ -1606,8 +1606,15 @@ int usb_drv_request_endpoint(int type, int dir)
struct usb_dw_ep* dw_ep = usb_dw_get_ep(ep, epdir);
if (!dw_ep->active)
{
int maxpktsize = 64;
if (type == EPTYP_ISOCHRONOUS){
maxpktsize = 1023;
} else {
maxpktsize = usb_drv_port_speed() ? 512 : 64;
}
if (usb_dw_configure_ep(ep, epdir, type,
usb_drv_port_speed() ? 512 : 64) >= 0)
maxpktsize) >= 0)
{
dw_ep->active = true;
request_ep = ep | dir;
@ -1679,3 +1686,11 @@ void usb_drv_control_response(enum usb_control_response resp,
usb_dw_control_response(resp, data, length);
usb_dw_target_enable_irq();
}
int usb_drv_get_frame_number()
{
// SOFFN is 14 bits, the least significant 3 appear to be some sort of microframe count.
// The USB spec says a frame number is 11 bits. This way we get 1 frame per millisecond,
// just like we're supposed to!
return (DWC_DSTS >> 11) & 0x7FF;
}

View file

@ -1347,6 +1347,7 @@ Lyre prototype 1 */
#elif (CONFIG_USBOTG == USBOTG_DESIGNWARE)
#define USB_HAS_BULK
#define USB_HAS_INTERRUPT
#define USB_HAS_ISOCHRONOUS
#elif (CONFIG_USBOTG == USBOTG_ARC) || \
(CONFIG_USBOTG == USBOTG_JZ4740) || \
(CONFIG_USBOTG == USBOTG_JZ4760) || \
@ -1356,6 +1357,9 @@ Lyre prototype 1 */
(CONFIG_USBOTG == USBOTG_TNETV105)
#define USB_HAS_BULK
#define USB_HAS_INTERRUPT
#if (CONFIG_USBOTG == USBOTG_ARC)
#define USB_HAS_ISOCHRONOUS
#endif
#define USB_LEGACY_CONTROL_API
#elif defined(CPU_TCC780X)
#define USB_HAS_BULK
@ -1366,11 +1370,6 @@ Lyre prototype 1 */
//#define USB_HAS_INTERRUPT -- seems to be broken
#endif /* CONFIG_USBOTG */
#if (CONFIG_USBOTG == USBOTG_ARC) || \
(CONFIG_USBOTG == USBOTG_AS3525)
#define USB_HAS_ISOCHRONOUS
#endif
/* define the class drivers to enable */
#ifdef BOOTLOADER
@ -1398,6 +1397,10 @@ Lyre prototype 1 */
#endif
#endif
#ifdef USB_HAS_ISOCHRONOUS
#define USB_ENABLE_AUDIO
#endif
#endif /* BOOTLOADER */
#endif /* HAVE_USBSTACK */

View file

@ -72,6 +72,9 @@
enum pcm_mixer_channel
{
PCM_MIXER_CHAN_PLAYBACK = 0,
#ifdef USB_ENABLE_AUDIO
PCM_MIXER_CHAN_USBAUDIO,
#endif
PCM_MIXER_CHAN_VOICE,
#ifndef HAVE_HARDWARE_BEEP
PCM_MIXER_CHAN_BEEP,

View file

@ -200,7 +200,8 @@
#define DWC_DOEPCTL(x) (*((REG32_PTR_T)(OTGBASE + 0xb00 + 0x20*(x))))
#define EPENA (1<<31)
#define EPDIS (1<<30)
#define SD0PID (1<<28)
#define SETD1PIDOF (1<<29)
#define SETD0PIDEF (1<<28)
#define SNAK (1<<27)
#define CNAK (1<<26)
#define DTXFNUM(x) ((x)<<22)

View file

@ -167,6 +167,9 @@ enum {
#endif
#ifdef USB_ENABLE_HID
USB_DRIVER_HID,
#endif
#ifdef USB_ENABLE_AUDIO
USB_DRIVER_AUDIO,
#endif
USB_NUM_DRIVERS
};
@ -256,6 +259,10 @@ void usb_firewire_connect_event(void);
void usb_set_hid(bool enable);
#endif
#ifdef USB_ENABLE_AUDIO
void usb_set_audio(int value);
#endif
#if defined(USB_ENABLE_STORAGE) && defined(HAVE_MULTIDRIVE)
/* when the target has several drives, decide whether mass storage should
* skip the first drive. This is useful when the second drive is a SD card

View file

@ -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_MASK 0x0c /* in bmAttributes */
#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

View file

@ -74,6 +74,7 @@ void usb_drv_stall(int endpoint, bool stall,bool in);
bool usb_drv_stalled(int endpoint,bool in);
int usb_drv_send(int endpoint, void* ptr, int length);
int usb_drv_send_nonblocking(int endpoint, void* ptr, int length);
int usb_drv_recv_blocking(int endpoint, void* ptr, int length);
int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length);
void usb_drv_control_response(enum usb_control_response resp,
void* data, int length);
@ -86,6 +87,14 @@ void usb_drv_set_test_mode(int mode);
bool usb_drv_connected(void);
int usb_drv_request_endpoint(int type, int dir);
void usb_drv_release_endpoint(int ep);
#ifdef USB_HAS_ISOCHRONOUS
/* returns the last received frame number (the 11-bit number contained in the last SOF):
* - full-speed: the host sends one SOF every 1ms (so 1000 SOF/s)
* - high-speed: the hosts sends one SOF every 125us but each consecutive 8 SOF have the same frame
* number
* thus in all mode, the frame number can be interpreted as the current millisecond *in USB time*. */
int usb_drv_get_frame_number(void);
#endif
/* USB_STRING_INITIALIZER(u"Example String") */
#define USB_STRING_INITIALIZER(S) { \

View file

@ -599,11 +599,22 @@ int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length)
return prime_transfer(EP_NUM(endpoint), ptr, length, false, false);
}
int usb_drv_recv_blocking(int endpoint, void* ptr, int length)
{
return prime_transfer(EP_NUM(endpoint), ptr, length, false, true);
}
int usb_drv_port_speed(void)
{
return (REG_PORTSC1 & 0x08000000) ? 1 : 0;
}
int usb_drv_get_frame_number(void)
{
/* the lower 3 bits store the microframe (in HS mode), discard them */
return (REG_FRINDEX & USB_FRINDEX_MASKS) >> 3;
}
bool usb_drv_connected(void)
{
return (REG_PORTSC1 &
@ -970,10 +981,8 @@ static void init_control_queue_heads(void)
/* manual: 32.14.4.1 Queue Head Initialization */
static void init_queue_heads(void)
{
/* FIXME the packetsize for isochronous transfers is 1023 : 1024 but
* the current code only support one type of packet size so we restrict
* isochronous packet size for now also */
int packetsize = (usb_drv_port_speed() ? 512 : 64);
int isopacketsize = (usb_drv_port_speed() ? 1024 : 1024);
int i;
/* TODO: this should take ep_allocation into account */
@ -982,7 +991,7 @@ static void init_queue_heads(void)
/* OUT */
if(endpoints[i].type[DIR_OUT] == USB_ENDPOINT_XFER_ISOC)
/* FIXME: we can adjust the number of packets per frame, currently use one */
qh_array[i*2].max_pkt_length = packetsize << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL | 1 << QH_MULT_POS;
qh_array[i*2].max_pkt_length = isopacketsize << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL | 1 << QH_MULT_POS;
else
qh_array[i*2].max_pkt_length = packetsize << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL;
@ -991,7 +1000,7 @@ static void init_queue_heads(void)
/* IN */
if(endpoints[i].type[DIR_IN] == USB_ENDPOINT_XFER_ISOC)
/* FIXME: we can adjust the number of packets per frame, currently use one */
qh_array[i*2+1].max_pkt_length = packetsize << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL | 1 << QH_MULT_POS;
qh_array[i*2+1].max_pkt_length = isopacketsize << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL | 1 << QH_MULT_POS;
else
qh_array[i*2+1].max_pkt_length = packetsize << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL;

View file

@ -97,6 +97,9 @@ static bool exclusive_storage_access = false;
#ifdef USB_ENABLE_HID
static bool usb_hid = true;
#endif
#ifdef USB_ENABLE_AUDIO
static int usb_audio = 0;
#endif
#ifdef USB_FULL_INIT
static bool usb_host_present = false;
@ -194,6 +197,10 @@ static inline void usb_handle_hotswap(long id)
static inline bool usb_configure_drivers(int for_state)
{
#ifdef USB_ENABLE_AUDIO
// FIXME: doesn't seem to get set when loaded at boot...
usb_audio = global_settings.usb_audio;
#endif
switch(for_state)
{
case USB_POWERED:
@ -207,6 +214,9 @@ static inline bool usb_configure_drivers(int for_state)
usb_core_enable_driver(USB_DRIVER_HID, true);
#endif /* USB_ENABLE_CHARGING_ONLY */
#endif /* USB_ENABLE_HID */
#ifdef USB_ENABLE_AUDIO
usb_core_enable_driver(USB_DRIVER_AUDIO, (usb_audio == 1) || (usb_audio == 2)); // while "always" or "only in charge-only mode"
#endif /* USB_ENABLE_AUDIO */
#ifdef USB_ENABLE_CHARGING_ONLY
usb_core_enable_driver(USB_DRIVER_CHARGING_ONLY, true);
@ -224,6 +234,9 @@ static inline bool usb_configure_drivers(int for_state)
#ifdef USB_ENABLE_HID
usb_core_enable_driver(USB_DRIVER_HID, usb_hid);
#endif
#ifdef USB_ENABLE_AUDIO
usb_core_enable_driver(USB_DRIVER_AUDIO, (usb_audio == 1) || (usb_audio == 3)); // while "always" or "only in mass-storage mode"
#endif /* USB_ENABLE_AUDIO */
#ifdef USB_ENABLE_CHARGING_ONLY
usb_core_enable_driver(USB_DRIVER_CHARGING_ONLY, false);
#endif
@ -845,6 +858,13 @@ void usb_set_hid(bool enable)
}
#endif /* USB_ENABLE_HID */
#ifdef USB_ENABLE_AUDIO
void usb_set_audio(int value)
{
usb_audio = value;
}
#endif /* USB_ENABLE_AUDIO */
#ifdef HAVE_USB_POWER
bool usb_powered_only(void)
{

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,223 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id: $
*
* Copyright (C) 2014 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"
/*
* usb_audio_request_endpoints():
*
* Calls usb_core_request_endpoint() to request one IN and one OUT
* isochronous endpoint.
*
* Called by allocate_interfaces_and_endpoints().
*
* Returns -1 if either request fails, returns 0 if success.
*
* Also requests buffer allocations. If allocation fails,
* returns -1 so that the driver will be disabled by the USB core.
*/
int usb_audio_request_endpoints(struct usb_class_driver *);
/*
* usb_audio_set_first_interface():
*
* Required function for the class driver.
*
* Called by allocate_interfaces_and_endpoints() to
* tell the class driver what its first interface number is.
* Returns the number of the interface available for the next
* class driver to use.
*
* We need 2 interfaces, AudioControl and AudioStreaming.
* Return interface+2.
*/
int usb_audio_set_first_interface(int interface);
/*
* usb_audio_get_config_descriptor():
*
* Required function for the class driver.
*
* Called by request_handler_device_get_descriptor(), which expects
* this function to fill *dest with the configuration descriptor for this
* class driver.
*
* Return the size of this descriptor in bytes.
*/
int usb_audio_get_config_descriptor(unsigned char *dest,int max_packet_size);
/*
* usb_audio_init_connection():
*
* Called by usb_core_do_set_config() when the
* connection is ready to be used. Currently just sets
* the audio sample rate to default.
*/
void usb_audio_init_connection(void);
/*
* usb_audio_init():
*
* Initialize the driver. Called by usb_core_init().
* Currently initializes the sampling frequency values available
* to the AudioStreaming interface.
*/
void usb_audio_init(void);
/*
* usb_audio_disconnect():
*
* Called by usb_core_exit() AND usb_core_do_set_config().
*
* Indicates to the Class driver that the connection is no
* longer active. Currently just calls usb_audio_stop_playback().
*/
void usb_audio_disconnect(void);
/*
* usb_audio_get_playing():
*
* Returns playing/not playing status of usbaudio.
*/
bool usb_audio_get_playing(void);
/*
* usb_audio_get_alloc_failed():
*
* Return whether the buffer allocation succeeded (0)
* or failed (1).
*/
bool usb_audio_get_alloc_failed(void);
/*
* usb_audio_transfer_complete():
*
* Dummy function.
*
* The fast_transfer_complete() function needs to be used instead.
*/
void usb_audio_transfer_complete(int ep,int dir, int status, int length);
/*
* usb_audio_fast_transfer_complete():
*
* Called by usb_core_transfer_complete().
* The normal transfer complete handler system is too slow to deal with
* ISO data at the rate required, so this is required.
*
* Return true if the transfer is handled, false otherwise.
*/
bool usb_audio_fast_transfer_complete(int ep,int dir, int status, int length);
/*
* usb_audio_control_request():
*
* Called by control_request_handler_drivers().
* Pass control requests down to the appropriate functions.
*
* Return true if this driver handles the request, false otherwise.
*/
bool usb_audio_control_request(struct usb_ctrlrequest* req, void* reqdata, unsigned char* dest);
/*
* usb_audio_set_interface():
*
* Called by control_request_handler_drivers().
* Deal with changing the interface between control and streaming.
*
* Return 0 for success, -1 otherwise.
*/
int usb_audio_set_interface(int intf, int alt);
/*
* usb_audio_get_interface():
*
* Called by control_request_handler_drivers().
* Get the alternate of the given interface.
*
* Return the alternate of the given interface, -1 if unknown.
*/
int usb_audio_get_interface(int intf);
/*
* usb_audio_get_playback_sampling_frequency():
*
* Return the sample rate currently set.
*/
unsigned long usb_audio_get_playback_sampling_frequency(void);
/*
* usb_audio_get_main_intf():
*
* Return the main usb interface
*/
int usb_audio_get_main_intf(void);
/*
* usb_audio_get_alt_intf():
*
* Return the alternate usb interface
*/
int usb_audio_get_alt_intf(void);
/*
* usb_audio_get_out_ep():
*
* Return the out (to device) endpoint
*/
unsigned int usb_audio_get_out_ep(void);
/*
* usb_audio_get_in_ep():
*
* Return the in (to host) endpoint
*/
unsigned int usb_audio_get_in_ep(void);
/*
* usb_audio_get_prebuffering():
*
* Return number of buffers filled ahead of playback
*/
int usb_audio_get_prebuffering(void);
/*
* usb_audio_get_underflow():
*
* Return whether playback is in "underflow" state
*/
bool usb_audio_get_underflow(void);
/*
* usb_audio_get_overflow():
*
* Return whether usb is in "overflow" state
*/
bool usb_audio_get_overflow(void);
/*
* usb_audio_get_cur_volume():
*
* Return current audio volume in db
*/
int usb_audio_get_cur_volume(void);
#endif

View file

@ -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 */

View file

@ -74,7 +74,16 @@ struct usb_class_driver {
/* Tells the driver that a usb transfer has been completed. Note that "dir"
is relative to the host
Optional function */
void (*transfer_complete)(int ep,int dir, int status, int length);
void (*transfer_complete)(int ep, int dir, int status, int length);
/* Similar to transfer_complete but called directly instead of going through
* the usb queue. Since it might be called in an interrupt context,
* processing should be kept to a minimum. This is mainly intended for
* isochronous transfers.
* The function must return true if it handled the completion, and false
* otherwise so that it is dispatched to the normal handler
* Optional function */
bool (*fast_transfer_complete)(int ep, int dir, int status, int length);
/* Tells the driver that a control request has come in. If the driver is
able to handle it, it should ack the request, and return true. Otherwise
@ -88,6 +97,16 @@ 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);
};
#define PACK_DATA(dest, data) pack_data(dest, &(data), sizeof(data))

View file

@ -48,6 +48,11 @@
#include "usb_hid.h"
#endif
#ifdef USB_ENABLE_AUDIO
#include "usb_audio.h"
#include "usb_audio_def.h" // DEBUG
#endif
/* TODO: Move target-specific stuff somewhere else (serial number reading) */
#if defined(IPOD_ARCH) && defined(CPU_PP)
@ -166,12 +171,14 @@ static int usb_no_host_callback(struct timeout *tmo)
static int usb_core_num_interfaces;
typedef void (*completion_handler_t)(int ep, int dir, int status, int length);
typedef bool (*fast_completion_handler_t)(int ep, int dir, int status, int length);
typedef bool (*control_handler_t)(struct usb_ctrlrequest* req, void* reqdata,
unsigned char* dest);
static struct
{
completion_handler_t completion_handler[2];
fast_completion_handler_t fast_completion_handler[2];
control_handler_t control_handler[2];
struct usb_transfer_completion_event_data completion_event[2];
} ep_data[USB_NUM_ENDPOINTS];
@ -254,6 +261,28 @@ 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,
.fast_transfer_complete = usb_audio_fast_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,
},
#endif
};
#ifdef USB_LEGACY_CONTROL_API
@ -542,6 +571,7 @@ int usb_core_request_endpoint(int type, int dir, struct usb_class_driver* drv)
ep = EP_NUM(ret);
ep_data[ep].completion_handler[dir] = drv->transfer_complete;
ep_data[ep].fast_completion_handler[dir] = drv->fast_transfer_complete;
ep_data[ep].control_handler[dir] = drv->control_request;
return ret;
@ -598,16 +628,46 @@ static void control_request_handler_drivers(struct usb_ctrlrequest* req, void* r
if(drivers[i].enabled &&
drivers[i].control_request &&
drivers[i].first_interface <= interface &&
drivers[i].last_interface > interface)
{
handled = drivers[i].control_request(req, reqdata, response_data);
if(handled)
drivers[i].last_interface > 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) {
logf("usb_core: SET INTERFACE 0x%x 0x%x", req->wValue, req->wIndex);
if(drivers[i].set_interface &&
drivers[i].set_interface(req->wIndex, req->wValue) >= 0) {
usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
handled = true;
}
break;
}
else if(req->bRequest == USB_REQ_GET_INTERFACE) {
int alt = -1;
logf("usb_core: GET INTERFACE 0x%x", req->wIndex);
if(drivers[i].get_interface)
alt = drivers[i].get_interface(req->wIndex);
if(alt >= 0 && alt < 255) {
response_data[0] = alt;
usb_drv_control_response(USB_CONTROL_ACK, response_data, 1);
handled = true;
}
break;
}
/* fallback */
}
handled = drivers[i].control_request(req, reqdata, response_data);
break; /* no other driver can handle it because it's interface specific */
}
}
if(!handled) {
/* nope. flag error */
logf("bad req:desc %d:%d", req->bRequest, req->wValue >> 8);
logf("bad req 0x%x:0x%x:0x%x:0x%x:0x%x", req->bRequestType,req->bRequest,
req->wValue, req->wIndex, req->wLength);
usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
}
}
@ -804,13 +864,9 @@ static void request_handler_interface_standard(struct usb_ctrlrequest* req, void
{
case USB_REQ_SET_INTERFACE:
logf("usb_core: SET_INTERFACE");
usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
break;
case USB_REQ_GET_INTERFACE:
logf("usb_core: GET_INTERFACE");
response_data[0] = 0;
usb_drv_control_response(USB_CONTROL_ACK, response_data, 1);
control_request_handler_drivers(req, reqdata);
break;
break;
case USB_REQ_GET_STATUS:
response_data[0] = 0;
@ -860,7 +916,8 @@ static void request_handler_endpoint_drivers(struct usb_ctrlrequest* req, void*
if(!handled) {
/* nope. flag error */
logf("usb bad req %d", req->bRequest);
logf("bad req 0x%x:0x%x:0x%x:0x%x:0x%x", req->bRequestType,req->bRequest,
req->wValue, req->wIndex, req->wLength);
usb_drv_control_response(USB_CONTROL_STALL, NULL, 0);
}
}
@ -970,6 +1027,10 @@ void usb_core_transfer_complete(int endpoint, int dir, int status, int length)
{
struct usb_transfer_completion_event_data* completion_event =
&ep_data[endpoint].completion_event[EP_DIR(dir)];
/* Fast notification */
fast_completion_handler_t handler = ep_data[endpoint].fast_completion_handler[EP_DIR(dir)];
if(handler != NULL && handler(endpoint, dir, status, length))
return; /* do not dispatch to the queue if handled */
void* data0 = NULL;
void* data1 = NULL;