Some chips have their own hardware buffers and the DMA transfer from the host memory is not available. In such a case, you need to either 1) copy/set the audio data directly to the external hardware buffer, or 2) make an intermediate buffer and copy/set the data from it to the external hardware buffer in interrupts (or in tasklets, preferably).
        The first case works fine if the external hardware buffer is large
      enough.  This method doesn't need any extra buffers and thus is
      more effective. You need to define the
      copy and
      silence callbacks for 
      the data transfer. However, there is a drawback: it cannot
      be mmapped. The examples are GUS's GF1 PCM or emu8000's
      wavetable PCM. 
      
The second case allows for mmap on the buffer, although you have to handle an interrupt or a tasklet to transfer the data from the intermediate buffer to the hardware buffer. You can find an example in the vxpocket driver.
        Another case is when the chip uses a PCI memory-map
      region for the buffer instead of the host memory. In this case,
      mmap is available only on certain architectures like the Intel one.
      In non-mmap mode, the data cannot be transferred as in the normal
      way. Thus you need to define the copy and
      silence callbacks as well, 
      as in the cases above. The examples are found in
      rme32.c and rme96.c. 
      
        The implementation of the copy and
        silence callbacks depends upon 
        whether the hardware supports interleaved or non-interleaved
        samples. The copy callback is
        defined like below, a bit 
        differently depending whether the direction is playback or
        capture: 
        
  static int playback_copy(struct snd_pcm_substream *substream, int channel,
               snd_pcm_uframes_t pos, void *src, snd_pcm_uframes_t count);
  static int capture_copy(struct snd_pcm_substream *substream, int channel,
               snd_pcm_uframes_t pos, void *dst, snd_pcm_uframes_t count);
          
        In the case of interleaved samples, the second argument
      (channel) is not used. The third argument
      (pos) points the 
      current position offset in frames. 
      
The meaning of the fourth argument is different between playback and capture. For playback, it holds the source data pointer, and for capture, it's the destination data pointer.
The last argument is the number of frames to be copied.
        What you have to do in this callback is again different
        between playback and capture directions. In the
        playback case, you copy the given amount of data
        (count) at the specified pointer
        (src) to the specified offset
        (pos) on the hardware buffer. When
        coded like memcpy-like way, the copy would be like: 
        
  my_memcpy(my_buffer + frames_to_bytes(runtime, pos), src,
            frames_to_bytes(runtime, count));
          
        For the capture direction, you copy the given amount of
        data (count) at the specified offset
        (pos) on the hardware buffer to the
        specified pointer (dst). 
        
  my_memcpy(dst, my_buffer + frames_to_bytes(runtime, pos),
            frames_to_bytes(runtime, count));
          Note that both the position and the amount of data are given in frames.
In the case of non-interleaved samples, the implementation will be a bit more complicated.
        You need to check the channel argument, and if it's -1, copy
      the whole channels. Otherwise, you have to copy only the
      specified channel. Please check
      isa/gus/gus_pcm.c as an example. 
      
        The silence callback is also
        implemented in a similar way. 
        
  static int silence(struct snd_pcm_substream *substream, int channel,
                     snd_pcm_uframes_t pos, snd_pcm_uframes_t count);
          
        The meanings of arguments are the same as in the
      copy 
      callback, although there is no src/dst
      argument. In the case of interleaved samples, the channel
      argument has no meaning, as well as on
      copy callback.  
      
        The role of silence callback is to
        set the given amount 
        (count) of silence data at the
        specified offset (pos) on the hardware
        buffer. Suppose that the data format is signed (that is, the
        silent-data is 0), and the implementation using a memset-like
        function would be like: 
        
  my_memcpy(my_buffer + frames_to_bytes(runtime, pos), 0,
            frames_to_bytes(runtime, count));
          
        In the case of non-interleaved samples, again, the
      implementation becomes a bit more complicated. See, for example,
      isa/gus/gus_pcm.c.