root/src/add-ons/kernel/drivers/audio/null/null_hardware.c
/*
 * Copyright 2007 Haiku Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Bek, host.haiku@gmx.de
 */
#include "driver.h"


status_t
null_hw_create_virtual_buffers(device_stream_t* stream, const char* name)
{
        uint32 i;
        int buffer_size;
        int area_size;
        uint8* buffer;

        buffer_size = stream->num_channels
                                * format_to_sample_size(stream->format)
                                * stream->buffer_length;
        buffer_size = (buffer_size + 127) & (~127);

        area_size = buffer_size * stream->num_buffers;
        area_size = (area_size + B_PAGE_SIZE - 1) & (~(B_PAGE_SIZE -1));

        stream->buffer_area = create_area("null_audio_buffers", (void**)&buffer,
                                                        B_ANY_KERNEL_ADDRESS, area_size,
                                                        B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA);
        if (stream->buffer_area < B_OK)
                return stream->buffer_area;

        // Get the correct address for setting up the buffers
        // pointers being passed back to userland
        for (i = 0; i < stream->num_buffers; i++)
                stream->buffers[i] = buffer + (i * buffer_size);

        stream->buffer_ready_sem = create_sem(0, name);
        return B_OK;
}


static int32
null_fake_interrupt(void* cookie)
{
        // This thread is supposed to fake the interrupt
        // handling done in communication with the
        // hardware usually. What it does is nearly the
        // same like all soundrivers, get the interrupt
        // exchange the buffer pointer and update the
        // time information. Instead of exiting, we wait
        // until the next fake interrupt appears.
        bigtime_t sleepTime;
        device_t* device = (device_t*) cookie;
        int sampleRate;

        switch (device->playback_stream.rate) {
                case B_SR_48000:
                        sampleRate = 48000;
                        break;
                case B_SR_44100:
                default:
                        sampleRate = 44100;
                        break;
        }

        // The time between until we get a new valid buffer
        // from our soundcard: buffer_length / samplerate
        sleepTime = (device->playback_stream.buffer_length * 1000000LL) / sampleRate;

        while (device->running) {
                cpu_status status;
                status = disable_interrupts();
                acquire_spinlock(&device->playback_stream.lock);
                device->playback_stream.real_time = system_time();
                device->playback_stream.frames_count += device->playback_stream.buffer_length;
                device->playback_stream.buffer_cycle = (device->playback_stream.buffer_cycle +1) % device->playback_stream.num_buffers;
                release_spinlock(&device->playback_stream.lock);

                // TODO: Create a simple sinus wave, so that recording from
                // the virtual device actually returns something useful
                acquire_spinlock(&device->record_stream.lock);
                device->record_stream.real_time = device->playback_stream.real_time;
                device->record_stream.frames_count += device->record_stream.buffer_length;
                device->record_stream.buffer_cycle = (device->record_stream.buffer_cycle +1) % device->record_stream.num_buffers;
                release_spinlock(&device->record_stream.lock);

                restore_interrupts(status);

                release_sem_etc(device->playback_stream.buffer_ready_sem, 1, B_DO_NOT_RESCHEDULE);
                release_sem_etc(device->record_stream.buffer_ready_sem, 1, B_DO_NOT_RESCHEDULE);
                snooze(sleepTime);
        }
        return B_OK;
}


status_t
null_start_hardware(device_t* device)
{
        dprintf("null_audio: %s spawning fake interrupter\n", __func__);
        device->running = true;
        device->interrupt_thread = spawn_kernel_thread(null_fake_interrupt, "null_audio interrupter",
                                                                B_REAL_TIME_PRIORITY, (void*)device);
        return resume_thread(device->interrupt_thread);
}


void
null_stop_hardware(device_t* device)
{
        device->running = false;
}