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