xref: /haiku/src/add-ons/kernel/drivers/audio/null/null_hardware.c (revision dd2a1e350b303b855a50fd64e6cb55618be1ae6a)
1 /*
2  * Copyright 2007 Haiku Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Bek, host.haiku@gmx.de
7  */
8 #include "driver.h"
9 
10 
11 status_t
12 null_hw_create_virtual_buffers(device_stream_t* stream, const char* name)
13 {
14 	uint32 i;
15 	int buffer_size;
16 	int area_size;
17 	uint8* buffer;
18 
19 	buffer_size = stream->num_channels
20 				* format_to_sample_size(stream->format)
21 				* stream->buffer_length;
22 	buffer_size = (buffer_size + 127) & (~127);
23 
24 	area_size = buffer_size * stream->num_buffers;
25 	area_size = (area_size + B_PAGE_SIZE - 1) & (~(B_PAGE_SIZE -1));
26 
27 	stream->buffer_area = create_area("null_audio_buffers", (void**)&buffer,
28 							B_ANY_KERNEL_ADDRESS, area_size,
29 							B_CONTIGUOUS, B_READ_AREA | B_WRITE_AREA);
30 	if (stream->buffer_area < B_OK)
31 		return stream->buffer_area;
32 
33 	// Get the correct address for setting up the buffers
34 	// pointers being passed back to userland
35 	for (i = 0; i < stream->num_buffers; i++)
36 		stream->buffers[i] = buffer + (i * buffer_size);
37 
38 	stream->buffer_ready_sem = create_sem(0, name);
39 	return B_OK;
40 }
41 
42 
43 static int32
44 null_fake_interrupt(void* cookie)
45 {
46 	// This thread is supposed to fake the interrupt
47 	// handling done in communication with the
48 	// hardware usually. What it does is nearly the
49 	// same like all soundrivers, get the interrupt
50 	// exchange the buffer pointer and update the
51 	// time information. Instead of exiting, we wait
52 	// until the next fake interrupt appears.
53 	bigtime_t sleepTime;
54 	device_t* device = (device_t*) cookie;
55 	int sampleRate;
56 
57 	switch (device->playback_stream.rate) {
58 		case B_SR_48000:
59 			sampleRate = 48000;
60 			break;
61 		case B_SR_44100:
62 		default:
63 			sampleRate = 44100;
64 			break;
65 	}
66 
67 	// The time between until we get a new valid buffer
68 	// from our soundcard: buffer_length / samplerate
69 	sleepTime = (device->playback_stream.buffer_length * 1000000LL) / sampleRate;
70 
71 	while (device->running) {
72 		cpu_status status;
73 		status = disable_interrupts();
74 		acquire_spinlock(&device->playback_stream.lock);
75 		device->playback_stream.real_time = system_time();
76 		device->playback_stream.frames_count += device->playback_stream.buffer_length;
77 		device->playback_stream.buffer_cycle = (device->playback_stream.buffer_cycle +1) % device->playback_stream.num_buffers;
78 		release_spinlock(&device->playback_stream.lock);
79 
80 		// TODO: Create a simple sinus wave, so that recording from
81 		// the virtual device actually returns something useful
82 		acquire_spinlock(&device->record_stream.lock);
83 		device->record_stream.real_time = device->playback_stream.real_time;
84 		device->record_stream.frames_count += device->record_stream.buffer_length;
85 		device->record_stream.buffer_cycle = (device->record_stream.buffer_cycle +1) % device->record_stream.num_buffers;
86 		release_spinlock(&device->record_stream.lock);
87 
88 		restore_interrupts(status);
89 
90 		release_sem_etc(device->playback_stream.buffer_ready_sem, 1, B_DO_NOT_RESCHEDULE);
91 		release_sem_etc(device->record_stream.buffer_ready_sem, 1, B_DO_NOT_RESCHEDULE);
92 		snooze(sleepTime);
93 	}
94 	return B_OK;
95 }
96 
97 
98 status_t
99 null_start_hardware(device_t* device)
100 {
101 	dprintf("null_audio: %s spawning fake interrupter\n", __func__);
102 	device->running = true;
103 	device->interrupt_thread = spawn_kernel_thread(null_fake_interrupt, "null_audio interrupter",
104 								B_REAL_TIME_PRIORITY, (void*)device);
105 	return resume_thread(device->interrupt_thread);
106 }
107 
108 
109 void
110 null_stop_hardware(device_t* device)
111 {
112 	device->running = false;
113 }
114 
115