1/*
2 * BeOS Driver for Intel ICH AC'97 Link interface
3 *
4 * Copyright (c) 2002, Marcus Overhagen <marcus@overhagen.de>
5 *
6 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or without modification,
8 * are permitted provided that the following conditions are met:
9 *
10 * - Redistributions of source code must retain the above copyright notice,
11 *   this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright notice,
13 *   this list of conditions and the following disclaimer in the documentation
14 *   and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
25 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 *
27 */
28#include <OS.h>
29#include <MediaDefs.h>
30#include "multi_audio.h"
31#include "ac97_multi.h"
32
33//#define DEBUG 1
34
35#include "debug.h"
36#include "ich.h"
37#include "util.h"
38#include "config.h"
39
40/*
41 *
42 * XXX!!! ALL "MIX" ioctls are not implemented by the BeOS R5, get never called :-(
43 *        The concept described by B_MULTI_SET_BUFFERS is impossible to implement
44 *        B_MULTI_GET_BUFFERS can not be handled as efficient as envisioned by the API creators
45 *
46 */
47
48static status_t list_mix_controls(multi_mix_control_info *data);
49static status_t list_mix_connections(multi_mix_connection_info *data);
50static status_t list_mix_channels(multi_mix_channel_info *data);
51static status_t get_description(multi_description *data);
52static status_t get_enabled_channels(multi_channel_enable *data);
53static status_t get_global_format(multi_format_info *data);
54static status_t get_buffers(multi_buffer_list *data);
55static status_t buffer_exchange(multi_buffer_info *data);
56static status_t buffer_force_stop();
57
58static status_t list_mix_controls(multi_mix_control_info * data)
59{
60	return B_OK;
61}
62
63static status_t list_mix_connections(multi_mix_connection_info * data)
64{
65	data->actual_count = 0;
66	return B_OK;
67}
68
69static status_t list_mix_channels(multi_mix_channel_info *data)
70{
71	return B_OK;
72}
73
74multi_channel_info chans[] = {
75{  0, B_MULTI_OUTPUT_CHANNEL, 	B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 },
76{  1, B_MULTI_OUTPUT_CHANNEL, 	B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 },
77{  2, B_MULTI_INPUT_CHANNEL, 	B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 0 },
78{  3, B_MULTI_INPUT_CHANNEL, 	B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, 0 },
79{  4, B_MULTI_OUTPUT_BUS, 		B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 	B_CHANNEL_MINI_JACK_STEREO },
80{  5, B_MULTI_OUTPUT_BUS, 		B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO },
81{  6, B_MULTI_INPUT_BUS, 		B_CHANNEL_LEFT | B_CHANNEL_STEREO_BUS, 	B_CHANNEL_MINI_JACK_STEREO },
82{  7, B_MULTI_INPUT_BUS, 		B_CHANNEL_RIGHT | B_CHANNEL_STEREO_BUS, B_CHANNEL_MINI_JACK_STEREO },
83};
84
85static status_t get_description(multi_description *data)
86{
87	data->interface_version = B_CURRENT_INTERFACE_VERSION;
88	data->interface_minimum = B_CURRENT_INTERFACE_VERSION;
89
90	strcpy(data->friendly_name,"AC97 (ICH)");
91	strcpy(data->vendor_info,"Marcus Overhagen");
92
93	data->output_channel_count = 2;
94	data->input_channel_count = 2;
95	data->output_bus_channel_count = 2;
96	data->input_bus_channel_count = 2;
97	data->aux_bus_channel_count = 0;
98
99	// for each channel, starting with the first output channel,
100	// then the second, third..., followed by the first input
101	// channel, second, third, ..., followed by output bus
102	// channels and input bus channels and finally auxillary channels,
103
104	LOG(("request_channel_count = %d\n",data->request_channel_count));
105	if (data->request_channel_count >= (int)(sizeof(chans) / sizeof(chans[0]))) {
106		LOG(("copying data\n"));
107		memcpy(data->channels,&chans,sizeof(chans));
108	}
109
110	/* determine output rates */
111	data->output_rates = 0;
112	if (ac97_set_rate(config->ac97, AC97_PCM_FRONT_DAC_RATE, 8000))
113		data->output_rates |= B_SR_8000;
114	if (ac97_set_rate(config->ac97, AC97_PCM_FRONT_DAC_RATE, 11025))
115		data->output_rates |= B_SR_11025;
116	if (ac97_set_rate(config->ac97, AC97_PCM_FRONT_DAC_RATE, 12000))
117		data->output_rates |= B_SR_12000;
118	if (ac97_set_rate(config->ac97, AC97_PCM_FRONT_DAC_RATE, 16000))
119		data->output_rates |= B_SR_16000;
120	if (ac97_set_rate(config->ac97, AC97_PCM_FRONT_DAC_RATE, 22050))
121		data->output_rates |= B_SR_22050;
122	if (ac97_set_rate(config->ac97, AC97_PCM_FRONT_DAC_RATE, 24000))
123		data->output_rates |= B_SR_24000;
124	if (ac97_set_rate(config->ac97, AC97_PCM_FRONT_DAC_RATE, 32000))
125		data->output_rates |= B_SR_32000;
126	if (ac97_set_rate(config->ac97, AC97_PCM_FRONT_DAC_RATE, 44100))
127		data->output_rates |= B_SR_44100;
128	if (ac97_set_rate(config->ac97, AC97_PCM_FRONT_DAC_RATE, 48000))
129		data->output_rates |= B_SR_48000;
130
131	/* determine input rates */
132	data->input_rates = 0;
133	if (ac97_set_rate(config->ac97, AC97_PCM_L_R_ADC_RATE, 8000))
134		data->input_rates |= B_SR_8000;
135	if (ac97_set_rate(config->ac97, AC97_PCM_L_R_ADC_RATE, 11025))
136		data->input_rates |= B_SR_11025;
137	if (ac97_set_rate(config->ac97, AC97_PCM_L_R_ADC_RATE, 12000))
138		data->input_rates |= B_SR_12000;
139	if (ac97_set_rate(config->ac97, AC97_PCM_L_R_ADC_RATE, 16000))
140		data->input_rates |= B_SR_16000;
141	if (ac97_set_rate(config->ac97, AC97_PCM_L_R_ADC_RATE, 22050))
142		data->input_rates |= B_SR_22050;
143	if (ac97_set_rate(config->ac97, AC97_PCM_L_R_ADC_RATE, 24000))
144		data->input_rates |= B_SR_24000;
145	if (ac97_set_rate(config->ac97, AC97_PCM_L_R_ADC_RATE, 32000))
146		data->input_rates |= B_SR_32000;
147	if (ac97_set_rate(config->ac97, AC97_PCM_L_R_ADC_RATE, 44100))
148		data->input_rates |= B_SR_44100;
149	if (ac97_set_rate(config->ac97, AC97_PCM_L_R_ADC_RATE, 48000))
150		data->input_rates |= B_SR_48000;
151
152	/* try to set 44100 rate */
153	if (ac97_set_rate(config->ac97, AC97_PCM_FRONT_DAC_RATE, 44100)
154		&& ac97_set_rate(config->ac97, AC97_PCM_L_R_ADC_RATE, 44100)) {
155		config->input_rate = B_SR_44100;
156		config->output_rate = B_SR_44100;
157	} else {
158		/* if that didn't work, continue with 48000 */
159		ac97_set_rate(config->ac97, AC97_PCM_FRONT_DAC_RATE, 48000);
160		ac97_set_rate(config->ac97, AC97_PCM_L_R_ADC_RATE, 48000);
161		config->input_rate = B_SR_48000;
162		config->output_rate = B_SR_48000;
163	}
164
165	/* force existance of 48kHz if variable rates are not supported */
166	if (data->output_rates == 0)
167		data->output_rates = B_SR_48000;
168	if (data->input_rates == 0)
169		data->input_rates = B_SR_48000;
170
171	data->max_cvsr_rate = 0;
172	data->min_cvsr_rate = 0;
173
174	data->output_formats = B_FMT_16BIT;
175	data->input_formats = B_FMT_16BIT;
176	data->lock_sources = B_MULTI_LOCK_INTERNAL;
177	data->timecode_sources = 0;
178	data->interface_flags = B_MULTI_INTERFACE_PLAYBACK | B_MULTI_INTERFACE_RECORD;
179	data->start_latency = 30000;
180
181	strcpy(data->control_panel,"");
182
183	return B_OK;
184}
185
186static status_t get_enabled_channels(multi_channel_enable *data)
187{
188	B_SET_CHANNEL(data->enable_bits, 0, true);
189	B_SET_CHANNEL(data->enable_bits, 1, true);
190	B_SET_CHANNEL(data->enable_bits, 2, true);
191	B_SET_CHANNEL(data->enable_bits, 3, true);
192	data->lock_source = B_MULTI_LOCK_INTERNAL;
193/*
194	uint32			lock_source;
195	int32			lock_data;
196	uint32			timecode_source;
197	uint32 *		connectors;
198*/
199	return B_OK;
200}
201
202static status_t get_global_format(multi_format_info *data)
203{
204	data->output_latency = 0;
205	data->input_latency = 0;
206	data->timecode_kind = 0;
207	data->input.format = B_FMT_16BIT;
208	data->output.format = B_FMT_16BIT;
209	data->input.rate = config->input_rate;
210	data->output.rate = config->output_rate;
211	return B_OK;
212}
213
214static status_t set_global_format(multi_format_info *data)
215{
216	bool in, out;
217	LOG(("set_global_format: input.rate  = 0x%x\n", data->input.rate));
218	LOG(("set_global_format: input.cvsr  = %d\n", data->input.cvsr));
219	LOG(("set_global_format: output.rate = 0x%x\n", data->output.rate));
220	LOG(("set_global_format: output.cvsr = %d\n", data->output.cvsr));
221	switch (data->input.rate) {
222		case B_SR_8000:
223			in = ac97_set_rate(config->ac97, AC97_PCM_L_R_ADC_RATE, 8000);
224			break;
225		case B_SR_11025:
226			in = ac97_set_rate(config->ac97, AC97_PCM_L_R_ADC_RATE, 11025);
227			break;
228		case B_SR_12000:
229			in = ac97_set_rate(config->ac97, AC97_PCM_L_R_ADC_RATE, 12000);
230			break;
231		case B_SR_16000:
232			in = ac97_set_rate(config->ac97, AC97_PCM_L_R_ADC_RATE, 16000);
233			break;
234		case B_SR_22050:
235			in = ac97_set_rate(config->ac97, AC97_PCM_L_R_ADC_RATE, 22050);
236			break;
237		case B_SR_24000:
238			in = ac97_set_rate(config->ac97, AC97_PCM_L_R_ADC_RATE, 24000);
239			break;
240		case B_SR_32000:
241			in = ac97_set_rate(config->ac97, AC97_PCM_L_R_ADC_RATE, 32000);
242			break;
243		case B_SR_44100:
244			in = ac97_set_rate(config->ac97, AC97_PCM_L_R_ADC_RATE, 44100);
245			break;
246		case B_SR_48000:
247			in = ac97_set_rate(config->ac97, AC97_PCM_L_R_ADC_RATE, 48000);
248			break;
249		default:
250			in = false;
251	}
252
253	switch (data->output.rate) {
254		case B_SR_8000:
255			out = ac97_set_rate(config->ac97, AC97_PCM_FRONT_DAC_RATE, 8000);
256			break;
257		case B_SR_11025:
258			out = ac97_set_rate(config->ac97, AC97_PCM_FRONT_DAC_RATE, 11025);
259			break;
260		case B_SR_12000:
261			out = ac97_set_rate(config->ac97, AC97_PCM_FRONT_DAC_RATE, 12000);
262			break;
263		case B_SR_16000:
264			out = ac97_set_rate(config->ac97, AC97_PCM_FRONT_DAC_RATE, 16000);
265			break;
266		case B_SR_22050:
267			out = ac97_set_rate(config->ac97, AC97_PCM_FRONT_DAC_RATE, 22050);
268			break;
269		case B_SR_24000:
270			out = ac97_set_rate(config->ac97, AC97_PCM_FRONT_DAC_RATE, 24000);
271			break;
272		case B_SR_32000:
273			out = ac97_set_rate(config->ac97, AC97_PCM_FRONT_DAC_RATE, 32000);
274			break;
275		case B_SR_44100:
276			out = ac97_set_rate(config->ac97, AC97_PCM_FRONT_DAC_RATE, 44100);
277			break;
278		case B_SR_48000:
279			out = ac97_set_rate(config->ac97, AC97_PCM_FRONT_DAC_RATE, 48000);
280			break;
281		default:
282			out = false;
283	}
284
285	if (!in || !out)
286		return B_ERROR;
287
288	config->input_rate = data->input.rate;
289	config->output_rate = data->output.rate;
290	return B_OK;
291}
292
293
294static status_t get_buffers(multi_buffer_list *data)
295{
296	LOG(("flags = %#x\n",data->flags));
297	LOG(("request_playback_buffers = %#x\n",data->request_playback_buffers));
298	LOG(("request_playback_channels = %#x\n",data->request_playback_channels));
299	LOG(("request_playback_buffer_size = %#x\n",data->request_playback_buffer_size));
300	LOG(("request_record_buffers = %#x\n",data->request_record_buffers));
301	LOG(("request_record_channels = %#x\n",data->request_record_channels));
302	LOG(("request_record_buffer_size = %#x\n",data->request_record_buffer_size));
303
304	if (data->request_playback_buffers < 2 ||
305		data->request_playback_channels < 2 ||
306		data->request_record_buffers < 2 ||
307		data->request_record_channels < 2) {
308		LOG(("not enough channels/buffers\n"));
309	}
310
311	ASSERT(BUFFER_COUNT == 2);
312
313//	data->flags = B_MULTI_BUFFER_PLAYBACK | B_MULTI_BUFFER_RECORD; // XXX ???
314	data->flags = 0;
315
316	data->return_playback_buffers = 2;			/* playback_buffers[b][] */
317	data->return_playback_channels = 2;			/* playback_buffers[][c] */
318	data->return_playback_buffer_size = BUFFER_SIZE / 4;		/* frames */
319
320	/* buffer 0, left channel */
321	data->playback_buffers[0][0].base = chan_po->userbuffer[0]; /* pointer to first sample for channel for buffer */
322	data->playback_buffers[0][0].stride = 4; /* offset to next sample */
323	/* buffer 0, right channel */
324	data->playback_buffers[0][1].base = ((char*)chan_po->userbuffer[0]) + 2; /* pointer to first sample for channel for buffer */
325	data->playback_buffers[0][1].stride = 4; /* offset to next sample */
326	/* buffer 1, left channel */
327	data->playback_buffers[1][0].base = chan_po->userbuffer[1]; /* pointer to first sample for channel for buffer */
328	data->playback_buffers[1][0].stride = 4; /* offset to next sample */
329	/* buffer 1, right channel */
330	data->playback_buffers[1][1].base = ((char*)chan_po->userbuffer[1]) + 2; /* pointer to first sample for channel for buffer */
331	data->playback_buffers[1][1].stride = 4; /* offset to next sample */
332
333	data->return_record_buffers = 2;
334	data->return_record_channels = 2;
335	data->return_record_buffer_size = BUFFER_SIZE / 4;			/* frames */
336
337	/* buffer 0, left channel */
338	data->record_buffers[0][0].base = chan_pi->userbuffer[0]; /* pointer to first sample for channel for buffer */
339	data->record_buffers[0][0].stride = 4; /* offset to next sample */
340	/* buffer 0, right channel */
341	data->record_buffers[0][1].base = ((char*)chan_pi->userbuffer[0]) + 2; /* pointer to first sample for channel for buffer */
342	data->record_buffers[0][1].stride = 4; /* offset to next sample */
343	/* buffer 1, left channel */
344	data->record_buffers[1][0].base = chan_pi->userbuffer[1]; /* pointer to first sample for channel for buffer */
345	data->record_buffers[1][0].stride = 4; /* offset to next sample */
346	/* buffer 1, right channel */
347	data->record_buffers[1][1].base = ((char*)chan_pi->userbuffer[1]) + 2; /* pointer to first sample for channel for buffer */
348	data->record_buffers[1][1].stride = 4; /* offset to next sample */
349
350	return B_OK;
351}
352
353static status_t buffer_exchange(multi_buffer_info *data)
354{
355#if DEBUG
356	static int debug_buffers_exchanged = 0;
357#endif
358	cpu_status status;
359	void *backbuffer;
360/*
361	static int next_input = 0;
362*/
363	/*
364	 * Only playback works for now, recording seems to be broken, but I don't know why
365	 */
366
367	if (!chan_po->running)
368		start_chan(chan_po);
369
370/*
371	if (!chan_pi->running)
372		start_chan(chan_pi);
373*/
374
375	/* do playback */
376	acquire_sem(chan_po->buffer_ready_sem);
377	status = lock();
378	backbuffer = (void *)chan_po->backbuffer;
379	data->played_real_time = chan_po->played_real_time;
380	data->played_frames_count = chan_po->played_frames_count;
381	unlock(status);
382	memcpy(backbuffer, chan_po->userbuffer[data->playback_buffer_cycle], BUFFER_SIZE);
383
384	/* do record */
385	data->record_buffer_cycle = 0;
386	data->recorded_frames_count = 0;
387/*
388	if (B_OK == acquire_sem_etc(chan_pi->buffer_ready_sem, 1, B_CAN_INTERRUPT | B_RELATIVE_TIMEOUT,200)) {
389		status = lock();
390		backbuffer = (void *)chan_pi->backbuffer;
391		data->recorded_real_time = chan_pi->played_real_time;
392		unlock(status);
393		data->recorded_frames_count = BUFFER_SIZE / 4;
394		data->record_buffer_cycle = next_input;
395		next_input = (next_input + 1) % 2;
396		memcpy(chan_pi->userbuffer[data->record_buffer_cycle],backbuffer, BUFFER_SIZE);
397	} else {
398		data->record_buffer_cycle = next_input;
399		data->recorded_frames_count = 0;
400	}
401*/
402#if DEBUG
403	debug_buffers_exchanged++;
404	if (((debug_buffers_exchanged % 100) == 1) && (debug_buffers_exchanged < 1111)) {
405		LOG(("buffer_exchange: %d buffers processed\n", debug_buffers_exchanged));
406	}
407#endif
408
409	return B_OK;
410
411/*
412	if (data->flags & B_MULTI_BUFFER_PLAYBACK) {
413		dprintf("B_MULTI_BUFFER_PLAYBACK\n");
414	}
415
416	if (data->flags & B_MULTI_BUFFER_RECORD) {
417		dprintf("B_MULTI_BUFFER_RECORD\n");
418		data->recorded_real_time = 0;
419		data->recorded_frames_count = 0;
420		data->record_buffer_cycle = 0;
421	}
422	if (data->flags & B_MULTI_BUFFER_METERING) {
423		dprintf("B_MULTI_BUFFER_METERING\n");
424		data->meter_channel_count = 0;
425		// data->meters_peak
426		// data->meters_average
427	}
428	if (data->flags & B_MULTI_BUFFER_TIMECODE) {
429		dprintf("B_MULTI_BUFFER_TIMECODE\n");
430		data->hours = 0;
431		data->minutes = 0;
432		data->seconds = 0;
433		data->tc_frames = 0;
434		data->at_frame_delta = 0;
435	}
436	return B_OK;
437*/
438}
439
440static status_t buffer_force_stop()
441{
442	return B_OK;
443}
444
445status_t multi_control(void *cookie, uint32 op, void *data, size_t length)
446{
447    switch (op) {
448		case B_MULTI_GET_DESCRIPTION:
449			LOG(("B_MULTI_GET_DESCRIPTION\n"));
450			return get_description((multi_description *)data);
451		case B_MULTI_GET_EVENT_INFO:
452			LOG(("B_MULTI_GET_EVENT_INFO\n"));
453			return B_ERROR;
454		case B_MULTI_SET_EVENT_INFO:
455			LOG(("B_MULTI_SET_EVENT_INFO\n"));
456			return B_ERROR;
457		case B_MULTI_GET_EVENT:
458			LOG(("B_MULTI_GET_EVENT\n"));
459			return B_ERROR;
460		case B_MULTI_GET_ENABLED_CHANNELS:
461			LOG(("B_MULTI_GET_ENABLED_CHANNELS\n"));
462			return get_enabled_channels((multi_channel_enable *)data);
463		case B_MULTI_SET_ENABLED_CHANNELS:
464			LOG(("B_MULTI_SET_ENABLED_CHANNELS\n"));
465			return B_OK; break;
466		case B_MULTI_GET_GLOBAL_FORMAT:
467			LOG(("B_MULTI_GET_GLOBAL_FORMAT\n"));
468			return get_global_format((multi_format_info *)data);
469		case B_MULTI_SET_GLOBAL_FORMAT:
470			LOG(("B_MULTI_SET_GLOBAL_FORMAT\n"));
471			set_global_format((multi_format_info *)data);
472			return B_OK; /* XXX BUG! we *MUST* return B_OK, returning B_ERROR will prevent
473						  * BeOS to accept the format returned in B_MULTI_GET_GLOBAL_FORMAT
474						  */
475		case B_MULTI_GET_CHANNEL_FORMATS:
476			LOG(("B_MULTI_GET_CHANNEL_FORMATS\n"));
477			return B_ERROR;
478		case B_MULTI_SET_CHANNEL_FORMATS:	/* only implemented if possible */
479			LOG(("B_MULTI_SET_CHANNEL_FORMATS\n"));
480			return B_ERROR;
481		case B_MULTI_GET_MIX:
482			LOG(("B_MULTI_GET_MIX\n"));
483			return B_ERROR;
484		case B_MULTI_SET_MIX:
485			LOG(("B_MULTI_SET_MIX\n"));
486			return B_ERROR;
487		case B_MULTI_LIST_MIX_CHANNELS:
488			LOG(("B_MULTI_LIST_MIX_CHANNELS\n"));
489			return list_mix_channels((multi_mix_channel_info *)data);
490		case B_MULTI_LIST_MIX_CONTROLS:
491			LOG(("B_MULTI_LIST_MIX_CONTROLS\n"));
492			return list_mix_controls((multi_mix_control_info *)data);
493		case B_MULTI_LIST_MIX_CONNECTIONS:
494			LOG(("B_MULTI_LIST_MIX_CONNECTIONS\n"));
495			return list_mix_connections((multi_mix_connection_info *)data);
496		case B_MULTI_GET_BUFFERS:			/* Fill out the struct for the first time; doesn't start anything. */
497			LOG(("B_MULTI_GET_BUFFERS\n"));
498			return get_buffers(data);
499		case B_MULTI_SET_BUFFERS:			/* Set what buffers to use, if the driver supports soft buffers. */
500			LOG(("B_MULTI_SET_BUFFERS\n"));
501			return B_ERROR; /* we do not support soft buffers */
502		case B_MULTI_SET_START_TIME:			/* When to actually start */
503			LOG(("B_MULTI_SET_START_TIME\n"));
504			return B_ERROR;
505		case B_MULTI_BUFFER_EXCHANGE:		/* stop and go are derived from this being called */
506//			dprintf("B_MULTI_BUFFER_EXCHANGE\n");
507			return buffer_exchange((multi_buffer_info *)data);
508		case B_MULTI_BUFFER_FORCE_STOP:		/* force stop of playback */
509			LOG(("B_MULTI_BUFFER_FORCE_STOP\n"));
510			return buffer_force_stop();
511	}
512	LOG(("ERROR: unknown multi_control %#x\n",op));
513	return B_ERROR;
514}
515
516
517