Commit e2439a54 authored by Takashi Iwai's avatar Takashi Iwai

ALSA: usx2y: Don't peep the card internal object

Avoid traversing the device object list of the card instance just for
checking the PCM streams.  The driver's private object already
contains the array of substream pointers, so it can be simply looked
through.  The card internal may be restructured in future, thus better
not to rely on it.

Also, this fixes the possible deadlocks in PCM mutex.  Instead of
taking multiple PCM mutexes, just take the common mutex in all
places.  Along with it, rename prepare_mutex as pcm_mutex.
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 1f85a0f0
...@@ -353,7 +353,7 @@ static int usX2Y_create_card(struct usb_device *device, ...@@ -353,7 +353,7 @@ static int usX2Y_create_card(struct usb_device *device,
card->private_free = snd_usX2Y_card_private_free; card->private_free = snd_usX2Y_card_private_free;
usX2Y(card)->dev = device; usX2Y(card)->dev = device;
init_waitqueue_head(&usX2Y(card)->prepare_wait_queue); init_waitqueue_head(&usX2Y(card)->prepare_wait_queue);
mutex_init(&usX2Y(card)->prepare_mutex); mutex_init(&usX2Y(card)->pcm_mutex);
INIT_LIST_HEAD(&usX2Y(card)->midi_list); INIT_LIST_HEAD(&usX2Y(card)->midi_list);
strcpy(card->driver, "USB "NAME_ALLCAPS""); strcpy(card->driver, "USB "NAME_ALLCAPS"");
sprintf(card->shortname, "TASCAM "NAME_ALLCAPS""); sprintf(card->shortname, "TASCAM "NAME_ALLCAPS"");
......
...@@ -36,7 +36,7 @@ struct usX2Ydev { ...@@ -36,7 +36,7 @@ struct usX2Ydev {
unsigned int rate, unsigned int rate,
format; format;
int chip_status; int chip_status;
struct mutex prepare_mutex; struct mutex pcm_mutex;
struct us428ctls_sharedmem *us428ctls_sharedmem; struct us428ctls_sharedmem *us428ctls_sharedmem;
int wait_iso_frame; int wait_iso_frame;
wait_queue_head_t us428ctls_wait_queue_head; wait_queue_head_t us428ctls_wait_queue_head;
......
...@@ -752,36 +752,44 @@ static int snd_usX2Y_pcm_hw_params(struct snd_pcm_substream *substream, ...@@ -752,36 +752,44 @@ static int snd_usX2Y_pcm_hw_params(struct snd_pcm_substream *substream,
unsigned int rate = params_rate(hw_params); unsigned int rate = params_rate(hw_params);
snd_pcm_format_t format = params_format(hw_params); snd_pcm_format_t format = params_format(hw_params);
struct snd_card *card = substream->pstr->pcm->card; struct snd_card *card = substream->pstr->pcm->card;
struct list_head *list; struct usX2Ydev *dev = usX2Y(card);
int i;
mutex_lock(&usX2Y(card)->pcm_mutex);
snd_printdd("snd_usX2Y_hw_params(%p, %p)\n", substream, hw_params); snd_printdd("snd_usX2Y_hw_params(%p, %p)\n", substream, hw_params);
// all pcm substreams off one usX2Y have to operate at the same rate & format /* all pcm substreams off one usX2Y have to operate at the same
list_for_each(list, &card->devices) { * rate & format
struct snd_device *dev; */
struct snd_pcm *pcm; for (i = 0; i < dev->pcm_devs * 2; i++) {
int s; struct snd_usX2Y_substream *subs = dev->subs[i];
dev = snd_device(list); struct snd_pcm_substream *test_substream;
if (dev->type != SNDRV_DEV_PCM)
if (!subs)
continue;
test_substream = subs->pcm_substream;
if (!test_substream || test_substream == substream ||
!test_substream->runtime)
continue; continue;
pcm = dev->device_data; if ((test_substream->runtime->format &&
for (s = 0; s < 2; ++s) { test_substream->runtime->format != format) ||
struct snd_pcm_substream *test_substream; (test_substream->runtime->rate &&
test_substream = pcm->streams[s].substream; test_substream->runtime->rate != rate)) {
if (test_substream && test_substream != substream && err = -EINVAL;
test_substream->runtime && goto error;
((test_substream->runtime->format &&
test_substream->runtime->format != format) ||
(test_substream->runtime->rate &&
test_substream->runtime->rate != rate)))
return -EINVAL;
} }
} }
if (0 > (err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)))) {
err = snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
if (err < 0) {
snd_printk(KERN_ERR "snd_pcm_lib_malloc_pages(%p, %i) returned %i\n", snd_printk(KERN_ERR "snd_pcm_lib_malloc_pages(%p, %i) returned %i\n",
substream, params_buffer_bytes(hw_params), err); substream, params_buffer_bytes(hw_params), err);
return err; goto error;
} }
return 0;
error:
mutex_unlock(&usX2Y(card)->pcm_mutex);
return err;
} }
/* /*
...@@ -791,7 +799,7 @@ static int snd_usX2Y_pcm_hw_free(struct snd_pcm_substream *substream) ...@@ -791,7 +799,7 @@ static int snd_usX2Y_pcm_hw_free(struct snd_pcm_substream *substream)
{ {
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_usX2Y_substream *subs = runtime->private_data; struct snd_usX2Y_substream *subs = runtime->private_data;
mutex_lock(&subs->usX2Y->prepare_mutex); mutex_lock(&subs->usX2Y->pcm_mutex);
snd_printdd("snd_usX2Y_hw_free(%p)\n", substream); snd_printdd("snd_usX2Y_hw_free(%p)\n", substream);
if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) { if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
...@@ -812,7 +820,7 @@ static int snd_usX2Y_pcm_hw_free(struct snd_pcm_substream *substream) ...@@ -812,7 +820,7 @@ static int snd_usX2Y_pcm_hw_free(struct snd_pcm_substream *substream)
usX2Y_urbs_release(subs); usX2Y_urbs_release(subs);
} }
} }
mutex_unlock(&subs->usX2Y->prepare_mutex); mutex_unlock(&subs->usX2Y->pcm_mutex);
return snd_pcm_lib_free_pages(substream); return snd_pcm_lib_free_pages(substream);
} }
/* /*
...@@ -829,7 +837,7 @@ static int snd_usX2Y_pcm_prepare(struct snd_pcm_substream *substream) ...@@ -829,7 +837,7 @@ static int snd_usX2Y_pcm_prepare(struct snd_pcm_substream *substream)
int err = 0; int err = 0;
snd_printdd("snd_usX2Y_pcm_prepare(%p)\n", substream); snd_printdd("snd_usX2Y_pcm_prepare(%p)\n", substream);
mutex_lock(&usX2Y->prepare_mutex); mutex_lock(&usX2Y->pcm_mutex);
usX2Y_subs_prepare(subs); usX2Y_subs_prepare(subs);
// Start hardware streams // Start hardware streams
// SyncStream first.... // SyncStream first....
...@@ -849,7 +857,7 @@ static int snd_usX2Y_pcm_prepare(struct snd_pcm_substream *substream) ...@@ -849,7 +857,7 @@ static int snd_usX2Y_pcm_prepare(struct snd_pcm_substream *substream)
err = usX2Y_urbs_start(subs); err = usX2Y_urbs_start(subs);
up_prepare_mutex: up_prepare_mutex:
mutex_unlock(&usX2Y->prepare_mutex); mutex_unlock(&usX2Y->pcm_mutex);
return err; return err;
} }
......
...@@ -358,7 +358,7 @@ static int snd_usX2Y_usbpcm_hw_free(struct snd_pcm_substream *substream) ...@@ -358,7 +358,7 @@ static int snd_usX2Y_usbpcm_hw_free(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_usX2Y_substream *subs = runtime->private_data, struct snd_usX2Y_substream *subs = runtime->private_data,
*cap_subs2 = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2]; *cap_subs2 = subs->usX2Y->subs[SNDRV_PCM_STREAM_CAPTURE + 2];
mutex_lock(&subs->usX2Y->prepare_mutex); mutex_lock(&subs->usX2Y->pcm_mutex);
snd_printdd("snd_usX2Y_usbpcm_hw_free(%p)\n", substream); snd_printdd("snd_usX2Y_usbpcm_hw_free(%p)\n", substream);
if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) { if (SNDRV_PCM_STREAM_PLAYBACK == substream->stream) {
...@@ -387,7 +387,7 @@ static int snd_usX2Y_usbpcm_hw_free(struct snd_pcm_substream *substream) ...@@ -387,7 +387,7 @@ static int snd_usX2Y_usbpcm_hw_free(struct snd_pcm_substream *substream)
usX2Y_usbpcm_urbs_release(cap_subs2); usX2Y_usbpcm_urbs_release(cap_subs2);
} }
} }
mutex_unlock(&subs->usX2Y->prepare_mutex); mutex_unlock(&subs->usX2Y->pcm_mutex);
return snd_pcm_lib_free_pages(substream); return snd_pcm_lib_free_pages(substream);
} }
...@@ -493,7 +493,7 @@ static int snd_usX2Y_usbpcm_prepare(struct snd_pcm_substream *substream) ...@@ -493,7 +493,7 @@ static int snd_usX2Y_usbpcm_prepare(struct snd_pcm_substream *substream)
memset(usX2Y->hwdep_pcm_shm, 0, sizeof(struct snd_usX2Y_hwdep_pcm_shm)); memset(usX2Y->hwdep_pcm_shm, 0, sizeof(struct snd_usX2Y_hwdep_pcm_shm));
} }
mutex_lock(&usX2Y->prepare_mutex); mutex_lock(&usX2Y->pcm_mutex);
usX2Y_subs_prepare(subs); usX2Y_subs_prepare(subs);
// Start hardware streams // Start hardware streams
// SyncStream first.... // SyncStream first....
...@@ -534,7 +534,7 @@ static int snd_usX2Y_usbpcm_prepare(struct snd_pcm_substream *substream) ...@@ -534,7 +534,7 @@ static int snd_usX2Y_usbpcm_prepare(struct snd_pcm_substream *substream)
usX2Y->hwdep_pcm_shm->capture_iso_start = -1; usX2Y->hwdep_pcm_shm->capture_iso_start = -1;
up_prepare_mutex: up_prepare_mutex:
mutex_unlock(&usX2Y->prepare_mutex); mutex_unlock(&usX2Y->pcm_mutex);
return err; return err;
} }
...@@ -600,59 +600,30 @@ static struct snd_pcm_ops snd_usX2Y_usbpcm_ops = ...@@ -600,59 +600,30 @@ static struct snd_pcm_ops snd_usX2Y_usbpcm_ops =
}; };
static int usX2Y_pcms_lock_check(struct snd_card *card) static int usX2Y_pcms_busy_check(struct snd_card *card)
{ {
struct list_head *list; struct usX2Ydev *dev = usX2Y(card);
struct snd_device *dev; int i;
struct snd_pcm *pcm;
int err = 0;
list_for_each(list, &card->devices) {
dev = snd_device(list);
if (dev->type != SNDRV_DEV_PCM)
continue;
pcm = dev->device_data;
mutex_lock(&pcm->open_mutex);
}
list_for_each(list, &card->devices) {
int s;
dev = snd_device(list);
if (dev->type != SNDRV_DEV_PCM)
continue;
pcm = dev->device_data;
for (s = 0; s < 2; ++s) {
struct snd_pcm_substream *substream;
substream = pcm->streams[s].substream;
if (substream && SUBSTREAM_BUSY(substream))
err = -EBUSY;
}
}
return err;
}
static void usX2Y_pcms_unlock(struct snd_card *card) for (i = 0; i < dev->pcm_devs * 2; i++) {
{ struct snd_usX2Y_substream *subs = dev->subs[i];
struct list_head *list; if (subs && subs->pcm_substream &&
struct snd_device *dev; SUBSTREAM_BUSY(subs->pcm_substream))
struct snd_pcm *pcm; return -EBUSY;
list_for_each(list, &card->devices) {
dev = snd_device(list);
if (dev->type != SNDRV_DEV_PCM)
continue;
pcm = dev->device_data;
mutex_unlock(&pcm->open_mutex);
} }
return 0;
} }
static int snd_usX2Y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file) static int snd_usX2Y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file)
{ {
// we need to be the first
struct snd_card *card = hw->card; struct snd_card *card = hw->card;
int err = usX2Y_pcms_lock_check(card); int err;
if (0 == err)
mutex_lock(&usX2Y(card)->pcm_mutex);
err = usX2Y_pcms_busy_check(card);
if (!err)
usX2Y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS; usX2Y(card)->chip_status |= USX2Y_STAT_CHIP_MMAP_PCM_URBS;
usX2Y_pcms_unlock(card); mutex_unlock(&usX2Y(card)->pcm_mutex);
return err; return err;
} }
...@@ -660,10 +631,13 @@ static int snd_usX2Y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file) ...@@ -660,10 +631,13 @@ static int snd_usX2Y_hwdep_pcm_open(struct snd_hwdep *hw, struct file *file)
static int snd_usX2Y_hwdep_pcm_release(struct snd_hwdep *hw, struct file *file) static int snd_usX2Y_hwdep_pcm_release(struct snd_hwdep *hw, struct file *file)
{ {
struct snd_card *card = hw->card; struct snd_card *card = hw->card;
int err = usX2Y_pcms_lock_check(card); int err;
if (0 == err)
mutex_lock(&usX2Y(card)->pcm_mutex);
err = usX2Y_pcms_busy_check(card);
if (!err)
usX2Y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS; usX2Y(hw->card)->chip_status &= ~USX2Y_STAT_CHIP_MMAP_PCM_URBS;
usX2Y_pcms_unlock(card); mutex_unlock(&usX2Y(card)->pcm_mutex);
return err; return err;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment