Merge branch 'topic/misc' into for-linus
This commit is contained in:
Коммит
d226657022
|
@ -4288,7 +4288,7 @@ struct _snd_pcm_runtime {
|
|||
<![CDATA[
|
||||
struct snd_rawmidi *rmidi;
|
||||
snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, port, info_flags,
|
||||
irq, irq_flags, &rmidi);
|
||||
irq, &rmidi);
|
||||
]]>
|
||||
</programlisting>
|
||||
</informalexample>
|
||||
|
@ -4343,6 +4343,13 @@ struct _snd_pcm_runtime {
|
|||
by itself to start processing the output stream in the irq handler.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If the MPU-401 interface shares its interrupt with the other logical
|
||||
devices on the card, set <constant>MPU401_INFO_IRQ_HOOK</constant>
|
||||
(see <link linkend="midi-interface-interrupt-handler"><citetitle>
|
||||
below</citetitle></link>).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Usually, the port address corresponds to the command port and
|
||||
port + 1 corresponds to the data port. If not, you may change
|
||||
|
@ -4375,14 +4382,12 @@ struct _snd_pcm_runtime {
|
|||
</para>
|
||||
|
||||
<para>
|
||||
The 6th argument specifies the irq number for UART. If the irq
|
||||
is already allocated, pass 0 to the 7th argument
|
||||
(<parameter>irq_flags</parameter>). Otherwise, pass the flags
|
||||
for irq allocation
|
||||
(<constant>SA_XXX</constant> bits) to it, and the irq will be
|
||||
reserved by the mpu401-uart layer. If the card doesn't generate
|
||||
UART interrupts, pass -1 as the irq number. Then a timer
|
||||
interrupt will be invoked for polling.
|
||||
The 6th argument specifies the ISA irq number that will be
|
||||
allocated. If no interrupt is to be allocated (because your
|
||||
code is already allocating a shared interrupt, or because the
|
||||
device does not use interrupts), pass -1 instead.
|
||||
For a MPU-401 device without an interrupt, a polling timer
|
||||
will be used instead.
|
||||
</para>
|
||||
</section>
|
||||
|
||||
|
@ -4390,12 +4395,13 @@ struct _snd_pcm_runtime {
|
|||
<title>Interrupt Handler</title>
|
||||
<para>
|
||||
When the interrupt is allocated in
|
||||
<function>snd_mpu401_uart_new()</function>, the private
|
||||
interrupt handler is used, hence you don't have anything else to do
|
||||
than creating the mpu401 stuff. Otherwise, you have to call
|
||||
<function>snd_mpu401_uart_interrupt()</function> explicitly when
|
||||
a UART interrupt is invoked and checked in your own interrupt
|
||||
handler.
|
||||
<function>snd_mpu401_uart_new()</function>, an exclusive ISA
|
||||
interrupt handler is automatically used, hence you don't have
|
||||
anything else to do than creating the mpu401 stuff. Otherwise, you
|
||||
have to set <constant>MPU401_INFO_IRQ_HOOK</constant>, and call
|
||||
<function>snd_mpu401_uart_interrupt()</function> explicitly from your
|
||||
own interrupt handler when it has determined that a UART interrupt
|
||||
has occurred.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
|
|
|
@ -5990,7 +5990,7 @@ M: Jaroslav Kysela <perex@perex.cz>
|
|||
M: Takashi Iwai <tiwai@suse.de>
|
||||
L: alsa-devel@alsa-project.org (moderated for non-subscribers)
|
||||
W: http://www.alsa-project.org/
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound-2.6.git
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
|
||||
T: git git://git.alsa-project.org/alsa-kernel.git
|
||||
S: Maintained
|
||||
F: Documentation/sound/
|
||||
|
|
|
@ -377,12 +377,6 @@ struct usb_endpoint_descriptor {
|
|||
#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */
|
||||
#define USB_ENDPOINT_DIR_MASK 0x80
|
||||
|
||||
#define USB_ENDPOINT_SYNCTYPE 0x0c
|
||||
#define USB_ENDPOINT_SYNC_NONE (0 << 2)
|
||||
#define USB_ENDPOINT_SYNC_ASYNC (1 << 2)
|
||||
#define USB_ENDPOINT_SYNC_ADAPTIVE (2 << 2)
|
||||
#define USB_ENDPOINT_SYNC_SYNC (3 << 2)
|
||||
|
||||
#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */
|
||||
#define USB_ENDPOINT_XFER_CONTROL 0
|
||||
#define USB_ENDPOINT_XFER_ISOC 1
|
||||
|
@ -390,6 +384,17 @@ struct usb_endpoint_descriptor {
|
|||
#define USB_ENDPOINT_XFER_INT 3
|
||||
#define USB_ENDPOINT_MAX_ADJUSTABLE 0x80
|
||||
|
||||
#define USB_ENDPOINT_SYNCTYPE 0x0c
|
||||
#define USB_ENDPOINT_SYNC_NONE (0 << 2)
|
||||
#define USB_ENDPOINT_SYNC_ASYNC (1 << 2)
|
||||
#define USB_ENDPOINT_SYNC_ADAPTIVE (2 << 2)
|
||||
#define USB_ENDPOINT_SYNC_SYNC (3 << 2)
|
||||
|
||||
#define USB_ENDPOINT_USAGE_MASK 0x30
|
||||
#define USB_ENDPOINT_USAGE_DATA 0x00
|
||||
#define USB_ENDPOINT_USAGE_FEEDBACK 0x10
|
||||
#define USB_ENDPOINT_USAGE_IMPLICIT_FB 0x20 /* Implicit feedback Data endpoint */
|
||||
|
||||
/*-------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
|
|
|
@ -706,7 +706,7 @@ struct snd_timer_tread {
|
|||
* *
|
||||
****************************************************************************/
|
||||
|
||||
#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 6)
|
||||
#define SNDRV_CTL_VERSION SNDRV_PROTOCOL_VERSION(2, 0, 7)
|
||||
|
||||
struct snd_ctl_card_info {
|
||||
int card; /* card number */
|
||||
|
@ -803,6 +803,8 @@ struct snd_ctl_elem_info {
|
|||
unsigned int items; /* R: number of items */
|
||||
unsigned int item; /* W: item number */
|
||||
char name[64]; /* R: value name */
|
||||
__u64 names_ptr; /* W: names list (ELEM_ADD only) */
|
||||
unsigned int names_length;
|
||||
} enumerated;
|
||||
unsigned char reserved[128];
|
||||
} value;
|
||||
|
|
|
@ -50,7 +50,10 @@
|
|||
#define MPU401_INFO_INTEGRATED (1 << 2) /* integrated h/w port */
|
||||
#define MPU401_INFO_MMIO (1 << 3) /* MMIO access */
|
||||
#define MPU401_INFO_TX_IRQ (1 << 4) /* independent TX irq */
|
||||
#define MPU401_INFO_IRQ_HOOK (1 << 5) /* mpu401 irq handler is called
|
||||
from driver irq handler */
|
||||
#define MPU401_INFO_NO_ACK (1 << 6) /* No ACK cmd needed */
|
||||
#define MPU401_INFO_USE_TIMER (1 << 15) /* internal */
|
||||
|
||||
#define MPU401_MODE_BIT_INPUT 0
|
||||
#define MPU401_MODE_BIT_OUTPUT 1
|
||||
|
@ -73,8 +76,7 @@ struct snd_mpu401 {
|
|||
unsigned long port; /* base port of MPU-401 chip */
|
||||
unsigned long cport; /* port + 1 (usually) */
|
||||
struct resource *res; /* port resource */
|
||||
int irq; /* IRQ number of MPU-401 chip (-1 = poll) */
|
||||
int irq_flags;
|
||||
int irq; /* IRQ number of MPU-401 chip */
|
||||
|
||||
unsigned long mode; /* MPU401_MODE_XXXX */
|
||||
int timer_invoked;
|
||||
|
@ -131,7 +133,6 @@ int snd_mpu401_uart_new(struct snd_card *card,
|
|||
unsigned long port,
|
||||
unsigned int info_flags,
|
||||
int irq,
|
||||
int irq_flags,
|
||||
struct snd_rawmidi ** rrawmidi);
|
||||
|
||||
#endif /* __SOUND_MPU401_H */
|
||||
|
|
|
@ -825,6 +825,8 @@ int snd_pcm_hw_constraint_step(struct snd_pcm_runtime *runtime,
|
|||
int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime,
|
||||
unsigned int cond,
|
||||
snd_pcm_hw_param_t var);
|
||||
int snd_pcm_hw_rule_noresample(struct snd_pcm_runtime *runtime,
|
||||
unsigned int base_rate);
|
||||
int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime,
|
||||
unsigned int cond,
|
||||
int var,
|
||||
|
|
|
@ -1067,7 +1067,6 @@ static int onyx_i2c_probe(struct i2c_client *client,
|
|||
printk(KERN_DEBUG PFX "created and attached onyx instance\n");
|
||||
return 0;
|
||||
fail:
|
||||
i2c_set_clientdata(client, NULL);
|
||||
kfree(onyx);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
@ -1112,8 +1111,7 @@ static int onyx_i2c_remove(struct i2c_client *client)
|
|||
|
||||
aoa_codec_unregister(&onyx->codec);
|
||||
of_node_put(onyx->codec.node);
|
||||
if (onyx->codec_info)
|
||||
kfree(onyx->codec_info);
|
||||
kfree(onyx->codec_info);
|
||||
kfree(onyx);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -989,7 +989,6 @@ struct user_element {
|
|||
void *tlv_data; /* TLV data */
|
||||
unsigned long tlv_data_size; /* TLV data size */
|
||||
void *priv_data; /* private data (like strings for enumerated type) */
|
||||
unsigned long priv_data_size; /* size of private data in bytes */
|
||||
};
|
||||
|
||||
static int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol,
|
||||
|
@ -1001,6 +1000,28 @@ static int snd_ctl_elem_user_info(struct snd_kcontrol *kcontrol,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int snd_ctl_elem_user_enum_info(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_info *uinfo)
|
||||
{
|
||||
struct user_element *ue = kcontrol->private_data;
|
||||
const char *names;
|
||||
unsigned int item;
|
||||
|
||||
item = uinfo->value.enumerated.item;
|
||||
|
||||
*uinfo = ue->info;
|
||||
|
||||
item = min(item, uinfo->value.enumerated.items - 1);
|
||||
uinfo->value.enumerated.item = item;
|
||||
|
||||
names = ue->priv_data;
|
||||
for (; item > 0; --item)
|
||||
names += strlen(names) + 1;
|
||||
strcpy(uinfo->value.enumerated.name, names);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_ctl_elem_user_get(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
|
@ -1055,11 +1076,46 @@ static int snd_ctl_elem_user_tlv(struct snd_kcontrol *kcontrol,
|
|||
return change;
|
||||
}
|
||||
|
||||
static int snd_ctl_elem_init_enum_names(struct user_element *ue)
|
||||
{
|
||||
char *names, *p;
|
||||
size_t buf_len, name_len;
|
||||
unsigned int i;
|
||||
|
||||
if (ue->info.value.enumerated.names_length > 64 * 1024)
|
||||
return -EINVAL;
|
||||
|
||||
names = memdup_user(
|
||||
(const void __user *)ue->info.value.enumerated.names_ptr,
|
||||
ue->info.value.enumerated.names_length);
|
||||
if (IS_ERR(names))
|
||||
return PTR_ERR(names);
|
||||
|
||||
/* check that there are enough valid names */
|
||||
buf_len = ue->info.value.enumerated.names_length;
|
||||
p = names;
|
||||
for (i = 0; i < ue->info.value.enumerated.items; ++i) {
|
||||
name_len = strnlen(p, buf_len);
|
||||
if (name_len == 0 || name_len >= 64 || name_len == buf_len) {
|
||||
kfree(names);
|
||||
return -EINVAL;
|
||||
}
|
||||
p += name_len + 1;
|
||||
buf_len -= name_len + 1;
|
||||
}
|
||||
|
||||
ue->priv_data = names;
|
||||
ue->info.value.enumerated.names_ptr = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void snd_ctl_elem_user_free(struct snd_kcontrol *kcontrol)
|
||||
{
|
||||
struct user_element *ue = kcontrol->private_data;
|
||||
if (ue->tlv_data)
|
||||
kfree(ue->tlv_data);
|
||||
|
||||
kfree(ue->tlv_data);
|
||||
kfree(ue->priv_data);
|
||||
kfree(ue);
|
||||
}
|
||||
|
||||
|
@ -1072,8 +1128,8 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
|
|||
long private_size;
|
||||
struct user_element *ue;
|
||||
int idx, err;
|
||||
|
||||
if (card->user_ctl_count >= MAX_USER_CONTROLS)
|
||||
|
||||
if (!replace && card->user_ctl_count >= MAX_USER_CONTROLS)
|
||||
return -ENOMEM;
|
||||
if (info->count < 1)
|
||||
return -EINVAL;
|
||||
|
@ -1101,7 +1157,10 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
|
|||
memcpy(&kctl.id, &info->id, sizeof(info->id));
|
||||
kctl.count = info->owner ? info->owner : 1;
|
||||
access |= SNDRV_CTL_ELEM_ACCESS_USER;
|
||||
kctl.info = snd_ctl_elem_user_info;
|
||||
if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED)
|
||||
kctl.info = snd_ctl_elem_user_enum_info;
|
||||
else
|
||||
kctl.info = snd_ctl_elem_user_info;
|
||||
if (access & SNDRV_CTL_ELEM_ACCESS_READ)
|
||||
kctl.get = snd_ctl_elem_user_get;
|
||||
if (access & SNDRV_CTL_ELEM_ACCESS_WRITE)
|
||||
|
@ -1122,6 +1181,11 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
|
|||
if (info->count > 64)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
|
||||
private_size = sizeof(unsigned int);
|
||||
if (info->count > 128 || info->value.enumerated.items == 0)
|
||||
return -EINVAL;
|
||||
break;
|
||||
case SNDRV_CTL_ELEM_TYPE_BYTES:
|
||||
private_size = sizeof(unsigned char);
|
||||
if (info->count > 512)
|
||||
|
@ -1143,9 +1207,17 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
|
|||
ue->info.access = 0;
|
||||
ue->elem_data = (char *)ue + sizeof(*ue);
|
||||
ue->elem_data_size = private_size;
|
||||
if (ue->info.type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
|
||||
err = snd_ctl_elem_init_enum_names(ue);
|
||||
if (err < 0) {
|
||||
kfree(ue);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
kctl.private_free = snd_ctl_elem_user_free;
|
||||
_kctl = snd_ctl_new(&kctl, access);
|
||||
if (_kctl == NULL) {
|
||||
kfree(ue->priv_data);
|
||||
kfree(ue);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
|
|
@ -83,6 +83,8 @@ struct snd_ctl_elem_info32 {
|
|||
u32 items;
|
||||
u32 item;
|
||||
char name[64];
|
||||
u64 names_ptr;
|
||||
u32 names_length;
|
||||
} enumerated;
|
||||
unsigned char reserved[128];
|
||||
} value;
|
||||
|
@ -372,6 +374,8 @@ static int snd_ctl_elem_add_compat(struct snd_ctl_file *file,
|
|||
&data32->value.enumerated,
|
||||
sizeof(data->value.enumerated)))
|
||||
goto error;
|
||||
data->value.enumerated.names_ptr =
|
||||
(uintptr_t)compat_ptr(data->value.enumerated.names_ptr);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
|
@ -499,7 +499,7 @@ static struct snd_kcontrol *snd_mixer_oss_test_id(struct snd_mixer_oss *mixer, c
|
|||
|
||||
memset(&id, 0, sizeof(id));
|
||||
id.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
|
||||
strcpy(id.name, name);
|
||||
strlcpy(id.name, name, sizeof(id.name));
|
||||
id.index = index;
|
||||
return snd_ctl_find_id(card, &id);
|
||||
}
|
||||
|
|
|
@ -1399,6 +1399,32 @@ int snd_pcm_hw_constraint_pow2(struct snd_pcm_runtime *runtime,
|
|||
|
||||
EXPORT_SYMBOL(snd_pcm_hw_constraint_pow2);
|
||||
|
||||
static int snd_pcm_hw_rule_noresample_func(struct snd_pcm_hw_params *params,
|
||||
struct snd_pcm_hw_rule *rule)
|
||||
{
|
||||
unsigned int base_rate = (unsigned int)(uintptr_t)rule->private;
|
||||
struct snd_interval *rate;
|
||||
|
||||
rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
|
||||
return snd_interval_list(rate, 1, &base_rate, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* snd_pcm_hw_rule_noresample - add a rule to allow disabling hw resampling
|
||||
* @runtime: PCM runtime instance
|
||||
* @base_rate: the rate at which the hardware does not resample
|
||||
*/
|
||||
int snd_pcm_hw_rule_noresample(struct snd_pcm_runtime *runtime,
|
||||
unsigned int base_rate)
|
||||
{
|
||||
return snd_pcm_hw_rule_add(runtime, SNDRV_PCM_HW_PARAMS_NORESAMPLE,
|
||||
SNDRV_PCM_HW_PARAM_RATE,
|
||||
snd_pcm_hw_rule_noresample_func,
|
||||
(void *)(uintptr_t)base_rate,
|
||||
SNDRV_PCM_HW_PARAM_RATE, -1);
|
||||
}
|
||||
EXPORT_SYMBOL(snd_pcm_hw_rule_noresample);
|
||||
|
||||
static void _snd_pcm_hw_param_any(struct snd_pcm_hw_params *params,
|
||||
snd_pcm_hw_param_t var)
|
||||
{
|
||||
|
|
|
@ -2058,16 +2058,12 @@ EXPORT_SYMBOL(snd_pcm_open_substream);
|
|||
|
||||
static int snd_pcm_open_file(struct file *file,
|
||||
struct snd_pcm *pcm,
|
||||
int stream,
|
||||
struct snd_pcm_file **rpcm_file)
|
||||
int stream)
|
||||
{
|
||||
struct snd_pcm_file *pcm_file;
|
||||
struct snd_pcm_substream *substream;
|
||||
int err;
|
||||
|
||||
if (rpcm_file)
|
||||
*rpcm_file = NULL;
|
||||
|
||||
err = snd_pcm_open_substream(pcm, stream, file, &substream);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
@ -2083,8 +2079,7 @@ static int snd_pcm_open_file(struct file *file,
|
|||
substream->pcm_release = pcm_release_private;
|
||||
}
|
||||
file->private_data = pcm_file;
|
||||
if (rpcm_file)
|
||||
*rpcm_file = pcm_file;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2113,7 +2108,6 @@ static int snd_pcm_capture_open(struct inode *inode, struct file *file)
|
|||
static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)
|
||||
{
|
||||
int err;
|
||||
struct snd_pcm_file *pcm_file;
|
||||
wait_queue_t wait;
|
||||
|
||||
if (pcm == NULL) {
|
||||
|
@ -2131,7 +2125,7 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream)
|
|||
add_wait_queue(&pcm->open_wait, &wait);
|
||||
mutex_lock(&pcm->open_mutex);
|
||||
while (1) {
|
||||
err = snd_pcm_open_file(file, pcm, stream, &pcm_file);
|
||||
err = snd_pcm_open_file(file, pcm, stream);
|
||||
if (err >= 0)
|
||||
break;
|
||||
if (err == -EAGAIN) {
|
||||
|
|
|
@ -575,7 +575,8 @@ static void loopback_runtime_free(struct snd_pcm_runtime *runtime)
|
|||
static int loopback_hw_params(struct snd_pcm_substream *substream,
|
||||
struct snd_pcm_hw_params *params)
|
||||
{
|
||||
return snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
|
||||
return snd_pcm_lib_alloc_vmalloc_buffer(substream,
|
||||
params_buffer_bytes(params));
|
||||
}
|
||||
|
||||
static int loopback_hw_free(struct snd_pcm_substream *substream)
|
||||
|
@ -587,7 +588,7 @@ static int loopback_hw_free(struct snd_pcm_substream *substream)
|
|||
mutex_lock(&dpcm->loopback->cable_lock);
|
||||
cable->valid &= ~(1 << substream->stream);
|
||||
mutex_unlock(&dpcm->loopback->cable_lock);
|
||||
return snd_pcm_lib_free_pages(substream);
|
||||
return snd_pcm_lib_free_vmalloc_buffer(substream);
|
||||
}
|
||||
|
||||
static unsigned int get_cable_index(struct snd_pcm_substream *substream)
|
||||
|
@ -740,6 +741,8 @@ static struct snd_pcm_ops loopback_playback_ops = {
|
|||
.prepare = loopback_prepare,
|
||||
.trigger = loopback_trigger,
|
||||
.pointer = loopback_pointer,
|
||||
.page = snd_pcm_lib_get_vmalloc_page,
|
||||
.mmap = snd_pcm_lib_mmap_vmalloc,
|
||||
};
|
||||
|
||||
static struct snd_pcm_ops loopback_capture_ops = {
|
||||
|
@ -751,6 +754,8 @@ static struct snd_pcm_ops loopback_capture_ops = {
|
|||
.prepare = loopback_prepare,
|
||||
.trigger = loopback_trigger,
|
||||
.pointer = loopback_pointer,
|
||||
.page = snd_pcm_lib_get_vmalloc_page,
|
||||
.mmap = snd_pcm_lib_mmap_vmalloc,
|
||||
};
|
||||
|
||||
static int __devinit loopback_pcm_new(struct loopback *loopback,
|
||||
|
@ -771,10 +776,6 @@ static int __devinit loopback_pcm_new(struct loopback *loopback,
|
|||
strcpy(pcm->name, "Loopback PCM");
|
||||
|
||||
loopback->pcm[device] = pcm;
|
||||
|
||||
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
|
||||
snd_dma_continuous_data(GFP_KERNEL),
|
||||
0, 2 * 1024 * 1024);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -86,8 +86,7 @@ static int snd_mpu401_create(int dev, struct snd_card **rcard)
|
|||
}
|
||||
|
||||
err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401, port[dev], 0,
|
||||
irq[dev], irq[dev] >= 0 ? IRQF_DISABLED : 0,
|
||||
NULL);
|
||||
irq[dev], NULL);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "MPU401 not detected at 0x%lx\n", port[dev]);
|
||||
goto _err;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Routines for control of MPU-401 in UART mode
|
||||
*
|
||||
* MPU-401 supports UART mode which is not capable generate transmit
|
||||
* interrupts thus output is done via polling. Also, if irq < 0, then
|
||||
* interrupts thus output is done via polling. Without interrupt,
|
||||
* input is done also via polling. Do not expect good performance.
|
||||
*
|
||||
*
|
||||
|
@ -374,7 +374,7 @@ snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
|
|||
/* first time - flush FIFO */
|
||||
while (max-- > 0)
|
||||
mpu->read(mpu, MPU401D(mpu));
|
||||
if (mpu->irq < 0)
|
||||
if (mpu->info_flags & MPU401_INFO_USE_TIMER)
|
||||
snd_mpu401_uart_add_timer(mpu, 1);
|
||||
}
|
||||
|
||||
|
@ -383,7 +383,7 @@ snd_mpu401_uart_input_trigger(struct snd_rawmidi_substream *substream, int up)
|
|||
snd_mpu401_uart_input_read(mpu);
|
||||
spin_unlock_irqrestore(&mpu->input_lock, flags);
|
||||
} else {
|
||||
if (mpu->irq < 0)
|
||||
if (mpu->info_flags & MPU401_INFO_USE_TIMER)
|
||||
snd_mpu401_uart_remove_timer(mpu, 1);
|
||||
clear_bit(MPU401_MODE_BIT_INPUT_TRIGGER, &mpu->mode);
|
||||
}
|
||||
|
@ -496,7 +496,7 @@ static struct snd_rawmidi_ops snd_mpu401_uart_input =
|
|||
static void snd_mpu401_uart_free(struct snd_rawmidi *rmidi)
|
||||
{
|
||||
struct snd_mpu401 *mpu = rmidi->private_data;
|
||||
if (mpu->irq_flags && mpu->irq >= 0)
|
||||
if (mpu->irq >= 0)
|
||||
free_irq(mpu->irq, (void *) mpu);
|
||||
release_and_free_resource(mpu->res);
|
||||
kfree(mpu);
|
||||
|
@ -509,8 +509,7 @@ static void snd_mpu401_uart_free(struct snd_rawmidi *rmidi)
|
|||
* @hardware: the hardware type, MPU401_HW_XXXX
|
||||
* @port: the base address of MPU401 port
|
||||
* @info_flags: bitflags MPU401_INFO_XXX
|
||||
* @irq: the irq number, -1 if no interrupt for mpu
|
||||
* @irq_flags: the irq request flags (SA_XXX), 0 if irq was already reserved.
|
||||
* @irq: the ISA irq number, -1 if not to be allocated
|
||||
* @rrawmidi: the pointer to store the new rawmidi instance
|
||||
*
|
||||
* Creates a new MPU-401 instance.
|
||||
|
@ -525,7 +524,7 @@ int snd_mpu401_uart_new(struct snd_card *card, int device,
|
|||
unsigned short hardware,
|
||||
unsigned long port,
|
||||
unsigned int info_flags,
|
||||
int irq, int irq_flags,
|
||||
int irq,
|
||||
struct snd_rawmidi ** rrawmidi)
|
||||
{
|
||||
struct snd_mpu401 *mpu;
|
||||
|
@ -577,8 +576,8 @@ int snd_mpu401_uart_new(struct snd_card *card, int device,
|
|||
mpu->cport = port + 2;
|
||||
else
|
||||
mpu->cport = port + 1;
|
||||
if (irq >= 0 && irq_flags) {
|
||||
if (request_irq(irq, snd_mpu401_uart_interrupt, irq_flags,
|
||||
if (irq >= 0) {
|
||||
if (request_irq(irq, snd_mpu401_uart_interrupt, IRQF_DISABLED,
|
||||
"MPU401 UART", (void *) mpu)) {
|
||||
snd_printk(KERN_ERR "mpu401_uart: "
|
||||
"unable to grab IRQ %d\n", irq);
|
||||
|
@ -586,9 +585,10 @@ int snd_mpu401_uart_new(struct snd_card *card, int device,
|
|||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
if (irq < 0 && !(info_flags & MPU401_INFO_IRQ_HOOK))
|
||||
info_flags |= MPU401_INFO_USE_TIMER;
|
||||
mpu->info_flags = info_flags;
|
||||
mpu->irq = irq;
|
||||
mpu->irq_flags = irq_flags;
|
||||
if (card->shortname[0])
|
||||
snprintf(rmidi->name, sizeof(rmidi->name), "%s MIDI",
|
||||
card->shortname);
|
||||
|
|
|
@ -51,7 +51,6 @@ struct isight {
|
|||
struct fw_unit *unit;
|
||||
struct fw_device *device;
|
||||
u64 audio_base;
|
||||
struct fw_address_handler iris_handler;
|
||||
struct snd_pcm_substream *pcm;
|
||||
struct mutex mutex;
|
||||
struct iso_packets_buffer buffer;
|
||||
|
|
|
@ -778,9 +778,10 @@ static int __devexit fwspk_remove(struct device *dev)
|
|||
{
|
||||
struct fwspk *fwspk = dev_get_drvdata(dev);
|
||||
|
||||
mutex_lock(&fwspk->mutex);
|
||||
amdtp_out_stream_pcm_abort(&fwspk->stream);
|
||||
snd_card_disconnect(fwspk->card);
|
||||
|
||||
mutex_lock(&fwspk->mutex);
|
||||
fwspk_stop_stream(fwspk);
|
||||
mutex_unlock(&fwspk->mutex);
|
||||
|
||||
|
@ -796,8 +797,8 @@ static void fwspk_bus_reset(struct fw_unit *unit)
|
|||
fcp_bus_reset(fwspk->unit);
|
||||
|
||||
if (cmp_connection_update(&fwspk->connection) < 0) {
|
||||
mutex_lock(&fwspk->mutex);
|
||||
amdtp_out_stream_pcm_abort(&fwspk->stream);
|
||||
mutex_lock(&fwspk->mutex);
|
||||
fwspk_stop_stream(fwspk);
|
||||
mutex_unlock(&fwspk->mutex);
|
||||
return;
|
||||
|
|
|
@ -204,7 +204,7 @@ static int __devinit snd_card_ad1816a_probe(int dev, struct pnp_card_link *pcard
|
|||
|
||||
if (mpu_port[dev] > 0) {
|
||||
if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
|
||||
mpu_port[dev], 0, mpu_irq[dev], IRQF_DISABLED,
|
||||
mpu_port[dev], 0, mpu_irq[dev],
|
||||
NULL) < 0)
|
||||
printk(KERN_ERR PFX "no MPU-401 device at 0x%lx.\n", mpu_port[dev]);
|
||||
}
|
||||
|
|
|
@ -256,7 +256,6 @@ static int __devinit snd_card_als100_probe(int dev,
|
|||
mpu_type,
|
||||
mpu_port[dev], 0,
|
||||
mpu_irq[dev],
|
||||
mpu_irq[dev] >= 0 ? IRQF_DISABLED : 0,
|
||||
NULL) < 0)
|
||||
snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
|
||||
}
|
||||
|
|
|
@ -234,8 +234,7 @@ static int __devinit snd_card_azt2320_probe(int dev,
|
|||
if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
|
||||
if (snd_mpu401_uart_new(card, 0, MPU401_HW_AZT2320,
|
||||
mpu_port[dev], 0,
|
||||
mpu_irq[dev], IRQF_DISABLED,
|
||||
NULL) < 0)
|
||||
mpu_irq[dev], NULL) < 0)
|
||||
snd_printk(KERN_ERR PFX "no MPU-401 device at 0x%lx\n", mpu_port[dev]);
|
||||
}
|
||||
|
||||
|
|
|
@ -597,7 +597,7 @@ static int __devinit snd_cmi8330_probe(struct snd_card *card, int dev)
|
|||
if (mpuport[dev] != SNDRV_AUTO_PORT) {
|
||||
if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
|
||||
mpuport[dev], 0, mpuirq[dev],
|
||||
IRQF_DISABLED, NULL) < 0)
|
||||
NULL) < 0)
|
||||
printk(KERN_ERR PFX "no MPU-401 device at 0x%lx.\n",
|
||||
mpuport[dev]);
|
||||
}
|
||||
|
|
|
@ -131,7 +131,6 @@ static int __devinit snd_cs4231_probe(struct device *dev, unsigned int n)
|
|||
mpu_irq[n] = -1;
|
||||
if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232,
|
||||
mpu_port[n], 0, mpu_irq[n],
|
||||
mpu_irq[n] >= 0 ? IRQF_DISABLED : 0,
|
||||
NULL) < 0)
|
||||
dev_warn(dev, "MPU401 not detected\n");
|
||||
}
|
||||
|
|
|
@ -449,8 +449,7 @@ static int __devinit snd_cs423x_probe(struct snd_card *card, int dev)
|
|||
mpu_irq[dev] = -1;
|
||||
if (snd_mpu401_uart_new(card, 0, MPU401_HW_CS4232,
|
||||
mpu_port[dev], 0,
|
||||
mpu_irq[dev],
|
||||
mpu_irq[dev] >= 0 ? IRQF_DISABLED : 0, NULL) < 0)
|
||||
mpu_irq[dev], NULL) < 0)
|
||||
printk(KERN_WARNING IDENT ": MPU401 not detected\n");
|
||||
}
|
||||
|
||||
|
|
|
@ -174,7 +174,7 @@ static int __devinit snd_es1688_probe(struct snd_card *card, unsigned int n)
|
|||
chip->mpu_port > 0) {
|
||||
error = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688,
|
||||
chip->mpu_port, 0,
|
||||
mpu_irq[n], IRQF_DISABLED, NULL);
|
||||
mpu_irq[n], NULL);
|
||||
if (error < 0)
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -2160,8 +2160,8 @@ static int __devinit snd_audiodrive_probe(struct snd_card *card, int dev)
|
|||
|
||||
if (mpu_port[dev] > 0 && mpu_port[dev] != SNDRV_AUTO_PORT) {
|
||||
err = snd_mpu401_uart_new(card, 0, MPU401_HW_ES18XX,
|
||||
mpu_port[dev], 0,
|
||||
irq[dev], 0, &chip->rmidi);
|
||||
mpu_port[dev], MPU401_INFO_IRQ_HOOK,
|
||||
-1, &chip->rmidi);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -585,8 +585,7 @@ static int __devinit snd_galaxy_probe(struct device *dev, unsigned int n)
|
|||
|
||||
if (mpu_port[n] >= 0) {
|
||||
err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
|
||||
mpu_port[n], 0, mpu_irq[n],
|
||||
IRQF_DISABLED, NULL);
|
||||
mpu_port[n], 0, mpu_irq[n], NULL);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
}
|
||||
|
|
|
@ -317,8 +317,7 @@ static int __devinit snd_gusextreme_probe(struct device *dev, unsigned int n)
|
|||
|
||||
if (es1688->mpu_port >= 0x300) {
|
||||
error = snd_mpu401_uart_new(card, 0, MPU401_HW_ES1688,
|
||||
es1688->mpu_port, 0,
|
||||
mpu_irq[n], IRQF_DISABLED, NULL);
|
||||
es1688->mpu_port, 0, mpu_irq[n], NULL);
|
||||
if (error < 0)
|
||||
goto out;
|
||||
}
|
||||
|
|
|
@ -600,7 +600,7 @@ static int __devinit snd_msnd_attach(struct snd_card *card)
|
|||
mpu_io[0],
|
||||
MPU401_MODE_INPUT |
|
||||
MPU401_MODE_OUTPUT,
|
||||
mpu_irq[0], IRQF_DISABLED,
|
||||
mpu_irq[0],
|
||||
&chip->rmidi);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR LOGNAME
|
||||
|
|
|
@ -707,8 +707,9 @@ static int __devinit snd_opl3sa2_probe(struct snd_card *card, int dev)
|
|||
}
|
||||
if (midi_port[dev] >= 0x300 && midi_port[dev] < 0x340) {
|
||||
if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_OPL3SA2,
|
||||
midi_port[dev], 0,
|
||||
xirq, 0, &chip->rmidi)) < 0)
|
||||
midi_port[dev],
|
||||
MPU401_INFO_IRQ_HOOK, -1,
|
||||
&chip->rmidi)) < 0)
|
||||
return err;
|
||||
}
|
||||
sprintf(card->longname, "%s at 0x%lx, irq %d, dma %d",
|
||||
|
|
|
@ -1377,8 +1377,7 @@ static int __devinit snd_miro_probe(struct snd_card *card)
|
|||
rmidi = NULL;
|
||||
else {
|
||||
error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
|
||||
mpu_port, 0, miro->mpu_irq, IRQF_DISABLED,
|
||||
&rmidi);
|
||||
mpu_port, 0, miro->mpu_irq, &rmidi);
|
||||
if (error < 0)
|
||||
snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",
|
||||
mpu_port);
|
||||
|
|
|
@ -914,7 +914,7 @@ static int __devinit snd_opti9xx_probe(struct snd_card *card)
|
|||
rmidi = NULL;
|
||||
else {
|
||||
error = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
|
||||
mpu_port, 0, mpu_irq, IRQF_DISABLED, &rmidi);
|
||||
mpu_port, 0, mpu_irq, &rmidi);
|
||||
if (error)
|
||||
snd_printk(KERN_WARNING "no MPU-401 device at 0x%lx?\n",
|
||||
mpu_port);
|
||||
|
|
|
@ -322,7 +322,6 @@ static int __devinit snd_jazz16_probe(struct device *devptr, unsigned int dev)
|
|||
MPU401_HW_MPU401,
|
||||
mpu_port[dev], 0,
|
||||
mpu_irq[dev],
|
||||
mpu_irq[dev] >= 0 ? IRQF_DISABLED : 0,
|
||||
NULL) < 0)
|
||||
snd_printk(KERN_ERR "no MPU-401 device at 0x%lx\n",
|
||||
mpu_port[dev]);
|
||||
|
|
|
@ -394,8 +394,9 @@ static int __devinit snd_sb16_probe(struct snd_card *card, int dev)
|
|||
|
||||
if (chip->mpu_port > 0 && chip->mpu_port != SNDRV_AUTO_PORT) {
|
||||
if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_SB,
|
||||
chip->mpu_port, 0,
|
||||
xirq, 0, &chip->rmidi)) < 0)
|
||||
chip->mpu_port,
|
||||
MPU401_INFO_IRQ_HOOK, -1,
|
||||
&chip->rmidi)) < 0)
|
||||
return err;
|
||||
chip->rmidi_callback = snd_mpu401_uart_interrupt;
|
||||
}
|
||||
|
|
|
@ -658,8 +658,7 @@ static int __devinit snd_sc6000_probe(struct device *devptr, unsigned int dev)
|
|||
if (snd_mpu401_uart_new(card, 0,
|
||||
MPU401_HW_MPU401,
|
||||
mpu_port[dev], 0,
|
||||
mpu_irq[dev], IRQF_DISABLED,
|
||||
NULL) < 0)
|
||||
mpu_irq[dev], NULL) < 0)
|
||||
snd_printk(KERN_ERR "no MPU-401 device at 0x%lx ?\n",
|
||||
mpu_port[dev]);
|
||||
}
|
||||
|
|
|
@ -825,8 +825,7 @@ static int __devinit create_mpu401(struct snd_card *card, int devnum,
|
|||
int err;
|
||||
|
||||
err = snd_mpu401_uart_new(card, devnum, MPU401_HW_MPU401, port,
|
||||
MPU401_INFO_INTEGRATED, irq, IRQF_DISABLED,
|
||||
&rawmidi);
|
||||
MPU401_INFO_INTEGRATED, irq, &rawmidi);
|
||||
if (err == 0) {
|
||||
struct snd_mpu401 *mpu = rawmidi->private_data;
|
||||
mpu->open_input = mpu401_open;
|
||||
|
|
|
@ -449,8 +449,7 @@ snd_wavefront_probe (struct snd_card *card, int dev)
|
|||
if (cs4232_mpu_port[dev] > 0 && cs4232_mpu_port[dev] != SNDRV_AUTO_PORT) {
|
||||
err = snd_mpu401_uart_new(card, midi_dev, MPU401_HW_CS4232,
|
||||
cs4232_mpu_port[dev], 0,
|
||||
cs4232_mpu_irq[dev], IRQF_DISABLED,
|
||||
NULL);
|
||||
cs4232_mpu_irq[dev], NULL);
|
||||
if (err < 0) {
|
||||
snd_printk (KERN_ERR "can't allocate CS4232 MPU-401 device\n");
|
||||
return err;
|
||||
|
|
|
@ -320,7 +320,7 @@ void sound_timer_init(struct sound_lowlev_timer *t, char *name)
|
|||
n = sound_alloc_timerdev();
|
||||
if (n == -1)
|
||||
n = 0; /* Overwrite the system timer */
|
||||
strcpy(sound_timer.info.name, name);
|
||||
strlcpy(sound_timer.info.name, name, sizeof(sound_timer.info.name));
|
||||
sound_timer_devs[n] = &sound_timer;
|
||||
}
|
||||
EXPORT_SYMBOL(sound_timer_init);
|
||||
|
|
|
@ -931,8 +931,9 @@ static int __devinit snd_card_als4000_probe(struct pci_dev *pci,
|
|||
|
||||
if ((err = snd_mpu401_uart_new( card, 0, MPU401_HW_ALS4000,
|
||||
iobase + ALS4K_IOB_30_MIDI_DATA,
|
||||
MPU401_INFO_INTEGRATED,
|
||||
pci->irq, 0, &chip->rmidi)) < 0) {
|
||||
MPU401_INFO_INTEGRATED |
|
||||
MPU401_INFO_IRQ_HOOK,
|
||||
-1, &chip->rmidi)) < 0) {
|
||||
printk(KERN_ERR "als4000: no MPU-401 device at 0x%lx?\n",
|
||||
iobase + ALS4K_IOB_30_MIDI_DATA);
|
||||
goto out_err;
|
||||
|
|
|
@ -84,7 +84,7 @@ static int __devinit snd_vortex_midi(vortex_t * vortex)
|
|||
#ifdef VORTEX_MPU401_LEGACY
|
||||
if ((temp =
|
||||
snd_mpu401_uart_new(vortex->card, 0, MPU401_HW_MPU401, 0x330,
|
||||
0, 0, 0, &rmidi)) != 0) {
|
||||
MPU401_INFO_IRQ_HOOK, -1, &rmidi)) != 0) {
|
||||
hwwrite(vortex->mmio, VORTEX_CTRL,
|
||||
(hwread(vortex->mmio, VORTEX_CTRL) &
|
||||
~CTRL_MIDI_PORT) & ~CTRL_MIDI_EN);
|
||||
|
@ -94,8 +94,8 @@ static int __devinit snd_vortex_midi(vortex_t * vortex)
|
|||
port = (unsigned long)(vortex->mmio + VORTEX_MIDI_DATA);
|
||||
if ((temp =
|
||||
snd_mpu401_uart_new(vortex->card, 0, MPU401_HW_AUREAL, port,
|
||||
MPU401_INFO_INTEGRATED | MPU401_INFO_MMIO,
|
||||
0, 0, &rmidi)) != 0) {
|
||||
MPU401_INFO_INTEGRATED | MPU401_INFO_MMIO |
|
||||
MPU401_INFO_IRQ_HOOK, -1, &rmidi)) != 0) {
|
||||
hwwrite(vortex->mmio, VORTEX_CTRL,
|
||||
(hwread(vortex->mmio, VORTEX_CTRL) &
|
||||
~CTRL_MIDI_PORT) & ~CTRL_MIDI_EN);
|
||||
|
|
|
@ -2652,8 +2652,9 @@ snd_azf3328_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
|
|||
since our hardware ought to be similar, thus use same ID. */
|
||||
err = snd_mpu401_uart_new(
|
||||
card, 0,
|
||||
MPU401_HW_AZT2320, chip->mpu_io, MPU401_INFO_INTEGRATED,
|
||||
pci->irq, 0, &chip->rmidi
|
||||
MPU401_HW_AZT2320, chip->mpu_io,
|
||||
MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK,
|
||||
-1, &chip->rmidi
|
||||
);
|
||||
if (err < 0) {
|
||||
snd_printk(KERN_ERR "azf3328: no MPU-401 device at 0x%lx?\n",
|
||||
|
|
|
@ -3228,8 +3228,9 @@ static int __devinit snd_cmipci_create(struct snd_card *card, struct pci_dev *pc
|
|||
if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI,
|
||||
iomidi,
|
||||
(integrated_midi ?
|
||||
MPU401_INFO_INTEGRATED : 0),
|
||||
cm->irq, 0, &cm->rmidi)) < 0) {
|
||||
MPU401_INFO_INTEGRATED : 0) |
|
||||
MPU401_INFO_IRQ_HOOK,
|
||||
-1, &cm->rmidi)) < 0) {
|
||||
printk(KERN_ERR "cmipci: no UART401 device at 0x%lx\n", iomidi);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -404,7 +404,7 @@ int ct_alsa_pcm_create(struct ct_atc *atc,
|
|||
int err;
|
||||
int playback_count, capture_count;
|
||||
|
||||
playback_count = (IEC958 == device) ? 1 : 8;
|
||||
playback_count = (IEC958 == device) ? 1 : 256;
|
||||
capture_count = (FRONT == device) ? 1 : 0;
|
||||
err = snd_pcm_new(atc->card, "ctxfi", device,
|
||||
playback_count, capture_count, &pcm);
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
#include "cthardware.h"
|
||||
#include <linux/slab.h>
|
||||
|
||||
#define SRC_RESOURCE_NUM 64
|
||||
#define SRC_RESOURCE_NUM 256
|
||||
#define SRCIMP_RESOURCE_NUM 256
|
||||
|
||||
static unsigned int conj_mask;
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
#ifndef CTVMEM_H
|
||||
#define CTVMEM_H
|
||||
|
||||
#define CT_PTP_NUM 1 /* num of device page table pages */
|
||||
#define CT_PTP_NUM 4 /* num of device page table pages */
|
||||
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/list.h>
|
||||
|
|
|
@ -1146,6 +1146,11 @@ static int snd_emu10k1_playback_open(struct snd_pcm_substream *substream)
|
|||
kfree(epcm);
|
||||
return err;
|
||||
}
|
||||
err = snd_pcm_hw_rule_noresample(runtime, 48000);
|
||||
if (err < 0) {
|
||||
kfree(epcm);
|
||||
return err;
|
||||
}
|
||||
mix = &emu->pcm_mixer[substream->number];
|
||||
for (i = 0; i < 4; i++)
|
||||
mix->send_routing[0][i] = mix->send_routing[1][i] = mix->send_routing[2][i] = i;
|
||||
|
|
|
@ -1854,8 +1854,9 @@ static int __devinit snd_es1938_probe(struct pci_dev *pci,
|
|||
}
|
||||
}
|
||||
if (snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
|
||||
chip->mpu_port, MPU401_INFO_INTEGRATED,
|
||||
chip->irq, 0, &chip->rmidi) < 0) {
|
||||
chip->mpu_port,
|
||||
MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK,
|
||||
-1, &chip->rmidi) < 0) {
|
||||
printk(KERN_ERR "es1938: unable to initialize MPU-401\n");
|
||||
} else {
|
||||
// this line is vital for MIDI interrupt handling on ess-solo1
|
||||
|
|
|
@ -2843,8 +2843,9 @@ static int __devinit snd_es1968_probe(struct pci_dev *pci,
|
|||
if (enable_mpu[dev]) {
|
||||
if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_MPU401,
|
||||
chip->io_port + ESM_MPU401_PORT,
|
||||
MPU401_INFO_INTEGRATED,
|
||||
chip->irq, 0, &chip->rmidi)) < 0) {
|
||||
MPU401_INFO_INTEGRATED |
|
||||
MPU401_INFO_IRQ_HOOK,
|
||||
-1, &chip->rmidi)) < 0) {
|
||||
printk(KERN_WARNING "es1968: skipping MPU-401 MIDI support..\n");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -729,11 +729,14 @@ static struct snd_fm801_tea575x_gpio snd_fm801_tea575x_gpios[] = {
|
|||
{ .data = 2, .clk = 0, .wren = 1, .most = 3, .name = "SF64-PCR" },
|
||||
};
|
||||
|
||||
#define get_tea575x_gpio(chip) \
|
||||
(&snd_fm801_tea575x_gpios[((chip)->tea575x_tuner & TUNER_TYPE_MASK) - 1])
|
||||
|
||||
static void snd_fm801_tea575x_set_pins(struct snd_tea575x *tea, u8 pins)
|
||||
{
|
||||
struct fm801 *chip = tea->private_data;
|
||||
unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL));
|
||||
struct snd_fm801_tea575x_gpio gpio = snd_fm801_tea575x_gpios[(chip->tea575x_tuner & TUNER_TYPE_MASK) - 1];
|
||||
struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip);
|
||||
|
||||
reg &= ~(FM801_GPIO_GP(gpio.data) |
|
||||
FM801_GPIO_GP(gpio.clk) |
|
||||
|
@ -751,7 +754,7 @@ static u8 snd_fm801_tea575x_get_pins(struct snd_tea575x *tea)
|
|||
{
|
||||
struct fm801 *chip = tea->private_data;
|
||||
unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL));
|
||||
struct snd_fm801_tea575x_gpio gpio = snd_fm801_tea575x_gpios[(chip->tea575x_tuner & TUNER_TYPE_MASK) - 1];
|
||||
struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip);
|
||||
|
||||
return (reg & FM801_GPIO_GP(gpio.data)) ? TEA575X_DATA : 0 |
|
||||
(reg & FM801_GPIO_GP(gpio.most)) ? TEA575X_MOST : 0;
|
||||
|
@ -761,7 +764,7 @@ static void snd_fm801_tea575x_set_direction(struct snd_tea575x *tea, bool output
|
|||
{
|
||||
struct fm801 *chip = tea->private_data;
|
||||
unsigned short reg = inw(FM801_REG(chip, GPIO_CTRL));
|
||||
struct snd_fm801_tea575x_gpio gpio = snd_fm801_tea575x_gpios[(chip->tea575x_tuner & TUNER_TYPE_MASK) - 1];
|
||||
struct snd_fm801_tea575x_gpio gpio = *get_tea575x_gpio(chip);
|
||||
|
||||
/* use GPIO lines and set write enable bit */
|
||||
reg |= FM801_GPIO_GS(gpio.data) |
|
||||
|
@ -1246,7 +1249,7 @@ static int __devinit snd_fm801_create(struct snd_card *card,
|
|||
chip->tea575x_tuner = tea575x_tuner;
|
||||
if (!snd_tea575x_init(&chip->tea)) {
|
||||
snd_printk(KERN_INFO "detected TEA575x radio type %s\n",
|
||||
snd_fm801_tea575x_gpios[tea575x_tuner - 1].name);
|
||||
get_tea575x_gpio(chip)->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1256,9 +1259,7 @@ static int __devinit snd_fm801_create(struct snd_card *card,
|
|||
}
|
||||
}
|
||||
if (!(chip->tea575x_tuner & TUNER_DISABLED)) {
|
||||
strlcpy(chip->tea.card,
|
||||
snd_fm801_tea575x_gpios[(tea575x_tuner &
|
||||
TUNER_TYPE_MASK) - 1].name,
|
||||
strlcpy(chip->tea.card, get_tea575x_gpio(chip)->name,
|
||||
sizeof(chip->tea.card));
|
||||
}
|
||||
#endif
|
||||
|
@ -1311,8 +1312,9 @@ static int __devinit snd_card_fm801_probe(struct pci_dev *pci,
|
|||
}
|
||||
if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_FM801,
|
||||
FM801_REG(chip, MPU401_DATA),
|
||||
MPU401_INFO_INTEGRATED,
|
||||
chip->irq, 0, &chip->rmidi)) < 0) {
|
||||
MPU401_INFO_INTEGRATED |
|
||||
MPU401_INFO_IRQ_HOOK,
|
||||
-1, &chip->rmidi)) < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -2748,8 +2748,9 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci,
|
|||
if (!c->no_mpu401) {
|
||||
err = snd_mpu401_uart_new(card, 0, MPU401_HW_ICE1712,
|
||||
ICEREG(ice, MPU1_CTRL),
|
||||
(c->mpu401_1_info_flags | MPU401_INFO_INTEGRATED),
|
||||
ice->irq, 0, &ice->rmidi[0]);
|
||||
c->mpu401_1_info_flags |
|
||||
MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK,
|
||||
-1, &ice->rmidi[0]);
|
||||
if (err < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
|
@ -2764,8 +2765,9 @@ static int __devinit snd_ice1712_probe(struct pci_dev *pci,
|
|||
/* 2nd port used */
|
||||
err = snd_mpu401_uart_new(card, 1, MPU401_HW_ICE1712,
|
||||
ICEREG(ice, MPU2_CTRL),
|
||||
(c->mpu401_2_info_flags | MPU401_INFO_INTEGRATED),
|
||||
ice->irq, 0, &ice->rmidi[1]);
|
||||
c->mpu401_2_info_flags |
|
||||
MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK,
|
||||
-1, &ice->rmidi[1]);
|
||||
|
||||
if (err < 0) {
|
||||
snd_card_free(card);
|
||||
|
|
|
@ -2820,8 +2820,8 @@ snd_m3_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
|
|||
/* TODO enable MIDI IRQ and I/O */
|
||||
err = snd_mpu401_uart_new(chip->card, 0, MPU401_HW_MPU401,
|
||||
chip->iobase + MPU401_DATA_PORT,
|
||||
MPU401_INFO_INTEGRATED,
|
||||
chip->irq, 0, &chip->rmidi);
|
||||
MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK,
|
||||
-1, &chip->rmidi);
|
||||
if (err < 0)
|
||||
printk(KERN_WARNING "maestro3: no MIDI support.\n");
|
||||
#endif
|
||||
|
|
|
@ -678,15 +678,15 @@ int oxygen_pci_probe(struct pci_dev *pci, int index, char *id,
|
|||
goto err_card;
|
||||
|
||||
if (chip->model.device_config & (MIDI_OUTPUT | MIDI_INPUT)) {
|
||||
unsigned int info_flags = MPU401_INFO_INTEGRATED;
|
||||
unsigned int info_flags =
|
||||
MPU401_INFO_INTEGRATED | MPU401_INFO_IRQ_HOOK;
|
||||
if (chip->model.device_config & MIDI_OUTPUT)
|
||||
info_flags |= MPU401_INFO_OUTPUT;
|
||||
if (chip->model.device_config & MIDI_INPUT)
|
||||
info_flags |= MPU401_INFO_INPUT;
|
||||
err = snd_mpu401_uart_new(card, 0, MPU401_HW_CMIPCI,
|
||||
chip->addr + OXYGEN_MPU401,
|
||||
info_flags, 0, 0,
|
||||
&chip->midi);
|
||||
info_flags, -1, &chip->midi);
|
||||
if (err < 0)
|
||||
goto err_card;
|
||||
}
|
||||
|
|
|
@ -1074,6 +1074,7 @@ static const struct oxygen_model model_xonar_st = {
|
|||
.device_config = PLAYBACK_0_TO_I2S |
|
||||
PLAYBACK_1_TO_SPDIF |
|
||||
CAPTURE_0_FROM_I2S_2 |
|
||||
CAPTURE_1_FROM_SPDIF |
|
||||
AC97_FMIC_SWITCH,
|
||||
.dac_channels_pcm = 2,
|
||||
.dac_channels_mixer = 2,
|
||||
|
|
|
@ -2109,7 +2109,7 @@ snd_card_riptide_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
|
|||
val = mpu_port[dev];
|
||||
pci_write_config_word(chip->pci, PCI_EXT_MPU_Base, val);
|
||||
err = snd_mpu401_uart_new(card, 0, MPU401_HW_RIPTIDE,
|
||||
val, 0, chip->irq, 0,
|
||||
val, MPU401_INFO_IRQ_HOOK, -1,
|
||||
&chip->rmidi);
|
||||
if (err < 0)
|
||||
snd_printk(KERN_WARNING
|
||||
|
|
|
@ -1241,10 +1241,30 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm)
|
|||
return rate;
|
||||
}
|
||||
|
||||
/* return latency in samples per period */
|
||||
static int hdspm_get_latency(struct hdspm *hdspm)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = hdspm_decode_latency(hdspm->control_register);
|
||||
|
||||
/* Special case for new RME cards with 32 samples period size.
|
||||
* The three latency bits in the control register
|
||||
* (HDSP_LatencyMask) encode latency values of 64 samples as
|
||||
* 0, 128 samples as 1 ... 4096 samples as 6. For old cards, 7
|
||||
* denotes 8192 samples, but on new cards like RayDAT or AIO,
|
||||
* it corresponds to 32 samples.
|
||||
*/
|
||||
if ((7 == n) && (RayDAT == hdspm->io_type || AIO == hdspm->io_type))
|
||||
n = -1;
|
||||
|
||||
return 1 << (n + 6);
|
||||
}
|
||||
|
||||
/* Latency function */
|
||||
static inline void hdspm_compute_period_size(struct hdspm *hdspm)
|
||||
{
|
||||
hdspm->period_bytes = 1 << ((hdspm_decode_latency(hdspm->control_register) + 8));
|
||||
hdspm->period_bytes = 4 * hdspm_get_latency(hdspm);
|
||||
}
|
||||
|
||||
|
||||
|
@ -1303,12 +1323,27 @@ static int hdspm_set_interrupt_interval(struct hdspm *s, unsigned int frames)
|
|||
|
||||
spin_lock_irq(&s->lock);
|
||||
|
||||
frames >>= 7;
|
||||
n = 0;
|
||||
while (frames) {
|
||||
n++;
|
||||
frames >>= 1;
|
||||
if (32 == frames) {
|
||||
/* Special case for new RME cards like RayDAT/AIO which
|
||||
* support period sizes of 32 samples. Since latency is
|
||||
* encoded in the three bits of HDSP_LatencyMask, we can only
|
||||
* have values from 0 .. 7. While 0 still means 64 samples and
|
||||
* 6 represents 4096 samples on all cards, 7 represents 8192
|
||||
* on older cards and 32 samples on new cards.
|
||||
*
|
||||
* In other words, period size in samples is calculated by
|
||||
* 2^(n+6) with n ranging from 0 .. 7.
|
||||
*/
|
||||
n = 7;
|
||||
} else {
|
||||
frames >>= 7;
|
||||
n = 0;
|
||||
while (frames) {
|
||||
n++;
|
||||
frames >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
s->control_register &= ~HDSPM_LatencyMask;
|
||||
s->control_register |= hdspm_encode_latency(n);
|
||||
|
||||
|
@ -4801,8 +4836,7 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry,
|
|||
|
||||
snd_iprintf(buffer, "--- Settings ---\n");
|
||||
|
||||
x = 1 << (6 + hdspm_decode_latency(hdspm->control_register &
|
||||
HDSPM_LatencyMask));
|
||||
x = hdspm_get_latency(hdspm);
|
||||
|
||||
snd_iprintf(buffer,
|
||||
"Size (Latency): %d samples (2 periods of %lu bytes)\n",
|
||||
|
@ -4965,8 +4999,7 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry,
|
|||
|
||||
snd_iprintf(buffer, "--- Settings ---\n");
|
||||
|
||||
x = 1 << (6 + hdspm_decode_latency(hdspm->control_register &
|
||||
HDSPM_LatencyMask));
|
||||
x = hdspm_get_latency(hdspm);
|
||||
|
||||
snd_iprintf(buffer,
|
||||
"Size (Latency): %d samples (2 periods of %lu bytes)\n",
|
||||
|
@ -5672,19 +5705,6 @@ static int snd_hdspm_prepare(struct snd_pcm_substream *substream)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static unsigned int period_sizes_old[] = {
|
||||
64, 128, 256, 512, 1024, 2048, 4096
|
||||
};
|
||||
|
||||
static unsigned int period_sizes_new[] = {
|
||||
32, 64, 128, 256, 512, 1024, 2048, 4096
|
||||
};
|
||||
|
||||
/* RayDAT and AIO always have a buffer of 16384 samples per channel */
|
||||
static unsigned int raydat_aio_buffer_sizes[] = {
|
||||
16384
|
||||
};
|
||||
|
||||
static struct snd_pcm_hardware snd_hdspm_playback_subinfo = {
|
||||
.info = (SNDRV_PCM_INFO_MMAP |
|
||||
SNDRV_PCM_INFO_MMAP_VALID |
|
||||
|
@ -5703,8 +5723,8 @@ static struct snd_pcm_hardware snd_hdspm_playback_subinfo = {
|
|||
.channels_max = HDSPM_MAX_CHANNELS,
|
||||
.buffer_bytes_max =
|
||||
HDSPM_CHANNEL_BUFFER_BYTES * HDSPM_MAX_CHANNELS,
|
||||
.period_bytes_min = (64 * 4),
|
||||
.period_bytes_max = (4096 * 4) * HDSPM_MAX_CHANNELS,
|
||||
.period_bytes_min = (32 * 4),
|
||||
.period_bytes_max = (8192 * 4) * HDSPM_MAX_CHANNELS,
|
||||
.periods_min = 2,
|
||||
.periods_max = 512,
|
||||
.fifo_size = 0
|
||||
|
@ -5728,31 +5748,13 @@ static struct snd_pcm_hardware snd_hdspm_capture_subinfo = {
|
|||
.channels_max = HDSPM_MAX_CHANNELS,
|
||||
.buffer_bytes_max =
|
||||
HDSPM_CHANNEL_BUFFER_BYTES * HDSPM_MAX_CHANNELS,
|
||||
.period_bytes_min = (64 * 4),
|
||||
.period_bytes_max = (4096 * 4) * HDSPM_MAX_CHANNELS,
|
||||
.period_bytes_min = (32 * 4),
|
||||
.period_bytes_max = (8192 * 4) * HDSPM_MAX_CHANNELS,
|
||||
.periods_min = 2,
|
||||
.periods_max = 512,
|
||||
.fifo_size = 0
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes_old = {
|
||||
.count = ARRAY_SIZE(period_sizes_old),
|
||||
.list = period_sizes_old,
|
||||
.mask = 0
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list hw_constraints_period_sizes_new = {
|
||||
.count = ARRAY_SIZE(period_sizes_new),
|
||||
.list = period_sizes_new,
|
||||
.mask = 0
|
||||
};
|
||||
|
||||
static struct snd_pcm_hw_constraint_list hw_constraints_raydat_io_buffer = {
|
||||
.count = ARRAY_SIZE(raydat_aio_buffer_sizes),
|
||||
.list = raydat_aio_buffer_sizes,
|
||||
.mask = 0
|
||||
};
|
||||
|
||||
static int snd_hdspm_hw_rule_in_channels_rate(struct snd_pcm_hw_params *params,
|
||||
struct snd_pcm_hw_rule *rule)
|
||||
{
|
||||
|
@ -5953,26 +5955,29 @@ static int snd_hdspm_playback_open(struct snd_pcm_substream *substream)
|
|||
spin_unlock_irq(&hdspm->lock);
|
||||
|
||||
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
|
||||
snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
|
||||
|
||||
switch (hdspm->io_type) {
|
||||
case AIO:
|
||||
case RayDAT:
|
||||
snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
|
||||
&hw_constraints_period_sizes_new);
|
||||
snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
|
||||
&hw_constraints_raydat_io_buffer);
|
||||
|
||||
snd_pcm_hw_constraint_minmax(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
|
||||
32, 4096);
|
||||
/* RayDAT & AIO have a fixed buffer of 16384 samples per channel */
|
||||
snd_pcm_hw_constraint_minmax(runtime,
|
||||
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
|
||||
16384, 16384);
|
||||
break;
|
||||
|
||||
default:
|
||||
snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
|
||||
&hw_constraints_period_sizes_old);
|
||||
snd_pcm_hw_constraint_minmax(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
|
||||
64, 8192);
|
||||
break;
|
||||
}
|
||||
|
||||
if (AES32 == hdspm->io_type) {
|
||||
runtime->hw.rates |= SNDRV_PCM_RATE_KNOT;
|
||||
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
|
||||
&hdspm_hw_constraints_aes32_sample_rates);
|
||||
} else {
|
||||
|
@ -6025,24 +6030,28 @@ static int snd_hdspm_capture_open(struct snd_pcm_substream *substream)
|
|||
spin_unlock_irq(&hdspm->lock);
|
||||
|
||||
snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
|
||||
snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE);
|
||||
|
||||
switch (hdspm->io_type) {
|
||||
case AIO:
|
||||
case RayDAT:
|
||||
snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
|
||||
&hw_constraints_period_sizes_new);
|
||||
snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
|
||||
&hw_constraints_raydat_io_buffer);
|
||||
break;
|
||||
snd_pcm_hw_constraint_minmax(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
|
||||
32, 4096);
|
||||
snd_pcm_hw_constraint_minmax(runtime,
|
||||
SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
|
||||
16384, 16384);
|
||||
break;
|
||||
|
||||
default:
|
||||
snd_pcm_hw_constraint_list(runtime, 0,
|
||||
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
|
||||
&hw_constraints_period_sizes_old);
|
||||
snd_pcm_hw_constraint_minmax(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
|
||||
64, 8192);
|
||||
break;
|
||||
}
|
||||
|
||||
if (AES32 == hdspm->io_type) {
|
||||
runtime->hw.rates |= SNDRV_PCM_RATE_KNOT;
|
||||
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
|
||||
&hdspm_hw_constraints_aes32_sample_rates);
|
||||
} else {
|
||||
|
@ -6088,7 +6097,7 @@ static inline int copy_u32_le(void __user *dest, void __iomem *src)
|
|||
}
|
||||
|
||||
static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
|
||||
unsigned int cmd, unsigned long __user arg)
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
void __user *argp = (void __user *)arg;
|
||||
struct hdspm *hdspm = hw->private_data;
|
||||
|
@ -6213,11 +6222,13 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
|
|||
info.line_out = hdspm_line_out(hdspm);
|
||||
info.passthru = 0;
|
||||
spin_unlock_irq(&hdspm->lock);
|
||||
if (copy_to_user((void __user *) arg, &info, sizeof(info)))
|
||||
if (copy_to_user(argp, &info, sizeof(info)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
|
||||
case SNDRV_HDSPM_IOCTL_GET_STATUS:
|
||||
memset(&status, 0, sizeof(status));
|
||||
|
||||
status.card_type = hdspm->io_type;
|
||||
|
||||
status.autosync_source = hdspm_autosync_ref(hdspm);
|
||||
|
@ -6250,13 +6261,15 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
|
|||
break;
|
||||
}
|
||||
|
||||
if (copy_to_user((void __user *) arg, &status, sizeof(status)))
|
||||
if (copy_to_user(argp, &status, sizeof(status)))
|
||||
return -EFAULT;
|
||||
|
||||
|
||||
break;
|
||||
|
||||
case SNDRV_HDSPM_IOCTL_GET_VERSION:
|
||||
memset(&hdspm_version, 0, sizeof(hdspm_version));
|
||||
|
||||
hdspm_version.card_type = hdspm->io_type;
|
||||
strncpy(hdspm_version.cardname, hdspm->card_name,
|
||||
sizeof(hdspm_version.cardname));
|
||||
|
@ -6267,13 +6280,13 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
|
|||
if (hdspm->tco)
|
||||
hdspm_version.addons |= HDSPM_ADDON_TCO;
|
||||
|
||||
if (copy_to_user((void __user *) arg, &hdspm_version,
|
||||
if (copy_to_user(argp, &hdspm_version,
|
||||
sizeof(hdspm_version)))
|
||||
return -EFAULT;
|
||||
break;
|
||||
|
||||
case SNDRV_HDSPM_IOCTL_GET_MIXER:
|
||||
if (copy_from_user(&mixer, (void __user *)arg, sizeof(mixer)))
|
||||
if (copy_from_user(&mixer, argp, sizeof(mixer)))
|
||||
return -EFAULT;
|
||||
if (copy_to_user((void __user *)mixer.mixer, hdspm->mixer,
|
||||
sizeof(struct hdspm_mixer)))
|
||||
|
|
|
@ -1493,9 +1493,10 @@ static int __devinit snd_sonic_probe(struct pci_dev *pci,
|
|||
return err;
|
||||
}
|
||||
if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_SONICVIBES,
|
||||
sonic->midi_port, MPU401_INFO_INTEGRATED,
|
||||
sonic->irq, 0,
|
||||
&midi_uart)) < 0) {
|
||||
sonic->midi_port,
|
||||
MPU401_INFO_INTEGRATED |
|
||||
MPU401_INFO_IRQ_HOOK,
|
||||
-1, &midi_uart)) < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -148,8 +148,9 @@ static int __devinit snd_trident_probe(struct pci_dev *pci,
|
|||
if (trident->device != TRIDENT_DEVICE_ID_SI7018 &&
|
||||
(err = snd_mpu401_uart_new(card, 0, MPU401_HW_TRID4DWAVE,
|
||||
trident->midi_port,
|
||||
MPU401_INFO_INTEGRATED,
|
||||
trident->irq, 0, &trident->rmidi)) < 0) {
|
||||
MPU401_INFO_INTEGRATED |
|
||||
MPU401_INFO_IRQ_HOOK,
|
||||
-1, &trident->rmidi)) < 0) {
|
||||
snd_card_free(card);
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -1175,6 +1175,7 @@ static int snd_via82xx_pcm_open(struct via82xx *chip, struct viadev *viadev,
|
|||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
int err;
|
||||
struct via_rate_lock *ratep;
|
||||
bool use_src = false;
|
||||
|
||||
runtime->hw = snd_via82xx_hw;
|
||||
|
||||
|
@ -1196,6 +1197,7 @@ static int snd_via82xx_pcm_open(struct via82xx *chip, struct viadev *viadev,
|
|||
SNDRV_PCM_RATE_8000_48000);
|
||||
runtime->hw.rate_min = 8000;
|
||||
runtime->hw.rate_max = 48000;
|
||||
use_src = true;
|
||||
} else if (! ratep->rate) {
|
||||
int idx = viadev->direction ? AC97_RATES_ADC : AC97_RATES_FRONT_DAC;
|
||||
runtime->hw.rates = chip->ac97->rates[idx];
|
||||
|
@ -1212,6 +1214,12 @@ static int snd_via82xx_pcm_open(struct via82xx *chip, struct viadev *viadev,
|
|||
if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
|
||||
return err;
|
||||
|
||||
if (use_src) {
|
||||
err = snd_pcm_hw_rule_noresample(runtime, 48000);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
|
||||
runtime->private_data = viadev;
|
||||
viadev->substream = substream;
|
||||
|
||||
|
@ -2068,8 +2076,9 @@ static int __devinit snd_via686_init_misc(struct via82xx *chip)
|
|||
pci_write_config_byte(chip->pci, VIA_PNP_CONTROL, legacy_cfg);
|
||||
if (chip->mpu_res) {
|
||||
if (snd_mpu401_uart_new(chip->card, 0, MPU401_HW_VIA686A,
|
||||
mpu_port, MPU401_INFO_INTEGRATED,
|
||||
chip->irq, 0, &chip->rmidi) < 0) {
|
||||
mpu_port, MPU401_INFO_INTEGRATED |
|
||||
MPU401_INFO_IRQ_HOOK, -1,
|
||||
&chip->rmidi) < 0) {
|
||||
printk(KERN_WARNING "unable to initialize MPU-401"
|
||||
" at 0x%lx, skipping\n", mpu_port);
|
||||
legacy &= ~VIA_FUNC_ENABLE_MIDI;
|
||||
|
|
|
@ -305,8 +305,9 @@ static int __devinit snd_card_ymfpci_probe(struct pci_dev *pci,
|
|||
if (chip->mpu_res) {
|
||||
if ((err = snd_mpu401_uart_new(card, 0, MPU401_HW_YMFPCI,
|
||||
mpu_port[dev],
|
||||
MPU401_INFO_INTEGRATED,
|
||||
pci->irq, 0, &chip->rawmidi)) < 0) {
|
||||
MPU401_INFO_INTEGRATED |
|
||||
MPU401_INFO_IRQ_HOOK,
|
||||
-1, &chip->rawmidi)) < 0) {
|
||||
printk(KERN_WARNING "ymfpci: cannot initialize MPU401 at 0x%lx, skipping...\n", mpu_port[dev]);
|
||||
legacy_ctrl &= ~YMFPCI_LEGACY_MIEN; /* disable MPU401 irq */
|
||||
pci_write_config_word(pci, PCIR_DSXG_LEGACY, legacy_ctrl);
|
||||
|
|
|
@ -897,6 +897,18 @@ static int snd_ymfpci_playback_open_1(struct snd_pcm_substream *substream)
|
|||
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_ymfpci_pcm *ypcm;
|
||||
int err;
|
||||
|
||||
runtime->hw = snd_ymfpci_playback;
|
||||
/* FIXME? True value is 256/48 = 5.33333 ms */
|
||||
err = snd_pcm_hw_constraint_minmax(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIOD_TIME,
|
||||
5334, UINT_MAX);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_pcm_hw_rule_noresample(runtime, 48000);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
ypcm = kzalloc(sizeof(*ypcm), GFP_KERNEL);
|
||||
if (ypcm == NULL)
|
||||
|
@ -904,11 +916,8 @@ static int snd_ymfpci_playback_open_1(struct snd_pcm_substream *substream)
|
|||
ypcm->chip = chip;
|
||||
ypcm->type = PLAYBACK_VOICE;
|
||||
ypcm->substream = substream;
|
||||
runtime->hw = snd_ymfpci_playback;
|
||||
runtime->private_data = ypcm;
|
||||
runtime->private_free = snd_ymfpci_pcm_free_substream;
|
||||
/* FIXME? True value is 256/48 = 5.33333 ms */
|
||||
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1013,6 +1022,18 @@ static int snd_ymfpci_capture_open(struct snd_pcm_substream *substream,
|
|||
struct snd_ymfpci *chip = snd_pcm_substream_chip(substream);
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_ymfpci_pcm *ypcm;
|
||||
int err;
|
||||
|
||||
runtime->hw = snd_ymfpci_capture;
|
||||
/* FIXME? True value is 256/48 = 5.33333 ms */
|
||||
err = snd_pcm_hw_constraint_minmax(runtime,
|
||||
SNDRV_PCM_HW_PARAM_PERIOD_TIME,
|
||||
5334, UINT_MAX);
|
||||
if (err < 0)
|
||||
return err;
|
||||
err = snd_pcm_hw_rule_noresample(runtime, 48000);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
ypcm = kzalloc(sizeof(*ypcm), GFP_KERNEL);
|
||||
if (ypcm == NULL)
|
||||
|
@ -1022,9 +1043,6 @@ static int snd_ymfpci_capture_open(struct snd_pcm_substream *substream,
|
|||
ypcm->substream = substream;
|
||||
ypcm->capture_bank_number = capture_bank_number;
|
||||
chip->capture_substream[capture_bank_number] = substream;
|
||||
runtime->hw = snd_ymfpci_capture;
|
||||
/* FIXME? True value is 256/48 = 5.33333 ms */
|
||||
snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_TIME, 5333, UINT_MAX);
|
||||
runtime->private_data = ypcm;
|
||||
runtime->private_free = snd_ymfpci_pcm_free_substream;
|
||||
snd_ymfpci_hw_start(chip);
|
||||
|
@ -1615,7 +1633,7 @@ YMFPCI_DOUBLE("ADC Playback Volume", 0, YDSXGR_PRIADCOUTVOL),
|
|||
YMFPCI_DOUBLE("ADC Capture Volume", 0, YDSXGR_PRIADCLOOPVOL),
|
||||
YMFPCI_DOUBLE("ADC Playback Volume", 1, YDSXGR_SECADCOUTVOL),
|
||||
YMFPCI_DOUBLE("ADC Capture Volume", 1, YDSXGR_SECADCLOOPVOL),
|
||||
YMFPCI_DOUBLE("FM Legacy Volume", 0, YDSXGR_LEGACYOUTVOL),
|
||||
YMFPCI_DOUBLE("FM Legacy Playback Volume", 0, YDSXGR_LEGACYOUTVOL),
|
||||
YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("AC97 ", PLAYBACK,VOLUME), 0, YDSXGR_ZVOUTVOL),
|
||||
YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("", CAPTURE,VOLUME), 0, YDSXGR_ZVLOOPVOL),
|
||||
YMFPCI_DOUBLE(SNDRV_CTL_NAME_IEC958("AC97 ",PLAYBACK,VOLUME), 1, YDSXGR_SPDIFOUTVOL),
|
||||
|
|
|
@ -82,7 +82,6 @@ static int keywest_attach_adapter(struct i2c_adapter *adapter)
|
|||
|
||||
static int keywest_remove(struct i2c_client *client)
|
||||
{
|
||||
i2c_set_clientdata(client, NULL);
|
||||
if (! keywest_ctx)
|
||||
return 0;
|
||||
if (client == keywest_ctx->client)
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/bitrev.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include "firmware.h"
|
||||
#include "chip.h"
|
||||
|
@ -59,21 +60,19 @@ struct ihex_record {
|
|||
unsigned int txt_offset; /* current position in txt_data */
|
||||
};
|
||||
|
||||
static u8 usb6fire_fw_ihex_nibble(const u8 n)
|
||||
{
|
||||
if (n >= '0' && n <= '9')
|
||||
return n - '0';
|
||||
else if (n >= 'A' && n <= 'F')
|
||||
return n - ('A' - 10);
|
||||
else if (n >= 'a' && n <= 'f')
|
||||
return n - ('a' - 10);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u8 usb6fire_fw_ihex_hex(const u8 *data, u8 *crc)
|
||||
{
|
||||
u8 val = (usb6fire_fw_ihex_nibble(data[0]) << 4) |
|
||||
usb6fire_fw_ihex_nibble(data[1]);
|
||||
u8 val = 0;
|
||||
int hval;
|
||||
|
||||
hval = hex_to_bin(data[0]);
|
||||
if (hval >= 0)
|
||||
val |= (hval << 4);
|
||||
|
||||
hval = hex_to_bin(data[1]);
|
||||
if (hval >= 0)
|
||||
val |= hval;
|
||||
|
||||
*crc += val;
|
||||
return val;
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ config SND_USB_CAIAQ
|
|||
* Native Instruments Guitar Rig mobile
|
||||
* Native Instruments Traktor Kontrol X1
|
||||
* Native Instruments Traktor Kontrol S4
|
||||
* Native Instruments Maschine Controller
|
||||
|
||||
To compile this driver as a module, choose M here: the module
|
||||
will be called snd-usb-caiaq.
|
||||
|
@ -85,6 +86,7 @@ config SND_USB_CAIAQ_INPUT
|
|||
* Native Instruments Kore Controller 2
|
||||
* Native Instruments Audio Kontrol 1
|
||||
* Native Instruments Traktor Kontrol S4
|
||||
* Native Instruments Maschine Controller
|
||||
|
||||
config SND_USB_US122L
|
||||
tristate "Tascam US-122L USB driver"
|
||||
|
|
|
@ -3,16 +3,16 @@
|
|||
#
|
||||
|
||||
snd-usb-audio-objs := card.o \
|
||||
clock.o \
|
||||
endpoint.o \
|
||||
format.o \
|
||||
helper.o \
|
||||
mixer.o \
|
||||
mixer_quirks.o \
|
||||
pcm.o \
|
||||
proc.o \
|
||||
quirks.o \
|
||||
format.o \
|
||||
endpoint.o \
|
||||
urb.o \
|
||||
pcm.o \
|
||||
helper.o \
|
||||
clock.o
|
||||
stream.o
|
||||
|
||||
snd-usbmidi-lib-objs := midi.o
|
||||
|
||||
|
|
|
@ -50,7 +50,8 @@ MODULE_SUPPORTED_DEVICE("{{Native Instruments, RigKontrol2},"
|
|||
"{Native Instruments, Session I/O},"
|
||||
"{Native Instruments, GuitarRig mobile}"
|
||||
"{Native Instruments, Traktor Kontrol X1}"
|
||||
"{Native Instruments, Traktor Kontrol S4}");
|
||||
"{Native Instruments, Traktor Kontrol S4}"
|
||||
"{Native Instruments, Maschine Controller}");
|
||||
|
||||
static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-max */
|
||||
static char* id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* Id for this card */
|
||||
|
@ -146,6 +147,11 @@ static struct usb_device_id snd_usb_id_table[] = {
|
|||
.idVendor = USB_VID_NATIVEINSTRUMENTS,
|
||||
.idProduct = USB_PID_TRAKTORAUDIO2
|
||||
},
|
||||
{
|
||||
.match_flags = USB_DEVICE_ID_MATCH_DEVICE,
|
||||
.idVendor = USB_VID_NATIVEINSTRUMENTS,
|
||||
.idProduct = USB_PID_MASCHINECONTROLLER
|
||||
},
|
||||
{ /* terminator */ }
|
||||
};
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#define USB_PID_TRAKTORKONTROLX1 0x2305
|
||||
#define USB_PID_TRAKTORKONTROLS4 0xbaff
|
||||
#define USB_PID_TRAKTORAUDIO2 0x041d
|
||||
#define USB_PID_MASCHINECONTROLLER 0x0808
|
||||
|
||||
#define EP1_BUFSIZE 64
|
||||
#define EP4_BUFSIZE 512
|
||||
|
|
|
@ -67,6 +67,61 @@ static unsigned short keycode_kore[] = {
|
|||
KEY_BRL_DOT5
|
||||
};
|
||||
|
||||
#define MASCHINE_BUTTONS (42)
|
||||
#define MASCHINE_BUTTON(X) ((X) + BTN_MISC)
|
||||
#define MASCHINE_PADS (16)
|
||||
#define MASCHINE_PAD(X) ((X) + ABS_PRESSURE)
|
||||
|
||||
static unsigned short keycode_maschine[] = {
|
||||
MASCHINE_BUTTON(40), /* mute */
|
||||
MASCHINE_BUTTON(39), /* solo */
|
||||
MASCHINE_BUTTON(38), /* select */
|
||||
MASCHINE_BUTTON(37), /* duplicate */
|
||||
MASCHINE_BUTTON(36), /* navigate */
|
||||
MASCHINE_BUTTON(35), /* pad mode */
|
||||
MASCHINE_BUTTON(34), /* pattern */
|
||||
MASCHINE_BUTTON(33), /* scene */
|
||||
KEY_RESERVED, /* spacer */
|
||||
|
||||
MASCHINE_BUTTON(30), /* rec */
|
||||
MASCHINE_BUTTON(31), /* erase */
|
||||
MASCHINE_BUTTON(32), /* shift */
|
||||
MASCHINE_BUTTON(28), /* grid */
|
||||
MASCHINE_BUTTON(27), /* > */
|
||||
MASCHINE_BUTTON(26), /* < */
|
||||
MASCHINE_BUTTON(25), /* restart */
|
||||
|
||||
MASCHINE_BUTTON(21), /* E */
|
||||
MASCHINE_BUTTON(22), /* F */
|
||||
MASCHINE_BUTTON(23), /* G */
|
||||
MASCHINE_BUTTON(24), /* H */
|
||||
MASCHINE_BUTTON(20), /* D */
|
||||
MASCHINE_BUTTON(19), /* C */
|
||||
MASCHINE_BUTTON(18), /* B */
|
||||
MASCHINE_BUTTON(17), /* A */
|
||||
|
||||
MASCHINE_BUTTON(0), /* control */
|
||||
MASCHINE_BUTTON(2), /* browse */
|
||||
MASCHINE_BUTTON(4), /* < */
|
||||
MASCHINE_BUTTON(6), /* snap */
|
||||
MASCHINE_BUTTON(7), /* autowrite */
|
||||
MASCHINE_BUTTON(5), /* > */
|
||||
MASCHINE_BUTTON(3), /* sampling */
|
||||
MASCHINE_BUTTON(1), /* step */
|
||||
|
||||
MASCHINE_BUTTON(15), /* 8 softkeys */
|
||||
MASCHINE_BUTTON(14),
|
||||
MASCHINE_BUTTON(13),
|
||||
MASCHINE_BUTTON(12),
|
||||
MASCHINE_BUTTON(11),
|
||||
MASCHINE_BUTTON(10),
|
||||
MASCHINE_BUTTON(9),
|
||||
MASCHINE_BUTTON(8),
|
||||
|
||||
MASCHINE_BUTTON(16), /* note repeat */
|
||||
MASCHINE_BUTTON(29) /* play */
|
||||
};
|
||||
|
||||
#define KONTROLX1_INPUTS (40)
|
||||
#define KONTROLS4_BUTTONS (12 * 8)
|
||||
#define KONTROLS4_AXIS (46)
|
||||
|
@ -218,6 +273,29 @@ static void snd_caiaq_input_read_erp(struct snd_usb_caiaqdev *dev,
|
|||
input_report_abs(input_dev, ABS_HAT3Y, i);
|
||||
input_sync(input_dev);
|
||||
break;
|
||||
|
||||
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER):
|
||||
/* 4 under the left screen */
|
||||
input_report_abs(input_dev, ABS_HAT0X, decode_erp(buf[21], buf[20]));
|
||||
input_report_abs(input_dev, ABS_HAT0Y, decode_erp(buf[15], buf[14]));
|
||||
input_report_abs(input_dev, ABS_HAT1X, decode_erp(buf[9], buf[8]));
|
||||
input_report_abs(input_dev, ABS_HAT1Y, decode_erp(buf[3], buf[2]));
|
||||
|
||||
/* 4 under the right screen */
|
||||
input_report_abs(input_dev, ABS_HAT2X, decode_erp(buf[19], buf[18]));
|
||||
input_report_abs(input_dev, ABS_HAT2Y, decode_erp(buf[13], buf[12]));
|
||||
input_report_abs(input_dev, ABS_HAT3X, decode_erp(buf[7], buf[6]));
|
||||
input_report_abs(input_dev, ABS_HAT3Y, decode_erp(buf[1], buf[0]));
|
||||
|
||||
/* volume */
|
||||
input_report_abs(input_dev, ABS_RX, decode_erp(buf[17], buf[16]));
|
||||
/* tempo */
|
||||
input_report_abs(input_dev, ABS_RY, decode_erp(buf[11], buf[10]));
|
||||
/* swing */
|
||||
input_report_abs(input_dev, ABS_RZ, decode_erp(buf[5], buf[4]));
|
||||
|
||||
input_sync(input_dev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -400,6 +478,25 @@ static void snd_usb_caiaq_tks4_dispatch(struct snd_usb_caiaqdev *dev,
|
|||
input_sync(dev->input_dev);
|
||||
}
|
||||
|
||||
#define MASCHINE_MSGBLOCK_SIZE 2
|
||||
|
||||
static void snd_usb_caiaq_maschine_dispatch(struct snd_usb_caiaqdev *dev,
|
||||
const unsigned char *buf,
|
||||
unsigned int len)
|
||||
{
|
||||
unsigned int i, pad_id;
|
||||
uint16_t pressure;
|
||||
|
||||
for (i = 0; i < MASCHINE_PADS; i++) {
|
||||
pressure = be16_to_cpu(buf[i * 2] << 8 | buf[(i * 2) + 1]);
|
||||
pad_id = pressure >> 12;
|
||||
|
||||
input_report_abs(dev->input_dev, MASCHINE_PAD(pad_id), pressure & 0xfff);
|
||||
}
|
||||
|
||||
input_sync(dev->input_dev);
|
||||
}
|
||||
|
||||
static void snd_usb_caiaq_ep4_reply_dispatch(struct urb *urb)
|
||||
{
|
||||
struct snd_usb_caiaqdev *dev = urb->context;
|
||||
|
@ -425,6 +522,13 @@ static void snd_usb_caiaq_ep4_reply_dispatch(struct urb *urb)
|
|||
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
|
||||
snd_usb_caiaq_tks4_dispatch(dev, buf, urb->actual_length);
|
||||
break;
|
||||
|
||||
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER):
|
||||
if (urb->actual_length < (MASCHINE_PADS * MASCHINE_MSGBLOCK_SIZE))
|
||||
goto requeue;
|
||||
|
||||
snd_usb_caiaq_maschine_dispatch(dev, buf, urb->actual_length);
|
||||
break;
|
||||
}
|
||||
|
||||
requeue:
|
||||
|
@ -444,6 +548,7 @@ static int snd_usb_caiaq_input_open(struct input_dev *idev)
|
|||
switch (dev->chip.usb_id) {
|
||||
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
|
||||
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
|
||||
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER):
|
||||
if (usb_submit_urb(dev->ep4_in_urb, GFP_KERNEL) != 0)
|
||||
return -EIO;
|
||||
break;
|
||||
|
@ -462,6 +567,7 @@ static void snd_usb_caiaq_input_close(struct input_dev *idev)
|
|||
switch (dev->chip.usb_id) {
|
||||
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLX1):
|
||||
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_TRAKTORKONTROLS4):
|
||||
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER):
|
||||
usb_kill_urb(dev->ep4_in_urb);
|
||||
break;
|
||||
}
|
||||
|
@ -652,6 +758,50 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev)
|
|||
|
||||
break;
|
||||
|
||||
case USB_ID(USB_VID_NATIVEINSTRUMENTS, USB_PID_MASCHINECONTROLLER):
|
||||
input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
|
||||
input->absbit[0] = BIT_MASK(ABS_HAT0X) | BIT_MASK(ABS_HAT0Y) |
|
||||
BIT_MASK(ABS_HAT1X) | BIT_MASK(ABS_HAT1Y) |
|
||||
BIT_MASK(ABS_HAT2X) | BIT_MASK(ABS_HAT2Y) |
|
||||
BIT_MASK(ABS_HAT3X) | BIT_MASK(ABS_HAT3Y) |
|
||||
BIT_MASK(ABS_RX) | BIT_MASK(ABS_RY) |
|
||||
BIT_MASK(ABS_RZ);
|
||||
|
||||
BUILD_BUG_ON(sizeof(dev->keycode) < sizeof(keycode_maschine));
|
||||
memcpy(dev->keycode, keycode_maschine, sizeof(keycode_maschine));
|
||||
input->keycodemax = ARRAY_SIZE(keycode_maschine);
|
||||
|
||||
for (i = 0; i < MASCHINE_PADS; i++) {
|
||||
input->absbit[0] |= MASCHINE_PAD(i);
|
||||
input_set_abs_params(input, MASCHINE_PAD(i), 0, 0xfff, 5, 10);
|
||||
}
|
||||
|
||||
input_set_abs_params(input, ABS_HAT0X, 0, 999, 0, 10);
|
||||
input_set_abs_params(input, ABS_HAT0Y, 0, 999, 0, 10);
|
||||
input_set_abs_params(input, ABS_HAT1X, 0, 999, 0, 10);
|
||||
input_set_abs_params(input, ABS_HAT1Y, 0, 999, 0, 10);
|
||||
input_set_abs_params(input, ABS_HAT2X, 0, 999, 0, 10);
|
||||
input_set_abs_params(input, ABS_HAT2Y, 0, 999, 0, 10);
|
||||
input_set_abs_params(input, ABS_HAT3X, 0, 999, 0, 10);
|
||||
input_set_abs_params(input, ABS_HAT3Y, 0, 999, 0, 10);
|
||||
input_set_abs_params(input, ABS_RX, 0, 999, 0, 10);
|
||||
input_set_abs_params(input, ABS_RY, 0, 999, 0, 10);
|
||||
input_set_abs_params(input, ABS_RZ, 0, 999, 0, 10);
|
||||
|
||||
dev->ep4_in_urb = usb_alloc_urb(0, GFP_KERNEL);
|
||||
if (!dev->ep4_in_urb) {
|
||||
ret = -ENOMEM;
|
||||
goto exit_free_idev;
|
||||
}
|
||||
|
||||
usb_fill_bulk_urb(dev->ep4_in_urb, usb_dev,
|
||||
usb_rcvbulkpipe(usb_dev, 0x4),
|
||||
dev->ep4_in_buf, EP4_BUFSIZE,
|
||||
snd_usb_caiaq_ep4_reply_dispatch, dev);
|
||||
|
||||
snd_usb_caiaq_set_auto_msg(dev, 1, 10, 5);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* no input methods supported on this device */
|
||||
goto exit_free_idev;
|
||||
|
@ -664,15 +814,17 @@ int snd_usb_caiaq_input_init(struct snd_usb_caiaqdev *dev)
|
|||
for (i = 0; i < input->keycodemax; i++)
|
||||
__set_bit(dev->keycode[i], input->keybit);
|
||||
|
||||
dev->input_dev = input;
|
||||
|
||||
ret = input_register_device(input);
|
||||
if (ret < 0)
|
||||
goto exit_free_idev;
|
||||
|
||||
dev->input_dev = input;
|
||||
return 0;
|
||||
|
||||
exit_free_idev:
|
||||
input_free_device(input);
|
||||
dev->input_dev = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -688,4 +840,3 @@ void snd_usb_caiaq_input_free(struct snd_usb_caiaqdev *dev)
|
|||
input_unregister_device(dev->input_dev);
|
||||
dev->input_dev = NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -65,9 +65,9 @@
|
|||
#include "helper.h"
|
||||
#include "debug.h"
|
||||
#include "pcm.h"
|
||||
#include "urb.h"
|
||||
#include "format.h"
|
||||
#include "power.h"
|
||||
#include "stream.h"
|
||||
|
||||
MODULE_AUTHOR("Takashi Iwai <tiwai@suse.de>");
|
||||
MODULE_DESCRIPTION("USB Audio");
|
||||
|
@ -185,7 +185,7 @@ static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int int
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (! snd_usb_parse_audio_endpoints(chip, interface)) {
|
||||
if (! snd_usb_parse_audio_interface(chip, interface)) {
|
||||
usb_set_interface(dev, interface, 0); /* reset the current interface */
|
||||
usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1L);
|
||||
return -EINVAL;
|
||||
|
|
|
@ -94,6 +94,8 @@ struct snd_usb_substream {
|
|||
spinlock_t lock;
|
||||
|
||||
struct snd_urb_ops ops; /* callbacks (must be filled at init) */
|
||||
int last_frame_number; /* stored frame number */
|
||||
int last_delay; /* stored delay */
|
||||
};
|
||||
|
||||
struct snd_usb_stream {
|
||||
|
|
|
@ -91,7 +91,7 @@ static int uac_clock_selector_get_val(struct snd_usb_audio *chip, int selector_i
|
|||
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
|
||||
UAC2_CX_CLOCK_SELECTOR << 8,
|
||||
snd_usb_ctrl_intf(chip) | (selector_id << 8),
|
||||
&buf, sizeof(buf), 1000);
|
||||
&buf, sizeof(buf));
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
@ -118,7 +118,7 @@ static bool uac_clock_source_is_valid(struct snd_usb_audio *chip, int source_id)
|
|||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||
UAC2_CS_CONTROL_CLOCK_VALID << 8,
|
||||
snd_usb_ctrl_intf(chip) | (source_id << 8),
|
||||
&data, sizeof(data), 1000);
|
||||
&data, sizeof(data));
|
||||
|
||||
if (err < 0) {
|
||||
snd_printk(KERN_WARNING "%s(): cannot get clock validity for id %d\n",
|
||||
|
@ -222,7 +222,7 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
|
|||
if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
|
||||
USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT,
|
||||
UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
|
||||
data, sizeof(data), 1000)) < 0) {
|
||||
data, sizeof(data))) < 0) {
|
||||
snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d to ep %#x\n",
|
||||
dev->devnum, iface, fmt->altsetting, rate, ep);
|
||||
return err;
|
||||
|
@ -231,7 +231,7 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
|
|||
if ((err = snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), UAC_GET_CUR,
|
||||
USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_IN,
|
||||
UAC_EP_CS_ATTR_SAMPLE_RATE << 8, ep,
|
||||
data, sizeof(data), 1000)) < 0) {
|
||||
data, sizeof(data))) < 0) {
|
||||
snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq at ep %#x\n",
|
||||
dev->devnum, iface, fmt->altsetting, ep);
|
||||
return 0; /* some devices don't support reading */
|
||||
|
@ -273,7 +273,7 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
|
|||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
|
||||
UAC2_CS_CONTROL_SAM_FREQ << 8,
|
||||
snd_usb_ctrl_intf(chip) | (clock << 8),
|
||||
data, sizeof(data), 1000)) < 0) {
|
||||
data, sizeof(data))) < 0) {
|
||||
snd_printk(KERN_ERR "%d:%d:%d: cannot set freq %d (v2)\n",
|
||||
dev->devnum, iface, fmt->altsetting, rate);
|
||||
return err;
|
||||
|
@ -283,7 +283,7 @@ static int set_sample_rate_v2(struct snd_usb_audio *chip, int iface,
|
|||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||
UAC2_CS_CONTROL_SAM_FREQ << 8,
|
||||
snd_usb_ctrl_intf(chip) | (clock << 8),
|
||||
data, sizeof(data), 1000)) < 0) {
|
||||
data, sizeof(data))) < 0) {
|
||||
snd_printk(KERN_WARNING "%d:%d:%d: cannot get freq (v2)\n",
|
||||
dev->devnum, iface, fmt->altsetting);
|
||||
return err;
|
||||
|
|
1271
sound/usb/endpoint.c
1271
sound/usb/endpoint.c
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,11 +1,21 @@
|
|||
#ifndef __USBAUDIO_ENDPOINT_H
|
||||
#define __USBAUDIO_ENDPOINT_H
|
||||
|
||||
int snd_usb_parse_audio_endpoints(struct snd_usb_audio *chip,
|
||||
int iface_no);
|
||||
void snd_usb_init_substream(struct snd_usb_stream *as,
|
||||
int stream,
|
||||
struct audioformat *fp);
|
||||
|
||||
int snd_usb_add_audio_endpoint(struct snd_usb_audio *chip,
|
||||
int stream,
|
||||
struct audioformat *fp);
|
||||
int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
|
||||
unsigned int period_bytes,
|
||||
unsigned int rate,
|
||||
unsigned int frame_bits);
|
||||
|
||||
void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force);
|
||||
|
||||
int snd_usb_substream_prepare(struct snd_usb_substream *subs,
|
||||
struct snd_pcm_runtime *runtime);
|
||||
|
||||
int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd);
|
||||
int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd);
|
||||
|
||||
#endif /* __USBAUDIO_ENDPOINT_H */
|
||||
|
|
|
@ -286,7 +286,7 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
|
|||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||
UAC2_CS_CONTROL_SAM_FREQ << 8,
|
||||
snd_usb_ctrl_intf(chip) | (clock << 8),
|
||||
tmp, sizeof(tmp), 1000);
|
||||
tmp, sizeof(tmp));
|
||||
|
||||
if (ret < 0) {
|
||||
snd_printk(KERN_ERR "%s(): unable to retrieve number of sample rates (clock %d)\n",
|
||||
|
@ -307,7 +307,7 @@ static int parse_audio_format_rates_v2(struct snd_usb_audio *chip,
|
|||
USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
|
||||
UAC2_CS_CONTROL_SAM_FREQ << 8,
|
||||
snd_usb_ctrl_intf(chip) | (clock << 8),
|
||||
data, data_size, 1000);
|
||||
data, data_size);
|
||||
|
||||
if (ret < 0) {
|
||||
snd_printk(KERN_ERR "%s(): unable to retrieve sample rate range (clock %d)\n",
|
||||
|
|
|
@ -81,7 +81,7 @@ void *snd_usb_find_csint_desc(void *buffer, int buflen, void *after, u8 dsubtype
|
|||
*/
|
||||
int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
|
||||
__u8 requesttype, __u16 value, __u16 index, void *data,
|
||||
__u16 size, int timeout)
|
||||
__u16 size)
|
||||
{
|
||||
int err;
|
||||
void *buf = NULL;
|
||||
|
@ -92,7 +92,7 @@ int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
|
|||
return -ENOMEM;
|
||||
}
|
||||
err = usb_control_msg(dev, pipe, request, requesttype,
|
||||
value, index, buf, size, timeout);
|
||||
value, index, buf, size, 1000);
|
||||
if (size > 0) {
|
||||
memcpy(data, buf, size);
|
||||
kfree(buf);
|
||||
|
|
|
@ -8,7 +8,7 @@ void *snd_usb_find_csint_desc(void *descstart, int desclen, void *after, u8 dsub
|
|||
|
||||
int snd_usb_ctl_msg(struct usb_device *dev, unsigned int pipe,
|
||||
__u8 request, __u8 requesttype, __u16 value, __u16 index,
|
||||
void *data, __u16 size, int timeout);
|
||||
void *data, __u16 size);
|
||||
|
||||
unsigned char snd_usb_parse_datainterval(struct snd_usb_audio *chip,
|
||||
struct usb_host_interface *alts);
|
||||
|
|
|
@ -816,6 +816,22 @@ static struct usb_protocol_ops snd_usbmidi_raw_ops = {
|
|||
.output = snd_usbmidi_raw_output,
|
||||
};
|
||||
|
||||
/*
|
||||
* FTDI protocol: raw MIDI bytes, but input packets have two modem status bytes.
|
||||
*/
|
||||
|
||||
static void snd_usbmidi_ftdi_input(struct snd_usb_midi_in_endpoint* ep,
|
||||
uint8_t* buffer, int buffer_length)
|
||||
{
|
||||
if (buffer_length > 2)
|
||||
snd_usbmidi_input_data(ep, 0, buffer + 2, buffer_length - 2);
|
||||
}
|
||||
|
||||
static struct usb_protocol_ops snd_usbmidi_ftdi_ops = {
|
||||
.input = snd_usbmidi_ftdi_input,
|
||||
.output = snd_usbmidi_raw_output,
|
||||
};
|
||||
|
||||
static void snd_usbmidi_us122l_input(struct snd_usb_midi_in_endpoint *ep,
|
||||
uint8_t *buffer, int buffer_length)
|
||||
{
|
||||
|
@ -2163,6 +2179,17 @@ int snd_usbmidi_create(struct snd_card *card,
|
|||
/* endpoint 1 is input-only */
|
||||
endpoints[1].out_cables = 0;
|
||||
break;
|
||||
case QUIRK_MIDI_FTDI:
|
||||
umidi->usb_protocol_ops = &snd_usbmidi_ftdi_ops;
|
||||
|
||||
/* set baud rate to 31250 (48 MHz / 16 / 96) */
|
||||
err = usb_control_msg(umidi->dev, usb_sndctrlpipe(umidi->dev, 0),
|
||||
3, 0x40, 0x60, 0, NULL, 0, 1000);
|
||||
if (err < 0)
|
||||
break;
|
||||
|
||||
err = snd_usbmidi_detect_per_port_endpoints(umidi, endpoints);
|
||||
break;
|
||||
default:
|
||||
snd_printd(KERN_ERR "invalid quirk type %d\n", quirk->type);
|
||||
err = -ENXIO;
|
||||
|
|
|
@ -296,7 +296,7 @@ static int get_ctl_value_v1(struct usb_mixer_elem_info *cval, int request, int v
|
|||
if (snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), request,
|
||||
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
|
||||
validx, snd_usb_ctrl_intf(chip) | (cval->id << 8),
|
||||
buf, val_len, 100) >= val_len) {
|
||||
buf, val_len) >= val_len) {
|
||||
*value_ret = convert_signed_value(cval, snd_usb_combine_bytes(buf, val_len));
|
||||
snd_usb_autosuspend(cval->mixer->chip);
|
||||
return 0;
|
||||
|
@ -333,7 +333,7 @@ static int get_ctl_value_v2(struct usb_mixer_elem_info *cval, int request, int v
|
|||
ret = snd_usb_ctl_msg(chip->dev, usb_rcvctrlpipe(chip->dev, 0), bRequest,
|
||||
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN,
|
||||
validx, snd_usb_ctrl_intf(chip) | (cval->id << 8),
|
||||
buf, size, 1000);
|
||||
buf, size);
|
||||
snd_usb_autosuspend(chip);
|
||||
|
||||
if (ret < 0) {
|
||||
|
@ -445,7 +445,7 @@ int snd_usb_mixer_set_ctl_value(struct usb_mixer_elem_info *cval,
|
|||
usb_sndctrlpipe(chip->dev, 0), request,
|
||||
USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT,
|
||||
validx, snd_usb_ctrl_intf(chip) | (cval->id << 8),
|
||||
buf, val_len, 100) >= 0) {
|
||||
buf, val_len) >= 0) {
|
||||
snd_usb_autosuspend(chip);
|
||||
return 0;
|
||||
}
|
||||
|
@ -881,8 +881,17 @@ static int mixer_ctl_feature_info(struct snd_kcontrol *kcontrol, struct snd_ctl_
|
|||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max = 1;
|
||||
} else {
|
||||
if (! cval->initialized)
|
||||
get_min_max(cval, 0);
|
||||
if (!cval->initialized) {
|
||||
get_min_max(cval, 0);
|
||||
if (cval->initialized && cval->dBmin >= cval->dBmax) {
|
||||
kcontrol->vd[0].access &=
|
||||
~(SNDRV_CTL_ELEM_ACCESS_TLV_READ |
|
||||
SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK);
|
||||
snd_ctl_notify(cval->mixer->chip->card,
|
||||
SNDRV_CTL_EVENT_MASK_INFO,
|
||||
&kcontrol->id);
|
||||
}
|
||||
}
|
||||
uinfo->value.integer.min = 0;
|
||||
uinfo->value.integer.max =
|
||||
(cval->max - cval->min + cval->res - 1) / cval->res;
|
||||
|
@ -1250,7 +1259,7 @@ static int parse_audio_feature_unit(struct mixer_build *state, int unitid, void
|
|||
build_feature_ctl(state, _ftr, 0, i, &iterm, unitid, 0);
|
||||
}
|
||||
} else { /* UAC_VERSION_2 */
|
||||
for (i = 0; i < 30/2; i++) {
|
||||
for (i = 0; i < ARRAY_SIZE(audio_feature_info); i++) {
|
||||
unsigned int ch_bits = 0;
|
||||
unsigned int ch_read_only = 0;
|
||||
|
||||
|
|
|
@ -190,18 +190,18 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
|
|||
err = snd_usb_ctl_msg(mixer->chip->dev,
|
||||
usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
|
||||
!value, 0, NULL, 0, 100);
|
||||
!value, 0, NULL, 0);
|
||||
/* USB X-Fi S51 Pro */
|
||||
if (mixer->chip->usb_id == USB_ID(0x041e, 0x30df))
|
||||
err = snd_usb_ctl_msg(mixer->chip->dev,
|
||||
usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
|
||||
!value, 0, NULL, 0, 100);
|
||||
!value, 0, NULL, 0);
|
||||
else
|
||||
err = snd_usb_ctl_msg(mixer->chip->dev,
|
||||
usb_sndctrlpipe(mixer->chip->dev, 0), 0x24,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
|
||||
value, index + 2, NULL, 0, 100);
|
||||
value, index + 2, NULL, 0);
|
||||
if (err < 0)
|
||||
return err;
|
||||
mixer->audigy2nx_leds[index] = value;
|
||||
|
@ -299,7 +299,7 @@ static void snd_audigy2nx_proc_read(struct snd_info_entry *entry,
|
|||
usb_rcvctrlpipe(mixer->chip->dev, 0),
|
||||
UAC_GET_MEM, USB_DIR_IN | USB_TYPE_CLASS |
|
||||
USB_RECIP_INTERFACE, 0,
|
||||
jacks[i].unitid << 8, buf, 3, 100);
|
||||
jacks[i].unitid << 8, buf, 3);
|
||||
if (err == 3 && (buf[0] == 3 || buf[0] == 6))
|
||||
snd_iprintf(buffer, "%02x %02x\n", buf[1], buf[2]);
|
||||
else
|
||||
|
@ -332,7 +332,7 @@ static int snd_xonar_u1_switch_put(struct snd_kcontrol *kcontrol,
|
|||
err = snd_usb_ctl_msg(mixer->chip->dev,
|
||||
usb_sndctrlpipe(mixer->chip->dev, 0), 0x08,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
|
||||
50, 0, &new_status, 1, 100);
|
||||
50, 0, &new_status, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
mixer->xonar_u1_status = new_status;
|
||||
|
|
|
@ -28,12 +28,36 @@
|
|||
#include "card.h"
|
||||
#include "quirks.h"
|
||||
#include "debug.h"
|
||||
#include "urb.h"
|
||||
#include "endpoint.h"
|
||||
#include "helper.h"
|
||||
#include "pcm.h"
|
||||
#include "clock.h"
|
||||
#include "power.h"
|
||||
|
||||
/* return the estimated delay based on USB frame counters */
|
||||
snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs,
|
||||
unsigned int rate)
|
||||
{
|
||||
int current_frame_number;
|
||||
int frame_diff;
|
||||
int est_delay;
|
||||
|
||||
current_frame_number = usb_get_current_frame_number(subs->dev);
|
||||
/*
|
||||
* HCD implementations use different widths, use lower 8 bits.
|
||||
* The delay will be managed up to 256ms, which is more than
|
||||
* enough
|
||||
*/
|
||||
frame_diff = (current_frame_number - subs->last_frame_number) & 0xff;
|
||||
|
||||
/* Approximation based on number of samples per USB frame (ms),
|
||||
some truncation for 44.1 but the estimate is good enough */
|
||||
est_delay = subs->last_delay - (frame_diff * rate / 1000);
|
||||
if (est_delay < 0)
|
||||
est_delay = 0;
|
||||
return est_delay;
|
||||
}
|
||||
|
||||
/*
|
||||
* return the current pcm pointer. just based on the hwptr_done value.
|
||||
*/
|
||||
|
@ -45,6 +69,8 @@ static snd_pcm_uframes_t snd_usb_pcm_pointer(struct snd_pcm_substream *substream
|
|||
subs = (struct snd_usb_substream *)substream->runtime->private_data;
|
||||
spin_lock(&subs->lock);
|
||||
hwptr_done = subs->hwptr_done;
|
||||
substream->runtime->delay = snd_usb_pcm_delay(subs,
|
||||
substream->runtime->rate);
|
||||
spin_unlock(&subs->lock);
|
||||
return hwptr_done / (substream->runtime->frame_bits >> 3);
|
||||
}
|
||||
|
@ -126,7 +152,7 @@ static int init_pitch_v1(struct snd_usb_audio *chip, int iface,
|
|||
if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC_SET_CUR,
|
||||
USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT,
|
||||
UAC_EP_CS_ATTR_PITCH_CONTROL << 8, ep,
|
||||
data, sizeof(data), 1000)) < 0) {
|
||||
data, sizeof(data))) < 0) {
|
||||
snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH\n",
|
||||
dev->devnum, iface, ep);
|
||||
return err;
|
||||
|
@ -150,7 +176,7 @@ static int init_pitch_v2(struct snd_usb_audio *chip, int iface,
|
|||
if ((err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), UAC2_CS_CUR,
|
||||
USB_TYPE_CLASS | USB_RECIP_ENDPOINT | USB_DIR_OUT,
|
||||
UAC2_EP_CS_PITCH << 8, 0,
|
||||
data, sizeof(data), 1000)) < 0) {
|
||||
data, sizeof(data))) < 0) {
|
||||
snd_printk(KERN_ERR "%d:%d:%d: cannot set enable PITCH (v2)\n",
|
||||
dev->devnum, iface, fmt->altsetting);
|
||||
return err;
|
||||
|
@ -417,6 +443,8 @@ static int snd_usb_pcm_prepare(struct snd_pcm_substream *substream)
|
|||
subs->hwptr_done = 0;
|
||||
subs->transfer_done = 0;
|
||||
subs->phase = 0;
|
||||
subs->last_delay = 0;
|
||||
subs->last_frame_number = 0;
|
||||
runtime->delay = 0;
|
||||
|
||||
return snd_usb_substream_prepare(subs, runtime);
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
#ifndef __USBAUDIO_PCM_H
|
||||
#define __USBAUDIO_PCM_H
|
||||
|
||||
snd_pcm_uframes_t snd_usb_pcm_delay(struct snd_usb_substream *subs,
|
||||
unsigned int rate);
|
||||
|
||||
void snd_usb_set_pcm_ops(struct snd_pcm *pcm, int stream);
|
||||
|
||||
int snd_usb_init_pitch(struct snd_usb_audio *chip, int iface,
|
||||
|
|
|
@ -39,6 +39,17 @@
|
|||
.idProduct = prod, \
|
||||
.bInterfaceClass = USB_CLASS_VENDOR_SPEC
|
||||
|
||||
/* FTDI devices */
|
||||
{
|
||||
USB_DEVICE(0x0403, 0xb8d8),
|
||||
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
|
||||
/* .vendor_name = "STARR LABS", */
|
||||
/* .product_name = "Starr Labs MIDI USB device", */
|
||||
.ifnum = 0,
|
||||
.type = QUIRK_MIDI_FTDI
|
||||
}
|
||||
},
|
||||
|
||||
/* Creative/Toshiba Multimedia Center SB-0500 */
|
||||
{
|
||||
USB_DEVICE(0x041e, 0x3048),
|
||||
|
@ -1677,6 +1688,20 @@ YAMAHA_DEVICE(0x7010, "UB99"),
|
|||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
/* Added support for Roland UM-ONE which differs from UM-1 */
|
||||
USB_DEVICE(0x0582, 0x012a),
|
||||
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
|
||||
/* .vendor_name = "ROLAND", */
|
||||
/* .product_name = "UM-ONE", */
|
||||
.ifnum = 0,
|
||||
.type = QUIRK_MIDI_FIXED_ENDPOINT,
|
||||
.data = & (const struct snd_usb_midi_endpoint_info) {
|
||||
.out_cables = 0x0001,
|
||||
.in_cables = 0x0003
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
USB_DEVICE(0x0582, 0x011e),
|
||||
.driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#include "endpoint.h"
|
||||
#include "pcm.h"
|
||||
#include "clock.h"
|
||||
#include "stream.h"
|
||||
|
||||
/*
|
||||
* handle the quirks for the contained interfaces
|
||||
|
@ -106,7 +107,7 @@ static int create_standard_audio_quirk(struct snd_usb_audio *chip,
|
|||
|
||||
alts = &iface->altsetting[0];
|
||||
altsd = get_iface_desc(alts);
|
||||
err = snd_usb_parse_audio_endpoints(chip, altsd->bInterfaceNumber);
|
||||
err = snd_usb_parse_audio_interface(chip, altsd->bInterfaceNumber);
|
||||
if (err < 0) {
|
||||
snd_printk(KERN_ERR "cannot setup if %d: error %d\n",
|
||||
altsd->bInterfaceNumber, err);
|
||||
|
@ -147,7 +148,7 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
|
|||
|
||||
stream = (fp->endpoint & USB_DIR_IN)
|
||||
? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
|
||||
err = snd_usb_add_audio_endpoint(chip, stream, fp);
|
||||
err = snd_usb_add_audio_stream(chip, stream, fp);
|
||||
if (err < 0) {
|
||||
kfree(fp);
|
||||
kfree(rate_table);
|
||||
|
@ -254,7 +255,7 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
|
|||
|
||||
stream = (fp->endpoint & USB_DIR_IN)
|
||||
? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
|
||||
err = snd_usb_add_audio_endpoint(chip, stream, fp);
|
||||
err = snd_usb_add_audio_stream(chip, stream, fp);
|
||||
if (err < 0) {
|
||||
kfree(fp);
|
||||
return err;
|
||||
|
@ -306,6 +307,7 @@ int snd_usb_create_quirk(struct snd_usb_audio *chip,
|
|||
[QUIRK_MIDI_EMAGIC] = create_any_midi_quirk,
|
||||
[QUIRK_MIDI_CME] = create_any_midi_quirk,
|
||||
[QUIRK_MIDI_AKAI] = create_any_midi_quirk,
|
||||
[QUIRK_MIDI_FTDI] = create_any_midi_quirk,
|
||||
[QUIRK_AUDIO_STANDARD_INTERFACE] = create_standard_audio_quirk,
|
||||
[QUIRK_AUDIO_FIXED_ENDPOINT] = create_fixed_stream_quirk,
|
||||
[QUIRK_AUDIO_EDIROL_UAXX] = create_uaxx_quirk,
|
||||
|
@ -338,7 +340,7 @@ static int snd_usb_extigy_boot_quirk(struct usb_device *dev, struct usb_interfac
|
|||
snd_printdd("sending Extigy boot sequence...\n");
|
||||
/* Send message to force it to reconnect with full interface. */
|
||||
err = snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev,0),
|
||||
0x10, 0x43, 0x0001, 0x000a, NULL, 0, 1000);
|
||||
0x10, 0x43, 0x0001, 0x000a, NULL, 0);
|
||||
if (err < 0) snd_printdd("error sending boot message: %d\n", err);
|
||||
err = usb_get_descriptor(dev, USB_DT_DEVICE, 0,
|
||||
&dev->descriptor, sizeof(dev->descriptor));
|
||||
|
@ -359,11 +361,11 @@ static int snd_usb_audigy2nx_boot_quirk(struct usb_device *dev)
|
|||
|
||||
snd_usb_ctl_msg(dev, usb_rcvctrlpipe(dev, 0), 0x2a,
|
||||
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
|
||||
0, 0, &buf, 1, 1000);
|
||||
0, 0, &buf, 1);
|
||||
if (buf == 0) {
|
||||
snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), 0x29,
|
||||
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_OTHER,
|
||||
1, 2000, NULL, 0, 1000);
|
||||
1, 2000, NULL, 0);
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
|
@ -406,7 +408,7 @@ static int snd_usb_cm106_write_int_reg(struct usb_device *dev, int reg, u16 valu
|
|||
buf[3] = reg;
|
||||
return snd_usb_ctl_msg(dev, usb_sndctrlpipe(dev, 0), USB_REQ_SET_CONFIGURATION,
|
||||
USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT,
|
||||
0, 0, &buf, 4, 1000);
|
||||
0, 0, &buf, 4);
|
||||
}
|
||||
|
||||
static int snd_usb_cm106_boot_quirk(struct usb_device *dev)
|
||||
|
|
|
@ -0,0 +1,452 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/slab.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 "usbaudio.h"
|
||||
#include "card.h"
|
||||
#include "proc.h"
|
||||
#include "quirks.h"
|
||||
#include "endpoint.h"
|
||||
#include "pcm.h"
|
||||
#include "helper.h"
|
||||
#include "format.h"
|
||||
#include "clock.h"
|
||||
#include "stream.h"
|
||||
|
||||
/*
|
||||
* free a substream
|
||||
*/
|
||||
static void free_substream(struct snd_usb_substream *subs)
|
||||
{
|
||||
struct list_head *p, *n;
|
||||
|
||||
if (!subs->num_formats)
|
||||
return; /* not initialized */
|
||||
list_for_each_safe(p, n, &subs->fmt_list) {
|
||||
struct audioformat *fp = list_entry(p, struct audioformat, list);
|
||||
kfree(fp->rate_table);
|
||||
kfree(fp);
|
||||
}
|
||||
kfree(subs->rate_list.list);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* free a usb stream instance
|
||||
*/
|
||||
static void snd_usb_audio_stream_free(struct snd_usb_stream *stream)
|
||||
{
|
||||
free_substream(&stream->substream[0]);
|
||||
free_substream(&stream->substream[1]);
|
||||
list_del(&stream->list);
|
||||
kfree(stream);
|
||||
}
|
||||
|
||||
static void snd_usb_audio_pcm_free(struct snd_pcm *pcm)
|
||||
{
|
||||
struct snd_usb_stream *stream = pcm->private_data;
|
||||
if (stream) {
|
||||
stream->pcm = NULL;
|
||||
snd_usb_audio_stream_free(stream);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* add this endpoint to the chip instance.
|
||||
* if a stream with the same endpoint already exists, append to it.
|
||||
* if not, create a new pcm stream.
|
||||
*/
|
||||
int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
|
||||
int stream,
|
||||
struct audioformat *fp)
|
||||
{
|
||||
struct list_head *p;
|
||||
struct snd_usb_stream *as;
|
||||
struct snd_usb_substream *subs;
|
||||
struct snd_pcm *pcm;
|
||||
int err;
|
||||
|
||||
list_for_each(p, &chip->pcm_list) {
|
||||
as = list_entry(p, struct snd_usb_stream, list);
|
||||
if (as->fmt_type != fp->fmt_type)
|
||||
continue;
|
||||
subs = &as->substream[stream];
|
||||
if (!subs->endpoint)
|
||||
continue;
|
||||
if (subs->endpoint == fp->endpoint) {
|
||||
list_add_tail(&fp->list, &subs->fmt_list);
|
||||
subs->num_formats++;
|
||||
subs->formats |= fp->formats;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
/* look for an empty stream */
|
||||
list_for_each(p, &chip->pcm_list) {
|
||||
as = list_entry(p, struct snd_usb_stream, list);
|
||||
if (as->fmt_type != fp->fmt_type)
|
||||
continue;
|
||||
subs = &as->substream[stream];
|
||||
if (subs->endpoint)
|
||||
continue;
|
||||
err = snd_pcm_new_stream(as->pcm, stream, 1);
|
||||
if (err < 0)
|
||||
return err;
|
||||
snd_usb_init_substream(as, stream, fp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* create a new pcm */
|
||||
as = kzalloc(sizeof(*as), GFP_KERNEL);
|
||||
if (!as)
|
||||
return -ENOMEM;
|
||||
as->pcm_index = chip->pcm_devs;
|
||||
as->chip = chip;
|
||||
as->fmt_type = fp->fmt_type;
|
||||
err = snd_pcm_new(chip->card, "USB Audio", chip->pcm_devs,
|
||||
stream == SNDRV_PCM_STREAM_PLAYBACK ? 1 : 0,
|
||||
stream == SNDRV_PCM_STREAM_PLAYBACK ? 0 : 1,
|
||||
&pcm);
|
||||
if (err < 0) {
|
||||
kfree(as);
|
||||
return err;
|
||||
}
|
||||
as->pcm = pcm;
|
||||
pcm->private_data = as;
|
||||
pcm->private_free = snd_usb_audio_pcm_free;
|
||||
pcm->info_flags = 0;
|
||||
if (chip->pcm_devs > 0)
|
||||
sprintf(pcm->name, "USB Audio #%d", chip->pcm_devs);
|
||||
else
|
||||
strcpy(pcm->name, "USB Audio");
|
||||
|
||||
snd_usb_init_substream(as, stream, fp);
|
||||
|
||||
list_add(&as->list, &chip->pcm_list);
|
||||
chip->pcm_devs++;
|
||||
|
||||
snd_usb_proc_pcm_format_add(as);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_uac_endpoint_attributes(struct snd_usb_audio *chip,
|
||||
struct usb_host_interface *alts,
|
||||
int protocol, int iface_no)
|
||||
{
|
||||
/* parsed with a v1 header here. that's ok as we only look at the
|
||||
* header first which is the same for both versions */
|
||||
struct uac_iso_endpoint_descriptor *csep;
|
||||
struct usb_interface_descriptor *altsd = get_iface_desc(alts);
|
||||
int attributes = 0;
|
||||
|
||||
csep = snd_usb_find_desc(alts->endpoint[0].extra, alts->endpoint[0].extralen, NULL, USB_DT_CS_ENDPOINT);
|
||||
|
||||
/* Creamware Noah has this descriptor after the 2nd endpoint */
|
||||
if (!csep && altsd->bNumEndpoints >= 2)
|
||||
csep = snd_usb_find_desc(alts->endpoint[1].extra, alts->endpoint[1].extralen, NULL, USB_DT_CS_ENDPOINT);
|
||||
|
||||
if (!csep || csep->bLength < 7 ||
|
||||
csep->bDescriptorSubtype != UAC_EP_GENERAL) {
|
||||
snd_printk(KERN_WARNING "%d:%u:%d : no or invalid"
|
||||
" class specific endpoint descriptor\n",
|
||||
chip->dev->devnum, iface_no,
|
||||
altsd->bAlternateSetting);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (protocol == UAC_VERSION_1) {
|
||||
attributes = csep->bmAttributes;
|
||||
} else {
|
||||
struct uac2_iso_endpoint_descriptor *csep2 =
|
||||
(struct uac2_iso_endpoint_descriptor *) csep;
|
||||
|
||||
attributes = csep->bmAttributes & UAC_EP_CS_ATTR_FILL_MAX;
|
||||
|
||||
/* emulate the endpoint attributes of a v1 device */
|
||||
if (csep2->bmControls & UAC2_CONTROL_PITCH)
|
||||
attributes |= UAC_EP_CS_ATTR_PITCH_CONTROL;
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
static struct uac2_input_terminal_descriptor *
|
||||
snd_usb_find_input_terminal_descriptor(struct usb_host_interface *ctrl_iface,
|
||||
int terminal_id)
|
||||
{
|
||||
struct uac2_input_terminal_descriptor *term = NULL;
|
||||
|
||||
while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
|
||||
ctrl_iface->extralen,
|
||||
term, UAC_INPUT_TERMINAL))) {
|
||||
if (term->bTerminalID == terminal_id)
|
||||
return term;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct uac2_output_terminal_descriptor *
|
||||
snd_usb_find_output_terminal_descriptor(struct usb_host_interface *ctrl_iface,
|
||||
int terminal_id)
|
||||
{
|
||||
struct uac2_output_terminal_descriptor *term = NULL;
|
||||
|
||||
while ((term = snd_usb_find_csint_desc(ctrl_iface->extra,
|
||||
ctrl_iface->extralen,
|
||||
term, UAC_OUTPUT_TERMINAL))) {
|
||||
if (term->bTerminalID == terminal_id)
|
||||
return term;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int snd_usb_parse_audio_interface(struct snd_usb_audio *chip, int iface_no)
|
||||
{
|
||||
struct usb_device *dev;
|
||||
struct usb_interface *iface;
|
||||
struct usb_host_interface *alts;
|
||||
struct usb_interface_descriptor *altsd;
|
||||
int i, altno, err, stream;
|
||||
int format = 0, num_channels = 0;
|
||||
struct audioformat *fp = NULL;
|
||||
int num, protocol, clock = 0;
|
||||
struct uac_format_type_i_continuous_descriptor *fmt;
|
||||
|
||||
dev = chip->dev;
|
||||
|
||||
/* parse the interface's altsettings */
|
||||
iface = usb_ifnum_to_if(dev, iface_no);
|
||||
|
||||
num = iface->num_altsetting;
|
||||
|
||||
/*
|
||||
* Dallas DS4201 workaround: It presents 5 altsettings, but the last
|
||||
* one misses syncpipe, and does not produce any sound.
|
||||
*/
|
||||
if (chip->usb_id == USB_ID(0x04fa, 0x4201))
|
||||
num = 4;
|
||||
|
||||
for (i = 0; i < num; i++) {
|
||||
alts = &iface->altsetting[i];
|
||||
altsd = get_iface_desc(alts);
|
||||
protocol = altsd->bInterfaceProtocol;
|
||||
/* skip invalid one */
|
||||
if ((altsd->bInterfaceClass != USB_CLASS_AUDIO &&
|
||||
altsd->bInterfaceClass != USB_CLASS_VENDOR_SPEC) ||
|
||||
(altsd->bInterfaceSubClass != USB_SUBCLASS_AUDIOSTREAMING &&
|
||||
altsd->bInterfaceSubClass != USB_SUBCLASS_VENDOR_SPEC) ||
|
||||
altsd->bNumEndpoints < 1 ||
|
||||
le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) == 0)
|
||||
continue;
|
||||
/* must be isochronous */
|
||||
if ((get_endpoint(alts, 0)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) !=
|
||||
USB_ENDPOINT_XFER_ISOC)
|
||||
continue;
|
||||
/* check direction */
|
||||
stream = (get_endpoint(alts, 0)->bEndpointAddress & USB_DIR_IN) ?
|
||||
SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
|
||||
altno = altsd->bAlternateSetting;
|
||||
|
||||
if (snd_usb_apply_interface_quirk(chip, iface_no, altno))
|
||||
continue;
|
||||
|
||||
/* get audio formats */
|
||||
switch (protocol) {
|
||||
default:
|
||||
snd_printdd(KERN_WARNING "%d:%u:%d: unknown interface protocol %#02x, assuming v1\n",
|
||||
dev->devnum, iface_no, altno, protocol);
|
||||
protocol = UAC_VERSION_1;
|
||||
/* fall through */
|
||||
|
||||
case UAC_VERSION_1: {
|
||||
struct uac1_as_header_descriptor *as =
|
||||
snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
|
||||
|
||||
if (!as) {
|
||||
snd_printk(KERN_ERR "%d:%u:%d : UAC_AS_GENERAL descriptor not found\n",
|
||||
dev->devnum, iface_no, altno);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (as->bLength < sizeof(*as)) {
|
||||
snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_AS_GENERAL desc\n",
|
||||
dev->devnum, iface_no, altno);
|
||||
continue;
|
||||
}
|
||||
|
||||
format = le16_to_cpu(as->wFormatTag); /* remember the format value */
|
||||
break;
|
||||
}
|
||||
|
||||
case UAC_VERSION_2: {
|
||||
struct uac2_input_terminal_descriptor *input_term;
|
||||
struct uac2_output_terminal_descriptor *output_term;
|
||||
struct uac2_as_header_descriptor *as =
|
||||
snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_AS_GENERAL);
|
||||
|
||||
if (!as) {
|
||||
snd_printk(KERN_ERR "%d:%u:%d : UAC_AS_GENERAL descriptor not found\n",
|
||||
dev->devnum, iface_no, altno);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (as->bLength < sizeof(*as)) {
|
||||
snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_AS_GENERAL desc\n",
|
||||
dev->devnum, iface_no, altno);
|
||||
continue;
|
||||
}
|
||||
|
||||
num_channels = as->bNrChannels;
|
||||
format = le32_to_cpu(as->bmFormats);
|
||||
|
||||
/* lookup the terminal associated to this interface
|
||||
* to extract the clock */
|
||||
input_term = snd_usb_find_input_terminal_descriptor(chip->ctrl_intf,
|
||||
as->bTerminalLink);
|
||||
if (input_term) {
|
||||
clock = input_term->bCSourceID;
|
||||
break;
|
||||
}
|
||||
|
||||
output_term = snd_usb_find_output_terminal_descriptor(chip->ctrl_intf,
|
||||
as->bTerminalLink);
|
||||
if (output_term) {
|
||||
clock = output_term->bCSourceID;
|
||||
break;
|
||||
}
|
||||
|
||||
snd_printk(KERN_ERR "%d:%u:%d : bogus bTerminalLink %d\n",
|
||||
dev->devnum, iface_no, altno, as->bTerminalLink);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* get format type */
|
||||
fmt = snd_usb_find_csint_desc(alts->extra, alts->extralen, NULL, UAC_FORMAT_TYPE);
|
||||
if (!fmt) {
|
||||
snd_printk(KERN_ERR "%d:%u:%d : no UAC_FORMAT_TYPE desc\n",
|
||||
dev->devnum, iface_no, altno);
|
||||
continue;
|
||||
}
|
||||
if (((protocol == UAC_VERSION_1) && (fmt->bLength < 8)) ||
|
||||
((protocol == UAC_VERSION_2) && (fmt->bLength < 6))) {
|
||||
snd_printk(KERN_ERR "%d:%u:%d : invalid UAC_FORMAT_TYPE desc\n",
|
||||
dev->devnum, iface_no, altno);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
* Blue Microphones workaround: The last altsetting is identical
|
||||
* with the previous one, except for a larger packet size, but
|
||||
* is actually a mislabeled two-channel setting; ignore it.
|
||||
*/
|
||||
if (fmt->bNrChannels == 1 &&
|
||||
fmt->bSubframeSize == 2 &&
|
||||
altno == 2 && num == 3 &&
|
||||
fp && fp->altsetting == 1 && fp->channels == 1 &&
|
||||
fp->formats == SNDRV_PCM_FMTBIT_S16_LE &&
|
||||
protocol == UAC_VERSION_1 &&
|
||||
le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize) ==
|
||||
fp->maxpacksize * 2)
|
||||
continue;
|
||||
|
||||
fp = kzalloc(sizeof(*fp), GFP_KERNEL);
|
||||
if (! fp) {
|
||||
snd_printk(KERN_ERR "cannot malloc\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
fp->iface = iface_no;
|
||||
fp->altsetting = altno;
|
||||
fp->altset_idx = i;
|
||||
fp->endpoint = get_endpoint(alts, 0)->bEndpointAddress;
|
||||
fp->ep_attr = get_endpoint(alts, 0)->bmAttributes;
|
||||
fp->datainterval = snd_usb_parse_datainterval(chip, alts);
|
||||
fp->maxpacksize = le16_to_cpu(get_endpoint(alts, 0)->wMaxPacketSize);
|
||||
/* num_channels is only set for v2 interfaces */
|
||||
fp->channels = num_channels;
|
||||
if (snd_usb_get_speed(dev) == USB_SPEED_HIGH)
|
||||
fp->maxpacksize = (((fp->maxpacksize >> 11) & 3) + 1)
|
||||
* (fp->maxpacksize & 0x7ff);
|
||||
fp->attributes = parse_uac_endpoint_attributes(chip, alts, protocol, iface_no);
|
||||
fp->clock = clock;
|
||||
|
||||
/* some quirks for attributes here */
|
||||
|
||||
switch (chip->usb_id) {
|
||||
case USB_ID(0x0a92, 0x0053): /* AudioTrak Optoplay */
|
||||
/* Optoplay sets the sample rate attribute although
|
||||
* it seems not supporting it in fact.
|
||||
*/
|
||||
fp->attributes &= ~UAC_EP_CS_ATTR_SAMPLE_RATE;
|
||||
break;
|
||||
case USB_ID(0x041e, 0x3020): /* Creative SB Audigy 2 NX */
|
||||
case USB_ID(0x0763, 0x2003): /* M-Audio Audiophile USB */
|
||||
/* doesn't set the sample rate attribute, but supports it */
|
||||
fp->attributes |= UAC_EP_CS_ATTR_SAMPLE_RATE;
|
||||
break;
|
||||
case USB_ID(0x0763, 0x2001): /* M-Audio Quattro USB */
|
||||
case USB_ID(0x0763, 0x2012): /* M-Audio Fast Track Pro USB */
|
||||
case USB_ID(0x047f, 0x0ca1): /* plantronics headset */
|
||||
case USB_ID(0x077d, 0x07af): /* Griffin iMic (note that there is
|
||||
an older model 77d:223) */
|
||||
/*
|
||||
* plantronics headset and Griffin iMic have set adaptive-in
|
||||
* although it's really not...
|
||||
*/
|
||||
fp->ep_attr &= ~USB_ENDPOINT_SYNCTYPE;
|
||||
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
|
||||
fp->ep_attr |= USB_ENDPOINT_SYNC_ADAPTIVE;
|
||||
else
|
||||
fp->ep_attr |= USB_ENDPOINT_SYNC_SYNC;
|
||||
break;
|
||||
}
|
||||
|
||||
/* ok, let's parse further... */
|
||||
if (snd_usb_parse_audio_format(chip, fp, format, fmt, stream, alts) < 0) {
|
||||
kfree(fp->rate_table);
|
||||
kfree(fp);
|
||||
fp = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
snd_printdd(KERN_INFO "%d:%u:%d: add audio endpoint %#x\n", dev->devnum, iface_no, altno, fp->endpoint);
|
||||
err = snd_usb_add_audio_stream(chip, stream, fp);
|
||||
if (err < 0) {
|
||||
kfree(fp->rate_table);
|
||||
kfree(fp);
|
||||
return err;
|
||||
}
|
||||
/* try to set the interface... */
|
||||
usb_set_interface(chip->dev, iface_no, altno);
|
||||
snd_usb_init_pitch(chip, iface_no, alts, fp);
|
||||
snd_usb_init_sample_rate(chip, iface_no, alts, fp, fp->rate_max);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
#ifndef __USBAUDIO_STREAM_H
|
||||
#define __USBAUDIO_STREAM_H
|
||||
|
||||
int snd_usb_parse_audio_interface(struct snd_usb_audio *chip,
|
||||
int iface_no);
|
||||
|
||||
int snd_usb_add_audio_stream(struct snd_usb_audio *chip,
|
||||
int stream,
|
||||
struct audioformat *fp);
|
||||
|
||||
#endif /* __USBAUDIO_STREAM_H */
|
||||
|
941
sound/usb/urb.c
941
sound/usb/urb.c
|
@ -1,941 +0,0 @@
|
|||
/*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/usb/audio.h>
|
||||
|
||||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
|
||||
#include "usbaudio.h"
|
||||
#include "helper.h"
|
||||
#include "card.h"
|
||||
#include "urb.h"
|
||||
#include "pcm.h"
|
||||
|
||||
/*
|
||||
* convert a sampling rate into our full speed format (fs/1000 in Q16.16)
|
||||
* this will overflow at approx 524 kHz
|
||||
*/
|
||||
static inline unsigned get_usb_full_speed_rate(unsigned int rate)
|
||||
{
|
||||
return ((rate << 13) + 62) / 125;
|
||||
}
|
||||
|
||||
/*
|
||||
* convert a sampling rate into USB high speed format (fs/8000 in Q16.16)
|
||||
* this will overflow at approx 4 MHz
|
||||
*/
|
||||
static inline unsigned get_usb_high_speed_rate(unsigned int rate)
|
||||
{
|
||||
return ((rate << 10) + 62) / 125;
|
||||
}
|
||||
|
||||
/*
|
||||
* unlink active urbs.
|
||||
*/
|
||||
static int deactivate_urbs(struct snd_usb_substream *subs, int force, int can_sleep)
|
||||
{
|
||||
struct snd_usb_audio *chip = subs->stream->chip;
|
||||
unsigned int i;
|
||||
int async;
|
||||
|
||||
subs->running = 0;
|
||||
|
||||
if (!force && subs->stream->chip->shutdown) /* to be sure... */
|
||||
return -EBADFD;
|
||||
|
||||
async = !can_sleep && chip->async_unlink;
|
||||
|
||||
if (!async && in_interrupt())
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < subs->nurbs; i++) {
|
||||
if (test_bit(i, &subs->active_mask)) {
|
||||
if (!test_and_set_bit(i, &subs->unlink_mask)) {
|
||||
struct urb *u = subs->dataurb[i].urb;
|
||||
if (async)
|
||||
usb_unlink_urb(u);
|
||||
else
|
||||
usb_kill_urb(u);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (subs->syncpipe) {
|
||||
for (i = 0; i < SYNC_URBS; i++) {
|
||||
if (test_bit(i+16, &subs->active_mask)) {
|
||||
if (!test_and_set_bit(i+16, &subs->unlink_mask)) {
|
||||
struct urb *u = subs->syncurb[i].urb;
|
||||
if (async)
|
||||
usb_unlink_urb(u);
|
||||
else
|
||||
usb_kill_urb(u);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* release a urb data
|
||||
*/
|
||||
static void release_urb_ctx(struct snd_urb_ctx *u)
|
||||
{
|
||||
if (u->urb) {
|
||||
if (u->buffer_size)
|
||||
usb_free_coherent(u->subs->dev, u->buffer_size,
|
||||
u->urb->transfer_buffer,
|
||||
u->urb->transfer_dma);
|
||||
usb_free_urb(u->urb);
|
||||
u->urb = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* wait until all urbs are processed.
|
||||
*/
|
||||
static int wait_clear_urbs(struct snd_usb_substream *subs)
|
||||
{
|
||||
unsigned long end_time = jiffies + msecs_to_jiffies(1000);
|
||||
unsigned int i;
|
||||
int alive;
|
||||
|
||||
do {
|
||||
alive = 0;
|
||||
for (i = 0; i < subs->nurbs; i++) {
|
||||
if (test_bit(i, &subs->active_mask))
|
||||
alive++;
|
||||
}
|
||||
if (subs->syncpipe) {
|
||||
for (i = 0; i < SYNC_URBS; i++) {
|
||||
if (test_bit(i + 16, &subs->active_mask))
|
||||
alive++;
|
||||
}
|
||||
}
|
||||
if (! alive)
|
||||
break;
|
||||
schedule_timeout_uninterruptible(1);
|
||||
} while (time_before(jiffies, end_time));
|
||||
if (alive)
|
||||
snd_printk(KERN_ERR "timeout: still %d active urbs..\n", alive);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* release a substream
|
||||
*/
|
||||
void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* stop urbs (to be sure) */
|
||||
deactivate_urbs(subs, force, 1);
|
||||
wait_clear_urbs(subs);
|
||||
|
||||
for (i = 0; i < MAX_URBS; i++)
|
||||
release_urb_ctx(&subs->dataurb[i]);
|
||||
for (i = 0; i < SYNC_URBS; i++)
|
||||
release_urb_ctx(&subs->syncurb[i]);
|
||||
usb_free_coherent(subs->dev, SYNC_URBS * 4,
|
||||
subs->syncbuf, subs->sync_dma);
|
||||
subs->syncbuf = NULL;
|
||||
subs->nurbs = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* complete callback from data urb
|
||||
*/
|
||||
static void snd_complete_urb(struct urb *urb)
|
||||
{
|
||||
struct snd_urb_ctx *ctx = urb->context;
|
||||
struct snd_usb_substream *subs = ctx->subs;
|
||||
struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
|
||||
int err = 0;
|
||||
|
||||
if ((subs->running && subs->ops.retire(subs, substream->runtime, urb)) ||
|
||||
!subs->running || /* can be stopped during retire callback */
|
||||
(err = subs->ops.prepare(subs, substream->runtime, urb)) < 0 ||
|
||||
(err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
|
||||
clear_bit(ctx->index, &subs->active_mask);
|
||||
if (err < 0) {
|
||||
snd_printd(KERN_ERR "cannot submit urb (err = %d)\n", err);
|
||||
snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* complete callback from sync urb
|
||||
*/
|
||||
static void snd_complete_sync_urb(struct urb *urb)
|
||||
{
|
||||
struct snd_urb_ctx *ctx = urb->context;
|
||||
struct snd_usb_substream *subs = ctx->subs;
|
||||
struct snd_pcm_substream *substream = ctx->subs->pcm_substream;
|
||||
int err = 0;
|
||||
|
||||
if ((subs->running && subs->ops.retire_sync(subs, substream->runtime, urb)) ||
|
||||
!subs->running || /* can be stopped during retire callback */
|
||||
(err = subs->ops.prepare_sync(subs, substream->runtime, urb)) < 0 ||
|
||||
(err = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
|
||||
clear_bit(ctx->index + 16, &subs->active_mask);
|
||||
if (err < 0) {
|
||||
snd_printd(KERN_ERR "cannot submit sync urb (err = %d)\n", err);
|
||||
snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* initialize a substream for plaback/capture
|
||||
*/
|
||||
int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
|
||||
unsigned int period_bytes,
|
||||
unsigned int rate,
|
||||
unsigned int frame_bits)
|
||||
{
|
||||
unsigned int maxsize, i;
|
||||
int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
|
||||
unsigned int urb_packs, total_packs, packs_per_ms;
|
||||
struct snd_usb_audio *chip = subs->stream->chip;
|
||||
|
||||
/* calculate the frequency in 16.16 format */
|
||||
if (snd_usb_get_speed(subs->dev) == USB_SPEED_FULL)
|
||||
subs->freqn = get_usb_full_speed_rate(rate);
|
||||
else
|
||||
subs->freqn = get_usb_high_speed_rate(rate);
|
||||
subs->freqm = subs->freqn;
|
||||
subs->freqshift = INT_MIN;
|
||||
/* calculate max. frequency */
|
||||
if (subs->maxpacksize) {
|
||||
/* whatever fits into a max. size packet */
|
||||
maxsize = subs->maxpacksize;
|
||||
subs->freqmax = (maxsize / (frame_bits >> 3))
|
||||
<< (16 - subs->datainterval);
|
||||
} else {
|
||||
/* no max. packet size: just take 25% higher than nominal */
|
||||
subs->freqmax = subs->freqn + (subs->freqn >> 2);
|
||||
maxsize = ((subs->freqmax + 0xffff) * (frame_bits >> 3))
|
||||
>> (16 - subs->datainterval);
|
||||
}
|
||||
subs->phase = 0;
|
||||
|
||||
if (subs->fill_max)
|
||||
subs->curpacksize = subs->maxpacksize;
|
||||
else
|
||||
subs->curpacksize = maxsize;
|
||||
|
||||
if (snd_usb_get_speed(subs->dev) != USB_SPEED_FULL)
|
||||
packs_per_ms = 8 >> subs->datainterval;
|
||||
else
|
||||
packs_per_ms = 1;
|
||||
|
||||
if (is_playback) {
|
||||
urb_packs = max(chip->nrpacks, 1);
|
||||
urb_packs = min(urb_packs, (unsigned int)MAX_PACKS);
|
||||
} else
|
||||
urb_packs = 1;
|
||||
urb_packs *= packs_per_ms;
|
||||
if (subs->syncpipe)
|
||||
urb_packs = min(urb_packs, 1U << subs->syncinterval);
|
||||
|
||||
/* decide how many packets to be used */
|
||||
if (is_playback) {
|
||||
unsigned int minsize, maxpacks;
|
||||
/* determine how small a packet can be */
|
||||
minsize = (subs->freqn >> (16 - subs->datainterval))
|
||||
* (frame_bits >> 3);
|
||||
/* with sync from device, assume it can be 12% lower */
|
||||
if (subs->syncpipe)
|
||||
minsize -= minsize >> 3;
|
||||
minsize = max(minsize, 1u);
|
||||
total_packs = (period_bytes + minsize - 1) / minsize;
|
||||
/* we need at least two URBs for queueing */
|
||||
if (total_packs < 2) {
|
||||
total_packs = 2;
|
||||
} else {
|
||||
/* and we don't want too long a queue either */
|
||||
maxpacks = max(MAX_QUEUE * packs_per_ms, urb_packs * 2);
|
||||
total_packs = min(total_packs, maxpacks);
|
||||
}
|
||||
} else {
|
||||
while (urb_packs > 1 && urb_packs * maxsize >= period_bytes)
|
||||
urb_packs >>= 1;
|
||||
total_packs = MAX_URBS * urb_packs;
|
||||
}
|
||||
subs->nurbs = (total_packs + urb_packs - 1) / urb_packs;
|
||||
if (subs->nurbs > MAX_URBS) {
|
||||
/* too much... */
|
||||
subs->nurbs = MAX_URBS;
|
||||
total_packs = MAX_URBS * urb_packs;
|
||||
} else if (subs->nurbs < 2) {
|
||||
/* too little - we need at least two packets
|
||||
* to ensure contiguous playback/capture
|
||||
*/
|
||||
subs->nurbs = 2;
|
||||
}
|
||||
|
||||
/* allocate and initialize data urbs */
|
||||
for (i = 0; i < subs->nurbs; i++) {
|
||||
struct snd_urb_ctx *u = &subs->dataurb[i];
|
||||
u->index = i;
|
||||
u->subs = subs;
|
||||
u->packets = (i + 1) * total_packs / subs->nurbs
|
||||
- i * total_packs / subs->nurbs;
|
||||
u->buffer_size = maxsize * u->packets;
|
||||
if (subs->fmt_type == UAC_FORMAT_TYPE_II)
|
||||
u->packets++; /* for transfer delimiter */
|
||||
u->urb = usb_alloc_urb(u->packets, GFP_KERNEL);
|
||||
if (!u->urb)
|
||||
goto out_of_memory;
|
||||
u->urb->transfer_buffer =
|
||||
usb_alloc_coherent(subs->dev, u->buffer_size,
|
||||
GFP_KERNEL, &u->urb->transfer_dma);
|
||||
if (!u->urb->transfer_buffer)
|
||||
goto out_of_memory;
|
||||
u->urb->pipe = subs->datapipe;
|
||||
u->urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
|
||||
u->urb->interval = 1 << subs->datainterval;
|
||||
u->urb->context = u;
|
||||
u->urb->complete = snd_complete_urb;
|
||||
}
|
||||
|
||||
if (subs->syncpipe) {
|
||||
/* allocate and initialize sync urbs */
|
||||
subs->syncbuf = usb_alloc_coherent(subs->dev, SYNC_URBS * 4,
|
||||
GFP_KERNEL, &subs->sync_dma);
|
||||
if (!subs->syncbuf)
|
||||
goto out_of_memory;
|
||||
for (i = 0; i < SYNC_URBS; i++) {
|
||||
struct snd_urb_ctx *u = &subs->syncurb[i];
|
||||
u->index = i;
|
||||
u->subs = subs;
|
||||
u->packets = 1;
|
||||
u->urb = usb_alloc_urb(1, GFP_KERNEL);
|
||||
if (!u->urb)
|
||||
goto out_of_memory;
|
||||
u->urb->transfer_buffer = subs->syncbuf + i * 4;
|
||||
u->urb->transfer_dma = subs->sync_dma + i * 4;
|
||||
u->urb->transfer_buffer_length = 4;
|
||||
u->urb->pipe = subs->syncpipe;
|
||||
u->urb->transfer_flags = URB_ISO_ASAP |
|
||||
URB_NO_TRANSFER_DMA_MAP;
|
||||
u->urb->number_of_packets = 1;
|
||||
u->urb->interval = 1 << subs->syncinterval;
|
||||
u->urb->context = u;
|
||||
u->urb->complete = snd_complete_sync_urb;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
out_of_memory:
|
||||
snd_usb_release_substream_urbs(subs, 0);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* prepare urb for full speed capture sync pipe
|
||||
*
|
||||
* fill the length and offset of each urb descriptor.
|
||||
* the fixed 10.14 frequency is passed through the pipe.
|
||||
*/
|
||||
static int prepare_capture_sync_urb(struct snd_usb_substream *subs,
|
||||
struct snd_pcm_runtime *runtime,
|
||||
struct urb *urb)
|
||||
{
|
||||
unsigned char *cp = urb->transfer_buffer;
|
||||
struct snd_urb_ctx *ctx = urb->context;
|
||||
|
||||
urb->dev = ctx->subs->dev; /* we need to set this at each time */
|
||||
urb->iso_frame_desc[0].length = 3;
|
||||
urb->iso_frame_desc[0].offset = 0;
|
||||
cp[0] = subs->freqn >> 2;
|
||||
cp[1] = subs->freqn >> 10;
|
||||
cp[2] = subs->freqn >> 18;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* prepare urb for high speed capture sync pipe
|
||||
*
|
||||
* fill the length and offset of each urb descriptor.
|
||||
* the fixed 12.13 frequency is passed as 16.16 through the pipe.
|
||||
*/
|
||||
static int prepare_capture_sync_urb_hs(struct snd_usb_substream *subs,
|
||||
struct snd_pcm_runtime *runtime,
|
||||
struct urb *urb)
|
||||
{
|
||||
unsigned char *cp = urb->transfer_buffer;
|
||||
struct snd_urb_ctx *ctx = urb->context;
|
||||
|
||||
urb->dev = ctx->subs->dev; /* we need to set this at each time */
|
||||
urb->iso_frame_desc[0].length = 4;
|
||||
urb->iso_frame_desc[0].offset = 0;
|
||||
cp[0] = subs->freqn;
|
||||
cp[1] = subs->freqn >> 8;
|
||||
cp[2] = subs->freqn >> 16;
|
||||
cp[3] = subs->freqn >> 24;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* process after capture sync complete
|
||||
* - nothing to do
|
||||
*/
|
||||
static int retire_capture_sync_urb(struct snd_usb_substream *subs,
|
||||
struct snd_pcm_runtime *runtime,
|
||||
struct urb *urb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* prepare urb for capture data pipe
|
||||
*
|
||||
* fill the offset and length of each descriptor.
|
||||
*
|
||||
* we use a temporary buffer to write the captured data.
|
||||
* since the length of written data is determined by host, we cannot
|
||||
* write onto the pcm buffer directly... the data is thus copied
|
||||
* later at complete callback to the global buffer.
|
||||
*/
|
||||
static int prepare_capture_urb(struct snd_usb_substream *subs,
|
||||
struct snd_pcm_runtime *runtime,
|
||||
struct urb *urb)
|
||||
{
|
||||
int i, offs;
|
||||
struct snd_urb_ctx *ctx = urb->context;
|
||||
|
||||
offs = 0;
|
||||
urb->dev = ctx->subs->dev; /* we need to set this at each time */
|
||||
for (i = 0; i < ctx->packets; i++) {
|
||||
urb->iso_frame_desc[i].offset = offs;
|
||||
urb->iso_frame_desc[i].length = subs->curpacksize;
|
||||
offs += subs->curpacksize;
|
||||
}
|
||||
urb->transfer_buffer_length = offs;
|
||||
urb->number_of_packets = ctx->packets;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* process after capture complete
|
||||
*
|
||||
* copy the data from each desctiptor to the pcm buffer, and
|
||||
* update the current position.
|
||||
*/
|
||||
static int retire_capture_urb(struct snd_usb_substream *subs,
|
||||
struct snd_pcm_runtime *runtime,
|
||||
struct urb *urb)
|
||||
{
|
||||
unsigned long flags;
|
||||
unsigned char *cp;
|
||||
int i;
|
||||
unsigned int stride, frames, bytes, oldptr;
|
||||
int period_elapsed = 0;
|
||||
|
||||
stride = runtime->frame_bits >> 3;
|
||||
|
||||
for (i = 0; i < urb->number_of_packets; i++) {
|
||||
cp = (unsigned char *)urb->transfer_buffer + urb->iso_frame_desc[i].offset;
|
||||
if (urb->iso_frame_desc[i].status) {
|
||||
snd_printd(KERN_ERR "frame %d active: %d\n", i, urb->iso_frame_desc[i].status);
|
||||
// continue;
|
||||
}
|
||||
bytes = urb->iso_frame_desc[i].actual_length;
|
||||
frames = bytes / stride;
|
||||
if (!subs->txfr_quirk)
|
||||
bytes = frames * stride;
|
||||
if (bytes % (runtime->sample_bits >> 3) != 0) {
|
||||
#ifdef CONFIG_SND_DEBUG_VERBOSE
|
||||
int oldbytes = bytes;
|
||||
#endif
|
||||
bytes = frames * stride;
|
||||
snd_printdd(KERN_ERR "Corrected urb data len. %d->%d\n",
|
||||
oldbytes, bytes);
|
||||
}
|
||||
/* update the current pointer */
|
||||
spin_lock_irqsave(&subs->lock, flags);
|
||||
oldptr = subs->hwptr_done;
|
||||
subs->hwptr_done += bytes;
|
||||
if (subs->hwptr_done >= runtime->buffer_size * stride)
|
||||
subs->hwptr_done -= runtime->buffer_size * stride;
|
||||
frames = (bytes + (oldptr % stride)) / stride;
|
||||
subs->transfer_done += frames;
|
||||
if (subs->transfer_done >= runtime->period_size) {
|
||||
subs->transfer_done -= runtime->period_size;
|
||||
period_elapsed = 1;
|
||||
}
|
||||
spin_unlock_irqrestore(&subs->lock, flags);
|
||||
/* copy a data chunk */
|
||||
if (oldptr + bytes > runtime->buffer_size * stride) {
|
||||
unsigned int bytes1 =
|
||||
runtime->buffer_size * stride - oldptr;
|
||||
memcpy(runtime->dma_area + oldptr, cp, bytes1);
|
||||
memcpy(runtime->dma_area, cp + bytes1, bytes - bytes1);
|
||||
} else {
|
||||
memcpy(runtime->dma_area + oldptr, cp, bytes);
|
||||
}
|
||||
}
|
||||
if (period_elapsed)
|
||||
snd_pcm_period_elapsed(subs->pcm_substream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process after capture complete when paused. Nothing to do.
|
||||
*/
|
||||
static int retire_paused_capture_urb(struct snd_usb_substream *subs,
|
||||
struct snd_pcm_runtime *runtime,
|
||||
struct urb *urb)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* prepare urb for playback sync pipe
|
||||
*
|
||||
* set up the offset and length to receive the current frequency.
|
||||
*/
|
||||
static int prepare_playback_sync_urb(struct snd_usb_substream *subs,
|
||||
struct snd_pcm_runtime *runtime,
|
||||
struct urb *urb)
|
||||
{
|
||||
struct snd_urb_ctx *ctx = urb->context;
|
||||
|
||||
urb->dev = ctx->subs->dev; /* we need to set this at each time */
|
||||
urb->iso_frame_desc[0].length = min(4u, ctx->subs->syncmaxsize);
|
||||
urb->iso_frame_desc[0].offset = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* process after playback sync complete
|
||||
*
|
||||
* Full speed devices report feedback values in 10.14 format as samples per
|
||||
* frame, high speed devices in 16.16 format as samples per microframe.
|
||||
* Because the Audio Class 1 spec was written before USB 2.0, many high speed
|
||||
* devices use a wrong interpretation, some others use an entirely different
|
||||
* format. Therefore, we cannot predict what format any particular device uses
|
||||
* and must detect it automatically.
|
||||
*/
|
||||
static int retire_playback_sync_urb(struct snd_usb_substream *subs,
|
||||
struct snd_pcm_runtime *runtime,
|
||||
struct urb *urb)
|
||||
{
|
||||
unsigned int f;
|
||||
int shift;
|
||||
unsigned long flags;
|
||||
|
||||
if (urb->iso_frame_desc[0].status != 0 ||
|
||||
urb->iso_frame_desc[0].actual_length < 3)
|
||||
return 0;
|
||||
|
||||
f = le32_to_cpup(urb->transfer_buffer);
|
||||
if (urb->iso_frame_desc[0].actual_length == 3)
|
||||
f &= 0x00ffffff;
|
||||
else
|
||||
f &= 0x0fffffff;
|
||||
if (f == 0)
|
||||
return 0;
|
||||
|
||||
if (unlikely(subs->freqshift == INT_MIN)) {
|
||||
/*
|
||||
* The first time we see a feedback value, determine its format
|
||||
* by shifting it left or right until it matches the nominal
|
||||
* frequency value. This assumes that the feedback does not
|
||||
* differ from the nominal value more than +50% or -25%.
|
||||
*/
|
||||
shift = 0;
|
||||
while (f < subs->freqn - subs->freqn / 4) {
|
||||
f <<= 1;
|
||||
shift++;
|
||||
}
|
||||
while (f > subs->freqn + subs->freqn / 2) {
|
||||
f >>= 1;
|
||||
shift--;
|
||||
}
|
||||
subs->freqshift = shift;
|
||||
}
|
||||
else if (subs->freqshift >= 0)
|
||||
f <<= subs->freqshift;
|
||||
else
|
||||
f >>= -subs->freqshift;
|
||||
|
||||
if (likely(f >= subs->freqn - subs->freqn / 8 && f <= subs->freqmax)) {
|
||||
/*
|
||||
* If the frequency looks valid, set it.
|
||||
* This value is referred to in prepare_playback_urb().
|
||||
*/
|
||||
spin_lock_irqsave(&subs->lock, flags);
|
||||
subs->freqm = f;
|
||||
spin_unlock_irqrestore(&subs->lock, flags);
|
||||
} else {
|
||||
/*
|
||||
* Out of range; maybe the shift value is wrong.
|
||||
* Reset it so that we autodetect again the next time.
|
||||
*/
|
||||
subs->freqshift = INT_MIN;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* determine the number of frames in the next packet */
|
||||
static int snd_usb_audio_next_packet_size(struct snd_usb_substream *subs)
|
||||
{
|
||||
if (subs->fill_max)
|
||||
return subs->maxframesize;
|
||||
else {
|
||||
subs->phase = (subs->phase & 0xffff)
|
||||
+ (subs->freqm << subs->datainterval);
|
||||
return min(subs->phase >> 16, subs->maxframesize);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Prepare urb for streaming before playback starts or when paused.
|
||||
*
|
||||
* We don't have any data, so we send silence.
|
||||
*/
|
||||
static int prepare_nodata_playback_urb(struct snd_usb_substream *subs,
|
||||
struct snd_pcm_runtime *runtime,
|
||||
struct urb *urb)
|
||||
{
|
||||
unsigned int i, offs, counts;
|
||||
struct snd_urb_ctx *ctx = urb->context;
|
||||
int stride = runtime->frame_bits >> 3;
|
||||
|
||||
offs = 0;
|
||||
urb->dev = ctx->subs->dev;
|
||||
for (i = 0; i < ctx->packets; ++i) {
|
||||
counts = snd_usb_audio_next_packet_size(subs);
|
||||
urb->iso_frame_desc[i].offset = offs * stride;
|
||||
urb->iso_frame_desc[i].length = counts * stride;
|
||||
offs += counts;
|
||||
}
|
||||
urb->number_of_packets = ctx->packets;
|
||||
urb->transfer_buffer_length = offs * stride;
|
||||
memset(urb->transfer_buffer,
|
||||
runtime->format == SNDRV_PCM_FORMAT_U8 ? 0x80 : 0,
|
||||
offs * stride);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* prepare urb for playback data pipe
|
||||
*
|
||||
* Since a URB can handle only a single linear buffer, we must use double
|
||||
* buffering when the data to be transferred overflows the buffer boundary.
|
||||
* To avoid inconsistencies when updating hwptr_done, we use double buffering
|
||||
* for all URBs.
|
||||
*/
|
||||
static int prepare_playback_urb(struct snd_usb_substream *subs,
|
||||
struct snd_pcm_runtime *runtime,
|
||||
struct urb *urb)
|
||||
{
|
||||
int i, stride;
|
||||
unsigned int counts, frames, bytes;
|
||||
unsigned long flags;
|
||||
int period_elapsed = 0;
|
||||
struct snd_urb_ctx *ctx = urb->context;
|
||||
|
||||
stride = runtime->frame_bits >> 3;
|
||||
|
||||
frames = 0;
|
||||
urb->dev = ctx->subs->dev; /* we need to set this at each time */
|
||||
urb->number_of_packets = 0;
|
||||
spin_lock_irqsave(&subs->lock, flags);
|
||||
for (i = 0; i < ctx->packets; i++) {
|
||||
counts = snd_usb_audio_next_packet_size(subs);
|
||||
/* set up descriptor */
|
||||
urb->iso_frame_desc[i].offset = frames * stride;
|
||||
urb->iso_frame_desc[i].length = counts * stride;
|
||||
frames += counts;
|
||||
urb->number_of_packets++;
|
||||
subs->transfer_done += counts;
|
||||
if (subs->transfer_done >= runtime->period_size) {
|
||||
subs->transfer_done -= runtime->period_size;
|
||||
period_elapsed = 1;
|
||||
if (subs->fmt_type == UAC_FORMAT_TYPE_II) {
|
||||
if (subs->transfer_done > 0) {
|
||||
/* FIXME: fill-max mode is not
|
||||
* supported yet */
|
||||
frames -= subs->transfer_done;
|
||||
counts -= subs->transfer_done;
|
||||
urb->iso_frame_desc[i].length =
|
||||
counts * stride;
|
||||
subs->transfer_done = 0;
|
||||
}
|
||||
i++;
|
||||
if (i < ctx->packets) {
|
||||
/* add a transfer delimiter */
|
||||
urb->iso_frame_desc[i].offset =
|
||||
frames * stride;
|
||||
urb->iso_frame_desc[i].length = 0;
|
||||
urb->number_of_packets++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (period_elapsed) /* finish at the period boundary */
|
||||
break;
|
||||
}
|
||||
bytes = frames * stride;
|
||||
if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
|
||||
/* err, the transferred area goes over buffer boundary. */
|
||||
unsigned int bytes1 =
|
||||
runtime->buffer_size * stride - subs->hwptr_done;
|
||||
memcpy(urb->transfer_buffer,
|
||||
runtime->dma_area + subs->hwptr_done, bytes1);
|
||||
memcpy(urb->transfer_buffer + bytes1,
|
||||
runtime->dma_area, bytes - bytes1);
|
||||
} else {
|
||||
memcpy(urb->transfer_buffer,
|
||||
runtime->dma_area + subs->hwptr_done, bytes);
|
||||
}
|
||||
subs->hwptr_done += bytes;
|
||||
if (subs->hwptr_done >= runtime->buffer_size * stride)
|
||||
subs->hwptr_done -= runtime->buffer_size * stride;
|
||||
runtime->delay += frames;
|
||||
spin_unlock_irqrestore(&subs->lock, flags);
|
||||
urb->transfer_buffer_length = bytes;
|
||||
if (period_elapsed)
|
||||
snd_pcm_period_elapsed(subs->pcm_substream);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* process after playback data complete
|
||||
* - decrease the delay count again
|
||||
*/
|
||||
static int retire_playback_urb(struct snd_usb_substream *subs,
|
||||
struct snd_pcm_runtime *runtime,
|
||||
struct urb *urb)
|
||||
{
|
||||
unsigned long flags;
|
||||
int stride = runtime->frame_bits >> 3;
|
||||
int processed = urb->transfer_buffer_length / stride;
|
||||
|
||||
spin_lock_irqsave(&subs->lock, flags);
|
||||
if (processed > runtime->delay)
|
||||
runtime->delay = 0;
|
||||
else
|
||||
runtime->delay -= processed;
|
||||
spin_unlock_irqrestore(&subs->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *usb_error_string(int err)
|
||||
{
|
||||
switch (err) {
|
||||
case -ENODEV:
|
||||
return "no device";
|
||||
case -ENOENT:
|
||||
return "endpoint not enabled";
|
||||
case -EPIPE:
|
||||
return "endpoint stalled";
|
||||
case -ENOSPC:
|
||||
return "not enough bandwidth";
|
||||
case -ESHUTDOWN:
|
||||
return "device disabled";
|
||||
case -EHOSTUNREACH:
|
||||
return "device suspended";
|
||||
case -EINVAL:
|
||||
case -EAGAIN:
|
||||
case -EFBIG:
|
||||
case -EMSGSIZE:
|
||||
return "internal error";
|
||||
default:
|
||||
return "unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* set up and start data/sync urbs
|
||||
*/
|
||||
static int start_urbs(struct snd_usb_substream *subs, struct snd_pcm_runtime *runtime)
|
||||
{
|
||||
unsigned int i;
|
||||
int err;
|
||||
|
||||
if (subs->stream->chip->shutdown)
|
||||
return -EBADFD;
|
||||
|
||||
for (i = 0; i < subs->nurbs; i++) {
|
||||
if (snd_BUG_ON(!subs->dataurb[i].urb))
|
||||
return -EINVAL;
|
||||
if (subs->ops.prepare(subs, runtime, subs->dataurb[i].urb) < 0) {
|
||||
snd_printk(KERN_ERR "cannot prepare datapipe for urb %d\n", i);
|
||||
goto __error;
|
||||
}
|
||||
}
|
||||
if (subs->syncpipe) {
|
||||
for (i = 0; i < SYNC_URBS; i++) {
|
||||
if (snd_BUG_ON(!subs->syncurb[i].urb))
|
||||
return -EINVAL;
|
||||
if (subs->ops.prepare_sync(subs, runtime, subs->syncurb[i].urb) < 0) {
|
||||
snd_printk(KERN_ERR "cannot prepare syncpipe for urb %d\n", i);
|
||||
goto __error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subs->active_mask = 0;
|
||||
subs->unlink_mask = 0;
|
||||
subs->running = 1;
|
||||
for (i = 0; i < subs->nurbs; i++) {
|
||||
err = usb_submit_urb(subs->dataurb[i].urb, GFP_ATOMIC);
|
||||
if (err < 0) {
|
||||
snd_printk(KERN_ERR "cannot submit datapipe "
|
||||
"for urb %d, error %d: %s\n",
|
||||
i, err, usb_error_string(err));
|
||||
goto __error;
|
||||
}
|
||||
set_bit(i, &subs->active_mask);
|
||||
}
|
||||
if (subs->syncpipe) {
|
||||
for (i = 0; i < SYNC_URBS; i++) {
|
||||
err = usb_submit_urb(subs->syncurb[i].urb, GFP_ATOMIC);
|
||||
if (err < 0) {
|
||||
snd_printk(KERN_ERR "cannot submit syncpipe "
|
||||
"for urb %d, error %d: %s\n",
|
||||
i, err, usb_error_string(err));
|
||||
goto __error;
|
||||
}
|
||||
set_bit(i + 16, &subs->active_mask);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
__error:
|
||||
// snd_pcm_stop(subs->pcm_substream, SNDRV_PCM_STATE_XRUN);
|
||||
deactivate_urbs(subs, 0, 0);
|
||||
return -EPIPE;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
*/
|
||||
static struct snd_urb_ops audio_urb_ops[2] = {
|
||||
{
|
||||
.prepare = prepare_nodata_playback_urb,
|
||||
.retire = retire_playback_urb,
|
||||
.prepare_sync = prepare_playback_sync_urb,
|
||||
.retire_sync = retire_playback_sync_urb,
|
||||
},
|
||||
{
|
||||
.prepare = prepare_capture_urb,
|
||||
.retire = retire_capture_urb,
|
||||
.prepare_sync = prepare_capture_sync_urb,
|
||||
.retire_sync = retire_capture_sync_urb,
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* initialize the substream instance.
|
||||
*/
|
||||
|
||||
void snd_usb_init_substream(struct snd_usb_stream *as,
|
||||
int stream, struct audioformat *fp)
|
||||
{
|
||||
struct snd_usb_substream *subs = &as->substream[stream];
|
||||
|
||||
INIT_LIST_HEAD(&subs->fmt_list);
|
||||
spin_lock_init(&subs->lock);
|
||||
|
||||
subs->stream = as;
|
||||
subs->direction = stream;
|
||||
subs->dev = as->chip->dev;
|
||||
subs->txfr_quirk = as->chip->txfr_quirk;
|
||||
subs->ops = audio_urb_ops[stream];
|
||||
if (snd_usb_get_speed(subs->dev) >= USB_SPEED_HIGH)
|
||||
subs->ops.prepare_sync = prepare_capture_sync_urb_hs;
|
||||
|
||||
snd_usb_set_pcm_ops(as->pcm, stream);
|
||||
|
||||
list_add_tail(&fp->list, &subs->fmt_list);
|
||||
subs->formats |= fp->formats;
|
||||
subs->endpoint = fp->endpoint;
|
||||
subs->num_formats++;
|
||||
subs->fmt_type = fp->fmt_type;
|
||||
}
|
||||
|
||||
int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_usb_substream *subs = substream->runtime->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
subs->ops.prepare = prepare_playback_urb;
|
||||
return 0;
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
return deactivate_urbs(subs, 0, 0);
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
subs->ops.prepare = prepare_nodata_playback_urb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd)
|
||||
{
|
||||
struct snd_usb_substream *subs = substream->runtime->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
subs->ops.retire = retire_capture_urb;
|
||||
return start_urbs(subs, substream->runtime);
|
||||
case SNDRV_PCM_TRIGGER_STOP:
|
||||
return deactivate_urbs(subs, 0, 0);
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
|
||||
subs->ops.retire = retire_paused_capture_urb;
|
||||
return 0;
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
subs->ops.retire = retire_capture_urb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
int snd_usb_substream_prepare(struct snd_usb_substream *subs,
|
||||
struct snd_pcm_runtime *runtime)
|
||||
{
|
||||
/* clear urbs (to be sure) */
|
||||
deactivate_urbs(subs, 0, 1);
|
||||
wait_clear_urbs(subs);
|
||||
|
||||
/* for playback, submit the URBs now; otherwise, the first hwptr_done
|
||||
* updates for all URBs would happen at the same time when starting */
|
||||
if (subs->direction == SNDRV_PCM_STREAM_PLAYBACK) {
|
||||
subs->ops.prepare = prepare_nodata_playback_urb;
|
||||
return start_urbs(subs, runtime);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -1,21 +0,0 @@
|
|||
#ifndef __USBAUDIO_URB_H
|
||||
#define __USBAUDIO_URB_H
|
||||
|
||||
void snd_usb_init_substream(struct snd_usb_stream *as,
|
||||
int stream,
|
||||
struct audioformat *fp);
|
||||
|
||||
int snd_usb_init_substream_urbs(struct snd_usb_substream *subs,
|
||||
unsigned int period_bytes,
|
||||
unsigned int rate,
|
||||
unsigned int frame_bits);
|
||||
|
||||
void snd_usb_release_substream_urbs(struct snd_usb_substream *subs, int force);
|
||||
|
||||
int snd_usb_substream_prepare(struct snd_usb_substream *subs,
|
||||
struct snd_pcm_runtime *runtime);
|
||||
|
||||
int snd_usb_substream_playback_trigger(struct snd_pcm_substream *substream, int cmd);
|
||||
int snd_usb_substream_capture_trigger(struct snd_pcm_substream *substream, int cmd);
|
||||
|
||||
#endif /* __USBAUDIO_URB_H */
|
|
@ -80,6 +80,7 @@ enum quirk_type {
|
|||
QUIRK_MIDI_CME,
|
||||
QUIRK_MIDI_AKAI,
|
||||
QUIRK_MIDI_US122L,
|
||||
QUIRK_MIDI_FTDI,
|
||||
QUIRK_AUDIO_STANDARD_INTERFACE,
|
||||
QUIRK_AUDIO_FIXED_ENDPOINT,
|
||||
QUIRK_AUDIO_EDIROL_UAXX,
|
||||
|
|
Загрузка…
Ссылка в новой задаче