|
@@ -65,6 +65,7 @@ struct snd_timer_user {
|
|
|
int qtail;
|
|
|
int qused;
|
|
|
int queue_size;
|
|
|
+ bool disconnected;
|
|
|
struct snd_timer_read *queue;
|
|
|
struct snd_timer_tread *tqueue;
|
|
|
spinlock_t qlock;
|
|
@@ -290,6 +291,9 @@ int snd_timer_open(struct snd_timer_instance **ti,
|
|
|
mutex_unlock(®ister_mutex);
|
|
|
return -ENOMEM;
|
|
|
}
|
|
|
+ /* take a card refcount for safe disconnection */
|
|
|
+ if (timer->card)
|
|
|
+ get_device(&timer->card->card_dev);
|
|
|
timeri->slave_class = tid->dev_sclass;
|
|
|
timeri->slave_id = slave_id;
|
|
|
if (list_empty(&timer->open_list_head) && timer->hw.open)
|
|
@@ -359,6 +363,9 @@ int snd_timer_close(struct snd_timer_instance *timeri)
|
|
|
}
|
|
|
spin_unlock(&timer->lock);
|
|
|
spin_unlock_irq(&slave_active_lock);
|
|
|
+ /* release a card refcount for safe disconnection */
|
|
|
+ if (timer->card)
|
|
|
+ put_device(&timer->card->card_dev);
|
|
|
mutex_unlock(®ister_mutex);
|
|
|
}
|
|
|
out:
|
|
@@ -474,6 +481,8 @@ int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
|
|
|
timer = timeri->timer;
|
|
|
if (timer == NULL)
|
|
|
return -EINVAL;
|
|
|
+ if (timer->card && timer->card->shutdown)
|
|
|
+ return -ENODEV;
|
|
|
spin_lock_irqsave(&timer->lock, flags);
|
|
|
timeri->ticks = timeri->cticks = ticks;
|
|
|
timeri->pticks = 0;
|
|
@@ -505,6 +514,10 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int event)
|
|
|
spin_lock_irqsave(&timer->lock, flags);
|
|
|
list_del_init(&timeri->ack_list);
|
|
|
list_del_init(&timeri->active_list);
|
|
|
+ if (timer->card && timer->card->shutdown) {
|
|
|
+ spin_unlock_irqrestore(&timer->lock, flags);
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) &&
|
|
|
!(--timer->running)) {
|
|
|
timer->hw.stop(timer);
|
|
@@ -565,6 +578,8 @@ int snd_timer_continue(struct snd_timer_instance *timeri)
|
|
|
timer = timeri->timer;
|
|
|
if (! timer)
|
|
|
return -EINVAL;
|
|
|
+ if (timer->card && timer->card->shutdown)
|
|
|
+ return -ENODEV;
|
|
|
spin_lock_irqsave(&timer->lock, flags);
|
|
|
if (!timeri->cticks)
|
|
|
timeri->cticks = 1;
|
|
@@ -628,6 +643,9 @@ static void snd_timer_tasklet(unsigned long arg)
|
|
|
unsigned long resolution, ticks;
|
|
|
unsigned long flags;
|
|
|
|
|
|
+ if (timer->card && timer->card->shutdown)
|
|
|
+ return;
|
|
|
+
|
|
|
spin_lock_irqsave(&timer->lock, flags);
|
|
|
/* now process all callbacks */
|
|
|
while (!list_empty(&timer->sack_list_head)) {
|
|
@@ -668,6 +686,9 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
|
|
|
if (timer == NULL)
|
|
|
return;
|
|
|
|
|
|
+ if (timer->card && timer->card->shutdown)
|
|
|
+ return;
|
|
|
+
|
|
|
spin_lock_irqsave(&timer->lock, flags);
|
|
|
|
|
|
/* remember the current resolution */
|
|
@@ -881,8 +902,15 @@ static int snd_timer_dev_register(struct snd_device *dev)
|
|
|
static int snd_timer_dev_disconnect(struct snd_device *device)
|
|
|
{
|
|
|
struct snd_timer *timer = device->device_data;
|
|
|
+ struct snd_timer_instance *ti;
|
|
|
+
|
|
|
mutex_lock(®ister_mutex);
|
|
|
list_del_init(&timer->device_list);
|
|
|
+ /* wake up pending sleepers */
|
|
|
+ list_for_each_entry(ti, &timer->open_list_head, open_list) {
|
|
|
+ if (ti->disconnect)
|
|
|
+ ti->disconnect(ti);
|
|
|
+ }
|
|
|
mutex_unlock(®ister_mutex);
|
|
|
return 0;
|
|
|
}
|
|
@@ -893,6 +921,8 @@ void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstam
|
|
|
unsigned long resolution = 0;
|
|
|
struct snd_timer_instance *ti, *ts;
|
|
|
|
|
|
+ if (timer->card && timer->card->shutdown)
|
|
|
+ return;
|
|
|
if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE))
|
|
|
return;
|
|
|
if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART ||
|
|
@@ -1051,6 +1081,8 @@ static void snd_timer_proc_read(struct snd_info_entry *entry,
|
|
|
|
|
|
mutex_lock(®ister_mutex);
|
|
|
list_for_each_entry(timer, &snd_timer_list, device_list) {
|
|
|
+ if (timer->card && timer->card->shutdown)
|
|
|
+ continue;
|
|
|
switch (timer->tmr_class) {
|
|
|
case SNDRV_TIMER_CLASS_GLOBAL:
|
|
|
snd_iprintf(buffer, "G%i: ", timer->tmr_device);
|
|
@@ -1185,6 +1217,14 @@ static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
|
|
|
wake_up(&tu->qchange_sleep);
|
|
|
}
|
|
|
|
|
|
+static void snd_timer_user_disconnect(struct snd_timer_instance *timeri)
|
|
|
+{
|
|
|
+ struct snd_timer_user *tu = timeri->callback_data;
|
|
|
+
|
|
|
+ tu->disconnected = true;
|
|
|
+ wake_up(&tu->qchange_sleep);
|
|
|
+}
|
|
|
+
|
|
|
static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
|
|
|
unsigned long resolution,
|
|
|
unsigned long ticks)
|
|
@@ -1558,6 +1598,7 @@ static int snd_timer_user_tselect(struct file *file,
|
|
|
? snd_timer_user_tinterrupt : snd_timer_user_interrupt;
|
|
|
tu->timeri->ccallback = snd_timer_user_ccallback;
|
|
|
tu->timeri->callback_data = (void *)tu;
|
|
|
+ tu->timeri->disconnect = snd_timer_user_disconnect;
|
|
|
}
|
|
|
|
|
|
__err:
|
|
@@ -1876,6 +1917,10 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
|
|
|
|
|
|
remove_wait_queue(&tu->qchange_sleep, &wait);
|
|
|
|
|
|
+ if (tu->disconnected) {
|
|
|
+ err = -ENODEV;
|
|
|
+ break;
|
|
|
+ }
|
|
|
if (signal_pending(current)) {
|
|
|
err = -ERESTARTSYS;
|
|
|
break;
|
|
@@ -1925,6 +1970,8 @@ static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait)
|
|
|
mask = 0;
|
|
|
if (tu->qused)
|
|
|
mask |= POLLIN | POLLRDNORM;
|
|
|
+ if (tu->disconnected)
|
|
|
+ mask |= POLLERR;
|
|
|
|
|
|
return mask;
|
|
|
}
|