[alsa-devel] Questions about writing a new ALSA driver for a very limitted device
Greetings,
I'm setting out to write an ALSA driver for a very limited device thats behind a PLD. The actual codec is a uda1380 and does 16bit MSB stereo encode/decode. The way the device is driven the sampling rate is limited to 8KHz and data transfer to/from the device I have to handle manually.
I've read over the 'writing an ALSA driver' tutorial and have started the driver but have run into a few questions:
- for the specs I've given above: 8000Hz sampling rate, 16bit stereo MSB samples does the following snd_pcm_hardware_t look right?:
static snd_pcm_hardware_t my_playback_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_U16_BE, // is this 16bit MSB? .rates = SNDRV_PCM_RATE_8000, // seems redundant? .rate_min = 8000, .rate_max = 8000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 32768, .period_bytes_min = 4096, .period_bytes_max = 32768, .periods_min = 1, .periods_max = 1024, };
- I'm a little confused at how to deal with copying data from/to the device. It looks like I would implement the copy callback and copy the passed buffer to the device in the device specific manner I need to (playback), but I'm not clear where to put data that I receive from the device (capture). I notice that when the hw_params callback is called a dma buffer has been created by ALSA in the substream->runtime struct. I assume this is created based on hte buffer_bytes_max field above and I'm to copy data to this buffer? How do I know where to copy it within the buffer and what data ALSA has already consumed from the buffer?
- I might need to create an intermediate buffer between ALSA's rather large buffers and my hardware buffer. Is there any driver that I could look at that does this? The tutorial talks about the vxpocket driver but that code looks very foriegn from the rest of the ALSA drivers.
- I'm not sure I understand what the 'period' is about. Its clear to me that I need to call snd_pcm_period_elapsed() periodically from either an IRQ or other timed callback, but I'm not clear what the hardware pointer is or how that converts to 'frames' and 'periods'.
- in general, if my hardware device only allows 16bit MSB 8KHz, does ALSA provides a mechanism for converting data from other formats?
Thanks for any pointers,
Tim
At Wed, 11 Apr 2007 18:18:54 -0700 (PDT), Tim Harvey wrote:
Greetings,
I'm setting out to write an ALSA driver for a very limited device thats behind a PLD. The actual codec is a uda1380 and does 16bit MSB stereo encode/decode. The way the device is driven the sampling rate is limited to 8KHz and data transfer to/from the device I have to handle manually.
I've read over the 'writing an ALSA driver' tutorial and have started the driver but have run into a few questions:
- for the specs I've given above: 8000Hz sampling rate, 16bit stereo MSB
samples does the following snd_pcm_hardware_t look right?:
static snd_pcm_hardware_t my_playback_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_U16_BE, // is this 16bit MSB? .rates = SNDRV_PCM_RATE_8000, // seems redundant? .rate_min = 8000, .rate_max = 8000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 32768, .period_bytes_min = 4096, .period_bytes_max = 32768, .periods_min = 1, .periods_max = 1024, };
Looks fine to me. rates field shouldn't be empty, so it's not redundant (although it might work without it).
- I'm a little confused at how to deal with copying data from/to the device.
It looks like I would implement the copy callback and copy the passed buffer to the device in the device specific manner I need to (playback), but I'm not clear where to put data that I receive from the device (capture). I notice that when the hw_params callback is called a dma buffer has been created by ALSA in the substream->runtime struct. I assume this is created based on hte buffer_bytes_max field above and I'm to copy data to this buffer? How do I know where to copy it within the buffer and what data ALSA has already consumed from the buffer?
It's a little bit complicated right now. If you don't need mmap mode transfer, the thing gets easier, but it restricts application usage.
Without mmap mode, you can implement copy and silence callbacks to copy the user-space data directly to the hardware buffer. In this case, the period and buffer sizes in snd_pcm_hardware have to be identical with what the hardware can accept. ISA GUS and emu8000_pcm are examples.
Otherwise, you'd need an intermediate buffer. In this case, the transfer to the hardware is done either in the irq handler (via ack callback) or through a dedicated workqueue in background.
- I might need to create an intermediate buffer between ALSA's rather large
buffers and my hardware buffer. Is there any driver that I could look at that does this? The tutorial talks about the vxpocket driver but that code looks very foriegn from the rest of the ALSA drivers.
The vx driver implementation is complex and not well suitable as a reference. If the manual copy can be done relatively fast, you can use helper functions in pcm_indirect.h, as in emu10k1/emupcm.c, rme32 and cs46xx. They transfer the data via ack callback, which is invoked from snd_pcm_period_elapsed().
Actually, this is a missing piece in the current ALSA design, and now becoming a frequently asked thing. Maybe it's good to think of a standard framework for non-DMA type driver implementation.
- I'm not sure I understand what the 'period' is about. Its clear to me that
I need to call snd_pcm_period_elapsed() periodically from either an IRQ or other timed callback, but I'm not clear what the hardware pointer is or how that converts to 'frames' and 'periods'.
The hardware pointer (hw_ptr) is the current position of the transfer. Note that this is in frames unit. Meanwhile, the application pointer (appl_ptr) is the current position of the filled data by the app. Thus, appl_ptr - hw_ptr gives you the number of the rest data to be processed.
Both pointers are linear and _not_ the offset in a ring-buffer. It can be between 0 and runtime->boundary-1 (ca. LONG_MAX, aligned to period_size).
However, the pointer callback returns the offset in a ring-buffer, i.e. between 0 and (buffer_size-1). Then the pcm core layer recomputes the linear position.
The "frame" represents the unit, 1 frame = # channels x sample_bytes. In your case, 1 frame corresponds to 2 channels x 16 bits = 4 bytes.
The periods is the number of periods in a ring-buffer. In OSS, called as "fragments".
So, - buffer_size = period_size * periods - period_bytes = period_size * bytes_per_frame - bytes_per_frame = channels * bytes_per_sample
- in general, if my hardware device only allows 16bit MSB 8KHz, does ALSA
provides a mechanism for converting data from other formats?
Yes, it's done in alsa-lib on user-space.
Takashi
Thanks for the help! I've got a few more questions below:
--- Takashi Iwai tiwai@suse.de wrote:
At Wed, 11 Apr 2007 18:18:54 -0700 (PDT), Tim Harvey wrote:
Greetings,
I'm setting out to write an ALSA driver for a very limited device thats
behind
a PLD. The actual codec is a uda1380 and does 16bit MSB stereo
encode/decode.
The way the device is driven the sampling rate is limited to 8KHz and data transfer to/from the device I have to handle manually.
I've read over the 'writing an ALSA driver' tutorial and have started the driver but have run into a few questions:
- for the specs I've given above: 8000Hz sampling rate, 16bit stereo MSB
samples does the following snd_pcm_hardware_t look right?:
static snd_pcm_hardware_t my_playback_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_U16_BE, // is this 16bit MSB?
So, just to confirm, SNDRV_PCM_FMTBIT_U16_BE is appropriate for a 2byte sample where the first byte's msb is the msb of the sample and the 2nd bytes lsb is the lsb of the sample correct? And this will be the case regarding to the endianess of my system? (its an arm-be system).
.rates = SNDRV_PCM_RATE_8000, // seems redundant? .rate_min = 8000, .rate_max = 8000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 32768, .period_bytes_min = 4096, .period_bytes_max = 32768, .periods_min = 1, .periods_max = 1024,
};
Looks fine to me. rates field shouldn't be empty, so it's not redundant (although it might work without it).
- I'm a little confused at how to deal with copying data from/to the
device.
It looks like I would implement the copy callback and copy the passed
buffer to
the device in the device specific manner I need to (playback), but I'm not clear where to put data that I receive from the device (capture). I notice that when the hw_params callback is called a dma buffer has been created by ALSA in the substream->runtime struct. I assume this is created based on
hte
buffer_bytes_max field above and I'm to copy data to this buffer? How do I know where to copy it within the buffer and what data ALSA has already
consumed
from the buffer?
It's a little bit complicated right now. If you don't need mmap mode transfer, the thing gets easier, but it restricts application usage.
to know if I need mmap mode that would depend on what userland apps I wish to support and if they use this or not correct? Are there many out there with this requirement?
Without mmap mode, you can implement copy and silence callbacks to copy the user-space data directly to the hardware buffer. In this case, the period and buffer sizes in snd_pcm_hardware have to be identical with what the hardware can accept. ISA GUS and emu8000_pcm are examples.
If my hardware buffer is somewhat small (ie ~1k) could I simply set snd_pcm_hardware to reflect this? Is there a minimum size for buffer_bytes_max/min before you would want to use an intermediate buffer?
Otherwise, you'd need an intermediate buffer. In this case, the transfer to the hardware is done either in the irq handler (via ack callback) or through a dedicated workqueue in background.
- I might need to create an intermediate buffer between ALSA's rather
large
buffers and my hardware buffer. Is there any driver that I could look at
that
does this? The tutorial talks about the vxpocket driver but that code
looks
very foriegn from the rest of the ALSA drivers.
The vx driver implementation is complex and not well suitable as a reference. If the manual copy can be done relatively fast, you can use helper functions in pcm_indirect.h, as in emu10k1/emupcm.c, rme32 and cs46xx. They transfer the data via ack callback, which is invoked from snd_pcm_period_elapsed().
Actually, this is a missing piece in the current ALSA design, and now becoming a frequently asked thing. Maybe it's good to think of a standard framework for non-DMA type driver implementation.
- I'm not sure I understand what the 'period' is about. Its clear to me
that
I need to call snd_pcm_period_elapsed() periodically from either an IRQ or other timed callback, but I'm not clear what the hardware pointer is or how that converts to 'frames' and 'periods'.
The hardware pointer (hw_ptr) is the current position of the transfer. Note that this is in frames unit. Meanwhile, the application pointer (appl_ptr) is the current position of the filled data by the app. Thus, appl_ptr - hw_ptr gives you the number of the rest data to be processed.
Both pointers are linear and _not_ the offset in a ring-buffer. It can be between 0 and runtime->boundary-1 (ca. LONG_MAX, aligned to period_size).
However, the pointer callback returns the offset in a ring-buffer, i.e. between 0 and (buffer_size-1). Then the pcm core layer recomputes the linear position.
The "frame" represents the unit, 1 frame = # channels x sample_bytes. In your case, 1 frame corresponds to 2 channels x 16 bits = 4 bytes.
The periods is the number of periods in a ring-buffer. In OSS, called as "fragments".
So,
- buffer_size = period_size * periods
- period_bytes = period_size * bytes_per_frame
- bytes_per_frame = channels * bytes_per_sample
I still don't understand what 'period_size' and a 'period' is?
I've also noticed that I seem to 'have' to call snd_pcm_lib_preallocate_pages_for_all or I get an error when ALSA tries to use the drive. I assume that I need to 'preallocate' or allocate manually upon each 'open' (or another callback?). Is this 'preallocate' going to allocate 1 buffer or 1 buffer per playback/capture?
For capture would I grab this buffer pointer and copy data to it inside of a timer/irq event and adjust the hw_ptr manually?
Thanks,
Tim
- in general, if my hardware device only allows 16bit MSB 8KHz, does ALSA
provides a mechanism for converting data from other formats?
Yes, it's done in alsa-lib on user-space.
Takashi
At Sun, 15 Apr 2007 18:56:56 -0700 (PDT), Tim Harvey wrote:
At Wed, 11 Apr 2007 18:18:54 -0700 (PDT), Tim Harvey wrote:
Greetings,
I'm setting out to write an ALSA driver for a very limited device thats
behind
a PLD. The actual codec is a uda1380 and does 16bit MSB stereo
encode/decode.
The way the device is driven the sampling rate is limited to 8KHz and data transfer to/from the device I have to handle manually.
I've read over the 'writing an ALSA driver' tutorial and have started the driver but have run into a few questions:
- for the specs I've given above: 8000Hz sampling rate, 16bit stereo MSB
samples does the following snd_pcm_hardware_t look right?:
static snd_pcm_hardware_t my_playback_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_U16_BE, // is this 16bit MSB?
So, just to confirm, SNDRV_PCM_FMTBIT_U16_BE is appropriate for a 2byte sample where the first byte's msb is the msb of the sample and the 2nd bytes lsb is the lsb of the sample correct? And this will be the case regarding to the endianess of my system? (its an arm-be system).
Yes, U16_BE is 16bit big-endian format (but this is independent from the CPU endianess). This field specifies what the sound hardware supports.
.rates = SNDRV_PCM_RATE_8000, // seems redundant? .rate_min = 8000, .rate_max = 8000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 32768, .period_bytes_min = 4096, .period_bytes_max = 32768, .periods_min = 1, .periods_max = 1024,
};
Looks fine to me. rates field shouldn't be empty, so it's not redundant (although it might work without it).
- I'm a little confused at how to deal with copying data from/to the
device.
It looks like I would implement the copy callback and copy the passed
buffer to
the device in the device specific manner I need to (playback), but I'm not clear where to put data that I receive from the device (capture). I notice that when the hw_params callback is called a dma buffer has been created by ALSA in the substream->runtime struct. I assume this is created based on
hte
buffer_bytes_max field above and I'm to copy data to this buffer? How do I know where to copy it within the buffer and what data ALSA has already
consumed
from the buffer?
It's a little bit complicated right now. If you don't need mmap mode transfer, the thing gets easier, but it restricts application usage.
to know if I need mmap mode that would depend on what userland apps I wish to support and if they use this or not correct? Are there many out there with this requirement?
For example, the software multi-playback via alsa-lib (dmix plugin) uses the mmap mode essentially. Also, you'll likely want to have an intermediate buffer in the case with small hardware buffer, and this will require a similar implementation like mmap mode.
Without mmap mode, you can implement copy and silence callbacks to copy the user-space data directly to the hardware buffer. In this case, the period and buffer sizes in snd_pcm_hardware have to be identical with what the hardware can accept. ISA GUS and emu8000_pcm are examples.
If my hardware buffer is somewhat small (ie ~1k) could I simply set snd_pcm_hardware to reflect this? Is there a minimum size for buffer_bytes_max/min before you would want to use an intermediate buffer?
There is no strict definition. But, 1k sounds indeed too small without an intermediate buffer.
Otherwise, you'd need an intermediate buffer. In this case, the transfer to the hardware is done either in the irq handler (via ack callback) or through a dedicated workqueue in background.
- I might need to create an intermediate buffer between ALSA's rather
large
buffers and my hardware buffer. Is there any driver that I could look at
that
does this? The tutorial talks about the vxpocket driver but that code
looks
very foriegn from the rest of the ALSA drivers.
The vx driver implementation is complex and not well suitable as a reference. If the manual copy can be done relatively fast, you can use helper functions in pcm_indirect.h, as in emu10k1/emupcm.c, rme32 and cs46xx. They transfer the data via ack callback, which is invoked from snd_pcm_period_elapsed().
Actually, this is a missing piece in the current ALSA design, and now becoming a frequently asked thing. Maybe it's good to think of a standard framework for non-DMA type driver implementation.
- I'm not sure I understand what the 'period' is about. Its clear to me
that
I need to call snd_pcm_period_elapsed() periodically from either an IRQ or other timed callback, but I'm not clear what the hardware pointer is or how that converts to 'frames' and 'periods'.
The hardware pointer (hw_ptr) is the current position of the transfer. Note that this is in frames unit. Meanwhile, the application pointer (appl_ptr) is the current position of the filled data by the app. Thus, appl_ptr - hw_ptr gives you the number of the rest data to be processed.
Both pointers are linear and _not_ the offset in a ring-buffer. It can be between 0 and runtime->boundary-1 (ca. LONG_MAX, aligned to period_size).
However, the pointer callback returns the offset in a ring-buffer, i.e. between 0 and (buffer_size-1). Then the pcm core layer recomputes the linear position.
The "frame" represents the unit, 1 frame = # channels x sample_bytes. In your case, 1 frame corresponds to 2 channels x 16 bits = 4 bytes.
The periods is the number of periods in a ring-buffer. In OSS, called as "fragments".
So,
- buffer_size = period_size * periods
- period_bytes = period_size * bytes_per_frame
- bytes_per_frame = channels * bytes_per_sample
I still don't understand what 'period_size' and a 'period' is?
The "period" defines the frequency to update the status, usually via the invokation of interrupts. The "period_size" defines the frame sizes corresponding to the "period time". This term corresponds to the "fragment size" on OSS. On major sound hardwares, a ring-buffer is divided to several parts and an irq is issued on each boundary. The period_size defines the size of this chunk.
On some hardwares, the irq is controlled on the basis of a timer. In this case, the period is defined as the timer frequency to invoke an irq.
I've also noticed that I seem to 'have' to call snd_pcm_lib_preallocate_pages_for_all or I get an error when ALSA tries to use the drive. I assume that I need to 'preallocate' or allocate manually upon each 'open' (or another callback?). Is this 'preallocate' going to allocate 1 buffer or 1 buffer per playback/capture?
The pre-allocation is done only once at driver initialziation time. It's pooled, and assigned at each snd_pcm_lib_malloc() call. If snd_pcm_lib_malloc() requires more memory than pre-allocated, it tries to allocate memories dynamically.
For capture would I grab this buffer pointer and copy data to it inside of a timer/irq event and adjust the hw_ptr manually?
It's similar like playback. In the case with a intermediate buffer, the data copy can be again done in ack callback. You don't have to care about the update of hw_ptr. It's a job of PCM middle layer. The lowlevel driver needs to provide proper callbacks. The pointer callback must return the current transfer-position offset. Then hw_ptr will be automatically updated according to this value.
Takashi
--- Takashi Iwai tiwai@suse.de wrote:
The "period" defines the frequency to update the status, usually via the invokation of interrupts. The "period_size" defines the frame sizes corresponding to the "period time". This term corresponds to the "fragment size" on OSS. On major sound hardwares, a ring-buffer is divided to several parts and an irq is issued on each boundary. The period_size defines the size of this chunk.
On some hardwares, the irq is controlled on the basis of a timer. In this case, the period is defined as the timer frequency to invoke an irq.
My system has a hardware buffer of 4096 bytes (so I will use an intermediate buffer ) and is fixed at 8KHz 16bit stereo. The 4K hw buffer would accommodate 128ms of data which is pretty small. Therefore I'll choose to use a 32K intermediate buffer which will accommodate about 1sec of audio data. My IRQ is timer based so I will need to decide upon a reasonable period. As the period seems to dictate the ability for the middle layer to see the status of the buffer I'll use 4 periods allowing a 250ms granularity in my intermediate buffer.
I'm assuming that the snd_pcm_hardware struct has a min AND max period_bytes and periods because the framesize can vary if your hardware supports multiple configurations. As mine only supports 8Khz 16bit stereo, my min/max values should all be the same correct?
So using the above info my struct should look like:
static snd_pcm_hardware_t snd_my_playback_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_U16_BE, .rates = SNDRV_PCM_RATE_8000, .rate_min = 8000, .rate_max = 8000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 32768, .period_bytes_min = 8192, .period_bytes_max = 8192, .periods_min = 4, .periods_max = 4, };
Does this seem reasonable?
Because I'm using a 32K intermediate buffer I'll preallocate it with snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, snd_dma_continuous_data(GFP_KERNEL), 0, 32768);
I want to make sure I'm implementing things properly for full duplex capability. I'm creating the pcm device via:
snd_pcm_new(card, "my dev", 0, 1 /* play streams */, 1 /* capt streams */, &pcm)
If I understand this preallocation a 32K DMAable buffer will be allocated by ALSA for each stream my device can play/capture (= 2 streams, 1 playback and 1 capture). I can obtain the buffer pre-allocated from the runtime data in the prepare callback and use that buffer as my intermediate buffer correct? I don't have a great grasp of the 'prepare' callback yet, but because thats the first time you've got access to valid format/buffer info I'll assume that each time 'prepare' is called your PCM buffer/hardware/stream should be initialized.
If I understand correctly the pointer callback should return the num of frames processed in the 'intermediate buffer'. Because this is a virtual ring buffer, I guess I should simply return the offset in the buffer (divided by my bytes_per_frame which is 4) that I last pulled data from?
I've looked at the example dealing with the hw interrupt and period_elapsed in http://www.alsa-project.org/~iwai/writing-an-alsa-driver/x773.htm. The example doesn't specify where it was getting runtime pointer from so I am assuming that I'll need to keep a pointer to each (playback and capture) substream so that I can call period_elapsed appropriately for each if they are concurrent?
Thanks for all the help. I've got rudimentary playback working but I'm pretty sure I'm not implementing the period_elapsed and the pointer callback correctly as the relatively simple playback app I'm using sits for a few seconds after playback of the 2 sec test sample I'm using completes.
Tim
At Thu, 19 Apr 2007 14:30:29 -0700 (PDT), Tim Harvey wrote:
--- Takashi Iwai tiwai@suse.de wrote:
The "period" defines the frequency to update the status, usually via the invokation of interrupts. The "period_size" defines the frame sizes corresponding to the "period time". This term corresponds to the "fragment size" on OSS. On major sound hardwares, a ring-buffer is divided to several parts and an irq is issued on each boundary. The period_size defines the size of this chunk.
On some hardwares, the irq is controlled on the basis of a timer. In this case, the period is defined as the timer frequency to invoke an irq.
My system has a hardware buffer of 4096 bytes (so I will use an intermediate buffer ) and is fixed at 8KHz 16bit stereo. The 4K hw buffer would accommodate 128ms of data which is pretty small. Therefore I'll choose to use a 32K intermediate buffer which will accommodate about 1sec of audio data. My IRQ is timer based so I will need to decide upon a reasonable period. As the period seems to dictate the ability for the middle layer to see the status of the buffer I'll use 4 periods allowing a 250ms granularity in my intermediate buffer.
I'm assuming that the snd_pcm_hardware struct has a min AND max period_bytes and periods because the framesize can vary if your hardware supports multiple configurations. As mine only supports 8Khz 16bit stereo, my min/max values should all be the same correct?
So using the above info my struct should look like:
static snd_pcm_hardware_t snd_my_playback_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_MMAP_VALID), .formats = SNDRV_PCM_FMTBIT_U16_BE, .rates = SNDRV_PCM_RATE_8000, .rate_min = 8000, .rate_max = 8000, .channels_min = 2, .channels_max = 2, .buffer_bytes_max = 32768, .period_bytes_min = 8192, .period_bytes_max = 8192, .periods_min = 4, .periods_max = 4, };
Does this seem reasonable?
Should be OK, but maybe more finer periods would be better, I guess. But this depends on the irq source you can use.
Because I'm using a 32K intermediate buffer I'll preallocate it with snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, snd_dma_continuous_data(GFP_KERNEL), 0, 32768);
I want to make sure I'm implementing things properly for full duplex capability. I'm creating the pcm device via:
snd_pcm_new(card, "my dev", 0, 1 /* play streams */, 1 /* capt streams */, &pcm)
If I understand this preallocation a 32K DMAable buffer will be allocated by ALSA for each stream my device can play/capture (= 2 streams, 1 playback and 1 capture). I can obtain the buffer pre-allocated from the runtime data in the prepare callback and use that buffer as my intermediate buffer correct?
If you call snd_pcm_lib_malloc_pages() in hw_params callback (and snd_pcm_lib_free_pages() in hw_free callback). Otherwise, the pre-allocated buffer won't be assigned to the stream at all.
I don't have a great grasp of the 'prepare' callback yet, but because thats the first time you've got access to valid format/buffer info I'll assume that each time 'prepare' is called your PCM buffer/hardware/stream should be initialized.
If I understand correctly the pointer callback should return the num of frames processed in the 'intermediate buffer'. Because this is a virtual ring buffer, I guess I should simply return the offset in the buffer (divided by my bytes_per_frame which is 4) that I last pulled data from?
Exactly. It's an offset between 0 and (buffer_size-1).
I've looked at the example dealing with the hw interrupt and period_elapsed in http://www.alsa-project.org/~iwai/writing-an-alsa-driver/x773.htm. The example doesn't specify where it was getting runtime pointer from so I am assuming that I'll need to keep a pointer to each (playback and capture) substream so that I can call period_elapsed appropriately for each if they are concurrent?
Yes.
Takashi
On Sun, Apr 15, 2007 at 06:56:56PM -0700, Tim Harvey wrote:
I still don't understand what 'period_size' and a 'period' is?
A period is the interval between updates of the buffer. There must be at least two periods in a buffer to avoid clicks and pops. During playback, call snd_pcm_period_elapsed() each time your device has played a period. ALSA will then update that part of the buffer with new samples. When your device has played the last period in the buffer, it must be set up to start playing from the start of the buffer again.
I've also noticed that I seem to 'have' to call snd_pcm_lib_preallocate_pages_for_all or I get an error when ALSA tries to use the drive. I assume that I need to 'preallocate' or allocate manually upon each 'open' (or another callback?). Is this 'preallocate' going to allocate 1 buffer or 1 buffer per playback/capture?
Please see the document "Writing an ALSA driver".
participants (3)
-
Rask Ingemann Lambertsen
-
Takashi Iwai
-
Tim Harvey