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 #ifdef HAVE_USBSTACK
#include "usb_core.h" #include "usb_core.h"
#ifdef USB_ENABLE_AUDIO
#include "../usbstack/usb_audio.h"
#endif
#endif #endif
#include "talk.h" #include "talk.h"
@ -2509,18 +2512,56 @@ static bool dbg_talk(void)
} }
#ifdef HAVE_USBSTACK #ifdef HAVE_USBSTACK
#if defined(ROCKBOX_HAS_LOGF) && defined(USB_ENABLE_SERIAL) #if (defined(ROCKBOX_HAS_LOGF) && defined(USB_ENABLE_SERIAL))
static bool toggle_usb_serial(void) 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); usb_core_enable_driver(driver,enabled);
splashf(HZ, "USB Serial %sabled", enabled ? "en" : "dis"); splashf(HZ, "%s %s", msg, enabled ? "enabled" : "disabled");
return false; 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
#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 #if CONFIG_USBOTG == USBOTG_ISP1583
extern int dbg_usb_num_items(void); extern int dbg_usb_num_items(void);
@ -2847,6 +2888,9 @@ static const struct {
#if defined(ROCKBOX_HAS_LOGF) && defined(USB_ENABLE_SERIAL) #if defined(ROCKBOX_HAS_LOGF) && defined(USB_ENABLE_SERIAL)
{"USB Serial driver (logf)", toggle_usb_serial }, {"USB Serial driver (logf)", toggle_usb_serial },
#endif #endif
#if defined(USB_ENABLE_AUDIO)
{"USB-DAC", dbg_usb_audio},
#endif
#endif /* HAVE_USBSTACK */ #endif /* HAVE_USBSTACK */
#ifdef CPU_BOOST_LOGGING #ifdef CPU_BOOST_LOGGING
{"Show cpu_boost log",cpu_boost_log}, {"Show cpu_boost log",cpu_boost_log},

View file

@ -10370,6 +10370,62 @@
usb_hid: "Mouse" usb_hid: "Mouse"
</voice> </voice>
</phrase> </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> <phrase>
id: LANG_SCROLLBAR_WIDTH id: LANG_SCROLLBAR_WIDTH
desc: in Settings -> General -> Display -> Status-/Scrollbar 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_hid, &global_settings.usb_hid, NULL);
MENUITEM_SETTING(usb_keypad_mode, &global_settings.usb_keypad_mode, NULL); MENUITEM_SETTING(usb_keypad_mode, &global_settings.usb_keypad_mode, NULL);
#endif #endif
#ifdef USB_ENABLE_AUDIO
MENUITEM_SETTING(usb_audio, &global_settings.usb_audio, NULL);
#endif
#if defined(USB_ENABLE_STORAGE) && defined(HAVE_MULTIDRIVE) #if defined(USB_ENABLE_STORAGE) && defined(HAVE_MULTIDRIVE)
MENUITEM_SETTING(usb_skip_first_drive, &global_settings.usb_skip_first_drive, NULL); MENUITEM_SETTING(usb_skip_first_drive, &global_settings.usb_skip_first_drive, NULL);
#endif #endif
@ -454,6 +457,9 @@ MAKE_MENU(system_menu, ID2P(LANG_SYSTEM),
&usb_hid, &usb_hid,
&usb_keypad_mode, &usb_keypad_mode,
#endif #endif
#ifdef USB_ENABLE_AUDIO
&usb_audio,
#endif
#if defined(USB_ENABLE_STORAGE) && defined(HAVE_MULTIDRIVE) #if defined(USB_ENABLE_STORAGE) && defined(HAVE_MULTIDRIVE)
&usb_skip_first_drive, &usb_skip_first_drive,
#endif #endif

View file

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

View file

@ -2315,6 +2315,11 @@ const struct settings_list settings[] = {
), /* CHOICE_SETTING( usb_keypad_mode ) */ ), /* CHOICE_SETTING( usb_keypad_mode ) */
#endif #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) #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), OFFON_SETTING(0, usb_skip_first_drive, LANG_USB_SKIP_FIRST_DRIVE, false, "usb skip first drive", usb_set_skip_first_drive),
#endif #endif

View file

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

View file

@ -231,7 +231,7 @@ static void usb_dw_set_stall(int epnum, enum usb_dw_epdir epdir, int stall)
else else
{ {
DWC_EPCTL(epnum, epdir) &= ~STALL; 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, static int usb_dw_configure_ep(int epnum,
enum usb_dw_epdir epdir, int type, int maxpktsize) 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) if (epdir == USB_DW_EPDIR_IN)
{ {
@ -1259,7 +1259,7 @@ static void usb_dw_irq(void)
* FIFO and raises StatusRecvd | XferCompl. * FIFO and raises StatusRecvd | XferCompl.
* *
* We do not need or want this -- we've already handled * 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 * as a side effect of XferCompl, so we need to restart
* it to keep receiving packets. */ * it to keep receiving packets. */
usb_dw_ep0_recv(); usb_dw_ep0_recv();
@ -1528,7 +1528,7 @@ void usb_drv_cancel_all_transfers()
{ {
//usb_dw_flush_endpoint(ep, dir); //usb_dw_flush_endpoint(ep, dir);
usb_dw_abort_endpoint(ep, dir); usb_dw_abort_endpoint(ep, dir);
DWC_EPCTL(ep, dir) |= SD0PID; DWC_EPCTL(ep, dir) |= SETD0PIDEF;
} }
usb_dw_target_enable_irq(); 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); struct usb_dw_ep* dw_ep = usb_dw_get_ep(ep, epdir);
if (!dw_ep->active) 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, if (usb_dw_configure_ep(ep, epdir, type,
usb_drv_port_speed() ? 512 : 64) >= 0) maxpktsize) >= 0)
{ {
dw_ep->active = true; dw_ep->active = true;
request_ep = ep | dir; 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_control_response(resp, data, length);
usb_dw_target_enable_irq(); 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) #elif (CONFIG_USBOTG == USBOTG_DESIGNWARE)
#define USB_HAS_BULK #define USB_HAS_BULK
#define USB_HAS_INTERRUPT #define USB_HAS_INTERRUPT
#define USB_HAS_ISOCHRONOUS
#elif (CONFIG_USBOTG == USBOTG_ARC) || \ #elif (CONFIG_USBOTG == USBOTG_ARC) || \
(CONFIG_USBOTG == USBOTG_JZ4740) || \ (CONFIG_USBOTG == USBOTG_JZ4740) || \
(CONFIG_USBOTG == USBOTG_JZ4760) || \ (CONFIG_USBOTG == USBOTG_JZ4760) || \
@ -1356,6 +1357,9 @@ Lyre prototype 1 */
(CONFIG_USBOTG == USBOTG_TNETV105) (CONFIG_USBOTG == USBOTG_TNETV105)
#define USB_HAS_BULK #define USB_HAS_BULK
#define USB_HAS_INTERRUPT #define USB_HAS_INTERRUPT
#if (CONFIG_USBOTG == USBOTG_ARC)
#define USB_HAS_ISOCHRONOUS
#endif
#define USB_LEGACY_CONTROL_API #define USB_LEGACY_CONTROL_API
#elif defined(CPU_TCC780X) #elif defined(CPU_TCC780X)
#define USB_HAS_BULK #define USB_HAS_BULK
@ -1366,11 +1370,6 @@ Lyre prototype 1 */
//#define USB_HAS_INTERRUPT -- seems to be broken //#define USB_HAS_INTERRUPT -- seems to be broken
#endif /* CONFIG_USBOTG */ #endif /* CONFIG_USBOTG */
#if (CONFIG_USBOTG == USBOTG_ARC) || \
(CONFIG_USBOTG == USBOTG_AS3525)
#define USB_HAS_ISOCHRONOUS
#endif
/* define the class drivers to enable */ /* define the class drivers to enable */
#ifdef BOOTLOADER #ifdef BOOTLOADER
@ -1398,6 +1397,10 @@ Lyre prototype 1 */
#endif #endif
#endif #endif
#ifdef USB_HAS_ISOCHRONOUS
#define USB_ENABLE_AUDIO
#endif
#endif /* BOOTLOADER */ #endif /* BOOTLOADER */
#endif /* HAVE_USBSTACK */ #endif /* HAVE_USBSTACK */

View file

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

View file

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

View file

@ -167,6 +167,9 @@ enum {
#endif #endif
#ifdef USB_ENABLE_HID #ifdef USB_ENABLE_HID
USB_DRIVER_HID, USB_DRIVER_HID,
#endif
#ifdef USB_ENABLE_AUDIO
USB_DRIVER_AUDIO,
#endif #endif
USB_NUM_DRIVERS USB_NUM_DRIVERS
}; };
@ -256,6 +259,10 @@ void usb_firewire_connect_event(void);
void usb_set_hid(bool enable); void usb_set_hid(bool enable);
#endif #endif
#ifdef USB_ENABLE_AUDIO
void usb_set_audio(int value);
#endif
#if defined(USB_ENABLE_STORAGE) && defined(HAVE_MULTIDRIVE) #if defined(USB_ENABLE_STORAGE) && defined(HAVE_MULTIDRIVE)
/* when the target has several drives, decide whether mass storage should /* 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 * 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_NUMBER_MASK 0x0f /* in bEndpointAddress */
#define USB_ENDPOINT_DIR_MASK 0x80 #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_XFERTYPE_MASK 0x03 /* in bmAttributes */
#define USB_ENDPOINT_XFER_CONTROL 0 #define USB_ENDPOINT_XFER_CONTROL 0
#define USB_ENDPOINT_XFER_ISOC 1 #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); bool usb_drv_stalled(int endpoint,bool in);
int usb_drv_send(int endpoint, void* ptr, int length); int usb_drv_send(int endpoint, void* ptr, int length);
int usb_drv_send_nonblocking(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); int usb_drv_recv_nonblocking(int endpoint, void* ptr, int length);
void usb_drv_control_response(enum usb_control_response resp, void usb_drv_control_response(enum usb_control_response resp,
void* data, int length); void* data, int length);
@ -86,6 +87,14 @@ void usb_drv_set_test_mode(int mode);
bool usb_drv_connected(void); bool usb_drv_connected(void);
int usb_drv_request_endpoint(int type, int dir); int usb_drv_request_endpoint(int type, int dir);
void usb_drv_release_endpoint(int ep); 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") */ /* USB_STRING_INITIALIZER(u"Example String") */
#define USB_STRING_INITIALIZER(S) { \ #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); 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) int usb_drv_port_speed(void)
{ {
return (REG_PORTSC1 & 0x08000000) ? 1 : 0; 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) bool usb_drv_connected(void)
{ {
return (REG_PORTSC1 & return (REG_PORTSC1 &
@ -970,10 +981,8 @@ static void init_control_queue_heads(void)
/* manual: 32.14.4.1 Queue Head Initialization */ /* manual: 32.14.4.1 Queue Head Initialization */
static void init_queue_heads(void) 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 packetsize = (usb_drv_port_speed() ? 512 : 64);
int isopacketsize = (usb_drv_port_speed() ? 1024 : 1024);
int i; int i;
/* TODO: this should take ep_allocation into account */ /* TODO: this should take ep_allocation into account */
@ -982,7 +991,7 @@ static void init_queue_heads(void)
/* OUT */ /* OUT */
if(endpoints[i].type[DIR_OUT] == USB_ENDPOINT_XFER_ISOC) if(endpoints[i].type[DIR_OUT] == USB_ENDPOINT_XFER_ISOC)
/* FIXME: we can adjust the number of packets per frame, currently use one */ /* 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 else
qh_array[i*2].max_pkt_length = packetsize << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL; 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 */ /* IN */
if(endpoints[i].type[DIR_IN] == USB_ENDPOINT_XFER_ISOC) if(endpoints[i].type[DIR_IN] == USB_ENDPOINT_XFER_ISOC)
/* FIXME: we can adjust the number of packets per frame, currently use one */ /* 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 else
qh_array[i*2+1].max_pkt_length = packetsize << QH_MAX_PKT_LEN_POS | QH_ZLT_SEL; 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 #ifdef USB_ENABLE_HID
static bool usb_hid = true; static bool usb_hid = true;
#endif #endif
#ifdef USB_ENABLE_AUDIO
static int usb_audio = 0;
#endif
#ifdef USB_FULL_INIT #ifdef USB_FULL_INIT
static bool usb_host_present = false; 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) 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) switch(for_state)
{ {
case USB_POWERED: case USB_POWERED:
@ -207,6 +214,9 @@ static inline bool usb_configure_drivers(int for_state)
usb_core_enable_driver(USB_DRIVER_HID, true); usb_core_enable_driver(USB_DRIVER_HID, true);
#endif /* USB_ENABLE_CHARGING_ONLY */ #endif /* USB_ENABLE_CHARGING_ONLY */
#endif /* USB_ENABLE_HID */ #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 #ifdef USB_ENABLE_CHARGING_ONLY
usb_core_enable_driver(USB_DRIVER_CHARGING_ONLY, true); 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 #ifdef USB_ENABLE_HID
usb_core_enable_driver(USB_DRIVER_HID, usb_hid); usb_core_enable_driver(USB_DRIVER_HID, usb_hid);
#endif #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 #ifdef USB_ENABLE_CHARGING_ONLY
usb_core_enable_driver(USB_DRIVER_CHARGING_ONLY, false); usb_core_enable_driver(USB_DRIVER_CHARGING_ONLY, false);
#endif #endif
@ -845,6 +858,13 @@ void usb_set_hid(bool enable)
} }
#endif /* USB_ENABLE_HID */ #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 #ifdef HAVE_USB_POWER
bool usb_powered_only(void) 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" /* Tells the driver that a usb transfer has been completed. Note that "dir"
is relative to the host is relative to the host
Optional function */ 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 /* 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 able to handle it, it should ack the request, and return true. Otherwise
@ -88,6 +97,16 @@ struct usb_class_driver {
Optional function */ Optional function */
void (*notify_hotswap)(int volume, bool inserted); void (*notify_hotswap)(int volume, bool inserted);
#endif #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)) #define PACK_DATA(dest, data) pack_data(dest, &(data), sizeof(data))

View file

@ -48,6 +48,11 @@
#include "usb_hid.h" #include "usb_hid.h"
#endif #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) */ /* TODO: Move target-specific stuff somewhere else (serial number reading) */
#if defined(IPOD_ARCH) && defined(CPU_PP) #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; static int usb_core_num_interfaces;
typedef void (*completion_handler_t)(int ep, int dir, int status, int length); 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, typedef bool (*control_handler_t)(struct usb_ctrlrequest* req, void* reqdata,
unsigned char* dest); unsigned char* dest);
static struct static struct
{ {
completion_handler_t completion_handler[2]; completion_handler_t completion_handler[2];
fast_completion_handler_t fast_completion_handler[2];
control_handler_t control_handler[2]; control_handler_t control_handler[2];
struct usb_transfer_completion_event_data completion_event[2]; struct usb_transfer_completion_event_data completion_event[2];
} ep_data[USB_NUM_ENDPOINTS]; } ep_data[USB_NUM_ENDPOINTS];
@ -254,6 +261,28 @@ static struct usb_class_driver drivers[USB_NUM_DRIVERS] =
#endif #endif
}, },
#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 #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 = EP_NUM(ret);
ep_data[ep].completion_handler[dir] = drv->transfer_complete; 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; ep_data[ep].control_handler[dir] = drv->control_request;
return ret; return ret;
@ -598,16 +628,46 @@ static void control_request_handler_drivers(struct usb_ctrlrequest* req, void* r
if(drivers[i].enabled && if(drivers[i].enabled &&
drivers[i].control_request && drivers[i].control_request &&
drivers[i].first_interface <= interface && drivers[i].first_interface <= interface &&
drivers[i].last_interface > interface) drivers[i].last_interface > interface) {
{ /* Check for SET_INTERFACE and GET_INTERFACE */
handled = drivers[i].control_request(req, reqdata, response_data); if((req->bRequestType & USB_RECIP_MASK) == USB_RECIP_INTERFACE &&
if(handled) (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; 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) { if(!handled) {
/* nope. flag error */ /* 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); 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: case USB_REQ_SET_INTERFACE:
logf("usb_core: SET_INTERFACE"); logf("usb_core: SET_INTERFACE");
usb_drv_control_response(USB_CONTROL_ACK, NULL, 0);
break;
case USB_REQ_GET_INTERFACE: case USB_REQ_GET_INTERFACE:
logf("usb_core: GET_INTERFACE"); control_request_handler_drivers(req, reqdata);
response_data[0] = 0; break;
usb_drv_control_response(USB_CONTROL_ACK, response_data, 1);
break; break;
case USB_REQ_GET_STATUS: case USB_REQ_GET_STATUS:
response_data[0] = 0; response_data[0] = 0;
@ -860,7 +916,8 @@ static void request_handler_endpoint_drivers(struct usb_ctrlrequest* req, void*
if(!handled) { if(!handled) {
/* nope. flag error */ /* 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); 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 = struct usb_transfer_completion_event_data* completion_event =
&ep_data[endpoint].completion_event[EP_DIR(dir)]; &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* data0 = NULL;
void* data1 = NULL; void* data1 = NULL;