Skip to content
Navigation Menu
Toggle navigation
Sign in
In this repository
All GitHub Enterprise
↵
Jump to
↵
No suggested jump to results
In this repository
All GitHub Enterprise
↵
Jump to
↵
In this organization
All GitHub Enterprise
↵
Jump to
↵
In this repository
All GitHub Enterprise
↵
Jump to
↵
Sign in
Reseting focus
You signed in with another tab or window.
Reload
to refresh your session.
You signed out in another tab or window.
Reload
to refresh your session.
You switched accounts on another tab or window.
Reload
to refresh your session.
Dismiss alert
{{ message }}
mariux64
/
linux
Public
Notifications
You must be signed in to change notification settings
Fork
0
Star
0
Code
Issues
2
Pull requests
0
Actions
Projects
0
Wiki
Security
Insights
Additional navigation options
Code
Issues
Pull requests
Actions
Projects
Wiki
Security
Insights
Files
d86f43b
Documentation
LICENSES
arch
block
certs
crypto
drivers
fs
include
init
ipc
kernel
lib
mm
net
samples
scripts
security
sound
ac97
aoa
arm
atmel
core
drivers
firewire
hda
i2c
isa
mips
oss
parisc
pci
pcmcia
ppc
sh
soc
sparc
spi
synth
usb
6fire
bcd2000
caiaq
hiface
line6
misc
usx2y
Kconfig
Makefile
card.c
card.h
clock.c
clock.h
endpoint.c
endpoint.h
format.c
format.h
helper.c
helper.h
implicit.c
implicit.h
media.c
media.h
midi.c
midi.h
mixer.c
mixer.h
mixer_maps.c
mixer_quirks.c
mixer_quirks.h
mixer_s1810c.c
mixer_s1810c.h
mixer_scarlett.c
mixer_scarlett.h
mixer_scarlett_gen2.c
mixer_scarlett_gen2.h
mixer_us16x08.c
mixer_us16x08.h
pcm.c
pcm.h
power.c
power.h
proc.c
proc.h
quirks-table.h
quirks.c
quirks.h
stream.c
stream.h
usbaudio.h
validate.c
virtio
x86
xen
Kconfig
Makefile
ac97_bus.c
last.c
sound_core.c
tools
usr
virt
.clang-format
.cocciconfig
.get_maintainer.ignore
.gitattributes
.gitignore
.mailmap
COPYING
CREDITS
Kbuild
Kconfig
MAINTAINERS
Makefile
README
Breadcrumbs
linux
/
sound
/
usb
/
implicit.c
Blame
Blame
Latest commit
History
History
505 lines (450 loc) · 18 KB
Breadcrumbs
linux
/
sound
/
usb
/
implicit.c
Top
File metadata and controls
Code
Blame
505 lines (450 loc) · 18 KB
Raw
// SPDX-License-Identifier: GPL-2.0-or-later // // Special handling for implicit feedback mode // #include <linux/init.h> #include <linux/usb.h> #include <linux/usb/audio.h> #include <linux/usb/audio-v2.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include "usbaudio.h" #include "card.h" #include "helper.h" #include "implicit.h" enum { IMPLICIT_FB_NONE, IMPLICIT_FB_GENERIC, IMPLICIT_FB_FIXED, IMPLICIT_FB_BOTH, /* generic playback + capture (for BOSS) */ }; struct snd_usb_implicit_fb_match { unsigned int id; unsigned int iface_class; unsigned int ep_num; unsigned int iface; int type; }; #define IMPLICIT_FB_GENERIC_DEV(vend, prod) \ { .id = USB_ID(vend, prod), .type = IMPLICIT_FB_GENERIC } #define IMPLICIT_FB_FIXED_DEV(vend, prod, ep, ifnum) \ { .id = USB_ID(vend, prod), .type = IMPLICIT_FB_FIXED, .ep_num = (ep),\ .iface = (ifnum) } #define IMPLICIT_FB_BOTH_DEV(vend, prod, ep, ifnum) \ { .id = USB_ID(vend, prod), .type = IMPLICIT_FB_BOTH, .ep_num = (ep),\ .iface = (ifnum) } #define IMPLICIT_FB_SKIP_DEV(vend, prod) \ { .id = USB_ID(vend, prod), .type = IMPLICIT_FB_NONE } /* Implicit feedback quirk table for playback */ static const struct snd_usb_implicit_fb_match playback_implicit_fb_quirks[] = { /* Generic matching */ IMPLICIT_FB_GENERIC_DEV(0x0499, 0x1509), /* Steinberg UR22 */ IMPLICIT_FB_GENERIC_DEV(0x0763, 0x2080), /* M-Audio FastTrack Ultra */ IMPLICIT_FB_GENERIC_DEV(0x0763, 0x2081), /* M-Audio FastTrack Ultra */ IMPLICIT_FB_GENERIC_DEV(0x0763, 0x2030), /* M-Audio Fast Track C400 */ IMPLICIT_FB_GENERIC_DEV(0x0763, 0x2031), /* M-Audio Fast Track C600 */ /* Fixed EP */ /* FIXME: check the availability of generic matching */ IMPLICIT_FB_FIXED_DEV(0x1397, 0x0001, 0x81, 1), /* Behringer UFX1604 */ IMPLICIT_FB_FIXED_DEV(0x1397, 0x0002, 0x81, 1), /* Behringer UFX1204 */ IMPLICIT_FB_FIXED_DEV(0x2466, 0x8010, 0x81, 2), /* Fractal Audio Axe-Fx III */ IMPLICIT_FB_FIXED_DEV(0x31e9, 0x0001, 0x81, 2), /* Solid State Logic SSL2 */ IMPLICIT_FB_FIXED_DEV(0x31e9, 0x0002, 0x81, 2), /* Solid State Logic SSL2+ */ IMPLICIT_FB_FIXED_DEV(0x0499, 0x172f, 0x81, 2), /* Steinberg UR22C */ IMPLICIT_FB_FIXED_DEV(0x0d9a, 0x00df, 0x81, 2), /* RTX6001 */ IMPLICIT_FB_FIXED_DEV(0x22f0, 0x0006, 0x81, 3), /* Allen&Heath Qu-16 */ IMPLICIT_FB_FIXED_DEV(0x1686, 0xf029, 0x82, 2), /* Zoom UAC-2 */ IMPLICIT_FB_FIXED_DEV(0x2466, 0x8003, 0x86, 2), /* Fractal Audio Axe-Fx II */ IMPLICIT_FB_FIXED_DEV(0x0499, 0x172a, 0x86, 2), /* Yamaha MODX */ /* Special matching */ { .id = USB_ID(0x07fd, 0x0004), .iface_class = USB_CLASS_AUDIO, .type = IMPLICIT_FB_NONE }, /* MicroBook IIc */ /* ep = 0x84, ifnum = 0 */ { .id = USB_ID(0x07fd, 0x0004), .iface_class = USB_CLASS_VENDOR_SPEC, .type = IMPLICIT_FB_FIXED, .ep_num = 0x84, .iface = 0 }, /* MOTU MicroBook II */ {} /* terminator */ }; /* Implicit feedback quirk table for capture: only FIXED type */ static const struct snd_usb_implicit_fb_match capture_implicit_fb_quirks[] = { IMPLICIT_FB_FIXED_DEV(0x0582, 0x00a6, 0x0d, 0x01), /* Roland JUNO-G */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x00a9, 0x0d, 0x01), /* Roland MC-808 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x00ad, 0x0d, 0x01), /* Roland SH-201 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x00b2, 0x0d, 0x01), /* Roland VG-99 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x00b3, 0x0d, 0x01), /* Roland VG-99 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x00c2, 0x0d, 0x01), /* Roland SonicCell */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x00c4, 0x0d, 0x01), /* Edirol M-16DX */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x00c5, 0x0d, 0x01), /* Roland SP-555 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x00c7, 0x0d, 0x01), /* Roland V-Synth GT */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x00d1, 0x0d, 0x01), /* Roland Music Atelier */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x00da, 0x0d, 0x01), /* BOSS GT-10 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x00db, 0x0d, 0x01), /* BOSS GT-10 Guitar Effects Processor */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x00dc, 0x0d, 0x01), /* BOSS GT-10B */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x00de, 0x0d, 0x01), /* Roland Fantom-G */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x00eb, 0x0d, 0x01), /* Roland VS-100 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x00f8, 0x0d, 0x01), /* Roland JUNO Series */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x00fc, 0x0d, 0x01), /* Roland VS-700C */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x00fd, 0x0d, 0x01), /* Roland VS-700 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x00fe, 0x0d, 0x01), /* Roland VS-700 M1 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x00ff, 0x0d, 0x01), /* Roland VS-700 M2 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x0100, 0x0d, 0x01), /* Roland VS-700 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x0101, 0x0d, 0x01), /* Roland VS-700 M2 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x0102, 0x0d, 0x01), /* Roland VB-99 */ IMPLICIT_FB_BOTH_DEV(0x0582, 0x0109, 0x0d, 0x01), /* BOSS eBand JS-8 */ IMPLICIT_FB_BOTH_DEV(0x0582, 0x0111, 0x0d, 0x01), /* Roland GAIA SH-01 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x0113, 0x0d, 0x01), /* BOSS ME-25 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x0114, 0x0d, 0x01), /* Roland SD-50 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x0117, 0x0d, 0x01), /* Roland VS-20 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x0119, 0x0d, 0x01), /* Roland OCTAPAD SPD-30 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x011c, 0x0d, 0x01), /* Roland Lucina AX-09 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x011e, 0x0d, 0x01), /* BOSS BR-800 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x0120, 0x0d, 0x01), /* Roland OCTA-CAPTURE */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x0121, 0x0d, 0x01), /* Roland OCTA-CAPTURE */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x0123, 0x0d, 0x01), /* Roland JUNO-Gi */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x0124, 0x0d, 0x01), /* Roland M-300 */ IMPLICIT_FB_BOTH_DEV(0x0582, 0x0127, 0x0d, 0x01), /* Roland GR-55 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x012b, 0x0d, 0x01), /* Roland DUO-CAPTURE */ IMPLICIT_FB_BOTH_DEV(0x0582, 0x012f, 0x0d, 0x01), /* Roland QUAD-CAPTURE */ IMPLICIT_FB_BOTH_DEV(0x0582, 0x0130, 0x0d, 0x01), /* BOSS BR-80 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x0132, 0x0d, 0x01), /* Roland TRI-CAPTURE */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x0134, 0x0d, 0x01), /* Roland V-Mixer */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x0137, 0x0d, 0x01), /* Roland DUO-CAPTURE Advanced Mode */ IMPLICIT_FB_BOTH_DEV(0x0582, 0x0138, 0x0d, 0x01), /* BOSS RC-300 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x013a, 0x0d, 0x01), /* Roland JUPITER-80 */ IMPLICIT_FB_BOTH_DEV(0x0582, 0x013e, 0x0d, 0x01), /* Roland R-26 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x0145, 0x0d, 0x01), /* Roland SPD-SX */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x014b, 0x0d, 0x01), /* BOSS eBand JS-10 */ IMPLICIT_FB_BOTH_DEV(0x0582, 0x014d, 0x0d, 0x01), /* BOSS GT-100 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x0150, 0x0d, 0x01), /* Roland TD-15 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x0151, 0x0d, 0x01), /* Roland TD-11 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x0154, 0x0d, 0x01), /* Roland JUPITER-50 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x0158, 0x0d, 0x01), /* Roland TD-30 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x0159, 0x0d, 0x01), /* Roland DUO-CAPTURE EX */ IMPLICIT_FB_BOTH_DEV(0x0582, 0x015b, 0x0d, 0x01), /* Roland INTEGRA-7 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x015d, 0x0d, 0x01), /* Roland R-88 */ IMPLICIT_FB_BOTH_DEV(0x0582, 0x0171, 0x0d, 0x01), /* BOSS RC-505 */ IMPLICIT_FB_FIXED_DEV(0x0582, 0x017a, 0x0d, 0x01), /* Roland VT-3 */ IMPLICIT_FB_BOTH_DEV(0x0582, 0x017c, 0x0d, 0x01), /* Roland TR-8 */ IMPLICIT_FB_BOTH_DEV(0x0582, 0x0185, 0x0d, 0x01), /* BOSS GP-10 */ IMPLICIT_FB_BOTH_DEV(0x0582, 0x0189, 0x0d, 0x01), /* BOSS GT-100v2 */ IMPLICIT_FB_BOTH_DEV(0x0582, 0x01b5, 0x0d, 0x01), /* Roland Boutique JP-08 */ IMPLICIT_FB_BOTH_DEV(0x0582, 0x01d6, 0x0d, 0x01), /* BOSS GT-1 */ IMPLICIT_FB_BOTH_DEV(0x0582, 0x01d8, 0x0d, 0x01), /* BOSS Katana */ IMPLICIT_FB_BOTH_DEV(0x0582, 0x01e5, 0x0d, 0x01), /* BOSS GT-001 */ IMPLICIT_FB_BOTH_DEV(0x0582, 0x01fd, 0x0d, 0x01), /* Roland Boutique SH-01A */ IMPLICIT_FB_BOTH_DEV(0x0582, 0x01ff, 0x0d, 0x01), /* Roland Boutique D-05 */ IMPLICIT_FB_BOTH_DEV(0x0582, 0x0203, 0x0d, 0x01), /* BOSS AD-10 */ {} /* terminator */ }; /* set up sync EP information on the audioformat */ static int add_implicit_fb_sync_ep(struct snd_usb_audio *chip, struct audioformat *fmt, int ep, int ep_idx, int ifnum, const struct usb_host_interface *alts) { struct usb_interface *iface; if (!alts) { iface = usb_ifnum_to_if(chip->dev, ifnum); if (!iface || iface->num_altsetting < 2) return 0; alts = &iface->altsetting[1]; } fmt->sync_ep = ep; fmt->sync_iface = ifnum; fmt->sync_altsetting = alts->desc.bAlternateSetting; fmt->sync_ep_idx = ep_idx; fmt->implicit_fb = 1; usb_audio_dbg(chip, "%d:%d: added %s implicit_fb sync_ep %x, iface %d:%d\n", fmt->iface, fmt->altsetting, (ep & USB_DIR_IN) ? "playback" : "capture", fmt->sync_ep, fmt->sync_iface, fmt->sync_altsetting); return 1; } /* Check whether the given UAC2 iface:altset points to an implicit fb source */ static int add_generic_uac2_implicit_fb(struct snd_usb_audio *chip, struct audioformat *fmt, unsigned int ifnum, unsigned int altsetting) { struct usb_host_interface *alts; struct usb_endpoint_descriptor *epd; alts = snd_usb_get_host_interface(chip, ifnum, altsetting); if (!alts) return 0; if (alts->desc.bInterfaceClass != USB_CLASS_AUDIO || alts->desc.bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING || alts->desc.bInterfaceProtocol != UAC_VERSION_2 || alts->desc.bNumEndpoints < 1) return 0; epd = get_endpoint(alts, 0); if (!usb_endpoint_is_isoc_in(epd) || (epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) != USB_ENDPOINT_USAGE_IMPLICIT_FB) return 0; return add_implicit_fb_sync_ep(chip, fmt, epd->bEndpointAddress, 0, ifnum, alts); } /* Like the function above, but specific to Roland with vendor class and hack */ static int add_roland_implicit_fb(struct snd_usb_audio *chip, struct audioformat *fmt, unsigned int ifnum, unsigned int altsetting) { struct usb_host_interface *alts; struct usb_endpoint_descriptor *epd; alts = snd_usb_get_host_interface(chip, ifnum, altsetting); if (!alts) return 0; if (alts->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC || (alts->desc.bInterfaceSubClass != 2 && alts->desc.bInterfaceProtocol != 2) || alts->desc.bNumEndpoints < 1) return 0; epd = get_endpoint(alts, 0); if (!usb_endpoint_is_isoc_in(epd) || (epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) != USB_ENDPOINT_USAGE_IMPLICIT_FB) return 0; return add_implicit_fb_sync_ep(chip, fmt, epd->bEndpointAddress, 0, ifnum, alts); } /* Playback and capture EPs on Pioneer devices share the same iface/altset, * but they don't seem working with the implicit fb mode well, hence we * just return as if the sync were already set up. */ static int skip_pioneer_sync_ep(struct snd_usb_audio *chip, struct audioformat *fmt, struct usb_host_interface *alts) { struct usb_endpoint_descriptor *epd; if (alts->desc.bNumEndpoints != 2) return 0; epd = get_endpoint(alts, 1); if (!usb_endpoint_is_isoc_in(epd) || (epd->bmAttributes & USB_ENDPOINT_SYNCTYPE) != USB_ENDPOINT_SYNC_ASYNC || ((epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) != USB_ENDPOINT_USAGE_DATA && (epd->bmAttributes & USB_ENDPOINT_USAGE_MASK) != USB_ENDPOINT_USAGE_IMPLICIT_FB)) return 0; return 1; /* don't handle with the implicit fb, just skip sync EP */ } static int __add_generic_implicit_fb(struct snd_usb_audio *chip, struct audioformat *fmt, int iface, int altset) { struct usb_host_interface *alts; struct usb_endpoint_descriptor *epd; alts = snd_usb_get_host_interface(chip, iface, altset); if (!alts) return 0; if ((alts->desc.bInterfaceClass != USB_CLASS_VENDOR_SPEC && alts->desc.bInterfaceClass != USB_CLASS_AUDIO) || alts->desc.bNumEndpoints < 1) return 0; epd = get_endpoint(alts, 0); if (!usb_endpoint_is_isoc_in(epd) || (epd->bmAttributes & USB_ENDPOINT_SYNCTYPE) != USB_ENDPOINT_SYNC_ASYNC) return 0; return add_implicit_fb_sync_ep(chip, fmt, epd->bEndpointAddress, 0, iface, alts); } /* More generic quirk: look for the sync EP next to the data EP */ static int add_generic_implicit_fb(struct snd_usb_audio *chip, struct audioformat *fmt, struct usb_host_interface *alts) { if ((fmt->ep_attr & USB_ENDPOINT_SYNCTYPE) != USB_ENDPOINT_SYNC_ASYNC) return 0; if (__add_generic_implicit_fb(chip, fmt, alts->desc.bInterfaceNumber + 1, alts->desc.bAlternateSetting)) return 1; return __add_generic_implicit_fb(chip, fmt, alts->desc.bInterfaceNumber - 1, alts->desc.bAlternateSetting); } static const struct snd_usb_implicit_fb_match * find_implicit_fb_entry(struct snd_usb_audio *chip, const struct snd_usb_implicit_fb_match *match, const struct usb_host_interface *alts) { for (; match->id; match++) if (match->id == chip->usb_id && (!match->iface_class || (alts->desc.bInterfaceClass == match->iface_class))) return match; return NULL; } /* Setup an implicit feedback endpoint from a quirk. Returns 0 if no quirk * applies. Returns 1 if a quirk was found. */ static int audioformat_implicit_fb_quirk(struct snd_usb_audio *chip, struct audioformat *fmt, struct usb_host_interface *alts) { const struct snd_usb_implicit_fb_match *p; unsigned int attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE; p = find_implicit_fb_entry(chip, playback_implicit_fb_quirks, alts); if (p) { switch (p->type) { case IMPLICIT_FB_GENERIC: return add_generic_implicit_fb(chip, fmt, alts); case IMPLICIT_FB_NONE: return 0; /* No quirk */ case IMPLICIT_FB_FIXED: return add_implicit_fb_sync_ep(chip, fmt, p->ep_num, 0, p->iface, NULL); } } /* Special handling for devices with capture quirks */ p = find_implicit_fb_entry(chip, capture_implicit_fb_quirks, alts); if (p) { switch (p->type) { case IMPLICIT_FB_FIXED: return 0; /* no quirk */ case IMPLICIT_FB_BOTH: chip->playback_first = 1; return add_generic_implicit_fb(chip, fmt, alts); } } /* Generic UAC2 implicit feedback */ if (attr == USB_ENDPOINT_SYNC_ASYNC && alts->desc.bInterfaceClass == USB_CLASS_AUDIO && alts->desc.bInterfaceProtocol == UAC_VERSION_2 && alts->desc.bNumEndpoints == 1) { if (add_generic_uac2_implicit_fb(chip, fmt, alts->desc.bInterfaceNumber + 1, alts->desc.bAlternateSetting)) return 1; } /* Roland/BOSS implicit feedback with vendor spec class */ if (attr == USB_ENDPOINT_SYNC_ASYNC && alts->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC && alts->desc.bInterfaceProtocol == 2 && alts->desc.bNumEndpoints == 1 && USB_ID_VENDOR(chip->usb_id) == 0x0582 /* Roland */) { if (add_roland_implicit_fb(chip, fmt, alts->desc.bInterfaceNumber + 1, alts->desc.bAlternateSetting)) return 1; } /* Pioneer devices with vendor spec class */ if (attr == USB_ENDPOINT_SYNC_ASYNC && alts->desc.bInterfaceClass == USB_CLASS_VENDOR_SPEC && (USB_ID_VENDOR(chip->usb_id) == 0x2b73 || /* Pioneer */ USB_ID_VENDOR(chip->usb_id) == 0x08e4 /* Pioneer */)) { if (skip_pioneer_sync_ep(chip, fmt, alts)) return 1; } /* Try the generic implicit fb if available */ if (chip->generic_implicit_fb) return add_generic_implicit_fb(chip, fmt, alts); /* No quirk */ return 0; } /* same for capture, but only handling FIXED entry */ static int audioformat_capture_quirk(struct snd_usb_audio *chip, struct audioformat *fmt, struct usb_host_interface *alts) { const struct snd_usb_implicit_fb_match *p; p = find_implicit_fb_entry(chip, capture_implicit_fb_quirks, alts); if (p && (p->type == IMPLICIT_FB_FIXED || p->type == IMPLICIT_FB_BOTH)) return add_implicit_fb_sync_ep(chip, fmt, p->ep_num, 0, p->iface, NULL); return 0; } /* * Parse altset and set up implicit feedback endpoint on the audioformat */ int snd_usb_parse_implicit_fb_quirk(struct snd_usb_audio *chip, struct audioformat *fmt, struct usb_host_interface *alts) { if (fmt->endpoint & USB_DIR_IN) return audioformat_capture_quirk(chip, fmt, alts); else return audioformat_implicit_fb_quirk(chip, fmt, alts); } /* * Return the score of matching two audioformats. * Veto the audioformat if: * - It has no channels for some reason. * - Requested PCM format is not supported. * - Requested sample rate is not supported. */ static int match_endpoint_audioformats(struct snd_usb_substream *subs, const struct audioformat *fp, int rate, int channels, snd_pcm_format_t pcm_format) { int i, score; if (fp->channels < 1) return 0; if (!(fp->formats & pcm_format_to_bits(pcm_format))) return 0; if (fp->rates & SNDRV_PCM_RATE_CONTINUOUS) { if (rate < fp->rate_min || rate > fp->rate_max) return 0; } else { for (i = 0; i < fp->nr_rates; i++) { if (fp->rate_table[i] == rate) break; } if (i >= fp->nr_rates) return 0; } score = 1; if (fp->channels == channels) score++; return score; } static struct snd_usb_substream * find_matching_substream(struct snd_usb_audio *chip, int stream, int ep_num, int fmt_type) { struct snd_usb_stream *as; struct snd_usb_substream *subs; list_for_each_entry(as, &chip->pcm_list, list) { subs = &as->substream[stream]; if (as->fmt_type == fmt_type && subs->ep_num == ep_num) return subs; } return NULL; } /* * Return the audioformat that is suitable for the implicit fb */ const struct audioformat * snd_usb_find_implicit_fb_sync_format(struct snd_usb_audio *chip, const struct audioformat *target, const struct snd_pcm_hw_params *params, int stream) { struct snd_usb_substream *subs; const struct audioformat *fp, *sync_fmt = NULL; int score, high_score; /* Use the original audioformat as fallback for the shared altset */ if (target->iface == target->sync_iface && target->altsetting == target->sync_altsetting) sync_fmt = target; subs = find_matching_substream(chip, stream, target->sync_ep, target->fmt_type); if (!subs) return sync_fmt; high_score = 0; list_for_each_entry(fp, &subs->fmt_list, list) { score = match_endpoint_audioformats(subs, fp, params_rate(params), params_channels(params), params_format(params)); if (score > high_score) { sync_fmt = fp; high_score = score; } } return sync_fmt; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
You can’t perform that action at this time.