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 _______________________________________________ Alsa-devel mailing list Alsa-devel@xxxxxxxxxxxxxxxx http://mailman.alsa-project.org/mailman/listinfo/alsa-devel