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