1/*
2	Copyright 1999, Be Incorporated.   All Rights Reserved.
3	This file may be used under the terms of the Be Sample Code License.
4*/
5#include <string.h>
6
7#include <ByteOrder.h>
8#include "R3MediaDefs.h"
9
10#include "cm_private.h"
11#include "sound.h"
12
13#if !defined(_KERNEL_EXPORT_H)
14#include <KernelExport.h>
15#endif /* _KERNEL_EXPORT_H */
16
17extern int sprintf(char *, const char *, ...);
18
19
20extern void dump_card(cmedia_pci_dev * card);
21
22#if !defined(OLDAPI)
23 #if DEBUG
24  #define OLDAPI(x) dprintf x
25 #else
26  #define OLDAPI(x)
27 #endif
28#endif
29
30#if DEBUG
31int32 int_cnt;
32int32 put_cnt;
33bigtime_t the_time;
34#endif
35
36#if 0
37/* early Intel kernels forgot to export these functions */
38#undef B_HOST_TO_LENDIAN_FLOAT
39#undef B_HOST_TO_BENDIAN_FLOAT
40#undef B_LENDIAN_TO_HOST_FLOAT
41#undef B_BENDIAN_TO_HOST_FLOAT
42
43static float
44_swap_float_(float x)
45{
46	uint32 temp1 = *(uint32*)&x;
47	uint32 temp2 = ((temp1>>24)|((temp1>>8)&0xff00)|((temp1<<8)&0xff0000)|
48					(temp1<<24));
49	return *(float *)&temp2;
50}
51
52#if B_HOST_IS_BENDIAN
53#define B_HOST_TO_LENDIAN_FLOAT(x) _swap_float_(x)
54#define B_HOST_TO_BENDIAN_FLOAT(x) ((float)(x))
55#define B_LENDIAN_TO_HOST_FLOAT(x) _swap_float_(x)
56#define B_BENDIAN_TO_HOST_FLOAT(x) ((float)(x))
57#else
58#define B_HOST_TO_LENDIAN_FLOAT(x) ((float)(x))
59#define B_HOST_TO_BENDIAN_FLOAT(x) _swap_float_(x)
60#define B_LENDIAN_TO_HOST_FLOAT(x) ((float)(x))
61#define B_BENDIAN_TO_HOST_FLOAT(x) _swap_float_(x)
62#endif
63
64#endif
65
66
67static status_t pcm_open(const char *name, uint32 flags, void **cookie);
68static status_t pcm_close(void *cookie);
69static status_t pcm_free(void *cookie);
70static status_t pcm_control(void *cookie, uint32 op, void *data, size_t len);
71static status_t pcm_read(void *cookie, off_t pos, void *data, size_t *len);
72static status_t pcm_write(void *cookie, off_t pos, const void *data, size_t *len);
73//static status_t pcm_writev(void *cookie, off_t pos, const iovec *vec, size_t count, size_t *len); /* */
74
75device_hooks pcm_hooks = {
76    &pcm_open,
77    &pcm_close,
78    &pcm_free,
79    &pcm_control,
80    &pcm_read,
81    &pcm_write,
82    NULL,			/* select */
83    NULL,			/* deselect */
84    NULL,			/* readv */
85	NULL	// &pcm_writev		/* writev */
86};
87
88static pcm_cfg default_pcm = {
89	44100.0,	/* sample rate */
90	2,			/* channels */
91	0x2,		/* format */
92#if B_HOST_IS_BENDIAN
93	1,			/* endian (big) */
94#else
95	0,			/* endian (little) */
96#endif
97	0,			/* header size */
98	PLAYBACK_BUF_SIZE,	/* these are currently hard-coded */
99	RECORD_BUF_SIZE		/* and cannot be changed without re-compile */
100};
101
102
103#if 0
104
105typedef struct {
106	uint8 control;
107	uint8 imask;
108	uint8 regs[0x2e];
109} chip_state;
110
111static void
112save_state(
113	pcm_dev * port,
114	chip_state * state)
115{
116	int ix;
117	state->control = get_direct(port->card, 0);
118	state->imask = get_direct(port->card, 1);
119	for (ix=0; ix<0x0e; ix++) {
120		if (ix != 0x28 && ix != 0x29 && ix != 0x1a && ix != 0x1b) {
121			state->regs[ix] = get_indirect(port->card, ix+0x30);
122		}
123	}
124}
125
126
127static void
128restore_state(
129	pcm_dev * port,
130	const chip_state * state)
131{
132	int ix;
133	set_direct(port->card, 0, state->control, 0xff);
134	for (ix=0; ix<0x0e; ix++) {
135		if (ix != 0x28 && ix != 0x29 && ix != 0x1a && ix != 0x1b) {
136			set_indirect(port->card, ix, state->regs[ix]+0x30, 0xff);
137		}
138	}
139	set_direct(port->card, 1, state->imask, 0xff);
140}
141
142
143static void
144reset_chip(
145	pcm_dev * port)
146{
147	set_direct(port->card, 0x1b, 0x40, 0x40);
148	snooze(2);
149	set_direct(port->card, 0x1b, 0x00, 0x40);
150}
151
152#endif
153
154
155static void
156stop_dma(
157	pcm_dev * port)
158{
159	set_direct(port->card, 0x24, 0x40, 0x40);	// mute wave stream
160	set_direct(port->card, 0x02, 0, 0x03);		// stop both ch0 and ch1
161
162	set_direct(port->card, 0x02, 0x0c, 0x0c);	// reset both ch0 and ch1
163	set_direct(port->card, 0x02, 0, 0x0c);
164	ddprintf(("cmedia_pci: DMA stopped\n"));
165}
166
167
168static void
169start_dma(
170	pcm_dev * port)
171{
172	int	sample_size = 1;
173
174	/* start out with a clean slate */
175
176	KTRACE();
177	ddprintf(("cmedia_pci: start_dma()\n"));
178	if (port->config.format == 0x11) {
179		memset((void*)port->card->low_mem, 0x80, port->config.play_buf_size +
180			port->config.rec_buf_size);
181	}
182	else {
183		memset((void *)port->card->low_mem, 0, port->config.play_buf_size +
184			port->config.rec_buf_size);
185	}
186
187	port->wr_cur = port->wr_2;
188	port->wr_silence = port->config.play_buf_size;
189	port->was_written = 0;
190	port->rd_cur = port->rd_2;
191	port->was_read = port->config.rec_buf_size/2; /* how much has been read */
192	port->wr_total = 0;
193	port->rd_total = 0;
194
195	/* we split the available low memory buffer in a small chunk */
196	/* for playback, and a large chunk for recording. Then we split */
197	/* each of those in half for double-buffering. While the DMA */
198	/* just runs the entire range of the buffer, wrapping around when */
199	/* done, the count register is set up to generate interrupt after */
200	/* each half of the buffer. Because of latency requirements, we */
201	/* will get 187 interrupts a second from playback, and 94 interrupts */
202	/* a second from recording, at 48 kHz sampling rate, when buffers */
203	/* are 2048 for playback and 4096 for record. */
204
205	ddprintf(("play_buf_size %lx   rec_buf_size %lx\n",
206		port->config.play_buf_size/2, port->config.rec_buf_size/2));
207
208	PCI_IO_WR(port->dma_c, ((uint32)port->card->low_phys+
209		port->config.play_buf_size)&0xff);
210	PCI_IO_WR(port->dma_c+1, (((uint32)port->card->low_phys+
211		port->config.play_buf_size)>>8)&0xff);
212	PCI_IO_WR(port->dma_c+2, (((uint32)port->card->low_phys+
213		port->config.play_buf_size)>>16)&0xff);
214	PCI_IO_WR(port->dma_c+3, 0);
215	/* if this is a 16 bit channel, divide transfer count in 2 */
216	if (port->config.format != 0x11)
217		sample_size *= 2;
218	/* if this is a stereo channel, divide transfer count in 2 */
219	if (port->config.channels == 2)
220		sample_size *= 2;
221	PCI_IO_WR(port->dma_c+4, (port->config.rec_buf_size/sample_size-1)&0xff);
222	PCI_IO_WR(port->dma_c+5, ((port->config.rec_buf_size/sample_size-1)>>8)&0xff);
223	PCI_IO_WR(port->dma_c+6, (port->rd_size/sample_size-1)&0xff);
224	PCI_IO_WR(port->dma_c+7, ((port->rd_size/sample_size-1)>>8)&0xff);
225
226	PCI_IO_WR(port->dma_a, ((uint32)port->card->low_phys)&0xff);
227	PCI_IO_WR(port->dma_a+1, ((uint32)port->card->low_phys>>8)&0xff);
228	PCI_IO_WR(port->dma_a+2, ((uint32)port->card->low_phys>>16)&0xff);
229	PCI_IO_WR(port->dma_a+3, 0);
230	PCI_IO_WR(port->dma_a+4, (port->config.play_buf_size/sample_size-1)&0xff);
231	PCI_IO_WR(port->dma_a+5, ((port->config.play_buf_size/sample_size-1)>>8)&0xff);
232	PCI_IO_WR(port->dma_a+6, (port->wr_size/sample_size-1)&0xff);
233	PCI_IO_WR(port->dma_a+7, ((port->wr_size/sample_size-1)>>8)&0xff);
234
235/* here, we should mute the PCM output to avoid clicking */
236
237	ddprintf(("cmedia_pci: DMA starts as %lx/%lx\n", port->config.format, port->open_mode));
238	set_direct(port->card, 0x24, 0x00, 0x40);
239
240/* enable ch0 as play, and ch1 as record */
241	set_direct(port->card, 0, 0x02, 0x03);
242	set_direct(port->card, 0x02, 0x03, 0x03);
243
244/* here, we should snooze for 16 samples' time, then un-mute the PCM output */
245	KTRACE();
246}
247
248
249static status_t
250configure_pcm(
251	pcm_dev * port,
252	pcm_cfg * config,
253	bool force)
254{
255	status_t err = B_OK;
256	int m = 0, n = 0, r = 0;	/* parameters for the PLL sample rate synthesizer */
257	int asr = -1;	/* alternate sample rate divisor */
258	uint32 s;	/* size of buffer */
259
260	ddprintf(("cmedia_pci: configure_pcm()\n"));
261
262	/* check args */
263	if (config->sample_rate < 4000.0) {
264		config->sample_rate = default_pcm.sample_rate;
265	}
266	if (config->sample_rate > 48000.0) {
267		config->sample_rate = 48000.0;
268	}
269	if (config->channels < 1) {
270		config->channels = default_pcm.channels;
271	}
272	if (config->channels > 2) {
273		config->channels = default_pcm.channels;
274	}
275	/* secret format of format: upper nybble = signed, unsigned, float */
276	/* lower nybble = bytes per sample */
277	if ((config->format != 0x11) && (config->format != 0x2) &&
278		(config->format != 0x24) && (config->format != 0x4)) {
279		config->format = default_pcm.format;
280	}
281	if (config->buf_header < 0) {
282		config->buf_header = 0;
283	}
284
285	/* figure out buffer size that's a power of two and within size limits */
286	if (!config->play_buf_size) {
287		/* default is 256 samples for a comfy 6 ms latency */
288		s = 256*config->channels*(config->format&0xf);
289	}	/* minimum is 32 samples for a more extreme 0.75ms latency */
290	else for (s = 32*config->channels*(config->format&0xf); s < MIN_MEMORY_SIZE/2; s = (s<<1)) {
291		if (s >= config->play_buf_size) {
292			break;
293		}
294	}
295	config->play_buf_size = s;
296	if (!config->rec_buf_size) {
297		s = 256*config->channels*(config->format&0xf);
298	}
299	else for (s = 32*config->channels*(config->format&0xf); s < MIN_MEMORY_SIZE/2; s = (s<<1)) {
300		if (s >= config->rec_buf_size) {
301			break;
302		}
303	}
304	config->rec_buf_size = s;
305
306	/* calculate m, n and r (and asr) */
307
308	if (!force && abs(config->sample_rate - port->config.sample_rate) < config->sample_rate/250) {
309		n = -1;
310	}
311	else if (config->sample_rate == 48000.0) {
312		asr = 7;
313	}
314	else if (config->sample_rate == 32000.0) {
315		asr = 6;
316	}
317	else if (config->sample_rate == 16000.0) {
318		asr = 5;
319	}
320	else if (config->sample_rate == 8000.0) {
321		asr = 4;
322	}
323	else if (config->sample_rate == 44100.0) {
324		asr = 3;
325	}
326	else if (config->sample_rate == 22050.0) {
327		asr = 2;
328	}
329	else if (config->sample_rate == 11025.0) {
330		asr = 1;
331	}
332	else {
333		float freq;
334		float delta = 1000000.0;
335		int sr = -1;
336		int sn = -1;
337		int sm = -1;
338		float diff;
339
340		for (r=0; r<8; r++) {
341			if ((1<<r)*config->sample_rate*512 < MIN_FREQ) {
342				continue;
343			}
344			break;
345		}
346		if (r == 8) {
347			OLDAPI(("cmedia_pci: r value is 8!\n"));
348			r = 7;
349		}
350		n = 0;
351		do {
352			n++;
353			m = config->sample_rate*512/1000*(n+2)*(1<<r)/(F_REF/1000)-2;
354			if (m < 1) {
355				continue;
356			}
357			if (m > 255) {
358				ddprintf(("cmedia_pci: m > 255; outahere\n"));
359				break;
360			}
361			freq = (m+2)*(F_REF/1000)/(512*(n+2)*(1<<r)/1000);
362			diff = freq-config->sample_rate;
363			if (diff < 0) {
364				diff = -diff;
365			}
366			if (diff < delta) {
367				sr = r;
368				sn = n;
369				sm = m;
370			}
371		} while (n < 31);
372		r = sr;
373		n = sn;
374		m = sm;
375		ddprintf(("cmedia_pci: m = %d   r = %d   n = %d\n", m, r, n));
376	}
377
378	/* configure device */
379
380	if (!force) {
381		stop_dma(port);
382		/* should mute PCM out, too */
383	}
384	if (asr > -1 || n > -1) { /* new sampling rate */
385		if (asr > -1) {
386			port->config.sample_rate = config->sample_rate;
387			set_direct(port->card, 0x05, (asr<<5)|(asr<<2), 0xfc);
388		}
389		else {
390			port->config.sample_rate = ((float)m+2.0)*(F_REF/1000.0)/
391				(0.512*(n+2.0)*(1<<r));
392			config->sample_rate = port->config.sample_rate;
393#if 1
394			/* not exact the frequency supported */
395#else
396			set_indirect(port->card, 0x24, m, 0xff);
397			set_indirect(port->card, 0x25, (r<<5)|n, 0xff);
398			set_indirect(port->card, 0x22, 0x00, 0xff);
399#endif
400		}
401	}
402	if (force || config->channels != port->config.channels ||
403		config->format != port->config.format) {
404		uchar val = 0;
405		if (config->channels == 2) {
406			val |= 0x01;	/* stereo */
407		}
408		if (config->format != 0x11) {
409			val |= 0x02;	/* 16 bits */
410		}
411		set_direct(port->card, 0x08, (val<<2)|val, 0x0f); /* MCE -- may take time to take effect */
412		port->config.channels = config->channels;
413		port->config.format = config->format;
414	}
415	if (force || config->big_endian != port->config.big_endian) {
416		port->config.big_endian = config->big_endian;
417	}
418	if (force || config->buf_header != port->config.buf_header) {
419		port->config.buf_header = config->buf_header;
420	}
421	if (force || config->play_buf_size != port->config.play_buf_size*2) {
422		port->config.play_buf_size = config->play_buf_size*2;	/* because we break it in two */
423	}
424	if (force || config->rec_buf_size != port->config.rec_buf_size*2) {
425		port->config.rec_buf_size = config->rec_buf_size*2;	/* because we break it in two */
426	}
427
428/* here is where we should care about record and playback buffer sizes */
429
430	ddprintf(("cmedia_pci: play %04lx rec %04lx\n", port->config.play_buf_size/2,
431		port->config.rec_buf_size/2));
432
433	port->wr_1 = port->card->low_mem;
434	port->wr_2 = port->wr_1+port->config.play_buf_size/2;
435	port->wr_size = port->config.play_buf_size/2;
436
437	port->rd_1 = port->card->low_mem+port->config.play_buf_size;
438	port->rd_2 = port->rd_1+port->config.rec_buf_size/2;
439	port->rd_size = port->config.rec_buf_size/2;
440
441	if (!force) {
442		/* should un-mute PCM out, if we muted it */
443		start_dma(port);
444	}
445	return err;
446}
447
448
449static status_t
450pcm_open(
451	const char * name,
452	uint32 flags,
453	void ** cookie)
454{
455	int ix;
456	pcm_dev * port = NULL;
457	char name_buf[256];
458	int32 prev_mode;
459
460	ddprintf(("cmedia_pci: pcm_open()\n"));
461
462	*cookie = NULL;
463	for (ix=0; ix<num_cards; ix++) {
464		if (!strcmp(name, cards[ix].pcm.name)) {
465			goto gotit;
466		}
467	}
468	for (ix=0; ix<num_cards; ix++) {
469		if (!strcmp(name, cards[ix].pcm.oldname)) {
470			goto gotit;
471		}
472	}
473	ddprintf(("cmedia_pci: %s not found\n", name));
474	return ENODEV;
475
476gotit:
477	*cookie = port = &cards[ix].pcm;
478
479	acquire_sem(port->init_sem);
480
481	prev_mode = port->open_mode;
482	if ((flags & 3) == O_RDONLY) {
483		atomic_or(&port->open_mode, kRecord);
484	}
485	else if ((flags & 3) == O_WRONLY) {
486		atomic_or(&port->open_mode, kPlayback);
487	}
488	else {
489		atomic_or(&port->open_mode, kPlayback|kRecord);
490	}
491
492	if (atomic_add(&port->open_count, 1) == 0) {
493
494		/* initialize device first time */
495
496		port->card = &cards[ix];
497		port->config = default_pcm;
498		port->config.play_buf_size *= 2;
499		port->config.rec_buf_size *= 2;
500
501		/* playback */
502
503		port->wr_lock = 0;
504		port->dma_a = cards[ix].dma_base;
505		port->wr_1 = cards[ix].low_mem;
506		port->wr_2 = cards[ix].low_mem+port->config.play_buf_size/2;
507		port->wr_size = port->config.play_buf_size/2;
508		port->write_waiting = 0;
509		sprintf(name_buf, "WS:%s", port->name);
510		name_buf[B_OS_NAME_LENGTH-1] = 0;
511		port->write_sem = create_sem(0, name_buf);
512		if (port->write_sem < B_OK) {
513			port->open_count = 0;
514			return port->write_sem;
515		}
516		set_sem_owner(port->write_sem, B_SYSTEM_TEAM);
517		name_buf[0] = 'W'; name_buf[1] = 'E';
518		port->wr_entry = create_sem(1, name_buf);
519		if (port->wr_entry < B_OK) {
520			delete_sem(port->write_sem);
521			port->open_count = 0;
522			return port->wr_entry;
523		}
524		set_sem_owner(port->wr_entry, B_SYSTEM_TEAM);
525		name_buf[1] = 'T';
526		port->wr_time_wait = 0;
527		port->wr_time_sem = create_sem(0, name_buf);
528		if (port->wr_time_sem < B_OK) {
529			delete_sem(port->write_sem);
530			delete_sem(port->wr_entry);
531			port->open_count = 0;
532			return port->wr_time_sem;
533		}
534		set_sem_owner(port->wr_time_sem, B_SYSTEM_TEAM);
535
536		/* recording */
537
538		port->rd_lock = 0;
539		port->dma_c = cards[ix].dma_base+0x08;
540		port->rd_1 = cards[ix].low_mem+port->config.play_buf_size;
541		port->rd_2 = cards[ix].low_mem+port->config.play_buf_size+port->config.rec_buf_size/2;
542		port->rd_size = port->config.rec_buf_size/2;
543		port->read_waiting = 0;
544		name_buf[0] = 'R'; name_buf[1] = 'S';
545		port->read_sem = create_sem(0, name_buf);
546		if (port->read_sem < B_OK) {
547			delete_sem(port->write_sem);
548			delete_sem(port->wr_entry);
549			delete_sem(port->wr_time_sem);
550			port->open_count = 0;
551			return port->read_sem;
552		}
553		set_sem_owner(port->read_sem, B_SYSTEM_TEAM);
554		name_buf[0] = 'R'; name_buf[1] = 'E';
555		port->rd_entry = create_sem(1, name_buf);
556		if (port->rd_entry < B_OK) {
557			delete_sem(port->write_sem);
558			delete_sem(port->wr_entry);
559			delete_sem(port->read_sem);
560			delete_sem(port->wr_time_sem);
561			port->open_count = 0;
562			return port->rd_entry;
563		}
564		set_sem_owner(port->rd_entry, B_SYSTEM_TEAM);
565		name_buf[1] = 'T';
566		port->rd_time_wait = 0;
567		port->rd_time_sem = create_sem(0, name_buf);
568		if (port->rd_time_sem < B_OK) {
569			delete_sem(port->write_sem);
570			delete_sem(port->wr_entry);
571			delete_sem(port->read_sem);
572			delete_sem(port->wr_time_sem);
573			delete_sem(port->rd_entry);
574			port->open_count = 0;
575			return port->rd_time_sem;
576		}
577		set_sem_owner(port->rd_time_sem, B_SYSTEM_TEAM);
578
579		port->rd_time = 0;
580		port->next_rd_time = 0;
581		port->wr_time = 0;
582
583		/* old API */
584
585		port->old_cap_sem = -1;
586		port->old_play_sem = -1;
587
588		/* configuration */
589
590		configure_pcm(port, &default_pcm, true);
591
592		/* interrupts */
593		KTRACE();
594		increment_interrupt_handler(port->card);
595
596		set_direct(port->card, 0x0e, 0x03, 0x03);	/* */
597		start_dma(port);
598
599		/* initialization is done, let other clients of the driver go */
600	} else {
601		if (prev_mode != port->open_mode) {
602			pcm_cfg temp = port->config;
603			temp.play_buf_size /= 2;
604			temp.rec_buf_size /= 2;
605			configure_pcm(port, &temp, false);	/* change rec/play if needed */
606		}
607	}
608	release_sem(port->init_sem);
609
610#if DEBUG
611	dump_card(&cards[ix]);
612#endif
613
614	return B_OK;
615}
616
617
618static status_t
619pcm_close(
620	void * cookie)
621{
622	pcm_dev * port = (pcm_dev *)cookie;
623	cpu_status cp;
624	int spin = 0;
625
626	ddprintf(("cmedia_pci: pcm_close()\n"));
627
628	acquire_sem(port->init_sem);
629
630	if (atomic_add(&port->open_count, -1) == 1) {
631
632		KTRACE();
633		cp = disable_interrupts();
634		acquire_spinlock(&port->card->hardware);
635
636		/* turn off interrupts */
637		stop_dma(port);
638		set_direct(port->card, 0x0e, 0x00, 0x03);	/* */
639
640		if (port->config.format == 0x11) {
641			memset((void *)port->wr_1, 0x80, port->config.play_buf_size);	/* play silence */
642		}
643		else {
644			memset((void *)port->wr_1, 0, port->config.play_buf_size);	/* play silence */
645		}
646		spin = 1;
647
648		release_spinlock(&port->card->hardware);
649		restore_interrupts(cp);
650
651		delete_sem(port->write_sem);
652		delete_sem(port->read_sem);
653		delete_sem(port->wr_entry);
654		delete_sem(port->rd_entry);
655		delete_sem(port->rd_time_sem);
656		delete_sem(port->wr_time_sem);
657		port->write_sem = -1;
658		port->read_sem = -1;
659		port->wr_entry = -1;
660		port->rd_entry = -1;
661		port->rd_time_sem = -1;
662		port->wr_time_sem = -1;
663	}
664	release_sem(port->init_sem);
665
666	if (spin) {
667		/* wait so we know FIFO gets filled with silence */
668		snooze(port->config.play_buf_size*1000/(port->config.sample_rate*
669			(port->config.format&0xf)*port->config.channels/1000));
670	}
671	return B_OK;
672}
673
674
675static status_t
676pcm_free(
677	void * cookie)
678{
679	cpu_status cp;
680	pcm_dev * port = (pcm_dev *)cookie;
681
682	ddprintf(("cmedia_pci: pcm_free()\n"));
683
684	acquire_sem(port->init_sem);
685
686	if (((pcm_dev *)cookie)->open_count == 0) {
687
688		/* the last free will actually stop everything  */
689
690		KTRACE();
691		cp = disable_interrupts();
692		acquire_spinlock(&port->card->hardware);
693
694		decrement_interrupt_handler(port->card);
695
696		release_spinlock(&port->card->hardware);
697		restore_interrupts(cp);
698	}
699	release_sem(port->init_sem);
700
701	return B_OK;
702}
703
704
705static status_t
706pcm_control(
707	void * cookie,
708	uint32 iop,
709	void * data,
710	size_t len)
711{
712	// declarations for SPDIF settings I/O
713	static int32 chipinfo[] = { 0,0 };
714	uchar reg_value;
715	char DriverVersion[] = "1.3.2 (Jul 17, 2001)";
716
717	pcm_dev * port = (pcm_dev *)cookie;
718	status_t err = B_BAD_VALUE;
719	pcm_cfg config = port->config;
720	static float rates[7] = { 48000.0, 44100.0, 32000.0, 22050.0, 16000.0, 11025.0, 8000.0 };
721	bool configure = false;
722	config.play_buf_size /= 2;
723	config.rec_buf_size /= 2;
724
725	ddprintf(("cmedia_pci: pcm_control()\n"));
726
727	switch (iop) {
728	case B_AUDIO_GET_AUDIO_FORMAT:
729		memcpy(data, &config, sizeof(port->config));
730		err = B_OK;
731		break;
732	case B_AUDIO_GET_PREFERRED_SAMPLE_RATES:
733		memcpy(data, rates, sizeof(rates));
734		err = B_OK;
735		break;
736	case B_AUDIO_SET_AUDIO_FORMAT:
737		memcpy(&config, data, sizeof(config));
738		configure = true;
739		err = B_OK;
740		break;
741	case SV_RD_TIME_WAIT:
742		atomic_add(&port->rd_time_wait, 1);
743		err = acquire_sem(port->rd_time_sem);
744		if (err >= B_OK) {
745			cpu_status cp;
746			KTRACE();
747			cp = disable_interrupts();
748			acquire_spinlock(&port->rd_lock);
749			((sv_timing *)data)->time = port->rd_time;
750			((sv_timing *)data)->bytes = port->rd_total;
751			((sv_timing *)data)->skipped = port->rd_skipped;
752			((sv_timing *)data)->_reserved_[0] = 0xffffffffUL;
753			release_spinlock(&port->rd_lock);
754			restore_interrupts(cp);
755		}
756		break;
757	case SV_WR_TIME_WAIT:
758		atomic_add(&port->wr_time_wait, 1);
759		err = acquire_sem(port->wr_time_sem);
760		if (err >= B_OK) {
761			cpu_status cp;
762			KTRACE();
763			cp = disable_interrupts();
764			acquire_spinlock(&port->wr_lock);
765			((sv_timing *)data)->time = port->wr_time;
766			((sv_timing *)data)->bytes = port->wr_total;
767			((sv_timing *)data)->skipped = port->wr_skipped;
768			((sv_timing *)data)->_reserved_[0] = 0xffffffffUL;
769			release_spinlock(&port->wr_lock);
770			restore_interrupts(cp);
771		}
772		break;
773	case SV_SECRET_HANDSHAKE: {
774		cpu_status cp;
775		KTRACE();
776		cp = disable_interrupts();
777		acquire_spinlock(&port->wr_lock);
778		acquire_spinlock(&port->rd_lock);
779		((sv_handshake *)data)->wr_time = port->wr_time;
780		((sv_handshake *)data)->wr_skipped = port->wr_skipped;
781		((sv_handshake *)data)->rd_time = port->rd_time;
782		((sv_handshake *)data)->rd_skipped = port->rd_skipped;
783		((sv_handshake *)data)->wr_total = port->wr_total;
784		((sv_handshake *)data)->rd_total = port->rd_total;
785		((sv_handshake *)data)->_reserved_[0] = 0xffffffffUL;
786		err = B_OK;
787		release_spinlock(&port->rd_lock);
788		release_spinlock(&port->wr_lock);
789		restore_interrupts(cp);
790		} break;
791	case SOUND_GET_PARAMS: {
792		cpu_status cp;
793		uchar u;
794		sound_setup * sound = (sound_setup *)data;
795		err = B_OK;
796		cp = disable_interrupts();
797		acquire_spinlock(&port->card->hardware);
798		/* Here we get to hard-code the mix/mux values. */
799		/* Huh-huh; he said "hard-code"! */
800		sound->sample_rate = kHz_44_1;
801		if (!port->config.big_endian == !B_HOST_IS_BENDIAN) {
802			sound->playback_format = linear_16bit_big_endian_stereo;
803			sound->capture_format = linear_16bit_big_endian_stereo;
804		}
805		else {
806			sound->playback_format = linear_16bit_little_endian_stereo;
807			sound->capture_format = linear_16bit_little_endian_stereo;
808		}
809		sound->dither_enable = false;
810		sound->loop_attn = 0;
811		sound->loop_enable = 0;
812		sound->output_boost = 0;
813		sound->highpass_enable = 0;
814		/* this is a master control on C-Media... */
815		u = get_indirect(port->card, 0x30)>>2;
816		sound->mono_gain = u&63;
817		sound->mono_mute = 0;
818
819		/* left channel */
820		u = get_indirect(port->card, 0x3d); // Legacy SB compatible Mixer
821		switch (u)
822		{
823		case 0x10:
824			sound->left.adc_source = line;		//	record line left
825			break;
826
827		case 4:
828			sound->left.adc_source = aux1;		// record CD left ??
829			break;
830
831		case 1:
832			sound->left.adc_source = mic;		// record mic left
833			break;
834
835		default:
836			sound->left.adc_source = loopback;
837			break;
838		}
839		u = get_indirect(port->card, 0x3f)>>4;
840		sound->left.adc_gain = u&15;
841
842		u = get_direct(port->card, 0x25)<<4;
843		sound->left.mic_gain_enable = u&16;
844
845		u = get_indirect(port->card, 0x36)>>3;
846		sound->left.aux1_mix_gain = 31-(u&31);
847
848		u = get_indirect(port->card, 0x3c)<<5;
849		sound->left.aux1_mix_mute = ~u&128;
850
851		u = get_indirect(port->card, 0x34)>>3;
852		sound->left.aux2_mix_gain = 31-(u&31);
853
854		u = get_direct(port->card, 0x24);
855		sound->left.aux2_mix_mute = u&128;
856
857		u = get_indirect(port->card, 0x38)>>3;
858		sound->left.line_mix_gain = 31-(u&31);
859
860		u = get_indirect(port->card, 0x3c)<<3;
861		sound->left.line_mix_mute = ~u&128;
862
863		u = get_indirect(port->card, 0x32)>>2;
864		sound->left.dac_attn = 63-(u&63);
865
866		u = get_direct(port->card, 0x24)<<1;
867		sound->left.dac_mute = u&128;
868
869		/* right channel */
870		u = get_indirect(port->card, 0x3e);
871		switch (u)
872		{
873		case 8:
874			sound->right.adc_source = line;		//record line right
875			break;
876
877		case 2:
878			sound->right.adc_source = aux1;		// record CD right?
879			break;
880
881		case 1:
882			sound->right.adc_source = mic;		// record mic right
883			break;
884
885		default:
886			sound->right.adc_source = loopback;
887			break;
888		}
889		u = get_indirect(port->card, 0x40)>>4;
890		sound->right.adc_gain = u&15;
891		sound->right.mic_gain_enable = sound->left.mic_gain_enable;
892		u = get_indirect(port->card, 0x37)>>3;
893		sound->right.aux1_mix_gain = 31-(u&31);
894		u = get_indirect(port->card, 0x3c)<<6;
895		sound->right.aux1_mix_mute = ~u&128;
896		u = get_indirect(port->card, 0x35)>>3;
897		sound->right.aux2_mix_gain = 31-(u&31);
898		u = get_direct(port->card, 0x24);
899		sound->right.aux2_mix_mute = u&128;
900		u = get_indirect(port->card, 0x39)>>3;
901		sound->right.line_mix_gain = 31-(u&31);
902		u = get_indirect(port->card, 0x3c)<<4;
903		sound->right.line_mix_mute = ~u&128;
904		u = get_indirect(port->card, 0x33)>>2;
905		sound->right.dac_attn = 63-(u&63);
906		u = get_direct(port->card, 0x24)<<1;
907		sound->right.dac_mute = u&128;
908		/* done */
909		release_spinlock(&port->card->hardware);
910		restore_interrupts(cp);
911		} break;
912	case SOUND_SET_PARAMS: {
913		cpu_status cp;
914		uchar u;
915		sound_setup * sound = (sound_setup *)data;
916		err = B_OK;
917		cp = disable_interrupts();
918		acquire_spinlock(&port->card->hardware);
919		/* Here we get to hard-code the mix/mux values. */
920		/* Huh-huh; he said "hard-code"! */
921
922		/* ignore sample rate */
923		sound->sample_rate = kHz_44_1;
924		if (config.sample_rate < 43999 || config.sample_rate > 44201) {
925			config.sample_rate = 44100.0;
926			configure = true;
927		}
928		/* we only support 16-bit formats */
929		if (sound->playback_format == linear_16bit_big_endian_stereo &&
930			sound->capture_format == linear_16bit_big_endian_stereo) {
931			if (!config.big_endian != !B_HOST_IS_BENDIAN || config.format != 0x2) {
932				config.big_endian = B_HOST_IS_BENDIAN;
933				config.format = 0x2;
934				configure = true;
935			}
936			OLDAPI(("same_endian\n"));
937		}
938		else if (sound->playback_format == linear_16bit_little_endian_stereo &&
939			sound->capture_format == linear_16bit_little_endian_stereo) {
940			if (!config.big_endian != !!B_HOST_IS_BENDIAN || config.format != 0x2) {
941				config.big_endian = !B_HOST_IS_BENDIAN;
942				config.format = 0x2;
943				configure = true;
944			}
945			OLDAPI(("other_endian\n"));
946		}
947		else {
948			config.big_endian = !!B_HOST_IS_BENDIAN;
949			configure = true;
950			OLDAPI(("other format!!!\n"));
951		}
952		/* ignore these values */
953		sound->dither_enable = false;
954		sound->loop_attn = 0;
955		sound->loop_enable = 0;
956		sound->output_boost = 0;
957		sound->highpass_enable = 0;
958		/* this is a stereo control on C-Media... */
959		u = (sound->mono_gain>>1)&0x1f;
960		OLDAPI(("output: %x\n", u));
961		set_indirect(port->card, 0x30, u<<3, 0xff);
962		set_indirect(port->card, 0x31, u<<3, 0xff);
963		/* left channel */
964		switch (sound->left.adc_source)
965		{
966		case line:
967			u = 1<<4;
968			break;
969		case aux1:
970			u = 1<<2;
971			break;
972		case mic:
973			u = 1<<0;
974			break;
975		default:
976			u = 0x15;
977			break;
978		}
979		OLDAPI(("input: %x\n", u));
980		set_indirect(port->card, 0x3d, u, 0xff);
981		u = (sound->left.adc_gain&15);
982		set_indirect(port->card, 0x3f, u<<4, 0xff);
983		u = sound->left.mic_gain_enable ? 0 : 0x01;
984		set_direct(port->card, 0x25, u, 0x01);
985		u = 31-(sound->left.aux1_mix_gain&31);
986		OLDAPI(("cd: %x\n", u));
987		set_indirect(port->card, 0x36, u<<3, 0xff);
988		u = sound->left.aux1_mix_mute ? 0 : 0x04;
989		set_indirect(port->card, 0x3c, u, 0x04);
990		u = 31-(sound->left.aux2_mix_gain&31);
991		OLDAPI(("aux2: %x\n", u));
992		set_indirect(port->card, 0x34, u<<3, 0xff);
993		u = sound->left.aux2_mix_mute ? 0x80 : 0;
994		set_direct(port->card, 0x24, u, 0x80);
995		u = 31-(sound->left.line_mix_gain&31);
996		OLDAPI(("line: %x\n", u));
997		set_indirect(port->card, 0x38, u<<3, 0xff);
998		u = sound->left.line_mix_mute ? 0 : 0x10;
999		set_indirect(port->card, 0x3c, u, 0x10);
1000		u = 63-(sound->left.dac_attn & 63);
1001		OLDAPI(("PCM: %x\n", u));
1002		set_indirect(port->card, 0x32, u<<2, 0xff);
1003		u = sound->left.dac_mute ? 0x40 : 0;
1004		set_direct(port->card, 0x24, u, 0x40);
1005		/* right channel */
1006		switch (sound->right.adc_source) {
1007		case line:
1008			u = 1<<3;
1009			break;
1010		case aux1:
1011			u = 1<<1;
1012			break;
1013		case mic:
1014			u = 1<<0;
1015			break;
1016		default:
1017			u = 0x0a;
1018			break;
1019		}
1020		sound->right.mic_gain_enable = sound->left.mic_gain_enable;
1021		set_indirect(port->card, 0x3e, u, 0xff);
1022		u = (sound->right.adc_gain&15);
1023		set_indirect(port->card, 0x40, u<<4, 0xff);
1024		u = sound->right.mic_gain_enable ? 0 : 0x01;
1025		set_direct(port->card, 0x25, u, 0x01);
1026		u = 31-(sound->right.aux1_mix_gain&31);
1027		set_indirect(port->card, 0x37, u<<3, 0xff);
1028		u = sound->right.aux1_mix_mute ? 0 : 0x02;
1029		set_indirect(port->card, 0x3c, u, 0x02);
1030		u = 31-(sound->right.aux2_mix_gain&31);
1031		set_indirect(port->card, 0x35, u<<3, 0xff);
1032		u = sound->right.aux2_mix_mute ? 0x80 : 0;
1033		set_direct(port->card, 0x24, u, 0x80);
1034		u = 31-(sound->right.line_mix_gain&31);
1035		set_indirect(port->card, 0x39, u<<3, 0xff);
1036		u = sound->right.line_mix_mute ? 0 : 0x08;
1037		set_indirect(port->card, 0x3c, u, 0x08);
1038		u = 63-(sound->right.dac_attn & 63);
1039		set_indirect(port->card, 0x33, u<<2, 0xff);
1040		u = sound->right.dac_mute ? 0x40 : 0;
1041		set_direct(port->card, 0x24, u, 0x40);
1042		/* done */
1043		release_spinlock(&port->card->hardware);
1044		restore_interrupts(cp);
1045		} break;
1046	case SOUND_SET_PLAYBACK_COMPLETION_SEM:
1047		port->old_play_sem = *(sem_id *)data;
1048		err = B_OK;
1049		break;
1050	case SOUND_SET_CAPTURE_COMPLETION_SEM:
1051		port->old_cap_sem = *(sem_id *)data;
1052		err = B_OK;
1053		break;
1054//	case SOUND_GET_PLAYBACK_TIMESTAMP:
1055//		break;
1056//	case SOUND_GET_CAPTURE_TIMESTAMP:
1057//		break;
1058//	case SOUND_DEBUG_ON:
1059//		break;
1060//	case SOUND_DEBUG_OFF:
1061//		break;
1062	case SOUND_UNSAFE_WRITE: {
1063		audio_buffer_header * buf = (audio_buffer_header *)data;
1064		size_t n = buf->reserved_1-sizeof(*buf);
1065		pcm_write(cookie, 0, buf+1, &n);
1066		buf->time = port->wr_time;
1067		buf->sample_clock = port->wr_total/4 * 10000 / 441;
1068		err = release_sem(port->old_play_sem);
1069		} break;
1070	case SOUND_UNSAFE_READ: {
1071		audio_buffer_header * buf = (audio_buffer_header *)data;
1072		size_t n = buf->reserved_1-sizeof(*buf);
1073		pcm_read(cookie, 0, buf+1, &n);
1074		buf->time = port->rd_time;
1075		buf->sample_clock = port->rd_total/4 * 10000 / 441;
1076		err = release_sem(port->old_cap_sem);
1077		} break;
1078	case SOUND_LOCK_FOR_DMA:
1079		err = B_OK;
1080		break;
1081	case SOUND_SET_PLAYBACK_PREFERRED_BUF_SIZE:
1082		config.play_buf_size = (int32)data;
1083		configure = true;
1084		err = B_OK;
1085		break;
1086	case SOUND_SET_CAPTURE_PREFERRED_BUF_SIZE:
1087		config.rec_buf_size = (int32)data;
1088		configure = true;
1089		err = B_OK;
1090		break;
1091	case SOUND_GET_PLAYBACK_PREFERRED_BUF_SIZE:
1092		*(int32*)data = config.play_buf_size;
1093		err = B_OK;
1094		break;
1095	case SOUND_GET_CAPTURE_PREFERRED_BUF_SIZE:
1096		*(int32*)data = config.rec_buf_size;
1097		err = B_OK;
1098		break;
1099
1100
1101// control ports for SPDIF settings
1102	case SOUND_GET_SPDIF_IN_OUT_LOOPBACK:
1103		*(int8 *)data = 0;
1104		reg_value = get_direct( port->card, 0x04 );
1105		if( reg_value && 0x80 ) *(int8 *)data = 1;
1106		err = B_OK;
1107		break;
1108
1109	case SOUND_SET_SPDIF_IN_OUT_LOOPBACK:
1110		if( *(int8 *)data == 0 ) // disable SPDIF-IN loopback to SPDIF (bypass)
1111			set_direct( port->card, 0x04, 0x00, 0x80 );
1112		else // enable SPDIF-IN loopback to SPDIF (bypass)
1113			set_direct( port->card, 0x04, 0x80, 0x80 );
1114		err = B_OK;
1115		break;
1116
1117
1118
1119
1120	case SOUND_GET_SPDIF_OUT:
1121		*(int8 *)data = 0;
1122		reg_value = get_direct( port->card, 0x16 );			// Adresse 0x16
1123		if( reg_value && 0x80 ) *(int8 *)data = 1;
1124		err = B_OK;
1125		break;
1126
1127	case SOUND_SET_SPDIF_OUT:
1128		if( *(int8 *)data == 0 ) // disable SPDIF-OUT
1129			set_direct( port->card, 0x16, 0x00, 0x80);
1130		else // enable SPDIF-OUT
1131			set_direct( port->card, 0x16, 0x80, 0x80 );
1132		err = B_OK;
1133		break;
1134
1135
1136
1137	case SOUND_GET_SPDIF_MONITOR:
1138		*(int8 *)data = 0;
1139		reg_value = get_direct( port->card, 0x24 );
1140		if( reg_value && 0x01 ) *(int8 *)data = 1;
1141		err = B_OK;
1142		break;
1143
1144
1145	case SOUND_SET_SPDIF_MONITOR:
1146		if( *(int8 *)data == 0 ) // disable SPDIF_IN PCM to DAC (CDPlay)
1147			set_direct( port->card, 0x24, 0x00, 0x01 );
1148		else // enable SPDIF_IN PCM to DAC (CDPlay)
1149			set_direct( port->card, 0x24, 0x01, 0x01 );
1150		err = B_OK;
1151		break;
1152
1153	case SOUND_GET_SPDIF_OUT_LEVEL:
1154		*(int8 *)data = 0;
1155		reg_value = get_direct( port->card, 0x1b );
1156		if( reg_value && 0x02 ) *(int8 *)data = 1;
1157		err = B_OK;
1158		break;
1159
1160	case SOUND_SET_SPDIF_OUT_LEVEL:
1161		if( *(int8 *)data == 0 ) // enable SPDIF-OUT optical
1162			set_direct( port->card, 0x1b, 0x00, 0x02 );
1163		else // enable SPDIF-OUT coaxial
1164			set_direct( port->card, 0x1b, 0x02, 0x02 );
1165		break;
1166
1167	case SOUND_GET_SPDIF_IN_FORMAT:
1168		*(int8 *)data = 0;
1169		reg_value = get_direct( port->card, 0x08 );		// Adresse 0x08
1170		if( reg_value && 0x80 ) *(int8 *)data = 1;
1171		err = B_OK;
1172		break;
1173
1174
1175	case SOUND_SET_SPDIF_IN_FORMAT:
1176		if( *(int8 *)data == 0 ) // disable SPDIF inverse (SPDIF normal)
1177			set_direct( port->card, 0x08, 0x00, 0x80 );
1178		else // enable SPDIF inverse
1179			set_direct( port->card, 0x08, 0x80, 0x80 );	// Adresse 0x08, Daten 0x80
1180		err = B_OK;
1181		break;
1182
1183
1184	case SOUND_GET_SPDIF_IN_OUT_COPYRIGHT:
1185		*(int8 *)data = 0;
1186		reg_value = get_direct( port->card, 0x16 );
1187		if( reg_value && 0x40 ) *(int8 *)data = 1;
1188		err = B_OK;
1189		break;
1190
1191	case SOUND_SET_SPDIF_IN_OUT_COPYRIGHT:
1192		if( *(int8 *)data == 0 ) // disable SPDIF-IN/OUT copyright protection
1193			set_direct( port->card, 0x16, 0x00, 0x40 );
1194		else // enable SPDIF-IN/OUT copyright protection
1195			set_direct( port->card, 0x16, 0x40, 0x40 );
1196		err = B_OK;
1197		break;
1198
1199	case SOUND_GET_SPDIF_IN_VALIDITY:
1200		*(int8 *)data = 0;
1201		reg_value = get_direct( port->card, 0x27 );
1202		if( reg_value && 0x02 ) *(int8 *)data = 1;
1203		err = B_OK;
1204		break;
1205
1206	case SOUND_SET_SPDIF_IN_VALIDITY:
1207		if( *(int8 *)data == 0 ) // disable SPDIF-IN validity detection
1208			set_direct( port->card, 0x27, 0x00, 0x02 );
1209		else // enable SPDIF-IN validity detection
1210			set_direct( port->card, 0x27, 0x02, 0x02 );
1211		err = B_OK;
1212		break;
1213// control ports for analog settings
1214
1215	case SOUND_GET_4_CHANNEL_DUPLICATE:
1216		*(int8 *)data = 0;
1217		reg_value = get_direct( port->card, 0x1b );
1218		if( reg_value && 0x04 ) *(int8 *)data = 1;
1219
1220//		0x1b, 0x04, 0x04,	/* dual channel mode enable */
1221//		0x1a, 0x00, 0x80,	/* Double DAC structure disable */
1222
1223		err = B_OK;
1224		break;
1225
1226	case SOUND_SET_4_CHANNEL_DUPLICATE:
1227		if( *(int8 *)data == 0 ) // disable 4 channel analog duplicate mode
1228			set_direct( port->card, 0x1b, 0x00, 0x04 );
1229		else // enable 4 channel analog duplicate mode
1230			set_direct( port->card, 0x1b, 0x04, 0x04 );
1231		err = B_OK;
1232		break;
1233// control ports for additional info
1234
1235	case SOUND_GET_DEVICE_ID:
1236//		*(int32*)data.vendor_id = cards[0].info.vendor_id;
1237//		*(int32*)data.device_id = cards[0].info.device_id;
1238
1239//		chipinfo[0] = cards[0].info.vendor_id;
1240		*(int32 *)data = cards[0].info.device_id;
1241
1242//		memcpy(data, &chipinfo, sizeof(chipinfo));
1243		err = B_OK;
1244		break;
1245
1246	case SOUND_GET_INTERNAL_CHIP_ID:
1247		// XXX
1248		break;
1249
1250	case SOUND_GET_DRIVER_VERSION:
1251		memcpy(data, &DriverVersion, sizeof(DriverVersion));
1252		break;
1253
1254	default:
1255		OLDAPI(("cmedia_pci: unknown code %ld\n", iop));
1256		err = B_BAD_VALUE;
1257		break;
1258	}
1259	if ((err == B_OK) && configure) {
1260		cpu_status cp;
1261		KTRACE();
1262		cp = disable_interrupts();
1263		acquire_spinlock(&port->card->hardware);
1264		err = configure_pcm(port, &config, false);
1265		release_spinlock(&port->card->hardware);
1266		restore_interrupts(cp);
1267	}
1268	return err;
1269}
1270
1271
1272static void
1273copy_short_to_float(
1274	float * f,
1275	const short * s,
1276	int c,
1277	int endian)	/*	endian means float data in big-endian	*/
1278{
1279	if (endian) {
1280		while (c > 1) {
1281			short sh = B_LENDIAN_TO_HOST_FLOAT(*s);
1282			*(f++) = B_HOST_TO_BENDIAN_FLOAT((float)B_LENDIAN_TO_HOST_INT16(sh));
1283			s++;
1284			c -= 2;
1285		}
1286	}
1287	else {
1288		while (c > 1) {
1289			short sh = B_LENDIAN_TO_HOST_FLOAT(*s);
1290			*(f++) = B_HOST_TO_LENDIAN_FLOAT((float)B_LENDIAN_TO_HOST_INT16(sh));
1291			s++;
1292			c -= 2;
1293		}
1294	}
1295}
1296
1297
1298static void
1299copy_float_to_short(
1300	short * s,
1301	const float * f,
1302	int c,
1303	int endian)	/*	endian means float data in big-endian	*/
1304{
1305	if (endian) {
1306		while (c > 1) {
1307			float fl = *(f++);
1308			*(s++) = B_HOST_TO_LENDIAN_INT16((float)B_BENDIAN_TO_HOST_FLOAT(fl));
1309			c -= 2;
1310		}
1311	}
1312	else {
1313		while (c > 1) {
1314			float fl = *(f++);
1315			*(s++) = B_HOST_TO_LENDIAN_INT16((float)B_LENDIAN_TO_HOST_FLOAT(fl));
1316			c -= 2;
1317		}
1318	}
1319}
1320
1321
1322static void
1323swap_copy(
1324	short * dest,
1325	const short * src,
1326	int c)
1327{
1328	while (c > 1) {
1329		unsigned short sh = *(src++);
1330		*(dest++) = ((sh << 8) | (sh >> 8));
1331		c -= 2;
1332	}
1333}
1334
1335
1336static status_t
1337pcm_read(
1338	void * cookie,
1339	off_t pos,
1340	void * data,
1341	size_t * nread)
1342{
1343	pcm_dev * port = (pcm_dev *)cookie;
1344	size_t to_read = *nread;
1345	status_t err;
1346	int block;
1347	cpu_status cp;
1348	int bytes_xferred;
1349	void * hdrptr = data;
1350	int hdrsize = port->config.buf_header;
1351	cmedia_pci_audio_buf_header hdr;
1352
1353//	ddprintf(("cmedia_pci: pcm_read()\n")); /* we're here */
1354
1355	*nread = 0;
1356	data = ((char *)data)+hdrsize;
1357	to_read -= hdrsize;
1358
1359	err = acquire_sem_etc(port->rd_entry, 1, B_CAN_INTERRUPT, 0);
1360	if (err < B_OK) {
1361		return err;
1362	}
1363
1364	hdr.capture_time = port->rd_time;
1365
1366	goto first_time;
1367
1368	while (to_read > 0) {
1369		/* wait for more data */
1370		atomic_add(&port->read_waiting, 1);
1371		err = acquire_sem_etc(port->read_sem, 1, B_CAN_INTERRUPT, 0);
1372		if (err < B_OK) {
1373			release_sem(port->rd_entry);
1374			return err;
1375		}
1376
1377first_time:	/* we need to check whether anything's available first */
1378		KTRACE();
1379		cp = disable_interrupts();
1380		acquire_spinlock(&port->rd_lock);
1381
1382		block = port->rd_size-port->was_read;
1383
1384		if (port->config.format == 0x24) {
1385			if (block > (to_read>>1)) {	/*	floats expand by factor 2	*/
1386				block = to_read>>1;
1387			}
1388		}
1389		else if (block > to_read) {
1390			block = to_read;
1391		}
1392		switch (port->config.format) {
1393		case 0x24:	/*	floats	*/
1394			copy_short_to_float((float *)data, (const short *)(port->rd_cur+port->was_read),
1395				block, !B_HOST_IS_LENDIAN == !port->config.big_endian);
1396			bytes_xferred = block * 2;
1397			break;
1398		case 0x02:	/*	shorts	*/
1399			if (!B_HOST_IS_LENDIAN == !port->config.big_endian) {
1400				/*	we need to swap	*/
1401				swap_copy((short *)data, (const short *)(port->rd_cur+port->was_read), block);
1402				bytes_xferred = block;
1403				break;
1404			}
1405			/*	else fall through to default case	*/
1406		case 0x11:	/*	bytes	*/
1407		default:
1408			memcpy(data, (void *)(port->rd_cur+port->was_read), block);
1409			bytes_xferred = block;
1410			break;
1411		}
1412		port->was_read += block;
1413
1414		release_spinlock(&port->rd_lock);
1415		restore_interrupts(cp);
1416
1417		to_read -= bytes_xferred;
1418		data = ((char *)data)+bytes_xferred;
1419		*nread += bytes_xferred;
1420	}
1421
1422	/*	provide header if requested	*/
1423	if (hdrsize > 0) {
1424		ddprintf(("header %d\n", hdrsize));
1425		*nread += hdrsize;
1426		hdr.capture_size = *nread;
1427		hdr.sample_rate = port->config.sample_rate;
1428		if (hdrsize > sizeof(hdr)) {
1429			hdrsize = sizeof(hdr);
1430		}
1431		memcpy(hdrptr, &hdr, hdrsize);
1432	}
1433
1434	release_sem(port->rd_entry);
1435
1436	return B_OK;
1437}
1438
1439
1440static status_t
1441pcm_write(
1442	void * cookie,
1443	off_t pos,
1444	const void * data,
1445	size_t * nwritten)
1446{
1447	pcm_dev * port = (pcm_dev *)cookie;
1448	status_t err;
1449	cpu_status cp;
1450	int written = 0;
1451	int to_write = *nwritten;	/*	 in play bytes, not input bytes!	*/
1452	int block;
1453	int bytes_xferred;
1454
1455//	ddprintf(("cmedia_pci: pcm_write()\n")); /* we're here */
1456
1457	*nwritten = 0;
1458
1459	err = acquire_sem_etc(port->wr_entry, 1, B_CAN_INTERRUPT, 0);
1460	if (err < B_OK) {
1461		return err;
1462	}
1463
1464	atomic_add(&port->write_waiting, 1);
1465	if (port->config.format == 0x24) {
1466		to_write >>= 1;	/*	floats collapse by 2	*/
1467	}
1468	while (to_write > 0) {
1469
1470		/* wait to write */
1471
1472		err = acquire_sem_etc(port->write_sem, 1, B_CAN_INTERRUPT, 0);
1473		if (err < B_OK) {
1474			release_sem(port->wr_entry);
1475			return err;
1476		}
1477
1478#if DEBUG
1479		put_cnt++;
1480		{
1481			bigtime_t delta = system_time() - the_time;
1482			if (delta < 1) {
1483				ddprintf(("cmedia_pci: delta %Ld (low!) #%ld\n", delta, put_cnt));
1484			}
1485			else if (delta > 2000) {
1486				ddprintf(("cmedia_pci: delta %Ld (high!) #%ld\n", delta, put_cnt));
1487			}
1488		}
1489		if (put_cnt != int_cnt) {
1490	static int last;
1491			if (last != int_cnt-put_cnt)
1492				OLDAPI(("cmedia_pci: %ld mismatch\n", int_cnt-put_cnt));
1493			last = int_cnt-put_cnt;
1494		}
1495#endif /* DEBUG */
1496
1497		KTRACE();
1498		cp = disable_interrupts();
1499		acquire_spinlock(&port->wr_lock);
1500
1501		block = port->wr_size-port->was_written;
1502		if (block > to_write) {
1503			/* must let next guy in */
1504			if (atomic_add(&port->write_waiting, -1) > 0) {
1505				release_sem_etc(port->write_sem, 1, B_DO_NOT_RESCHEDULE);
1506			}
1507			else {
1508				atomic_add(&port->write_waiting, 1); /* undo damage */
1509			}
1510			block = to_write;
1511		}
1512		else if (block < to_write) {
1513			atomic_add(&port->write_waiting, 1); /* we will loop back */
1514		}
1515		switch (port->config.format) {
1516		case 0x24:	/*	floats	*/
1517			copy_float_to_short((short *)(port->wr_cur+port->was_written), (const float *)data,
1518				block, !B_HOST_IS_LENDIAN == !port->config.big_endian);
1519			bytes_xferred = block * 2;
1520			break;
1521		case 0x02:	/*	shorts	*/
1522			if (!B_HOST_IS_LENDIAN == !port->config.big_endian) {
1523				/*	we need to swap	*/
1524				swap_copy((short *)(port->wr_cur+port->was_written), (const short *)data, block);
1525				bytes_xferred = block;
1526				break;
1527			}
1528			/*	else fall through to default case	*/
1529		case 0x11:	/*	bytes	*/
1530		default:
1531			memcpy((void *)(port->wr_cur+port->was_written), data, block);
1532			bytes_xferred = block;
1533			break;
1534		}
1535		port->was_written += block;
1536		port->wr_silence = 0;
1537
1538		release_spinlock(&port->wr_lock);
1539		restore_interrupts(cp);
1540
1541		data = ((char *)data)+bytes_xferred;
1542		written += bytes_xferred;
1543		to_write -= block;
1544	}
1545
1546	*nwritten = written;
1547	release_sem(port->wr_entry);
1548
1549	return B_OK;
1550}
1551
1552
1553bool
1554dma_a_interrupt(
1555	cmedia_pci_dev * dev)
1556{
1557	bool ret = false;
1558	pcm_dev * port = &dev->pcm;
1559	volatile uchar * ptr;
1560	uint32 addr;
1561	uint32 offs;
1562	bigtime_t st = system_time();
1563	int32 ww;
1564
1565#if 0
1566ddprintf(("cmedia_pci: dma_a 0x%x+0x%x\n", PCI_IO_RD_32((int)port->dma_a), PCI_IO_RD_32((int)port->dma_a+4)));
1567#endif
1568//	KTRACE(); /* */
1569	acquire_spinlock(&port->wr_lock);
1570
1571	if (port->write_sem < 0) {
1572		kprintf("cmedia_pci: spurious DMA A interrupt!\n");
1573		release_spinlock(&port->wr_lock);
1574		return false;
1575	}
1576	/* copy possible silence into playback buffer */
1577
1578	if (port->was_written > 0 && port->was_written < port->wr_size) {
1579		if (port->config.format == 0x11) {
1580			memset((void *)(port->wr_cur+port->was_written), 0x80, port->wr_size-port->was_written);
1581		}
1582		else {
1583			memset((void *)(port->wr_cur+port->was_written), 0, port->wr_size-port->was_written);
1584		}
1585	}
1586
1587	/* because the system may be lacking and not hand us the */
1588	/* interrupt in time, we check which half is currently being */
1589	/* played, and set the pointer to the other half */
1590
1591	addr = PCI_IO_RD_32((uint32)port->dma_a);
1592	if ((offs = addr-(uint32)port->card->low_phys) < port->wr_size) {
1593		ptr = port->wr_2;
1594	}
1595	else {
1596		ptr = port->wr_1;
1597	}
1598	port->wr_total += port->config.play_buf_size/2;
1599	/* compensate for interrupt latency */
1600	/* assuming 4 byte frames */
1601	port->wr_time = st-(offs&(port->config.play_buf_size/2-1))*250000LL/(int64)port->config.sample_rate;
1602	if ((ww = atomic_add(&port->wr_time_wait, -1)) > 0) {
1603		release_sem_etc(port->wr_time_sem, 1, B_DO_NOT_RESCHEDULE);
1604		ret = true;
1605	}
1606	else {
1607		atomic_add(&port->wr_time_wait, 1); /* re-set to 0 */
1608	}
1609
1610	if (port->wr_cur == ptr) {
1611		port->wr_skipped++;
1612		OLDAPI(("cmedia_pci: write skipped %ld\n", port->wr_skipped));
1613	}
1614	port->wr_cur = ptr;
1615	port->was_written = 0;
1616
1617	/* check for client there to write into buffer */
1618
1619	if (atomic_add(&port->write_waiting, -1) > 0) {
1620#if DEBUG
1621		int_cnt++;
1622		the_time = st;
1623#endif
1624		release_sem_etc(port->write_sem, 1, B_DO_NOT_RESCHEDULE);
1625		ret = true;
1626	}
1627	else {
1628		atomic_add(&port->write_waiting, 1);
1629		/* if none there, fill with silence */
1630		if (port->wr_silence < port->config.play_buf_size*2) {
1631			if (port->config.format == 0x11) {
1632				memset((void *)ptr, 0x80, port->wr_size);
1633			}
1634			else {
1635				memset((void *)ptr, 0, port->wr_size);
1636			}
1637			port->wr_silence += port->wr_size;
1638		}
1639	}
1640	/* copying will be done in user thread */
1641
1642	release_spinlock(&port->wr_lock);
1643	return ret;
1644}
1645
1646
1647bool
1648dma_c_interrupt(
1649	cmedia_pci_dev * dev)
1650{
1651	bool ret = false;
1652	pcm_dev * port = &dev->pcm;
1653	volatile uchar * ptr;
1654	uint32 addr;
1655	uint32 offs;
1656	int32 rr;
1657	bigtime_t st = system_time();
1658
1659	/* mark data as readable in record buffer */
1660#if 0
1661ddprintf(("cmedia_pci: dma_c 0x%x+0x%x\n", PCI_IO_RD_32((int)port->dma_c), PCI_IO_RD_32((int)port->dma_c+4)));
1662#endif
1663//	KTRACE(); /* */
1664	acquire_spinlock(&port->rd_lock);
1665
1666	if (port->read_sem < 0) {
1667		kprintf("cmedia_pci: spurious DMA C interrupt!\n");
1668		release_spinlock(&port->rd_lock);
1669		return false;
1670	}
1671	/* if we lose an interrupt, we automatically avoid constant glitching by setting */
1672	/* the write pointer based on where the DMA counter is everytime */
1673	addr = PCI_IO_RD_32((uint32)port->dma_c);
1674	if ((offs = addr-port->config.play_buf_size-(uint32)port->card->low_phys) < port->rd_size) {
1675		ptr = port->rd_2;
1676	}
1677	else {
1678		ptr = port->rd_1;
1679	}
1680	if (port->rd_cur == ptr) {
1681		port->rd_skipped++;
1682		OLDAPI(("cmedia_pci: read skipped %ld\n", port->rd_skipped));
1683	}
1684	port->rd_total += port->rd_size;
1685
1686	port->rd_cur = ptr;
1687	port->was_read = 0;
1688	port->rd_time = port->next_rd_time;
1689	/*	time stamp when this buffer became available -- compensate for interrupt latency	*/
1690	port->next_rd_time = st-(offs&(port->config.rec_buf_size/2-1))*1000000LL/(int64)port->config.sample_rate;
1691	if ((rr = atomic_add(&port->rd_time_wait, -1)) > 0) {
1692		release_sem_etc(port->rd_time_sem, 1, B_DO_NOT_RESCHEDULE);
1693		ret = true;
1694	}
1695	else {
1696		atomic_add(&port->rd_time_wait, 1); /* re-set to 0 */
1697	}
1698
1699	if (atomic_add(&port->read_waiting, -1) > 0) {
1700		release_sem_etc(port->read_sem, 1, B_DO_NOT_RESCHEDULE);
1701		ret = true;
1702	}
1703	else {
1704		atomic_add(&port->read_waiting, 1);
1705	}
1706	/* copying will be done in the user thread */
1707	release_spinlock(&port->rd_lock);
1708	return ret;
1709}
1710
1711