mirror of
https://github.com/torvalds/linux.git
synced 2024-11-15 00:21:59 +00:00
sound fixes for 4.5-rc3
This was a busy week and I had to prepare a pile of duct tapes for the bugs reported by syzkaller fuzzer in wide range of ALSA core APIs: timer, rawmidi, sequencer, and PCM OSS emulation. Let's see how many other holes we need to plug. Besides that, a few usual boring stuff, HD- and USB-audio quirks, have been added. -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJWtIgQAAoJEGwxgFQ9KSmkxRIP/3Bj1y8JPgUcjZeTRyRqZ56/ AETmk3J4NLvVKfW4GGUDc6ywkpqjau+srviKhvpgK8VOZ7zIdK5TfvDYUumcbv5Z lYMQamaqu9Huq8O05u11v8iD04TkhAeNWmyRqh25AlgOpoCDEVIWoOqwsq1/rToU 9TD9zEgSxdbkCKHwVBw6CHZUTkBVFUnFZnvw1idETG3DTKPkNbtk1oRTTe+yaaW4 EVunsCsoWk9vQPh5jeWsOmpz/QdecDTA7M0T0NWXKWIaEMZhegQbuv8Mx63R87zO 3QpDaxJ4Ied9KSGIubhuzx7yXGtqY/oBPuTqQJDZYG5juVOo1ZlK7uM9hOnxTvLT Ap5KW6n5sVBI6vVMXtUUzW6jkETXkJT4sxxcYosOflwAc98a29waKB4gxw9ih1PK 4D+OuU1GNKUQA+0f0in94YFliMX1iu4IecH0KjtMU9CCqv20QDO4AvWtN0CotJXZ CKNmzuR+ETPHnwplN1FWqiVw5w6OteZ8+JIFmda19lU3tF5cU1RUAqdBEvo1LJBN pAXip+9LQEmUqYafDtivujJO5epYPCx0pTas8L8+5z22BLDfq0A/L7fqRSgpyDDg uZMhsuZfzrFMlStb9dsOcHfHJlQYb4tnPup1FNX695BvRVKNBvNnyzo79IBtN35s VkCRMpyympGnWaSpedG5 =b0/S -----END PGP SIGNATURE----- Merge tag 'sound-4.5-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound Pull sound fixes from Takashi Iwai: "This was a busy week and I had to prepare a pile of duct tapes for the bugs reported by syzkaller fuzzer in wide range of ALSA core APIs: timer, rawmidi, sequencer, and PCM OSS emulation. Let's see how many other holes we need to plug. Besides that, a few usual boring stuff, HD- and USB-audio quirks, have been added" * tag 'sound-4.5-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: ALSA: timer: Fix leftover link at closing ALSA: seq: Fix lockdep warnings due to double mutex locks ALSA: rawmidi: Fix race at copying & updating the position ALSA: rawmidi: Make snd_rawmidi_transmit() race-free ALSA: hda - Add fixup for Mac Mini 7,1 model ALSA: hda/realtek - Support headset mode for ALC225 ALSA: hda/realtek - Support Dell headset mode for ALC225 ALSA: hda/realtek - New codec support of ALC225 ALSA: timer: Sync timer deletion at closing the system timer ALSA: timer: Fix link corruption due to double start or stop ALSA: seq: Fix yet another races among ALSA timer accesses ALSA: pcm: Fix potential deadlock in OSS emulation ALSA: rawmidi: Remove kernel WARNING for NULL user-space buffer check ALSA: seq: Fix race at closing in virmidi driver ALSA: emu10k1: correctly handling failed thread creation ALSA: usb-audio: Add quirk for Microsoft LifeCam HD-6000 ALSA: usb-audio: Add native DSD support for PS Audio NuWave DAC ALSA: usb-audio: Fix OPPO HA-1 vendor ID
This commit is contained in:
commit
ea5a273c76
@ -167,6 +167,10 @@ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
|
||||
int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count);
|
||||
int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream,
|
||||
unsigned char *buffer, int count);
|
||||
int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
|
||||
unsigned char *buffer, int count);
|
||||
int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream,
|
||||
int count);
|
||||
|
||||
/* main midi functions */
|
||||
|
||||
|
@ -835,7 +835,8 @@ static int choose_rate(struct snd_pcm_substream *substream,
|
||||
return snd_pcm_hw_param_near(substream, params, SNDRV_PCM_HW_PARAM_RATE, best_rate, NULL);
|
||||
}
|
||||
|
||||
static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
|
||||
static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream,
|
||||
bool trylock)
|
||||
{
|
||||
struct snd_pcm_runtime *runtime = substream->runtime;
|
||||
struct snd_pcm_hw_params *params, *sparams;
|
||||
@ -849,7 +850,10 @@ static int snd_pcm_oss_change_params(struct snd_pcm_substream *substream)
|
||||
struct snd_mask sformat_mask;
|
||||
struct snd_mask mask;
|
||||
|
||||
if (mutex_lock_interruptible(&runtime->oss.params_lock))
|
||||
if (trylock) {
|
||||
if (!(mutex_trylock(&runtime->oss.params_lock)))
|
||||
return -EAGAIN;
|
||||
} else if (mutex_lock_interruptible(&runtime->oss.params_lock))
|
||||
return -EINTR;
|
||||
sw_params = kzalloc(sizeof(*sw_params), GFP_KERNEL);
|
||||
params = kmalloc(sizeof(*params), GFP_KERNEL);
|
||||
@ -1092,7 +1096,7 @@ static int snd_pcm_oss_get_active_substream(struct snd_pcm_oss_file *pcm_oss_fil
|
||||
if (asubstream == NULL)
|
||||
asubstream = substream;
|
||||
if (substream->runtime->oss.params) {
|
||||
err = snd_pcm_oss_change_params(substream);
|
||||
err = snd_pcm_oss_change_params(substream, false);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
@ -1132,7 +1136,7 @@ static int snd_pcm_oss_make_ready(struct snd_pcm_substream *substream)
|
||||
return 0;
|
||||
runtime = substream->runtime;
|
||||
if (runtime->oss.params) {
|
||||
err = snd_pcm_oss_change_params(substream);
|
||||
err = snd_pcm_oss_change_params(substream, false);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
@ -2163,7 +2167,7 @@ static int snd_pcm_oss_get_space(struct snd_pcm_oss_file *pcm_oss_file, int stre
|
||||
runtime = substream->runtime;
|
||||
|
||||
if (runtime->oss.params &&
|
||||
(err = snd_pcm_oss_change_params(substream)) < 0)
|
||||
(err = snd_pcm_oss_change_params(substream, false)) < 0)
|
||||
return err;
|
||||
|
||||
info.fragsize = runtime->oss.period_bytes;
|
||||
@ -2804,7 +2808,12 @@ static int snd_pcm_oss_mmap(struct file *file, struct vm_area_struct *area)
|
||||
return -EIO;
|
||||
|
||||
if (runtime->oss.params) {
|
||||
if ((err = snd_pcm_oss_change_params(substream)) < 0)
|
||||
/* use mutex_trylock() for params_lock for avoiding a deadlock
|
||||
* between mmap_sem and params_lock taken by
|
||||
* copy_from/to_user() in snd_pcm_oss_write/read()
|
||||
*/
|
||||
err = snd_pcm_oss_change_params(substream, true);
|
||||
if (err < 0)
|
||||
return err;
|
||||
}
|
||||
#ifdef CONFIG_SND_PCM_OSS_PLUGINS
|
||||
|
@ -942,31 +942,36 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
|
||||
unsigned long flags;
|
||||
long result = 0, count1;
|
||||
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
||||
unsigned long appl_ptr;
|
||||
|
||||
spin_lock_irqsave(&runtime->lock, flags);
|
||||
while (count > 0 && runtime->avail) {
|
||||
count1 = runtime->buffer_size - runtime->appl_ptr;
|
||||
if (count1 > count)
|
||||
count1 = count;
|
||||
spin_lock_irqsave(&runtime->lock, flags);
|
||||
if (count1 > (int)runtime->avail)
|
||||
count1 = runtime->avail;
|
||||
|
||||
/* update runtime->appl_ptr before unlocking for userbuf */
|
||||
appl_ptr = runtime->appl_ptr;
|
||||
runtime->appl_ptr += count1;
|
||||
runtime->appl_ptr %= runtime->buffer_size;
|
||||
runtime->avail -= count1;
|
||||
|
||||
if (kernelbuf)
|
||||
memcpy(kernelbuf + result, runtime->buffer + runtime->appl_ptr, count1);
|
||||
memcpy(kernelbuf + result, runtime->buffer + appl_ptr, count1);
|
||||
if (userbuf) {
|
||||
spin_unlock_irqrestore(&runtime->lock, flags);
|
||||
if (copy_to_user(userbuf + result,
|
||||
runtime->buffer + runtime->appl_ptr, count1)) {
|
||||
runtime->buffer + appl_ptr, count1)) {
|
||||
return result > 0 ? result : -EFAULT;
|
||||
}
|
||||
spin_lock_irqsave(&runtime->lock, flags);
|
||||
}
|
||||
runtime->appl_ptr += count1;
|
||||
runtime->appl_ptr %= runtime->buffer_size;
|
||||
runtime->avail -= count1;
|
||||
spin_unlock_irqrestore(&runtime->lock, flags);
|
||||
result += count1;
|
||||
count -= count1;
|
||||
}
|
||||
spin_unlock_irqrestore(&runtime->lock, flags);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -1055,23 +1060,16 @@ int snd_rawmidi_transmit_empty(struct snd_rawmidi_substream *substream)
|
||||
EXPORT_SYMBOL(snd_rawmidi_transmit_empty);
|
||||
|
||||
/**
|
||||
* snd_rawmidi_transmit_peek - copy data from the internal buffer
|
||||
* __snd_rawmidi_transmit_peek - copy data from the internal buffer
|
||||
* @substream: the rawmidi substream
|
||||
* @buffer: the buffer pointer
|
||||
* @count: data size to transfer
|
||||
*
|
||||
* Copies data from the internal output buffer to the given buffer.
|
||||
*
|
||||
* Call this in the interrupt handler when the midi output is ready,
|
||||
* and call snd_rawmidi_transmit_ack() after the transmission is
|
||||
* finished.
|
||||
*
|
||||
* Return: The size of copied data, or a negative error code on failure.
|
||||
* This is a variant of snd_rawmidi_transmit_peek() without spinlock.
|
||||
*/
|
||||
int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
|
||||
int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
|
||||
unsigned char *buffer, int count)
|
||||
{
|
||||
unsigned long flags;
|
||||
int result, count1;
|
||||
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
||||
|
||||
@ -1081,7 +1079,6 @@ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
|
||||
return -EINVAL;
|
||||
}
|
||||
result = 0;
|
||||
spin_lock_irqsave(&runtime->lock, flags);
|
||||
if (runtime->avail >= runtime->buffer_size) {
|
||||
/* warning: lowlevel layer MUST trigger down the hardware */
|
||||
goto __skip;
|
||||
@ -1106,11 +1103,67 @@ int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
|
||||
}
|
||||
}
|
||||
__skip:
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL(__snd_rawmidi_transmit_peek);
|
||||
|
||||
/**
|
||||
* snd_rawmidi_transmit_peek - copy data from the internal buffer
|
||||
* @substream: the rawmidi substream
|
||||
* @buffer: the buffer pointer
|
||||
* @count: data size to transfer
|
||||
*
|
||||
* Copies data from the internal output buffer to the given buffer.
|
||||
*
|
||||
* Call this in the interrupt handler when the midi output is ready,
|
||||
* and call snd_rawmidi_transmit_ack() after the transmission is
|
||||
* finished.
|
||||
*
|
||||
* Return: The size of copied data, or a negative error code on failure.
|
||||
*/
|
||||
int snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream,
|
||||
unsigned char *buffer, int count)
|
||||
{
|
||||
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
||||
int result;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&runtime->lock, flags);
|
||||
result = __snd_rawmidi_transmit_peek(substream, buffer, count);
|
||||
spin_unlock_irqrestore(&runtime->lock, flags);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_rawmidi_transmit_peek);
|
||||
|
||||
/**
|
||||
* __snd_rawmidi_transmit_ack - acknowledge the transmission
|
||||
* @substream: the rawmidi substream
|
||||
* @count: the transferred count
|
||||
*
|
||||
* This is a variant of __snd_rawmidi_transmit_ack() without spinlock.
|
||||
*/
|
||||
int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
|
||||
{
|
||||
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
||||
|
||||
if (runtime->buffer == NULL) {
|
||||
rmidi_dbg(substream->rmidi,
|
||||
"snd_rawmidi_transmit_ack: output is not active!!!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
snd_BUG_ON(runtime->avail + count > runtime->buffer_size);
|
||||
runtime->hw_ptr += count;
|
||||
runtime->hw_ptr %= runtime->buffer_size;
|
||||
runtime->avail += count;
|
||||
substream->bytes += count;
|
||||
if (count > 0) {
|
||||
if (runtime->drain || snd_rawmidi_ready(substream))
|
||||
wake_up(&runtime->sleep);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
EXPORT_SYMBOL(__snd_rawmidi_transmit_ack);
|
||||
|
||||
/**
|
||||
* snd_rawmidi_transmit_ack - acknowledge the transmission
|
||||
* @substream: the rawmidi substream
|
||||
@ -1124,26 +1177,14 @@ EXPORT_SYMBOL(snd_rawmidi_transmit_peek);
|
||||
*/
|
||||
int snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count)
|
||||
{
|
||||
unsigned long flags;
|
||||
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
||||
int result;
|
||||
unsigned long flags;
|
||||
|
||||
if (runtime->buffer == NULL) {
|
||||
rmidi_dbg(substream->rmidi,
|
||||
"snd_rawmidi_transmit_ack: output is not active!!!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
spin_lock_irqsave(&runtime->lock, flags);
|
||||
snd_BUG_ON(runtime->avail + count > runtime->buffer_size);
|
||||
runtime->hw_ptr += count;
|
||||
runtime->hw_ptr %= runtime->buffer_size;
|
||||
runtime->avail += count;
|
||||
substream->bytes += count;
|
||||
if (count > 0) {
|
||||
if (runtime->drain || snd_rawmidi_ready(substream))
|
||||
wake_up(&runtime->sleep);
|
||||
}
|
||||
result = __snd_rawmidi_transmit_ack(substream, count);
|
||||
spin_unlock_irqrestore(&runtime->lock, flags);
|
||||
return count;
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
|
||||
|
||||
@ -1160,12 +1201,22 @@ EXPORT_SYMBOL(snd_rawmidi_transmit_ack);
|
||||
int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream,
|
||||
unsigned char *buffer, int count)
|
||||
{
|
||||
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
||||
int result;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&runtime->lock, flags);
|
||||
if (!substream->opened)
|
||||
return -EBADFD;
|
||||
count = snd_rawmidi_transmit_peek(substream, buffer, count);
|
||||
if (count < 0)
|
||||
return count;
|
||||
return snd_rawmidi_transmit_ack(substream, count);
|
||||
result = -EBADFD;
|
||||
else {
|
||||
count = __snd_rawmidi_transmit_peek(substream, buffer, count);
|
||||
if (count <= 0)
|
||||
result = count;
|
||||
else
|
||||
result = __snd_rawmidi_transmit_ack(substream, count);
|
||||
}
|
||||
spin_unlock_irqrestore(&runtime->lock, flags);
|
||||
return result;
|
||||
}
|
||||
EXPORT_SYMBOL(snd_rawmidi_transmit);
|
||||
|
||||
@ -1177,8 +1228,9 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
|
||||
unsigned long flags;
|
||||
long count1, result;
|
||||
struct snd_rawmidi_runtime *runtime = substream->runtime;
|
||||
unsigned long appl_ptr;
|
||||
|
||||
if (snd_BUG_ON(!kernelbuf && !userbuf))
|
||||
if (!kernelbuf && !userbuf)
|
||||
return -EINVAL;
|
||||
if (snd_BUG_ON(!runtime->buffer))
|
||||
return -EINVAL;
|
||||
@ -1197,12 +1249,19 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
|
||||
count1 = count;
|
||||
if (count1 > (long)runtime->avail)
|
||||
count1 = runtime->avail;
|
||||
|
||||
/* update runtime->appl_ptr before unlocking for userbuf */
|
||||
appl_ptr = runtime->appl_ptr;
|
||||
runtime->appl_ptr += count1;
|
||||
runtime->appl_ptr %= runtime->buffer_size;
|
||||
runtime->avail -= count1;
|
||||
|
||||
if (kernelbuf)
|
||||
memcpy(runtime->buffer + runtime->appl_ptr,
|
||||
memcpy(runtime->buffer + appl_ptr,
|
||||
kernelbuf + result, count1);
|
||||
else if (userbuf) {
|
||||
spin_unlock_irqrestore(&runtime->lock, flags);
|
||||
if (copy_from_user(runtime->buffer + runtime->appl_ptr,
|
||||
if (copy_from_user(runtime->buffer + appl_ptr,
|
||||
userbuf + result, count1)) {
|
||||
spin_lock_irqsave(&runtime->lock, flags);
|
||||
result = result > 0 ? result : -EFAULT;
|
||||
@ -1210,9 +1269,6 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream,
|
||||
}
|
||||
spin_lock_irqsave(&runtime->lock, flags);
|
||||
}
|
||||
runtime->appl_ptr += count1;
|
||||
runtime->appl_ptr %= runtime->buffer_size;
|
||||
runtime->avail -= count1;
|
||||
result += count1;
|
||||
count -= count1;
|
||||
}
|
||||
|
@ -678,6 +678,9 @@ static int deliver_to_subscribers(struct snd_seq_client *client,
|
||||
else
|
||||
down_read(&grp->list_mutex);
|
||||
list_for_each_entry(subs, &grp->list_head, src_list) {
|
||||
/* both ports ready? */
|
||||
if (atomic_read(&subs->ref_count) != 2)
|
||||
continue;
|
||||
event->dest = subs->info.dest;
|
||||
if (subs->info.flags & SNDRV_SEQ_PORT_SUBS_TIMESTAMP)
|
||||
/* convert time according to flag with subscription */
|
||||
|
@ -173,10 +173,6 @@ struct snd_seq_client_port *snd_seq_create_port(struct snd_seq_client *client,
|
||||
}
|
||||
|
||||
/* */
|
||||
enum group_type {
|
||||
SRC_LIST, DEST_LIST
|
||||
};
|
||||
|
||||
static int subscribe_port(struct snd_seq_client *client,
|
||||
struct snd_seq_client_port *port,
|
||||
struct snd_seq_port_subs_info *grp,
|
||||
@ -203,6 +199,20 @@ static struct snd_seq_client_port *get_client_port(struct snd_seq_addr *addr,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void delete_and_unsubscribe_port(struct snd_seq_client *client,
|
||||
struct snd_seq_client_port *port,
|
||||
struct snd_seq_subscribers *subs,
|
||||
bool is_src, bool ack);
|
||||
|
||||
static inline struct snd_seq_subscribers *
|
||||
get_subscriber(struct list_head *p, bool is_src)
|
||||
{
|
||||
if (is_src)
|
||||
return list_entry(p, struct snd_seq_subscribers, src_list);
|
||||
else
|
||||
return list_entry(p, struct snd_seq_subscribers, dest_list);
|
||||
}
|
||||
|
||||
/*
|
||||
* remove all subscribers on the list
|
||||
* this is called from port_delete, for each src and dest list.
|
||||
@ -210,7 +220,7 @@ static struct snd_seq_client_port *get_client_port(struct snd_seq_addr *addr,
|
||||
static void clear_subscriber_list(struct snd_seq_client *client,
|
||||
struct snd_seq_client_port *port,
|
||||
struct snd_seq_port_subs_info *grp,
|
||||
int grptype)
|
||||
int is_src)
|
||||
{
|
||||
struct list_head *p, *n;
|
||||
|
||||
@ -219,15 +229,13 @@ static void clear_subscriber_list(struct snd_seq_client *client,
|
||||
struct snd_seq_client *c;
|
||||
struct snd_seq_client_port *aport;
|
||||
|
||||
if (grptype == SRC_LIST) {
|
||||
subs = list_entry(p, struct snd_seq_subscribers, src_list);
|
||||
subs = get_subscriber(p, is_src);
|
||||
if (is_src)
|
||||
aport = get_client_port(&subs->info.dest, &c);
|
||||
} else {
|
||||
subs = list_entry(p, struct snd_seq_subscribers, dest_list);
|
||||
else
|
||||
aport = get_client_port(&subs->info.sender, &c);
|
||||
}
|
||||
list_del(p);
|
||||
unsubscribe_port(client, port, grp, &subs->info, 0);
|
||||
delete_and_unsubscribe_port(client, port, subs, is_src, false);
|
||||
|
||||
if (!aport) {
|
||||
/* looks like the connected port is being deleted.
|
||||
* we decrease the counter, and when both ports are deleted
|
||||
@ -235,21 +243,14 @@ static void clear_subscriber_list(struct snd_seq_client *client,
|
||||
*/
|
||||
if (atomic_dec_and_test(&subs->ref_count))
|
||||
kfree(subs);
|
||||
} else {
|
||||
/* ok we got the connected port */
|
||||
struct snd_seq_port_subs_info *agrp;
|
||||
agrp = (grptype == SRC_LIST) ? &aport->c_dest : &aport->c_src;
|
||||
down_write(&agrp->list_mutex);
|
||||
if (grptype == SRC_LIST)
|
||||
list_del(&subs->dest_list);
|
||||
else
|
||||
list_del(&subs->src_list);
|
||||
up_write(&agrp->list_mutex);
|
||||
unsubscribe_port(c, aport, agrp, &subs->info, 1);
|
||||
kfree(subs);
|
||||
snd_seq_port_unlock(aport);
|
||||
snd_seq_client_unlock(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* ok we got the connected port */
|
||||
delete_and_unsubscribe_port(c, aport, subs, !is_src, true);
|
||||
kfree(subs);
|
||||
snd_seq_port_unlock(aport);
|
||||
snd_seq_client_unlock(c);
|
||||
}
|
||||
}
|
||||
|
||||
@ -262,8 +263,8 @@ static int port_delete(struct snd_seq_client *client,
|
||||
snd_use_lock_sync(&port->use_lock);
|
||||
|
||||
/* clear subscribers info */
|
||||
clear_subscriber_list(client, port, &port->c_src, SRC_LIST);
|
||||
clear_subscriber_list(client, port, &port->c_dest, DEST_LIST);
|
||||
clear_subscriber_list(client, port, &port->c_src, true);
|
||||
clear_subscriber_list(client, port, &port->c_dest, false);
|
||||
|
||||
if (port->private_free)
|
||||
port->private_free(port->private_data);
|
||||
@ -479,6 +480,75 @@ static int match_subs_info(struct snd_seq_port_subscribe *r,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int check_and_subscribe_port(struct snd_seq_client *client,
|
||||
struct snd_seq_client_port *port,
|
||||
struct snd_seq_subscribers *subs,
|
||||
bool is_src, bool exclusive, bool ack)
|
||||
{
|
||||
struct snd_seq_port_subs_info *grp;
|
||||
struct list_head *p;
|
||||
struct snd_seq_subscribers *s;
|
||||
int err;
|
||||
|
||||
grp = is_src ? &port->c_src : &port->c_dest;
|
||||
err = -EBUSY;
|
||||
down_write(&grp->list_mutex);
|
||||
if (exclusive) {
|
||||
if (!list_empty(&grp->list_head))
|
||||
goto __error;
|
||||
} else {
|
||||
if (grp->exclusive)
|
||||
goto __error;
|
||||
/* check whether already exists */
|
||||
list_for_each(p, &grp->list_head) {
|
||||
s = get_subscriber(p, is_src);
|
||||
if (match_subs_info(&subs->info, &s->info))
|
||||
goto __error;
|
||||
}
|
||||
}
|
||||
|
||||
err = subscribe_port(client, port, grp, &subs->info, ack);
|
||||
if (err < 0) {
|
||||
grp->exclusive = 0;
|
||||
goto __error;
|
||||
}
|
||||
|
||||
/* add to list */
|
||||
write_lock_irq(&grp->list_lock);
|
||||
if (is_src)
|
||||
list_add_tail(&subs->src_list, &grp->list_head);
|
||||
else
|
||||
list_add_tail(&subs->dest_list, &grp->list_head);
|
||||
grp->exclusive = exclusive;
|
||||
atomic_inc(&subs->ref_count);
|
||||
write_unlock_irq(&grp->list_lock);
|
||||
err = 0;
|
||||
|
||||
__error:
|
||||
up_write(&grp->list_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void delete_and_unsubscribe_port(struct snd_seq_client *client,
|
||||
struct snd_seq_client_port *port,
|
||||
struct snd_seq_subscribers *subs,
|
||||
bool is_src, bool ack)
|
||||
{
|
||||
struct snd_seq_port_subs_info *grp;
|
||||
|
||||
grp = is_src ? &port->c_src : &port->c_dest;
|
||||
down_write(&grp->list_mutex);
|
||||
write_lock_irq(&grp->list_lock);
|
||||
if (is_src)
|
||||
list_del(&subs->src_list);
|
||||
else
|
||||
list_del(&subs->dest_list);
|
||||
grp->exclusive = 0;
|
||||
write_unlock_irq(&grp->list_lock);
|
||||
up_write(&grp->list_mutex);
|
||||
|
||||
unsubscribe_port(client, port, grp, &subs->info, ack);
|
||||
}
|
||||
|
||||
/* connect two ports */
|
||||
int snd_seq_port_connect(struct snd_seq_client *connector,
|
||||
@ -488,76 +558,42 @@ int snd_seq_port_connect(struct snd_seq_client *connector,
|
||||
struct snd_seq_client_port *dest_port,
|
||||
struct snd_seq_port_subscribe *info)
|
||||
{
|
||||
struct snd_seq_port_subs_info *src = &src_port->c_src;
|
||||
struct snd_seq_port_subs_info *dest = &dest_port->c_dest;
|
||||
struct snd_seq_subscribers *subs, *s;
|
||||
int err, src_called = 0;
|
||||
unsigned long flags;
|
||||
int exclusive;
|
||||
struct snd_seq_subscribers *subs;
|
||||
bool exclusive;
|
||||
int err;
|
||||
|
||||
subs = kzalloc(sizeof(*subs), GFP_KERNEL);
|
||||
if (! subs)
|
||||
if (!subs)
|
||||
return -ENOMEM;
|
||||
|
||||
subs->info = *info;
|
||||
atomic_set(&subs->ref_count, 2);
|
||||
atomic_set(&subs->ref_count, 0);
|
||||
INIT_LIST_HEAD(&subs->src_list);
|
||||
INIT_LIST_HEAD(&subs->dest_list);
|
||||
|
||||
down_write(&src->list_mutex);
|
||||
down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING);
|
||||
exclusive = !!(info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE);
|
||||
|
||||
exclusive = info->flags & SNDRV_SEQ_PORT_SUBS_EXCLUSIVE ? 1 : 0;
|
||||
err = -EBUSY;
|
||||
if (exclusive) {
|
||||
if (! list_empty(&src->list_head) || ! list_empty(&dest->list_head))
|
||||
goto __error;
|
||||
} else {
|
||||
if (src->exclusive || dest->exclusive)
|
||||
goto __error;
|
||||
/* check whether already exists */
|
||||
list_for_each_entry(s, &src->list_head, src_list) {
|
||||
if (match_subs_info(info, &s->info))
|
||||
goto __error;
|
||||
}
|
||||
list_for_each_entry(s, &dest->list_head, dest_list) {
|
||||
if (match_subs_info(info, &s->info))
|
||||
goto __error;
|
||||
}
|
||||
}
|
||||
err = check_and_subscribe_port(src_client, src_port, subs, true,
|
||||
exclusive,
|
||||
connector->number != src_client->number);
|
||||
if (err < 0)
|
||||
goto error;
|
||||
err = check_and_subscribe_port(dest_client, dest_port, subs, false,
|
||||
exclusive,
|
||||
connector->number != dest_client->number);
|
||||
if (err < 0)
|
||||
goto error_dest;
|
||||
|
||||
if ((err = subscribe_port(src_client, src_port, src, info,
|
||||
connector->number != src_client->number)) < 0)
|
||||
goto __error;
|
||||
src_called = 1;
|
||||
|
||||
if ((err = subscribe_port(dest_client, dest_port, dest, info,
|
||||
connector->number != dest_client->number)) < 0)
|
||||
goto __error;
|
||||
|
||||
/* add to list */
|
||||
write_lock_irqsave(&src->list_lock, flags);
|
||||
// write_lock(&dest->list_lock); // no other lock yet
|
||||
list_add_tail(&subs->src_list, &src->list_head);
|
||||
list_add_tail(&subs->dest_list, &dest->list_head);
|
||||
// write_unlock(&dest->list_lock); // no other lock yet
|
||||
write_unlock_irqrestore(&src->list_lock, flags);
|
||||
|
||||
src->exclusive = dest->exclusive = exclusive;
|
||||
|
||||
up_write(&dest->list_mutex);
|
||||
up_write(&src->list_mutex);
|
||||
return 0;
|
||||
|
||||
__error:
|
||||
if (src_called)
|
||||
unsubscribe_port(src_client, src_port, src, info,
|
||||
connector->number != src_client->number);
|
||||
error_dest:
|
||||
delete_and_unsubscribe_port(src_client, src_port, subs, true,
|
||||
connector->number != src_client->number);
|
||||
error:
|
||||
kfree(subs);
|
||||
up_write(&dest->list_mutex);
|
||||
up_write(&src->list_mutex);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/* remove the connection */
|
||||
int snd_seq_port_disconnect(struct snd_seq_client *connector,
|
||||
struct snd_seq_client *src_client,
|
||||
@ -567,37 +603,28 @@ int snd_seq_port_disconnect(struct snd_seq_client *connector,
|
||||
struct snd_seq_port_subscribe *info)
|
||||
{
|
||||
struct snd_seq_port_subs_info *src = &src_port->c_src;
|
||||
struct snd_seq_port_subs_info *dest = &dest_port->c_dest;
|
||||
struct snd_seq_subscribers *subs;
|
||||
int err = -ENOENT;
|
||||
unsigned long flags;
|
||||
|
||||
down_write(&src->list_mutex);
|
||||
down_write_nested(&dest->list_mutex, SINGLE_DEPTH_NESTING);
|
||||
|
||||
/* look for the connection */
|
||||
list_for_each_entry(subs, &src->list_head, src_list) {
|
||||
if (match_subs_info(info, &subs->info)) {
|
||||
write_lock_irqsave(&src->list_lock, flags);
|
||||
// write_lock(&dest->list_lock); // no lock yet
|
||||
list_del(&subs->src_list);
|
||||
list_del(&subs->dest_list);
|
||||
// write_unlock(&dest->list_lock);
|
||||
write_unlock_irqrestore(&src->list_lock, flags);
|
||||
src->exclusive = dest->exclusive = 0;
|
||||
unsubscribe_port(src_client, src_port, src, info,
|
||||
connector->number != src_client->number);
|
||||
unsubscribe_port(dest_client, dest_port, dest, info,
|
||||
connector->number != dest_client->number);
|
||||
kfree(subs);
|
||||
atomic_dec(&subs->ref_count); /* mark as not ready */
|
||||
err = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
up_write(&dest->list_mutex);
|
||||
up_write(&src->list_mutex);
|
||||
return err;
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
delete_and_unsubscribe_port(src_client, src_port, subs, true,
|
||||
connector->number != src_client->number);
|
||||
delete_and_unsubscribe_port(dest_client, dest_port, subs, false,
|
||||
connector->number != dest_client->number);
|
||||
kfree(subs);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -90,6 +90,9 @@ void snd_seq_timer_delete(struct snd_seq_timer **tmr)
|
||||
|
||||
void snd_seq_timer_defaults(struct snd_seq_timer * tmr)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&tmr->lock, flags);
|
||||
/* setup defaults */
|
||||
tmr->ppq = 96; /* 96 PPQ */
|
||||
tmr->tempo = 500000; /* 120 BPM */
|
||||
@ -105,21 +108,25 @@ void snd_seq_timer_defaults(struct snd_seq_timer * tmr)
|
||||
tmr->preferred_resolution = seq_default_timer_resolution;
|
||||
|
||||
tmr->skew = tmr->skew_base = SKEW_BASE;
|
||||
spin_unlock_irqrestore(&tmr->lock, flags);
|
||||
}
|
||||
|
||||
void snd_seq_timer_reset(struct snd_seq_timer * tmr)
|
||||
static void seq_timer_reset(struct snd_seq_timer *tmr)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&tmr->lock, flags);
|
||||
|
||||
/* reset time & songposition */
|
||||
tmr->cur_time.tv_sec = 0;
|
||||
tmr->cur_time.tv_nsec = 0;
|
||||
|
||||
tmr->tick.cur_tick = 0;
|
||||
tmr->tick.fraction = 0;
|
||||
}
|
||||
|
||||
void snd_seq_timer_reset(struct snd_seq_timer *tmr)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&tmr->lock, flags);
|
||||
seq_timer_reset(tmr);
|
||||
spin_unlock_irqrestore(&tmr->lock, flags);
|
||||
}
|
||||
|
||||
@ -138,8 +145,11 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri,
|
||||
tmr = q->timer;
|
||||
if (tmr == NULL)
|
||||
return;
|
||||
if (!tmr->running)
|
||||
spin_lock_irqsave(&tmr->lock, flags);
|
||||
if (!tmr->running) {
|
||||
spin_unlock_irqrestore(&tmr->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
resolution *= ticks;
|
||||
if (tmr->skew != tmr->skew_base) {
|
||||
@ -148,8 +158,6 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri,
|
||||
(((resolution & 0xffff) * tmr->skew) >> 16);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&tmr->lock, flags);
|
||||
|
||||
/* update timer */
|
||||
snd_seq_inc_time_nsec(&tmr->cur_time, resolution);
|
||||
|
||||
@ -296,26 +304,30 @@ int snd_seq_timer_open(struct snd_seq_queue *q)
|
||||
t->callback = snd_seq_timer_interrupt;
|
||||
t->callback_data = q;
|
||||
t->flags |= SNDRV_TIMER_IFLG_AUTO;
|
||||
spin_lock_irq(&tmr->lock);
|
||||
tmr->timeri = t;
|
||||
spin_unlock_irq(&tmr->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_seq_timer_close(struct snd_seq_queue *q)
|
||||
{
|
||||
struct snd_seq_timer *tmr;
|
||||
struct snd_timer_instance *t;
|
||||
|
||||
tmr = q->timer;
|
||||
if (snd_BUG_ON(!tmr))
|
||||
return -EINVAL;
|
||||
if (tmr->timeri) {
|
||||
snd_timer_stop(tmr->timeri);
|
||||
snd_timer_close(tmr->timeri);
|
||||
tmr->timeri = NULL;
|
||||
}
|
||||
spin_lock_irq(&tmr->lock);
|
||||
t = tmr->timeri;
|
||||
tmr->timeri = NULL;
|
||||
spin_unlock_irq(&tmr->lock);
|
||||
if (t)
|
||||
snd_timer_close(t);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_seq_timer_stop(struct snd_seq_timer * tmr)
|
||||
static int seq_timer_stop(struct snd_seq_timer *tmr)
|
||||
{
|
||||
if (! tmr->timeri)
|
||||
return -EINVAL;
|
||||
@ -326,6 +338,17 @@ int snd_seq_timer_stop(struct snd_seq_timer * tmr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_seq_timer_stop(struct snd_seq_timer *tmr)
|
||||
{
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
spin_lock_irqsave(&tmr->lock, flags);
|
||||
err = seq_timer_stop(tmr);
|
||||
spin_unlock_irqrestore(&tmr->lock, flags);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int initialize_timer(struct snd_seq_timer *tmr)
|
||||
{
|
||||
struct snd_timer *t;
|
||||
@ -358,13 +381,13 @@ static int initialize_timer(struct snd_seq_timer *tmr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_seq_timer_start(struct snd_seq_timer * tmr)
|
||||
static int seq_timer_start(struct snd_seq_timer *tmr)
|
||||
{
|
||||
if (! tmr->timeri)
|
||||
return -EINVAL;
|
||||
if (tmr->running)
|
||||
snd_seq_timer_stop(tmr);
|
||||
snd_seq_timer_reset(tmr);
|
||||
seq_timer_stop(tmr);
|
||||
seq_timer_reset(tmr);
|
||||
if (initialize_timer(tmr) < 0)
|
||||
return -EINVAL;
|
||||
snd_timer_start(tmr->timeri, tmr->ticks);
|
||||
@ -373,14 +396,25 @@ int snd_seq_timer_start(struct snd_seq_timer * tmr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_seq_timer_continue(struct snd_seq_timer * tmr)
|
||||
int snd_seq_timer_start(struct snd_seq_timer *tmr)
|
||||
{
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
spin_lock_irqsave(&tmr->lock, flags);
|
||||
err = seq_timer_start(tmr);
|
||||
spin_unlock_irqrestore(&tmr->lock, flags);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int seq_timer_continue(struct snd_seq_timer *tmr)
|
||||
{
|
||||
if (! tmr->timeri)
|
||||
return -EINVAL;
|
||||
if (tmr->running)
|
||||
return -EBUSY;
|
||||
if (! tmr->initialized) {
|
||||
snd_seq_timer_reset(tmr);
|
||||
seq_timer_reset(tmr);
|
||||
if (initialize_timer(tmr) < 0)
|
||||
return -EINVAL;
|
||||
}
|
||||
@ -390,11 +424,24 @@ int snd_seq_timer_continue(struct snd_seq_timer * tmr)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int snd_seq_timer_continue(struct snd_seq_timer *tmr)
|
||||
{
|
||||
unsigned long flags;
|
||||
int err;
|
||||
|
||||
spin_lock_irqsave(&tmr->lock, flags);
|
||||
err = seq_timer_continue(tmr);
|
||||
spin_unlock_irqrestore(&tmr->lock, flags);
|
||||
return err;
|
||||
}
|
||||
|
||||
/* return current 'real' time. use timeofday() to get better granularity. */
|
||||
snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr)
|
||||
{
|
||||
snd_seq_real_time_t cur_time;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&tmr->lock, flags);
|
||||
cur_time = tmr->cur_time;
|
||||
if (tmr->running) {
|
||||
struct timeval tm;
|
||||
@ -410,7 +457,7 @@ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr)
|
||||
}
|
||||
snd_seq_sanity_real_time(&cur_time);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&tmr->lock, flags);
|
||||
return cur_time;
|
||||
}
|
||||
|
||||
|
@ -155,21 +155,26 @@ static void snd_virmidi_output_trigger(struct snd_rawmidi_substream *substream,
|
||||
struct snd_virmidi *vmidi = substream->runtime->private_data;
|
||||
int count, res;
|
||||
unsigned char buf[32], *pbuf;
|
||||
unsigned long flags;
|
||||
|
||||
if (up) {
|
||||
vmidi->trigger = 1;
|
||||
if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH &&
|
||||
!(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) {
|
||||
snd_rawmidi_transmit_ack(substream, substream->runtime->buffer_size - substream->runtime->avail);
|
||||
return; /* ignored */
|
||||
while (snd_rawmidi_transmit(substream, buf,
|
||||
sizeof(buf)) > 0) {
|
||||
/* ignored */
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
|
||||
if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, in_atomic(), 0) < 0)
|
||||
return;
|
||||
vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
|
||||
}
|
||||
spin_lock_irqsave(&substream->runtime->lock, flags);
|
||||
while (1) {
|
||||
count = snd_rawmidi_transmit_peek(substream, buf, sizeof(buf));
|
||||
count = __snd_rawmidi_transmit_peek(substream, buf, sizeof(buf));
|
||||
if (count <= 0)
|
||||
break;
|
||||
pbuf = buf;
|
||||
@ -179,16 +184,18 @@ static void snd_virmidi_output_trigger(struct snd_rawmidi_substream *substream,
|
||||
snd_midi_event_reset_encode(vmidi->parser);
|
||||
continue;
|
||||
}
|
||||
snd_rawmidi_transmit_ack(substream, res);
|
||||
__snd_rawmidi_transmit_ack(substream, res);
|
||||
pbuf += res;
|
||||
count -= res;
|
||||
if (vmidi->event.type != SNDRV_SEQ_EVENT_NONE) {
|
||||
if (snd_seq_kernel_client_dispatch(vmidi->client, &vmidi->event, in_atomic(), 0) < 0)
|
||||
return;
|
||||
goto out;
|
||||
vmidi->event.type = SNDRV_SEQ_EVENT_NONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
out:
|
||||
spin_unlock_irqrestore(&substream->runtime->lock, flags);
|
||||
} else {
|
||||
vmidi->trigger = 0;
|
||||
}
|
||||
@ -254,9 +261,13 @@ static int snd_virmidi_output_open(struct snd_rawmidi_substream *substream)
|
||||
*/
|
||||
static int snd_virmidi_input_close(struct snd_rawmidi_substream *substream)
|
||||
{
|
||||
struct snd_virmidi_dev *rdev = substream->rmidi->private_data;
|
||||
struct snd_virmidi *vmidi = substream->runtime->private_data;
|
||||
snd_midi_event_free(vmidi->parser);
|
||||
|
||||
write_lock_irq(&rdev->filelist_lock);
|
||||
list_del(&vmidi->list);
|
||||
write_unlock_irq(&rdev->filelist_lock);
|
||||
snd_midi_event_free(vmidi->parser);
|
||||
substream->runtime->private_data = NULL;
|
||||
kfree(vmidi);
|
||||
return 0;
|
||||
|
@ -451,6 +451,10 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri)
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&slave_active_lock, flags);
|
||||
if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
|
||||
spin_unlock_irqrestore(&slave_active_lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
|
||||
if (timeri->master && timeri->timer) {
|
||||
spin_lock(&timeri->timer->lock);
|
||||
@ -475,7 +479,8 @@ int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
|
||||
return -EINVAL;
|
||||
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
|
||||
result = snd_timer_start_slave(timeri);
|
||||
snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
|
||||
if (result >= 0)
|
||||
snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
|
||||
return result;
|
||||
}
|
||||
timer = timeri->timer;
|
||||
@ -484,11 +489,18 @@ int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
|
||||
if (timer->card && timer->card->shutdown)
|
||||
return -ENODEV;
|
||||
spin_lock_irqsave(&timer->lock, flags);
|
||||
if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
|
||||
SNDRV_TIMER_IFLG_START)) {
|
||||
result = -EBUSY;
|
||||
goto unlock;
|
||||
}
|
||||
timeri->ticks = timeri->cticks = ticks;
|
||||
timeri->pticks = 0;
|
||||
result = snd_timer_start1(timer, timeri, ticks);
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&timer->lock, flags);
|
||||
snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
|
||||
if (result >= 0)
|
||||
snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -502,6 +514,10 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int event)
|
||||
|
||||
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
|
||||
spin_lock_irqsave(&slave_active_lock, flags);
|
||||
if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) {
|
||||
spin_unlock_irqrestore(&slave_active_lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
|
||||
list_del_init(&timeri->ack_list);
|
||||
list_del_init(&timeri->active_list);
|
||||
@ -512,6 +528,11 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int event)
|
||||
if (!timer)
|
||||
return -EINVAL;
|
||||
spin_lock_irqsave(&timer->lock, flags);
|
||||
if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
|
||||
SNDRV_TIMER_IFLG_START))) {
|
||||
spin_unlock_irqrestore(&timer->lock, flags);
|
||||
return -EBUSY;
|
||||
}
|
||||
list_del_init(&timeri->ack_list);
|
||||
list_del_init(&timeri->active_list);
|
||||
if (timer->card && timer->card->shutdown) {
|
||||
@ -581,10 +602,15 @@ int snd_timer_continue(struct snd_timer_instance *timeri)
|
||||
if (timer->card && timer->card->shutdown)
|
||||
return -ENODEV;
|
||||
spin_lock_irqsave(&timer->lock, flags);
|
||||
if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
|
||||
result = -EBUSY;
|
||||
goto unlock;
|
||||
}
|
||||
if (!timeri->cticks)
|
||||
timeri->cticks = 1;
|
||||
timeri->pticks = 0;
|
||||
result = snd_timer_start1(timer, timeri, timer->sticks);
|
||||
unlock:
|
||||
spin_unlock_irqrestore(&timer->lock, flags);
|
||||
snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_CONTINUE);
|
||||
return result;
|
||||
@ -718,8 +744,8 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
|
||||
ti->cticks = ti->ticks;
|
||||
} else {
|
||||
ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
|
||||
if (--timer->running)
|
||||
list_del_init(&ti->active_list);
|
||||
--timer->running;
|
||||
list_del_init(&ti->active_list);
|
||||
}
|
||||
if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) ||
|
||||
(ti->flags & SNDRV_TIMER_IFLG_FAST))
|
||||
@ -1032,11 +1058,21 @@ static int snd_timer_s_stop(struct snd_timer * timer)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snd_timer_s_close(struct snd_timer *timer)
|
||||
{
|
||||
struct snd_timer_system_private *priv;
|
||||
|
||||
priv = (struct snd_timer_system_private *)timer->private_data;
|
||||
del_timer_sync(&priv->tlist);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct snd_timer_hardware snd_timer_system =
|
||||
{
|
||||
.flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_TASKLET,
|
||||
.resolution = 1000000000L / HZ,
|
||||
.ticks = 10000000L,
|
||||
.close = snd_timer_s_close,
|
||||
.start = snd_timer_s_start,
|
||||
.stop = snd_timer_s_stop
|
||||
};
|
||||
|
@ -1141,6 +1141,14 @@ static int snd_emu10k1_emu1010_init(struct snd_emu10k1 *emu)
|
||||
emu->emu1010.firmware_thread =
|
||||
kthread_create(emu1010_firmware_thread, emu,
|
||||
"emu1010_firmware");
|
||||
if (IS_ERR(emu->emu1010.firmware_thread)) {
|
||||
err = PTR_ERR(emu->emu1010.firmware_thread);
|
||||
emu->emu1010.firmware_thread = NULL;
|
||||
dev_info(emu->card->dev,
|
||||
"emu1010: Creating thread failed\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
wake_up_process(emu->emu1010.firmware_thread);
|
||||
}
|
||||
|
||||
|
@ -614,6 +614,7 @@ enum {
|
||||
CS4208_MAC_AUTO,
|
||||
CS4208_MBA6,
|
||||
CS4208_MBP11,
|
||||
CS4208_MACMINI,
|
||||
CS4208_GPIO0,
|
||||
};
|
||||
|
||||
@ -621,6 +622,7 @@ static const struct hda_model_fixup cs4208_models[] = {
|
||||
{ .id = CS4208_GPIO0, .name = "gpio0" },
|
||||
{ .id = CS4208_MBA6, .name = "mba6" },
|
||||
{ .id = CS4208_MBP11, .name = "mbp11" },
|
||||
{ .id = CS4208_MACMINI, .name = "macmini" },
|
||||
{}
|
||||
};
|
||||
|
||||
@ -632,6 +634,7 @@ static const struct snd_pci_quirk cs4208_fixup_tbl[] = {
|
||||
/* codec SSID matching */
|
||||
static const struct snd_pci_quirk cs4208_mac_fixup_tbl[] = {
|
||||
SND_PCI_QUIRK(0x106b, 0x5e00, "MacBookPro 11,2", CS4208_MBP11),
|
||||
SND_PCI_QUIRK(0x106b, 0x6c00, "MacMini 7,1", CS4208_MACMINI),
|
||||
SND_PCI_QUIRK(0x106b, 0x7100, "MacBookAir 6,1", CS4208_MBA6),
|
||||
SND_PCI_QUIRK(0x106b, 0x7200, "MacBookAir 6,2", CS4208_MBA6),
|
||||
SND_PCI_QUIRK(0x106b, 0x7b00, "MacBookPro 12,1", CS4208_MBP11),
|
||||
@ -666,6 +669,24 @@ static void cs4208_fixup_mac(struct hda_codec *codec,
|
||||
snd_hda_apply_fixup(codec, action);
|
||||
}
|
||||
|
||||
/* MacMini 7,1 has the inverted jack detection */
|
||||
static void cs4208_fixup_macmini(struct hda_codec *codec,
|
||||
const struct hda_fixup *fix, int action)
|
||||
{
|
||||
static const struct hda_pintbl pincfgs[] = {
|
||||
{ 0x18, 0x00ab9150 }, /* mic (audio-in) jack: disable detect */
|
||||
{ 0x21, 0x004be140 }, /* SPDIF: disable detect */
|
||||
{ }
|
||||
};
|
||||
|
||||
if (action == HDA_FIXUP_ACT_PRE_PROBE) {
|
||||
/* HP pin (0x10) has an inverted detection */
|
||||
codec->inv_jack_detect = 1;
|
||||
/* disable the bogus Mic and SPDIF jack detections */
|
||||
snd_hda_apply_pincfgs(codec, pincfgs);
|
||||
}
|
||||
}
|
||||
|
||||
static int cs4208_spdif_sw_put(struct snd_kcontrol *kcontrol,
|
||||
struct snd_ctl_elem_value *ucontrol)
|
||||
{
|
||||
@ -709,6 +730,12 @@ static const struct hda_fixup cs4208_fixups[] = {
|
||||
.chained = true,
|
||||
.chain_id = CS4208_GPIO0,
|
||||
},
|
||||
[CS4208_MACMINI] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = cs4208_fixup_macmini,
|
||||
.chained = true,
|
||||
.chain_id = CS4208_GPIO0,
|
||||
},
|
||||
[CS4208_GPIO0] = {
|
||||
.type = HDA_FIXUP_FUNC,
|
||||
.v.func = cs4208_fixup_gpio0,
|
||||
|
@ -327,6 +327,7 @@ static void alc_fill_eapd_coef(struct hda_codec *codec)
|
||||
case 0x10ec0292:
|
||||
alc_update_coef_idx(codec, 0x4, 1<<15, 0);
|
||||
break;
|
||||
case 0x10ec0225:
|
||||
case 0x10ec0233:
|
||||
case 0x10ec0255:
|
||||
case 0x10ec0256:
|
||||
@ -900,6 +901,7 @@ static struct alc_codec_rename_pci_table rename_pci_tbl[] = {
|
||||
{ 0x10ec0899, 0x1028, 0, "ALC3861" },
|
||||
{ 0x10ec0298, 0x1028, 0, "ALC3266" },
|
||||
{ 0x10ec0256, 0x1028, 0, "ALC3246" },
|
||||
{ 0x10ec0225, 0x1028, 0, "ALC3253" },
|
||||
{ 0x10ec0670, 0x1025, 0, "ALC669X" },
|
||||
{ 0x10ec0676, 0x1025, 0, "ALC679X" },
|
||||
{ 0x10ec0282, 0x1043, 0, "ALC3229" },
|
||||
@ -2651,6 +2653,7 @@ enum {
|
||||
ALC269_TYPE_ALC298,
|
||||
ALC269_TYPE_ALC255,
|
||||
ALC269_TYPE_ALC256,
|
||||
ALC269_TYPE_ALC225,
|
||||
};
|
||||
|
||||
/*
|
||||
@ -2680,6 +2683,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
|
||||
case ALC269_TYPE_ALC298:
|
||||
case ALC269_TYPE_ALC255:
|
||||
case ALC269_TYPE_ALC256:
|
||||
case ALC269_TYPE_ALC225:
|
||||
ssids = alc269_ssids;
|
||||
break;
|
||||
default:
|
||||
@ -3658,6 +3662,16 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
|
||||
WRITE_COEF(0xb7, 0x802b),
|
||||
{}
|
||||
};
|
||||
static struct coef_fw coef0225[] = {
|
||||
UPDATE_COEF(0x4a, 1<<8, 0),
|
||||
UPDATE_COEFEX(0x57, 0x05, 1<<14, 0),
|
||||
UPDATE_COEF(0x63, 3<<14, 3<<14),
|
||||
UPDATE_COEF(0x4a, 3<<4, 2<<4),
|
||||
UPDATE_COEF(0x4a, 3<<10, 3<<10),
|
||||
UPDATE_COEF(0x45, 0x3f<<10, 0x34<<10),
|
||||
UPDATE_COEF(0x4a, 3<<10, 0),
|
||||
{}
|
||||
};
|
||||
|
||||
switch (codec->core.vendor_id) {
|
||||
case 0x10ec0255:
|
||||
@ -3682,6 +3696,9 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
|
||||
case 0x10ec0668:
|
||||
alc_process_coef_fw(codec, coef0668);
|
||||
break;
|
||||
case 0x10ec0225:
|
||||
alc_process_coef_fw(codec, coef0225);
|
||||
break;
|
||||
}
|
||||
codec_dbg(codec, "Headset jack set to unplugged mode.\n");
|
||||
}
|
||||
@ -3727,6 +3744,13 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
|
||||
UPDATE_COEF(0xc3, 0, 1<<12),
|
||||
{}
|
||||
};
|
||||
static struct coef_fw coef0225[] = {
|
||||
UPDATE_COEFEX(0x57, 0x05, 1<<14, 1<<14),
|
||||
UPDATE_COEF(0x4a, 3<<4, 2<<4),
|
||||
UPDATE_COEF(0x63, 3<<14, 0),
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
switch (codec->core.vendor_id) {
|
||||
case 0x10ec0255:
|
||||
@ -3772,6 +3796,12 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
|
||||
alc_process_coef_fw(codec, coef0688);
|
||||
snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
|
||||
break;
|
||||
case 0x10ec0225:
|
||||
alc_update_coef_idx(codec, 0x45, 0x3f<<10, 0x31<<10);
|
||||
snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
|
||||
alc_process_coef_fw(codec, coef0225);
|
||||
snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
|
||||
break;
|
||||
}
|
||||
codec_dbg(codec, "Headset jack set to mic-in mode.\n");
|
||||
}
|
||||
@ -3884,6 +3914,13 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
|
||||
WRITE_COEF(0xc3, 0x0000),
|
||||
{}
|
||||
};
|
||||
static struct coef_fw coef0225[] = {
|
||||
UPDATE_COEF(0x45, 0x3f<<10, 0x35<<10),
|
||||
UPDATE_COEF(0x49, 1<<8, 1<<8),
|
||||
UPDATE_COEF(0x4a, 7<<6, 7<<6),
|
||||
UPDATE_COEF(0x4a, 3<<4, 3<<4),
|
||||
{}
|
||||
};
|
||||
|
||||
switch (codec->core.vendor_id) {
|
||||
case 0x10ec0255:
|
||||
@ -3912,6 +3949,9 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
|
||||
case 0x10ec0668:
|
||||
alc_process_coef_fw(codec, coef0688);
|
||||
break;
|
||||
case 0x10ec0225:
|
||||
alc_process_coef_fw(codec, coef0225);
|
||||
break;
|
||||
}
|
||||
codec_dbg(codec, "Headset jack set to iPhone-style headset mode.\n");
|
||||
}
|
||||
@ -3955,6 +3995,13 @@ static void alc_headset_mode_omtp(struct hda_codec *codec)
|
||||
WRITE_COEF(0xc3, 0x0000),
|
||||
{}
|
||||
};
|
||||
static struct coef_fw coef0225[] = {
|
||||
UPDATE_COEF(0x45, 0x3f<<10, 0x39<<10),
|
||||
UPDATE_COEF(0x49, 1<<8, 1<<8),
|
||||
UPDATE_COEF(0x4a, 7<<6, 7<<6),
|
||||
UPDATE_COEF(0x4a, 3<<4, 3<<4),
|
||||
{}
|
||||
};
|
||||
|
||||
switch (codec->core.vendor_id) {
|
||||
case 0x10ec0255:
|
||||
@ -3983,6 +4030,9 @@ static void alc_headset_mode_omtp(struct hda_codec *codec)
|
||||
case 0x10ec0668:
|
||||
alc_process_coef_fw(codec, coef0688);
|
||||
break;
|
||||
case 0x10ec0225:
|
||||
alc_process_coef_fw(codec, coef0225);
|
||||
break;
|
||||
}
|
||||
codec_dbg(codec, "Headset jack set to Nokia-style headset mode.\n");
|
||||
}
|
||||
@ -4014,6 +4064,11 @@ static void alc_determine_headset_type(struct hda_codec *codec)
|
||||
WRITE_COEF(0xc3, 0x0c00),
|
||||
{}
|
||||
};
|
||||
static struct coef_fw coef0225[] = {
|
||||
UPDATE_COEF(0x45, 0x3f<<10, 0x34<<10),
|
||||
UPDATE_COEF(0x49, 1<<8, 1<<8),
|
||||
{}
|
||||
};
|
||||
|
||||
switch (codec->core.vendor_id) {
|
||||
case 0x10ec0255:
|
||||
@ -4058,6 +4113,12 @@ static void alc_determine_headset_type(struct hda_codec *codec)
|
||||
val = alc_read_coef_idx(codec, 0xbe);
|
||||
is_ctia = (val & 0x1c02) == 0x1c02;
|
||||
break;
|
||||
case 0x10ec0225:
|
||||
alc_process_coef_fw(codec, coef0225);
|
||||
msleep(800);
|
||||
val = alc_read_coef_idx(codec, 0x46);
|
||||
is_ctia = (val & 0x00f0) == 0x00f0;
|
||||
break;
|
||||
}
|
||||
|
||||
codec_dbg(codec, "Headset jack detected iPhone-style headset: %s\n",
|
||||
@ -5560,6 +5621,9 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
|
||||
{.id = ALC292_FIXUP_TPT440, .name = "tpt440"},
|
||||
{}
|
||||
};
|
||||
#define ALC225_STANDARD_PINS \
|
||||
{0x12, 0xb7a60130}, \
|
||||
{0x21, 0x04211020}
|
||||
|
||||
#define ALC256_STANDARD_PINS \
|
||||
{0x12, 0x90a60140}, \
|
||||
@ -5581,6 +5645,12 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
|
||||
{0x21, 0x03211020}
|
||||
|
||||
static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
|
||||
SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
|
||||
ALC225_STANDARD_PINS,
|
||||
{0x14, 0x901701a0}),
|
||||
SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
|
||||
ALC225_STANDARD_PINS,
|
||||
{0x14, 0x901701b0}),
|
||||
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
|
||||
{0x14, 0x90170110},
|
||||
{0x21, 0x02211020}),
|
||||
@ -5906,6 +5976,9 @@ static int patch_alc269(struct hda_codec *codec)
|
||||
spec->gen.mixer_nid = 0; /* ALC256 does not have any loopback mixer path */
|
||||
alc_update_coef_idx(codec, 0x36, 1 << 13, 1 << 5); /* Switch pcbeep path to Line in path*/
|
||||
break;
|
||||
case 0x10ec0225:
|
||||
spec->codec_variant = ALC269_TYPE_ALC225;
|
||||
break;
|
||||
}
|
||||
|
||||
if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) {
|
||||
@ -6796,6 +6869,7 @@ static int patch_alc680(struct hda_codec *codec)
|
||||
*/
|
||||
static const struct hda_device_id snd_hda_id_realtek[] = {
|
||||
HDA_CODEC_ENTRY(0x10ec0221, "ALC221", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0225, "ALC225", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0231, "ALC231", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0233, "ALC233", patch_alc269),
|
||||
HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269),
|
||||
|
@ -1121,6 +1121,7 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip)
|
||||
switch (chip->usb_id) {
|
||||
case USB_ID(0x045E, 0x075D): /* MS Lifecam Cinema */
|
||||
case USB_ID(0x045E, 0x076D): /* MS Lifecam HD-5000 */
|
||||
case USB_ID(0x045E, 0x076F): /* MS Lifecam HD-6000 */
|
||||
case USB_ID(0x045E, 0x0772): /* MS Lifecam Studio */
|
||||
case USB_ID(0x045E, 0x0779): /* MS Lifecam HD-3000 */
|
||||
case USB_ID(0x04D8, 0xFEEA): /* Benchmark DAC1 Pre */
|
||||
@ -1281,7 +1282,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
|
||||
case USB_ID(0x20b1, 0x3008): /* iFi Audio micro/nano iDSD */
|
||||
case USB_ID(0x20b1, 0x2008): /* Matrix Audio X-Sabre */
|
||||
case USB_ID(0x20b1, 0x300a): /* Matrix Audio Mini-i Pro */
|
||||
case USB_ID(0x22d8, 0x0416): /* OPPO HA-1*/
|
||||
case USB_ID(0x22d9, 0x0416): /* OPPO HA-1 */
|
||||
if (fp->altsetting == 2)
|
||||
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
|
||||
break;
|
||||
@ -1290,6 +1291,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
|
||||
case USB_ID(0x20b1, 0x2009): /* DIYINHK DSD DXD 384kHz USB to I2S/DSD */
|
||||
case USB_ID(0x20b1, 0x2023): /* JLsounds I2SoverUSB */
|
||||
case USB_ID(0x20b1, 0x3023): /* Aune X1S 32BIT/384 DSD DAC */
|
||||
case USB_ID(0x2616, 0x0106): /* PS Audio NuWave DAC */
|
||||
if (fp->altsetting == 3)
|
||||
return SNDRV_PCM_FMTBIT_DSD_U32_BE;
|
||||
break;
|
||||
|
Loading…
Reference in New Issue
Block a user