[alsa-devel] mapping externally allocated Scatter Gather DMA buffers

Manu Abraham abraham.manu at gmail.com
Fri Nov 12 10:29:50 CET 2010


On Fri, Nov 12, 2010 at 1:35 PM, Jaroslav Kysela <perex at perex.cz> wrote:
> On Fri, 12 Nov 2010, Manu Abraham wrote:
>
>> On Thu, Nov 11, 2010 at 6:04 PM, Manu Abraham <abraham.manu at gmail.com>
>> wrote:
>>>
>>> On Thu, Nov 11, 2010 at 4:00 PM, Jaroslav Kysela <perex at perex.cz> wrote:
>>>>
>>>> On Thu, 11 Nov 2010, Manu Abraham wrote:
>>>>
>>>>> On Wed, Nov 10, 2010 at 11:35 PM, Jaroslav Kysela <perex at perex.cz>
>>>>> wrote:
>>>
>>>>>
>>>>> I adapted the whole thing to make the buffersize divided amongst the
>>>>> XS2D_BUFFERS (8):
>>>>
>>>> Probably yes.
>>>>
>>>>> I hope the mapping of the buffers is okay ? It looks thus, now ..
>>>>
>>>> From information you sent me about hw privately, I think that the
>>>> period_bytes must be 4096 or multiple of this value with minumum count
>>>> of
>>>> periods 8 (or multiple of 8). Otherwise you get a non-continuous memory
>>>> area
>>>> (the hw uses only portion of system memory page, thus there'll be gaps).
>>>> The
>>>> problem is that we have MMAP_COMPLEX mode, but no application can handle
>>>> (does not implement) this mmap mode and I'm not sure, if we can even
>>>> describe the DMA buffer size layout for this case for your specific hw.
>>>>
>>>> I would use:
>>>>
>>>>        snd_pcm_hw_constraint_step(runtime, 0,
>>>> SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
>>>>                                  4096);
>>>>
>>>>
>>>>        snd_pcm_hw_constraint_step(runtime, 0,
>>>> SNDRV_PCM_HW_PARAM_PERIODS,
>>>>                                   8);
>>>>
>>>> And define periods_min = 8 (max to multiple 8 - choose your max limit)
>>>> and
>>>> period_bytes_min to 4096 (max to multiple 4096 - choose your max limit).
>>>>
>>>> Note that -EIO means that your driver does not called
>>>> snd_pcm_period_elapsed() and/or the pointer callback returns wrong
>>>> position
>>>> to the audio ring buffer.
>>>
>>>
>>>
>>> Ok, modified it, also added in code to acquire the stream, It's a bit
>>> more complete now.
>>> The crazy part that I do see now, is that I see an inconsistent lock
>>> state with the hda_intel
>>> driver which is the soundcard on my system. But I don't understand why
>>> the locking on it
>>> has to become inconsistent on loading this driver.
>>
>>
>> Ok, I found the reason: I was passing a single page for the
>> page_table, which I guess corrupted the whole stack !!
>>
>> Eventually, I fixed the same. but ran into another issue ? Should
>> trigger not sleep or something that way ?
>> Currently, I see the issue as follows in the log, but if the
>> ops->run() callback is commented out I don't get that
>> weird message/error about lock states.
>>
>> Now, ops->run(), ie xs2dtl_run() is called with other other streams,
>> such as an MPEG stream, where it doesn't
>> show any issues.
>>
>> Any idea, why uncommenting ops->run() produces that message ? Should
>> trigger not sleep ? Confused ...
>
> Trigger must be fast without any sleeping. Only fast spinlocks are allowed.
> If you need sleep, just activate a tasklet and do the start job in the
> tasklet or a thread.


Ok, I have now a tasklet to handle the commands. But still I do have
the same issue.
if i comment out ops->run() things do look sane. But with the tasklet,
also added in,
I can see the same error, but almost immediately, the machine freezes
completely.
I see quite a lot of junk in the syslog, looks like there has been
memory corruption.
Any idea, what could be going wrong ?


static int saa7231_hw_params(struct snd_pcm_substream *pcm, struct
snd_pcm_hw_params *params)
{
	struct saa7231_audio *audio	= snd_pcm_substream_chip(pcm);
	struct saa7231_dev *saa7231	= audio->saa7231;
	struct snd_pcm_runtime *rt	= pcm->runtime;

	struct saa7231_dmabuf *buffer;
	struct saa7231_stream *stream;
	struct saa7231_dmabuf *dmabuf;

	struct page **ptable;

	void *mem;
	void *dma_area;

	int i, j, pages, bytes, periods, bufsiz, idx;

#define MAX_ENTRIES_PER_PAGE		(PAGE_SIZE / 8)
#define PAGES_PER_XS2D(__pages)		(__pages / XS2D_BUFFERS)
#define BUFSIZE_PER_XS2D(__size)	(__size / XS2D_BUFFERS)

	dprintk(SAA7231_DEBUG, 1, "DEBUG: ()");

	periods = params_periods(params);
	bytes   = params_period_bytes(params);
	bufsiz  = params_buffer_bytes(params);
	pages	= snd_sgbuf_aligned_pages(bufsiz);

	audio->bufsize = bufsiz;

	dprintk(SAA7231_DEBUG, 1, "bufsiz=%d periods=%d bytes=%d pages=%d",
		bufsiz,
		periods,
		bytes,
		pages);

	/* enable stream */
	/* initializing 8 buffer with "pages" pages each .. */
	stream = saa7231_stream_init(saa7231, AUDIO_CAPTURE, ADAPTER_INT, 0,
(pages / XS2D_BUFFERS));
	if (!stream) {
		dprintk(SAA7231_ERROR, 1, "ERROR: Registering stream");
		return -ENOMEM;
	}
	audio->stream = stream;
	buffer = stream->dmabuf;
	saa7231_add_irqevent(saa7231, 43, SAA7231_EDGE_RISING,
saa7231_audio_evhandler, "AS2D_AVIS");
	dprintk(SAA7231_DEBUG, 1, "Mapping %d buffers with %d pages each",
XS2D_BUFFERS, PAGES_PER_XS2D(pages));

	dprintk(SAA7231_DEBUG, 1, "Page Table array size=%d", pages);
	ptable = kzalloc((sizeof (struct page) * pages), GFP_KERNEL);
	if (!ptable) {
		dprintk(SAA7231_ERROR, 1, "ERROR: No memory to allocate virtual map");
		return -ENOMEM;
	}
	audio->ptable = ptable;
	idx = 0;

	for (i = 0; i < XS2D_BUFFERS; i ++) {
		dmabuf = &buffer[i];
		mem = dmabuf->vmalloc;
		for (j = 0; j < PAGES_PER_XS2D(pages); j++) {
			BUG_ON(idx > pages);
			dprintk(SAA7231_DEBUG, 1, "Mapping Page:%d from XS2D_BUFFER:%d to
PTA Offset:%d", j, i, idx);
			ptable[idx] = virt_to_page(mem);
			mem += PAGE_SIZE;
			idx += 1;
		}
	}

	dma_area = vmap(ptable, pages, VM_MAP, PAGE_KERNEL);
	rt->dma_area	= dma_area;
	rt->dma_bytes	= pages * PAGE_SIZE;
	rt->dma_addr	= 0;

	return 0;
}

static void saa7231_cmd_tasklet(unsigned long data)
{
	struct saa7231_audio *audio	= (struct saa7231_audio *) data;
	struct saa7231_dev *saa7231	= audio->saa7231;
	struct saa7231_stream *stream	= audio->stream;
	struct stream_ops *ops		= &stream->ops;
	int cmd				= audio->cmd;

	int err = 0;

	switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
		dprintk(SAA7231_DEBUG, 1, "Trying to START stream, cmd=%d", cmd);
		if (ops->run) {
			err = ops->run(stream);
			if (err) {
				dprintk(SAA7231_ERROR, 1, "ERROR: starting stream, err=%d", err);
//				return -EIO;
			}
		}
		break;
        case SNDRV_PCM_TRIGGER_STOP:
		dprintk(SAA7231_DEBUG, 1, "Trying to STOP stream, cmd=%d", cmd);
		if (ops->stop) {
			err = ops->stop(stream);
			if (err) {
				dprintk(SAA7231_ERROR, 1, "ERROR: stopping stream, err=%d", err);
//				return -EIO;
			}
		}
		break;
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
        case SNDRV_PCM_TRIGGER_SUSPEND:
		dprintk(SAA7231_DEBUG, 1, "Trying to PAUSE stream, cmd=%d", cmd);
		if (ops->pause) {
			err = ops->pause(stream);
			if (err) {
				dprintk(SAA7231_ERROR, 1, "ERROR: pausing stream, err=%d", err);
//				return -EIO;
			}
		}
		break;
	default:
		dprintk(SAA7231_DEBUG, 1, "Unknown command, cmd=%d", cmd);
		snd_BUG();
//		return -EINVAL;
	}
}

/*
 * saa7231_capture_trigger()
 *
 * This callback is called when the PCM is started, stoppped or paused.
 */
static int saa7231_capture_trigger(struct snd_pcm_substream *pcm, int cmd)
{
	struct saa7231_audio *audio	= snd_pcm_substream_chip(pcm);
	struct saa7231_dev *saa7231	= audio->saa7231;

	dprintk(SAA7231_DEBUG, 1, "Scheduling cmd tasklet with cmd=%d", cmd);
	audio->cmd = cmd;
	tasklet_schedule(&audio->cmd_tasklet);
	return 0;
}

static snd_pcm_uframes_t saa7231_capture_pointer(struct snd_pcm_substream *pcm)
{
	struct saa7231_audio *audio	= snd_pcm_substream_chip(pcm);
	struct saa7231_dev *saa7231	= audio->saa7231;

	dprintk(SAA7231_DEBUG, 1, "DEBUG:()");

	return 0; //bytes_to_frames(rt, played);
}

int saa7231_alsa_init(struct saa7231_dev *saa7231)
{
	int err;
	struct snd_card *card;
	struct saa7231_audio *audio;

	struct saa7231_config *config	= saa7231->config;
	struct card_desc *desc		= config->desc;

	dprintk(SAA7231_DEBUG, 1, "Initializing Audio ..");

	audio = kzalloc(sizeof (struct saa7231_audio), GFP_KERNEL);
	if (!audio) {
		dprintk(SAA7231_ERROR, 1, "ERROR: Out of memory, audio=NULL");
		return -ENOMEM;
	}

	err = snd_card_create(index[saa7231->num], id[saa7231->num],
THIS_MODULE, 0, &card);
	if (err < 0) {
		dprintk(SAA7231_ERROR, 1, "snd_card_create() failed, err=%d", err);
		return err;
	}
	saa7231->audio	= audio;
	audio->saa7231	= saa7231;
	audio->card	= card;

	err = snd_saa7231_pcm_init(audio);
	if (err) {
		dprintk(SAA7231_ERROR, 1, "PCM Initialization failed, err=%d", err);
		return err;
	}
	err = snd_saa7231_mixer_init(audio);
	if (err) {
		dprintk(SAA7231_ERROR, 1, "Mixer initialization failed, err=%d", err);
		return err;
	}

	snd_card_set_dev(card, &saa7231->pdev->dev);
	strcpy(card->driver, "SAA7231");
	sprintf(card->shortname, "%s-%d", desc->product, saa7231->num);
	sprintf(card->mixername, "%s", saa7231->name);
	sprintf(card->longname, "%s %s (%s) @ 0x%p, irq %i",
		desc->vendor,
		desc->product,
		card->shortname,
		saa7231->mmio1,
		saa7231->pdev->irq);

	err = snd_card_register(card);
	if (err) {
		dprintk(SAA7231_ERROR, 1, "Sound Card registration failed, err=%d", err);
		return err;
	}
	tasklet_init(&audio->cmd_tasklet, saa7231_cmd_tasklet, (unsigned long) audio);
	return 0;
}


testbox ~ # cat /var/log/messages
ginInfo-AllowEmptySettings.X-KDE-PluginInfo-Author
2X-KDE-PluginInfo-Category
0X-KDE-PluginInfo-Depends
                         ,X-KDE-PluginInfo-Email
BX-KDE-PluginInfo-EnabledByDefault4X-KDE-PluginInfo-Immutable0X-KDE-PluginInfo-License
*X-KDE-PluginInfo-Name
0X-KDE-PluginInfo-Version
0X-KDE-PluginInfo-Website
X-KDE-PriorityX-KDE-Protocol
X-KDE-Protocols
               X-KDE-Read(X-KDE-ResourceFamily
$X-KDE-ResourceType
<X-KDE-SolidBackendInfo-Version
&X-KDE-StartupNotify&X-KDE-SubstituteUID<X-KDE-System-Settings-Category
JX-KDE-System-Settings-Parent-Category
X-KDE-Type
,X-KDE-UA-DYNAMIC-ENTRY▒X-KDE-UA-FULL
▒X-KDE-UA-NAME
 X-KDE-UA-SYSNAME
&X-KDE-UA-SYSRELEASE
▒X-KDE-UA-TAG
 X-KDE-UA-VERSION
X-KDE-Username
▒X-KDE-Version▒X-KDE-WMClass
▒X-KDE-WeightX-KDE-Write,X-KDE-okularAPIVersion>X-KDE-okularHasInternalSettingsX-KIPI-Id
"X-KIPI-Interfaces
                   X-KIPI-Mimetypes
                                   $X-KIPI-ReqFeatures

(X-Kate-MajorProfiles


Thanks,
Manu


More information about the Alsa-devel mailing list