diff options
| -rw-r--r-- | sound/drivers/aloop.c | 139 |
1 files changed, 123 insertions, 16 deletions
diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c index 838ad86311b8..12b44b0b6777 100644 --- a/sound/drivers/aloop.c +++ b/sound/drivers/aloop.c | |||
| @@ -39,6 +39,7 @@ | |||
| 39 | #include <sound/core.h> | 39 | #include <sound/core.h> |
| 40 | #include <sound/control.h> | 40 | #include <sound/control.h> |
| 41 | #include <sound/pcm.h> | 41 | #include <sound/pcm.h> |
| 42 | #include <sound/info.h> | ||
| 42 | #include <sound/initval.h> | 43 | #include <sound/initval.h> |
| 43 | 44 | ||
| 44 | MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); | 45 | MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); |
| @@ -76,6 +77,7 @@ struct loopback_cable { | |||
| 76 | /* flags */ | 77 | /* flags */ |
| 77 | unsigned int valid; | 78 | unsigned int valid; |
| 78 | unsigned int running; | 79 | unsigned int running; |
| 80 | unsigned int pause; | ||
| 79 | }; | 81 | }; |
| 80 | 82 | ||
| 81 | struct loopback_setup { | 83 | struct loopback_setup { |
| @@ -184,6 +186,7 @@ static void loopback_timer_start(struct loopback_pcm *dpcm) | |||
| 184 | static inline void loopback_timer_stop(struct loopback_pcm *dpcm) | 186 | static inline void loopback_timer_stop(struct loopback_pcm *dpcm) |
| 185 | { | 187 | { |
| 186 | del_timer(&dpcm->timer); | 188 | del_timer(&dpcm->timer); |
| 189 | dpcm->timer.expires = 0; | ||
| 187 | } | 190 | } |
| 188 | 191 | ||
| 189 | #define CABLE_VALID_PLAYBACK (1 << SNDRV_PCM_STREAM_PLAYBACK) | 192 | #define CABLE_VALID_PLAYBACK (1 << SNDRV_PCM_STREAM_PLAYBACK) |
| @@ -252,7 +255,7 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd) | |||
| 252 | struct snd_pcm_runtime *runtime = substream->runtime; | 255 | struct snd_pcm_runtime *runtime = substream->runtime; |
| 253 | struct loopback_pcm *dpcm = runtime->private_data; | 256 | struct loopback_pcm *dpcm = runtime->private_data; |
| 254 | struct loopback_cable *cable = dpcm->cable; | 257 | struct loopback_cable *cable = dpcm->cable; |
| 255 | int err; | 258 | int err, stream = 1 << substream->stream; |
| 256 | 259 | ||
| 257 | switch (cmd) { | 260 | switch (cmd) { |
| 258 | case SNDRV_PCM_TRIGGER_START: | 261 | case SNDRV_PCM_TRIGGER_START: |
| @@ -261,17 +264,36 @@ static int loopback_trigger(struct snd_pcm_substream *substream, int cmd) | |||
| 261 | return err; | 264 | return err; |
| 262 | dpcm->last_jiffies = jiffies; | 265 | dpcm->last_jiffies = jiffies; |
| 263 | dpcm->pcm_rate_shift = 0; | 266 | dpcm->pcm_rate_shift = 0; |
| 267 | spin_lock(&cable->lock); | ||
| 268 | cable->running |= stream; | ||
| 269 | cable->pause &= ~stream; | ||
| 270 | spin_unlock(&cable->lock); | ||
| 264 | loopback_timer_start(dpcm); | 271 | loopback_timer_start(dpcm); |
| 265 | cable->running |= (1 << substream->stream); | ||
| 266 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 272 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
| 267 | loopback_active_notify(dpcm); | 273 | loopback_active_notify(dpcm); |
| 268 | break; | 274 | break; |
| 269 | case SNDRV_PCM_TRIGGER_STOP: | 275 | case SNDRV_PCM_TRIGGER_STOP: |
| 270 | cable->running &= ~(1 << substream->stream); | 276 | spin_lock(&cable->lock); |
| 277 | cable->running &= ~stream; | ||
| 278 | cable->pause &= ~stream; | ||
| 279 | spin_unlock(&cable->lock); | ||
| 271 | loopback_timer_stop(dpcm); | 280 | loopback_timer_stop(dpcm); |
| 272 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) | 281 | if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) |
| 273 | loopback_active_notify(dpcm); | 282 | loopback_active_notify(dpcm); |
| 274 | break; | 283 | break; |
| 284 | case SNDRV_PCM_TRIGGER_PAUSE_PUSH: | ||
| 285 | spin_lock(&cable->lock); | ||
| 286 | cable->pause |= stream; | ||
| 287 | spin_unlock(&cable->lock); | ||
| 288 | loopback_timer_stop(dpcm); | ||
| 289 | break; | ||
| 290 | case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: | ||
| 291 | spin_lock(&cable->lock); | ||
| 292 | dpcm->last_jiffies = jiffies; | ||
| 293 | cable->pause &= ~stream; | ||
| 294 | spin_unlock(&cable->lock); | ||
| 295 | loopback_timer_start(dpcm); | ||
| 296 | break; | ||
| 275 | default: | 297 | default: |
| 276 | return -EINVAL; | 298 | return -EINVAL; |
| 277 | } | 299 | } |
| @@ -452,28 +474,30 @@ static void loopback_bytepos_update(struct loopback_pcm *dpcm, | |||
| 452 | } | 474 | } |
| 453 | } | 475 | } |
| 454 | 476 | ||
| 455 | static void loopback_pos_update(struct loopback_cable *cable) | 477 | static unsigned int loopback_pos_update(struct loopback_cable *cable) |
| 456 | { | 478 | { |
| 457 | struct loopback_pcm *dpcm_play = | 479 | struct loopback_pcm *dpcm_play = |
| 458 | cable->streams[SNDRV_PCM_STREAM_PLAYBACK]; | 480 | cable->streams[SNDRV_PCM_STREAM_PLAYBACK]; |
| 459 | struct loopback_pcm *dpcm_capt = | 481 | struct loopback_pcm *dpcm_capt = |
| 460 | cable->streams[SNDRV_PCM_STREAM_CAPTURE]; | 482 | cable->streams[SNDRV_PCM_STREAM_CAPTURE]; |
| 461 | unsigned long delta_play = 0, delta_capt = 0; | 483 | unsigned long delta_play = 0, delta_capt = 0; |
| 484 | unsigned int running; | ||
| 462 | 485 | ||
| 463 | spin_lock(&cable->lock); | 486 | spin_lock(&cable->lock); |
| 464 | if (cable->running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) { | 487 | running = cable->running ^ cable->pause; |
| 488 | if (running & (1 << SNDRV_PCM_STREAM_PLAYBACK)) { | ||
| 465 | delta_play = jiffies - dpcm_play->last_jiffies; | 489 | delta_play = jiffies - dpcm_play->last_jiffies; |
| 466 | dpcm_play->last_jiffies += delta_play; | 490 | dpcm_play->last_jiffies += delta_play; |
| 467 | } | 491 | } |
| 468 | 492 | ||
| 469 | if (cable->running & (1 << SNDRV_PCM_STREAM_CAPTURE)) { | 493 | if (running & (1 << SNDRV_PCM_STREAM_CAPTURE)) { |
| 470 | delta_capt = jiffies - dpcm_capt->last_jiffies; | 494 | delta_capt = jiffies - dpcm_capt->last_jiffies; |
| 471 | dpcm_capt->last_jiffies += delta_capt; | 495 | dpcm_capt->last_jiffies += delta_capt; |
| 472 | } | 496 | } |
| 473 | 497 | ||
| 474 | if (delta_play == 0 && delta_capt == 0) { | 498 | if (delta_play == 0 && delta_capt == 0) { |
| 475 | spin_unlock(&cable->lock); | 499 | spin_unlock(&cable->lock); |
| 476 | return; | 500 | return running; |
| 477 | } | 501 | } |
| 478 | 502 | ||
| 479 | if (delta_play > delta_capt) { | 503 | if (delta_play > delta_capt) { |
| @@ -488,27 +512,27 @@ static void loopback_pos_update(struct loopback_cable *cable) | |||
| 488 | 512 | ||
| 489 | if (delta_play == 0 && delta_capt == 0) { | 513 | if (delta_play == 0 && delta_capt == 0) { |
| 490 | spin_unlock(&cable->lock); | 514 | spin_unlock(&cable->lock); |
| 491 | return; | 515 | return running; |
| 492 | } | 516 | } |
| 493 | /* note delta_capt == delta_play at this moment */ | 517 | /* note delta_capt == delta_play at this moment */ |
| 494 | loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY); | 518 | loopback_bytepos_update(dpcm_capt, delta_capt, BYTEPOS_UPDATE_COPY); |
| 495 | loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY); | 519 | loopback_bytepos_update(dpcm_play, delta_play, BYTEPOS_UPDATE_POSONLY); |
| 496 | spin_unlock(&cable->lock); | 520 | spin_unlock(&cable->lock); |
| 521 | return running; | ||
| 497 | } | 522 | } |
| 498 | 523 | ||
| 499 | static void loopback_timer_function(unsigned long data) | 524 | static void loopback_timer_function(unsigned long data) |
| 500 | { | 525 | { |
| 501 | struct loopback_pcm *dpcm = (struct loopback_pcm *)data; | 526 | struct loopback_pcm *dpcm = (struct loopback_pcm *)data; |
| 502 | int stream; | 527 | unsigned int running; |
| 503 | 528 | ||
| 504 | loopback_pos_update(dpcm->cable); | 529 | running = loopback_pos_update(dpcm->cable); |
| 505 | stream = dpcm->substream->stream; | 530 | if (running & (1 << dpcm->substream->stream)) { |
| 506 | if (dpcm->cable->running & (1 << stream)) | ||
| 507 | loopback_timer_start(dpcm); | 531 | loopback_timer_start(dpcm); |
| 508 | if (dpcm->period_update_pending) { | 532 | if (dpcm->period_update_pending) { |
| 509 | dpcm->period_update_pending = 0; | 533 | dpcm->period_update_pending = 0; |
| 510 | if (dpcm->cable->running & (1 << stream)) | ||
| 511 | snd_pcm_period_elapsed(dpcm->substream); | 534 | snd_pcm_period_elapsed(dpcm->substream); |
| 535 | } | ||
| 512 | } | 536 | } |
| 513 | } | 537 | } |
| 514 | 538 | ||
| @@ -524,7 +548,7 @@ static snd_pcm_uframes_t loopback_pointer(struct snd_pcm_substream *substream) | |||
| 524 | static struct snd_pcm_hardware loopback_pcm_hardware = | 548 | static struct snd_pcm_hardware loopback_pcm_hardware = |
| 525 | { | 549 | { |
| 526 | .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | | 550 | .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP | |
| 527 | SNDRV_PCM_INFO_MMAP_VALID), | 551 | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_PAUSE), |
| 528 | .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | | 552 | .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | |
| 529 | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE | | 553 | SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE | |
| 530 | SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE), | 554 | SNDRV_PCM_FMTBIT_FLOAT_LE | SNDRV_PCM_FMTBIT_FLOAT_BE), |
| @@ -1011,6 +1035,87 @@ static int __devinit loopback_mixer_new(struct loopback *loopback, int notify) | |||
| 1011 | return 0; | 1035 | return 0; |
| 1012 | } | 1036 | } |
| 1013 | 1037 | ||
| 1038 | #ifdef CONFIG_PROC_FS | ||
| 1039 | |||
| 1040 | static void print_dpcm_info(struct snd_info_buffer *buffer, | ||
| 1041 | struct loopback_pcm *dpcm, | ||
| 1042 | const char *id) | ||
| 1043 | { | ||
| 1044 | snd_iprintf(buffer, " %s\n", id); | ||
| 1045 | if (dpcm == NULL) { | ||
| 1046 | snd_iprintf(buffer, " inactive\n"); | ||
| 1047 | return; | ||
| 1048 | } | ||
| 1049 | snd_iprintf(buffer, " buffer_size:\t%u\n", dpcm->pcm_buffer_size); | ||
| 1050 | snd_iprintf(buffer, " buffer_pos:\t\t%u\n", dpcm->buf_pos); | ||
| 1051 | snd_iprintf(buffer, " silent_size:\t%u\n", dpcm->silent_size); | ||
| 1052 | snd_iprintf(buffer, " period_size:\t%u\n", dpcm->pcm_period_size); | ||
| 1053 | snd_iprintf(buffer, " bytes_per_sec:\t%u\n", dpcm->pcm_bps); | ||
| 1054 | snd_iprintf(buffer, " sample_align:\t%u\n", dpcm->pcm_salign); | ||
| 1055 | snd_iprintf(buffer, " rate_shift:\t\t%u\n", dpcm->pcm_rate_shift); | ||
| 1056 | snd_iprintf(buffer, " update_pending:\t%u\n", | ||
| 1057 | dpcm->period_update_pending); | ||
| 1058 | snd_iprintf(buffer, " irq_pos:\t\t%u\n", dpcm->irq_pos); | ||
| 1059 | snd_iprintf(buffer, " period_frac:\t%u\n", dpcm->period_size_frac); | ||
| 1060 | snd_iprintf(buffer, " last_jiffies:\t%lu (%lu)\n", | ||
| 1061 | dpcm->last_jiffies, jiffies); | ||
| 1062 | snd_iprintf(buffer, " timer_expires:\t%lu\n", dpcm->timer.expires); | ||
| 1063 | } | ||
| 1064 | |||
| 1065 | static void print_substream_info(struct snd_info_buffer *buffer, | ||
| 1066 | struct loopback *loopback, | ||
| 1067 | int sub, | ||
| 1068 | int num) | ||
| 1069 | { | ||
| 1070 | struct loopback_cable *cable = loopback->cables[sub][num]; | ||
| 1071 | |||
| 1072 | snd_iprintf(buffer, "Cable %i substream %i:\n", num, sub); | ||
| 1073 | if (cable == NULL) { | ||
| 1074 | snd_iprintf(buffer, " inactive\n"); | ||
| 1075 | return; | ||
| 1076 | } | ||
| 1077 | snd_iprintf(buffer, " valid: %u\n", cable->valid); | ||
| 1078 | snd_iprintf(buffer, " running: %u\n", cable->running); | ||
| 1079 | snd_iprintf(buffer, " pause: %u\n", cable->pause); | ||
| 1080 | print_dpcm_info(buffer, cable->streams[0], "Playback"); | ||
| 1081 | print_dpcm_info(buffer, cable->streams[1], "Capture"); | ||
| 1082 | } | ||
| 1083 | |||
| 1084 | static void print_cable_info(struct snd_info_entry *entry, | ||
| 1085 | struct snd_info_buffer *buffer) | ||
| 1086 | { | ||
| 1087 | struct loopback *loopback = entry->private_data; | ||
| 1088 | int sub, num; | ||
| 1089 | |||
| 1090 | mutex_lock(&loopback->cable_lock); | ||
| 1091 | num = entry->name[strlen(entry->name)-1]; | ||
| 1092 | num = num == '0' ? 0 : 1; | ||
| 1093 | for (sub = 0; sub < MAX_PCM_SUBSTREAMS; sub++) | ||
| 1094 | print_substream_info(buffer, loopback, sub, num); | ||
| 1095 | mutex_unlock(&loopback->cable_lock); | ||
| 1096 | } | ||
| 1097 | |||
| 1098 | static int __devinit loopback_proc_new(struct loopback *loopback, int cidx) | ||
| 1099 | { | ||
| 1100 | char name[32]; | ||
| 1101 | struct snd_info_entry *entry; | ||
| 1102 | int err; | ||
| 1103 | |||
| 1104 | snprintf(name, sizeof(name), "cable#%d", cidx); | ||
| 1105 | err = snd_card_proc_new(loopback->card, name, &entry); | ||
| 1106 | if (err < 0) | ||
| 1107 | return err; | ||
| 1108 | |||
| 1109 | snd_info_set_text_ops(entry, loopback, print_cable_info); | ||
| 1110 | return 0; | ||
| 1111 | } | ||
| 1112 | |||
| 1113 | #else /* !CONFIG_PROC_FS */ | ||
| 1114 | |||
| 1115 | #define loopback_proc_new(loopback, cidx) do { } while (0) | ||
| 1116 | |||
| 1117 | #endif | ||
| 1118 | |||
| 1014 | static int __devinit loopback_probe(struct platform_device *devptr) | 1119 | static int __devinit loopback_probe(struct platform_device *devptr) |
| 1015 | { | 1120 | { |
| 1016 | struct snd_card *card; | 1121 | struct snd_card *card; |
| @@ -1041,6 +1146,8 @@ static int __devinit loopback_probe(struct platform_device *devptr) | |||
| 1041 | err = loopback_mixer_new(loopback, pcm_notify[dev] ? 1 : 0); | 1146 | err = loopback_mixer_new(loopback, pcm_notify[dev] ? 1 : 0); |
| 1042 | if (err < 0) | 1147 | if (err < 0) |
| 1043 | goto __nodev; | 1148 | goto __nodev; |
| 1149 | loopback_proc_new(loopback, 0); | ||
| 1150 | loopback_proc_new(loopback, 1); | ||
| 1044 | strcpy(card->driver, "Loopback"); | 1151 | strcpy(card->driver, "Loopback"); |
| 1045 | strcpy(card->shortname, "Loopback"); | 1152 | strcpy(card->shortname, "Loopback"); |
| 1046 | sprintf(card->longname, "Loopback %i", dev + 1); | 1153 | sprintf(card->longname, "Loopback %i", dev + 1); |
