浅析ac97声卡intel8x0的DMA内存substream->dma_buffer什么时候被赋值的
对于snd-intel8x0.ko驱动,为设备直接申请可以供DMA使用的DDR内存空间到substream->dma_buffer
insmod snd-intel8x0.ko
==> sys_init_module
==> alsa_card_intel8x0_init
==> __pci_register_driver
==> driver_register
==> bus_add_driver
==> driver_attach
==> driver_probe_device
==> really_probe
==> pci_device_probe
==> local_pci_probe
==> snd_intel8x0_probe
==> snd_intel8x0_pcm
==> snd_intel8x0_pcm1
==> snd_pcm_lib_preallocate_pages_for_all
==> snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
snd_dma_pci_data(chip->pci),
rec->prealloc_size, rec->prealloc_max_size);
这里rec就是snd_intel8x0_pcm中[luther.gliethttp]
intel_pcms[]对应的数据参数,如下:
static struct ich_pcm_table intel_pcms[] __devinitdata = {
{
.playback_ops = &snd_intel8x0_playback_ops,
.capture_ops = &snd_intel8x0_capture_ops,
.prealloc_size = 64 * 1024, // 预先申请DMA空间为64k[luther.gliethttp]
.prealloc_max_size = 128 * 1024,
},
......
};
/**
* snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continous memory type (all substreams)
* @pcm: the pcm instance
* @type: DMA type (SNDRV_DMA_TYPE_*)
* @data: DMA type dependant data
* @size: the requested pre-allocation size in bytes
* @max: the max. allowed pre-allocation size
*
* Do pre-allocation to all substreams of the given pcm for the
* specified DMA type.
*
* Returns zero if successful, or a negative error code on failure.
*/
int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
int type, void *data,
size_t size, size_t max)
{
struct snd_pcm_substream *substream;
int stream, err;
for (stream = 0; stream < 2; stream++)
for (substream = pcm->streams[stream].substream; substream; substream = substream->next)
if ((err = snd_pcm_lib_preallocate_pages(substream, type, data, size, max)) < 0)
return err;
return 0;
}
==> snd_pcm_lib_preallocate_pages
/**
* snd_pcm_lib_preallocate_pages - pre-allocation for the given DMA type
* @substream: the pcm substream instance
* @type: DMA type (SNDRV_DMA_TYPE_*)
* @data: DMA type dependant data
* @size: the requested pre-allocation size in bytes
* @max: the max. allowed pre-allocation size
*
* Do pre-allocation for the given DMA buffer type.
*
* When substream->dma_buf_id is set, the function tries to look for
* the reserved buffer, and the buffer is not freed but reserved at
* destruction time. The dma_buf_id must be unique for all systems
* (in the same DMA buffer type) e.g. using snd_dma_pci_buf_id().
*
* Returns zero if successful, or a negative error code on failure.
*/
int snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream,
int type, struct device *data,
size_t size, size_t max)
{
substream->dma_buffer.dev.type = type;
substream->dma_buffer.dev.dev = data;
return snd_pcm_lib_preallocate_pages1(substream, size, max);
}
==> snd_pcm_lib_preallocate_pages1
/*
* pre-allocate the buffer and create a proc file for the substream
*/
static int snd_pcm_lib_preallocate_pages1(struct snd_pcm_substream *substream,
size_t size, size_t max)
{
if (size > 0 && preallocate_dma && substream->number < maximum_substreams)
preallocate_pcm_pages(substream, size);
if (substream->dma_buffer.bytes > 0)
substream->buffer_bytes_max = substream->dma_buffer.bytes;
substream->dma_max = max;
preallocate_info_init(substream);
return 0;
}
==> preallocate_pcm_pages完成实际的内存申请动作[luther.gliethttp]
/*
* try to allocate as the large pages as possible.
* stores the resultant memory size in *res_size.
*
* the minimum size is snd_minimum_buffer. it should be power of 2.
*/
static int preallocate_pcm_pages(struct snd_pcm_substream *substream, size_t size)
{
struct snd_dma_buffer *dmab = &substream->dma_buffer; // dmab指向substream->dma_buffer
int err;
// 对于snd-intel8x0.ko驱动,该size值在intel_pcms[]中定义,比如这里playback放音流
// 预留DMA内存大小为64k[luther.gliethttp]
/* already reserved? */
if (snd_dma_get_reserved_buf(dmab, substream->dma_buf_id) > 0) {
if (dmab->bytes >= size)
return 0; /* yes */
/* no, free the reserved block */
snd_dma_free_pages(dmab);
dmab->bytes = 0;
}
do {
if ((err = snd_dma_alloc_pages(dmab->dev.type, dmab->dev.dev,
size, dmab)) < 0) { // 根据dmab->dev.type类型,在不同的地方申请DMA缓冲
if (err != -ENOMEM)
return err; /* fatal error */
} else
return 0;
size >>= 1;
} while (size >= snd_minimum_buffer);
dmab->bytes = 0; /* tell error */
return 0;
}
==> snd_dma_alloc_pages
/**
* snd_dma_alloc_pages - allocate the buffer area according to the given type
* @type: the DMA buffer type
* @device: the device pointer
* @size: the buffer size to allocate
* @dmab: buffer allocation record to store the allocated data
*
* Calls the memory-allocator function for the corresponding
* buffer type.
*
* Returns zero if the buffer with the given size is allocated successfuly,
* other a negative value at error.
*/
int snd_dma_alloc_pages(int type, struct device *device, size_t size,
struct snd_dma_buffer *dmab)
{
if (WARN_ON(!size))
return -ENXIO;
if (WARN_ON(!dmab))
return -ENXIO;
dmab->dev.type = type;
dmab->dev.dev = device;
dmab->bytes = 0;
switch (type) {
case SNDRV_DMA_TYPE_CONTINUOUS:
dmab->area = snd_malloc_pages(size, (unsigned long)device);
dmab->addr = 0;
break;
#ifdef CONFIG_HAS_DMA
case SNDRV_DMA_TYPE_DEV: // 对于snd-intel8x0.ko驱动,为设备直接申请可以供DMA使用的DDR内存空间
// 因为dmab指向substream->dma_buffer,所以substream->dma_buffer的area获得了数据
dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr);
break;
#endif
#ifdef CONFIG_SND_DMA_SGBUF
case SNDRV_DMA_TYPE_DEV_SG:
snd_malloc_sgbuf_pages(device, size, dmab, NULL);
break;
#endif
default:
printk(KERN_ERR "snd-malloc: invalid device type %d\n", type);
dmab->area = NULL;
dmab->addr = 0;
return -ENXIO;
}
if (! dmab->area)
return -ENOMEM;
dmab->bytes = size;
return 0;
}
阅读(2331) | 评论(0) | 转发(0) |