Hi all, I am writing a ALSA driver for my ARM 926 platform. The ARM communicates with the DSP by accessing a 128-byte shared memory. I don't use IRQ mechanism.(In fact, I am not sure the platform has this mechanism) Here is some code slices:
static struct snd_pcm_hardware snd_arch_playback_hw = {
.info = (SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_BLOCK_TRANSFER),
.formats = SNDRV_PCM_FMTBIT_S16_LE,
.rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
SNDRV_PCM_RATE_48000),
.rate_min = 8000,
.rate_max = 48000,
.channels_min = 2,
.channels_max = 2,
.buffer_bytes_max = 128,
.period_bytes_min = 2,
.period_bytes_max = 128,
.periods_min = 1,
.periods_max = 64,
};
static snd_pcm_uframes_t snd_arch_pcm_pointer(struct snd_pcm_substream
*substream)
{ …
/*
* get the current hardware pointer
*/
current_ptr = arch_fw_read_dsp_reg(chip, AFE_RWADDR) & AFE_RD_ADDR;
return bytes_to_frames(runtime, current_ptr);
}
static int snd_arch_pcm_playback_copy(struct snd_pcm_substream *substream,
int channel,
snd_pcm_uframes_t pos,
void __user *src,
snd_pcm_uframes_t count)
{ ……
count = frames_to_bytes(runtime, count);
pos = frames_to_bytes(runtime, pos);
chip->bufpos = pos;
….
spin_lock(chip->reg_lock);
if ( count > 0 && count <= MAX_BUF_SIZE ) {
if ( copy_from_user(chip->bufptr, src, count) ) {
snd_printk(KERN_ERR "can not copy user data!\n");
return -EFAULT;
}
if ( arch_fill_rx_buf(chip,
(signed short*)(chip->bufptr), count) < 0 ) {
snd_printk(KERN_ERR "fill rx buffer failed!\n");
return -1;
}
……………
} spin_unlock(chip->reg_lock);
return 0;
}
The problem is my driver can make sounds, but ends up a write error. After much debugging, I think there might be some problem in the interworking between middle-level driver and the device driver.
I found the runtime->control->avail_min=32, and the runtime->buffer_size and runtime->period_size are both 32. So at first middle-level driver call snd_pcm_lib_write1 transfers 32 frames(128 bytes) to the device driver's copy callback function and trigger function. Then when middle-level driver wants to transfer another 32 frames, it calls the pointer callback function and gets the position, because at this time the whole bytes has not been processed completely, so the pointer function's return value must less than 32, and the avail must less than runtime->control_->avail_min, and goes to this flow and ends up a write error.
_______________________________________________________________________________
if (((avail < runtime->control->avail_min && size > avail) ||
(size >= runtime->xfer_align && avail < runtime->xfer_align))) {
……………...
long tout;
init_waitqueue_entry(&wait, current);
add_wait_queue(&runtime->sleep, &wait);
while (1) {
if (signal_pending(current)) {
state = SIGNALED;
break;
}
set_current_state(TASK_INTERRUPTIBLE);
snd_pcm_stream_unlock_irq(substream);
tout = schedule_timeout(10 * HZ);
snd_pcm_stream_lock_irq(substream);
if (tout == 0) {
if (runtime->status->state != SNDRV_PCM_STATE_PREPARED &&
runtime->status->state != SNDRV_PCM_STATE_PAUSED) {
state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED;
break;
}
}
switch (runtime->status->state) {
…………..
:
state = ERROR;
goto _end_loop;
default:
break;
}
avail = snd_pcm_playback_avail(runtime);
if (avail >= runtime->control->avail_min) {
state = READY;
break;
}
}
_end_loop:
remove_wait_queue(&runtime->sleep, &wait);
switch (state) {
…….
case EXPIRED:
snd_printd("playback write error (DMA or IRQ trouble?)\n");
err = -EIO;
goto _end_unlock;
………
}
}
This code is a part of snd_pcm_lib_write1() in sound/core/pcm_lib.c
I think that I misunderstood the mechanism of middle-level driver. Could you please correct me?
What should the pointer callback function return? What's it for?
How comes the runtime->control->avail_min?
Because I don't use the IRQ, so should I use the snd_pcm_period_elapsed function? And where?
ALSA Version is 1.0.14rc1 and I use alsa-lib-1.0.15.
Can someone comment on this and give me some hints to solve this problem? Thanks in advance.
BRs,
David