diff options
Diffstat (limited to 'sound/core/timer.c')
-rw-r--r-- | sound/core/timer.c | 268 |
1 files changed, 123 insertions, 145 deletions
diff --git a/sound/core/timer.c b/sound/core/timer.c index dca817fc7894..aa1b15c155d1 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c | |||
@@ -305,8 +305,6 @@ int snd_timer_open(struct snd_timer_instance **ti, | |||
305 | return 0; | 305 | return 0; |
306 | } | 306 | } |
307 | 307 | ||
308 | static int _snd_timer_stop(struct snd_timer_instance *timeri, int event); | ||
309 | |||
310 | /* | 308 | /* |
311 | * close a timer instance | 309 | * close a timer instance |
312 | */ | 310 | */ |
@@ -318,25 +316,14 @@ int snd_timer_close(struct snd_timer_instance *timeri) | |||
318 | if (snd_BUG_ON(!timeri)) | 316 | if (snd_BUG_ON(!timeri)) |
319 | return -ENXIO; | 317 | return -ENXIO; |
320 | 318 | ||
319 | mutex_lock(®ister_mutex); | ||
320 | list_del(&timeri->open_list); | ||
321 | |||
321 | /* force to stop the timer */ | 322 | /* force to stop the timer */ |
322 | snd_timer_stop(timeri); | 323 | snd_timer_stop(timeri); |
323 | 324 | ||
324 | if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { | 325 | timer = timeri->timer; |
325 | /* wait, until the active callback is finished */ | 326 | if (timer) { |
326 | spin_lock_irq(&slave_active_lock); | ||
327 | while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { | ||
328 | spin_unlock_irq(&slave_active_lock); | ||
329 | udelay(10); | ||
330 | spin_lock_irq(&slave_active_lock); | ||
331 | } | ||
332 | spin_unlock_irq(&slave_active_lock); | ||
333 | mutex_lock(®ister_mutex); | ||
334 | list_del(&timeri->open_list); | ||
335 | mutex_unlock(®ister_mutex); | ||
336 | } else { | ||
337 | timer = timeri->timer; | ||
338 | if (snd_BUG_ON(!timer)) | ||
339 | goto out; | ||
340 | /* wait, until the active callback is finished */ | 327 | /* wait, until the active callback is finished */ |
341 | spin_lock_irq(&timer->lock); | 328 | spin_lock_irq(&timer->lock); |
342 | while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { | 329 | while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { |
@@ -345,11 +332,7 @@ int snd_timer_close(struct snd_timer_instance *timeri) | |||
345 | spin_lock_irq(&timer->lock); | 332 | spin_lock_irq(&timer->lock); |
346 | } | 333 | } |
347 | spin_unlock_irq(&timer->lock); | 334 | spin_unlock_irq(&timer->lock); |
348 | mutex_lock(®ister_mutex); | 335 | |
349 | list_del(&timeri->open_list); | ||
350 | if (list_empty(&timer->open_list_head) && | ||
351 | timer->hw.close) | ||
352 | timer->hw.close(timer); | ||
353 | /* remove slave links */ | 336 | /* remove slave links */ |
354 | spin_lock_irq(&slave_active_lock); | 337 | spin_lock_irq(&slave_active_lock); |
355 | spin_lock(&timer->lock); | 338 | spin_lock(&timer->lock); |
@@ -363,18 +346,27 @@ int snd_timer_close(struct snd_timer_instance *timeri) | |||
363 | } | 346 | } |
364 | spin_unlock(&timer->lock); | 347 | spin_unlock(&timer->lock); |
365 | spin_unlock_irq(&slave_active_lock); | 348 | spin_unlock_irq(&slave_active_lock); |
366 | /* release a card refcount for safe disconnection */ | 349 | |
367 | if (timer->card) | 350 | /* slave doesn't need to release timer resources below */ |
368 | put_device(&timer->card->card_dev); | 351 | if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) |
369 | mutex_unlock(®ister_mutex); | 352 | timer = NULL; |
370 | } | 353 | } |
371 | out: | 354 | |
372 | if (timeri->private_free) | 355 | if (timeri->private_free) |
373 | timeri->private_free(timeri); | 356 | timeri->private_free(timeri); |
374 | kfree(timeri->owner); | 357 | kfree(timeri->owner); |
375 | kfree(timeri); | 358 | kfree(timeri); |
376 | if (timer) | 359 | |
360 | if (timer) { | ||
361 | if (list_empty(&timer->open_list_head) && timer->hw.close) | ||
362 | timer->hw.close(timer); | ||
363 | /* release a card refcount for safe disconnection */ | ||
364 | if (timer->card) | ||
365 | put_device(&timer->card->card_dev); | ||
377 | module_put(timer->module); | 366 | module_put(timer->module); |
367 | } | ||
368 | |||
369 | mutex_unlock(®ister_mutex); | ||
378 | return 0; | 370 | return 0; |
379 | } | 371 | } |
380 | 372 | ||
@@ -395,7 +387,6 @@ unsigned long snd_timer_resolution(struct snd_timer_instance *timeri) | |||
395 | static void snd_timer_notify1(struct snd_timer_instance *ti, int event) | 387 | static void snd_timer_notify1(struct snd_timer_instance *ti, int event) |
396 | { | 388 | { |
397 | struct snd_timer *timer; | 389 | struct snd_timer *timer; |
398 | unsigned long flags; | ||
399 | unsigned long resolution = 0; | 390 | unsigned long resolution = 0; |
400 | struct snd_timer_instance *ts; | 391 | struct snd_timer_instance *ts; |
401 | struct timespec tstamp; | 392 | struct timespec tstamp; |
@@ -419,34 +410,66 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event) | |||
419 | return; | 410 | return; |
420 | if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) | 411 | if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) |
421 | return; | 412 | return; |
422 | spin_lock_irqsave(&timer->lock, flags); | ||
423 | list_for_each_entry(ts, &ti->slave_active_head, active_list) | 413 | list_for_each_entry(ts, &ti->slave_active_head, active_list) |
424 | if (ts->ccallback) | 414 | if (ts->ccallback) |
425 | ts->ccallback(ts, event + 100, &tstamp, resolution); | 415 | ts->ccallback(ts, event + 100, &tstamp, resolution); |
426 | spin_unlock_irqrestore(&timer->lock, flags); | ||
427 | } | 416 | } |
428 | 417 | ||
429 | static int snd_timer_start1(struct snd_timer *timer, struct snd_timer_instance *timeri, | 418 | /* start/continue a master timer */ |
430 | unsigned long sticks) | 419 | static int snd_timer_start1(struct snd_timer_instance *timeri, |
420 | bool start, unsigned long ticks) | ||
431 | { | 421 | { |
422 | struct snd_timer *timer; | ||
423 | int result; | ||
424 | unsigned long flags; | ||
425 | |||
426 | timer = timeri->timer; | ||
427 | if (!timer) | ||
428 | return -EINVAL; | ||
429 | |||
430 | spin_lock_irqsave(&timer->lock, flags); | ||
431 | if (timer->card && timer->card->shutdown) { | ||
432 | result = -ENODEV; | ||
433 | goto unlock; | ||
434 | } | ||
435 | if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | | ||
436 | SNDRV_TIMER_IFLG_START)) { | ||
437 | result = -EBUSY; | ||
438 | goto unlock; | ||
439 | } | ||
440 | |||
441 | if (start) | ||
442 | timeri->ticks = timeri->cticks = ticks; | ||
443 | else if (!timeri->cticks) | ||
444 | timeri->cticks = 1; | ||
445 | timeri->pticks = 0; | ||
446 | |||
432 | list_move_tail(&timeri->active_list, &timer->active_list_head); | 447 | list_move_tail(&timeri->active_list, &timer->active_list_head); |
433 | if (timer->running) { | 448 | if (timer->running) { |
434 | if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) | 449 | if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) |
435 | goto __start_now; | 450 | goto __start_now; |
436 | timer->flags |= SNDRV_TIMER_FLG_RESCHED; | 451 | timer->flags |= SNDRV_TIMER_FLG_RESCHED; |
437 | timeri->flags |= SNDRV_TIMER_IFLG_START; | 452 | timeri->flags |= SNDRV_TIMER_IFLG_START; |
438 | return 1; /* delayed start */ | 453 | result = 1; /* delayed start */ |
439 | } else { | 454 | } else { |
440 | timer->sticks = sticks; | 455 | if (start) |
456 | timer->sticks = ticks; | ||
441 | timer->hw.start(timer); | 457 | timer->hw.start(timer); |
442 | __start_now: | 458 | __start_now: |
443 | timer->running++; | 459 | timer->running++; |
444 | timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; | 460 | timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; |
445 | return 0; | 461 | result = 0; |
446 | } | 462 | } |
463 | snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START : | ||
464 | SNDRV_TIMER_EVENT_CONTINUE); | ||
465 | unlock: | ||
466 | spin_unlock_irqrestore(&timer->lock, flags); | ||
467 | return result; | ||
447 | } | 468 | } |
448 | 469 | ||
449 | static int snd_timer_start_slave(struct snd_timer_instance *timeri) | 470 | /* start/continue a slave timer */ |
471 | static int snd_timer_start_slave(struct snd_timer_instance *timeri, | ||
472 | bool start) | ||
450 | { | 473 | { |
451 | unsigned long flags; | 474 | unsigned long flags; |
452 | 475 | ||
@@ -460,88 +483,37 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri) | |||
460 | spin_lock(&timeri->timer->lock); | 483 | spin_lock(&timeri->timer->lock); |
461 | list_add_tail(&timeri->active_list, | 484 | list_add_tail(&timeri->active_list, |
462 | &timeri->master->slave_active_head); | 485 | &timeri->master->slave_active_head); |
486 | snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START : | ||
487 | SNDRV_TIMER_EVENT_CONTINUE); | ||
463 | spin_unlock(&timeri->timer->lock); | 488 | spin_unlock(&timeri->timer->lock); |
464 | } | 489 | } |
465 | spin_unlock_irqrestore(&slave_active_lock, flags); | 490 | spin_unlock_irqrestore(&slave_active_lock, flags); |
466 | return 1; /* delayed start */ | 491 | return 1; /* delayed start */ |
467 | } | 492 | } |
468 | 493 | ||
469 | /* | 494 | /* stop/pause a master timer */ |
470 | * start the timer instance | 495 | static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop) |
471 | */ | ||
472 | int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks) | ||
473 | { | 496 | { |
474 | struct snd_timer *timer; | 497 | struct snd_timer *timer; |
475 | int result = -EINVAL; | 498 | int result = 0; |
476 | unsigned long flags; | 499 | unsigned long flags; |
477 | 500 | ||
478 | if (timeri == NULL || ticks < 1) | ||
479 | return -EINVAL; | ||
480 | if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { | ||
481 | result = snd_timer_start_slave(timeri); | ||
482 | if (result >= 0) | ||
483 | snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START); | ||
484 | return result; | ||
485 | } | ||
486 | timer = timeri->timer; | ||
487 | if (timer == NULL) | ||
488 | return -EINVAL; | ||
489 | if (timer->card && timer->card->shutdown) | ||
490 | return -ENODEV; | ||
491 | spin_lock_irqsave(&timer->lock, flags); | ||
492 | if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | | ||
493 | SNDRV_TIMER_IFLG_START)) { | ||
494 | result = -EBUSY; | ||
495 | goto unlock; | ||
496 | } | ||
497 | timeri->ticks = timeri->cticks = ticks; | ||
498 | timeri->pticks = 0; | ||
499 | result = snd_timer_start1(timer, timeri, ticks); | ||
500 | unlock: | ||
501 | spin_unlock_irqrestore(&timer->lock, flags); | ||
502 | if (result >= 0) | ||
503 | snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START); | ||
504 | return result; | ||
505 | } | ||
506 | |||
507 | static int _snd_timer_stop(struct snd_timer_instance *timeri, int event) | ||
508 | { | ||
509 | struct snd_timer *timer; | ||
510 | unsigned long flags; | ||
511 | |||
512 | if (snd_BUG_ON(!timeri)) | ||
513 | return -ENXIO; | ||
514 | |||
515 | if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { | ||
516 | spin_lock_irqsave(&slave_active_lock, flags); | ||
517 | if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) { | ||
518 | spin_unlock_irqrestore(&slave_active_lock, flags); | ||
519 | return -EBUSY; | ||
520 | } | ||
521 | if (timeri->timer) | ||
522 | spin_lock(&timeri->timer->lock); | ||
523 | timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING; | ||
524 | list_del_init(&timeri->ack_list); | ||
525 | list_del_init(&timeri->active_list); | ||
526 | if (timeri->timer) | ||
527 | spin_unlock(&timeri->timer->lock); | ||
528 | spin_unlock_irqrestore(&slave_active_lock, flags); | ||
529 | goto __end; | ||
530 | } | ||
531 | timer = timeri->timer; | 501 | timer = timeri->timer; |
532 | if (!timer) | 502 | if (!timer) |
533 | return -EINVAL; | 503 | return -EINVAL; |
534 | spin_lock_irqsave(&timer->lock, flags); | 504 | spin_lock_irqsave(&timer->lock, flags); |
535 | if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | | 505 | if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | |
536 | SNDRV_TIMER_IFLG_START))) { | 506 | SNDRV_TIMER_IFLG_START))) { |
537 | spin_unlock_irqrestore(&timer->lock, flags); | 507 | result = -EBUSY; |
538 | return -EBUSY; | 508 | goto unlock; |
539 | } | 509 | } |
540 | list_del_init(&timeri->ack_list); | 510 | list_del_init(&timeri->ack_list); |
541 | list_del_init(&timeri->active_list); | 511 | list_del_init(&timeri->active_list); |
542 | if (timer->card && timer->card->shutdown) { | 512 | if (timer->card && timer->card->shutdown) |
543 | spin_unlock_irqrestore(&timer->lock, flags); | 513 | goto unlock; |
544 | return 0; | 514 | if (stop) { |
515 | timeri->cticks = timeri->ticks; | ||
516 | timeri->pticks = 0; | ||
545 | } | 517 | } |
546 | if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) && | 518 | if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) && |
547 | !(--timer->running)) { | 519 | !(--timer->running)) { |
@@ -556,35 +528,60 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int event) | |||
556 | } | 528 | } |
557 | } | 529 | } |
558 | timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START); | 530 | timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START); |
531 | snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP : | ||
532 | SNDRV_TIMER_EVENT_CONTINUE); | ||
533 | unlock: | ||
559 | spin_unlock_irqrestore(&timer->lock, flags); | 534 | spin_unlock_irqrestore(&timer->lock, flags); |
560 | __end: | 535 | return result; |
561 | if (event != SNDRV_TIMER_EVENT_RESOLUTION) | 536 | } |
562 | snd_timer_notify1(timeri, event); | 537 | |
538 | /* stop/pause a slave timer */ | ||
539 | static int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop) | ||
540 | { | ||
541 | unsigned long flags; | ||
542 | |||
543 | spin_lock_irqsave(&slave_active_lock, flags); | ||
544 | if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) { | ||
545 | spin_unlock_irqrestore(&slave_active_lock, flags); | ||
546 | return -EBUSY; | ||
547 | } | ||
548 | timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING; | ||
549 | if (timeri->timer) { | ||
550 | spin_lock(&timeri->timer->lock); | ||
551 | list_del_init(&timeri->ack_list); | ||
552 | list_del_init(&timeri->active_list); | ||
553 | snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP : | ||
554 | SNDRV_TIMER_EVENT_CONTINUE); | ||
555 | spin_unlock(&timeri->timer->lock); | ||
556 | } | ||
557 | spin_unlock_irqrestore(&slave_active_lock, flags); | ||
563 | return 0; | 558 | return 0; |
564 | } | 559 | } |
565 | 560 | ||
566 | /* | 561 | /* |
562 | * start the timer instance | ||
563 | */ | ||
564 | int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks) | ||
565 | { | ||
566 | if (timeri == NULL || ticks < 1) | ||
567 | return -EINVAL; | ||
568 | if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) | ||
569 | return snd_timer_start_slave(timeri, true); | ||
570 | else | ||
571 | return snd_timer_start1(timeri, true, ticks); | ||
572 | } | ||
573 | |||
574 | /* | ||
567 | * stop the timer instance. | 575 | * stop the timer instance. |
568 | * | 576 | * |
569 | * do not call this from the timer callback! | 577 | * do not call this from the timer callback! |
570 | */ | 578 | */ |
571 | int snd_timer_stop(struct snd_timer_instance *timeri) | 579 | int snd_timer_stop(struct snd_timer_instance *timeri) |
572 | { | 580 | { |
573 | struct snd_timer *timer; | 581 | if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) |
574 | unsigned long flags; | 582 | return snd_timer_stop_slave(timeri, true); |
575 | int err; | 583 | else |
576 | 584 | return snd_timer_stop1(timeri, true); | |
577 | err = _snd_timer_stop(timeri, SNDRV_TIMER_EVENT_STOP); | ||
578 | if (err < 0) | ||
579 | return err; | ||
580 | timer = timeri->timer; | ||
581 | if (!timer) | ||
582 | return -EINVAL; | ||
583 | spin_lock_irqsave(&timer->lock, flags); | ||
584 | timeri->cticks = timeri->ticks; | ||
585 | timeri->pticks = 0; | ||
586 | spin_unlock_irqrestore(&timer->lock, flags); | ||
587 | return 0; | ||
588 | } | 585 | } |
589 | 586 | ||
590 | /* | 587 | /* |
@@ -592,32 +589,10 @@ int snd_timer_stop(struct snd_timer_instance *timeri) | |||
592 | */ | 589 | */ |
593 | int snd_timer_continue(struct snd_timer_instance *timeri) | 590 | int snd_timer_continue(struct snd_timer_instance *timeri) |
594 | { | 591 | { |
595 | struct snd_timer *timer; | ||
596 | int result = -EINVAL; | ||
597 | unsigned long flags; | ||
598 | |||
599 | if (timeri == NULL) | ||
600 | return result; | ||
601 | if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) | 592 | if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) |
602 | return snd_timer_start_slave(timeri); | 593 | return snd_timer_start_slave(timeri, false); |
603 | timer = timeri->timer; | 594 | else |
604 | if (! timer) | 595 | return snd_timer_start1(timeri, false, 0); |
605 | return -EINVAL; | ||
606 | if (timer->card && timer->card->shutdown) | ||
607 | return -ENODEV; | ||
608 | spin_lock_irqsave(&timer->lock, flags); | ||
609 | if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) { | ||
610 | result = -EBUSY; | ||
611 | goto unlock; | ||
612 | } | ||
613 | if (!timeri->cticks) | ||
614 | timeri->cticks = 1; | ||
615 | timeri->pticks = 0; | ||
616 | result = snd_timer_start1(timer, timeri, timer->sticks); | ||
617 | unlock: | ||
618 | spin_unlock_irqrestore(&timer->lock, flags); | ||
619 | snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_CONTINUE); | ||
620 | return result; | ||
621 | } | 596 | } |
622 | 597 | ||
623 | /* | 598 | /* |
@@ -625,7 +600,10 @@ int snd_timer_continue(struct snd_timer_instance *timeri) | |||
625 | */ | 600 | */ |
626 | int snd_timer_pause(struct snd_timer_instance * timeri) | 601 | int snd_timer_pause(struct snd_timer_instance * timeri) |
627 | { | 602 | { |
628 | return _snd_timer_stop(timeri, SNDRV_TIMER_EVENT_PAUSE); | 603 | if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) |
604 | return snd_timer_stop_slave(timeri, false); | ||
605 | else | ||
606 | return snd_timer_stop1(timeri, false); | ||
629 | } | 607 | } |
630 | 608 | ||
631 | /* | 609 | /* |