xref: /haiku/src/add-ons/kernel/drivers/audio/echo/echo.cpp (revision 67bce78b48ed6d01b5a8eef89f5694c372b7e0a1)
1 //------------------------------------------------------------------------------
2 //
3 //  EchoGals/Echo24 BeOS Driver for Echo audio cards
4 //
5 //	Copyright (c) 2003, Jérôme Duval
6 //
7 //	Permission is hereby granted, free of charge, to any person obtaining a
8 //	copy of this software and associated documentation files (the "Software"),
9 //	to deal in the Software without restriction, including without limitation
10 //	the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 //	and/or sell copies of the Software, and to permit persons to whom the
12 //	Software is furnished to do so, subject to the following conditions:
13 //
14 //	The above copyright notice and this permission notice shall be included in
15 //	all copies or substantial portions of the Software.
16 //
17 //	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 //	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 //	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 //	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 //	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 //	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 //	DEALINGS IN THE SOFTWARE.
24 
25 #include <KernelExport.h>
26 #include <Drivers.h>
27 #include <malloc.h>
28 #include <unistd.h>
29 #include "OsSupportBeOS.h"
30 #include "EchoGalsXface.h"
31 #include "CDarla24.h"
32 #include "CDarla.h"
33 #include "CGina.h"
34 #include "CGina24.h"
35 #include "CLayla.h"
36 #include "CLayla24.h"
37 #include "CMia.h"
38 #include "CMona.h"
39 #include "echo.h"
40 #include "debug.h"
41 #include "util.h"
42 
43 static char pci_name[] = B_PCI_MODULE_NAME;
44 static pci_module_info	*pci;
45 int32 num_cards;
46 echo_dev cards[NUM_CARDS];
47 int32 num_names;
48 char * names[NUM_CARDS*20+1];
49 
50 extern device_hooks multi_hooks;
51 
52 int32 echo_int(void *arg);
53 status_t init_hardware(void);
54 status_t init_driver(void);
55 static void make_device_names(echo_dev * card);
56 
57 static status_t echo_setup(echo_dev * card);
58 static void echo_shutdown(echo_dev *card);
59 
60 void uninit_driver(void);
61 const char ** publish_devices(void);
62 device_hooks * find_device(const char * name);
63 
64 
65 /* Echo Memory management */
66 
67 echo_mem *
68 echo_mem_new(echo_dev *card, size_t size)
69 {
70 	echo_mem *mem;
71 
72 	if ((mem = (echo_mem *) malloc(sizeof(*mem))) == NULL)
73 		return (NULL);
74 
75 	mem->area = alloc_mem(&mem->phy_base, &mem->log_base, size, "echo buffer");
76 	mem->size = size;
77 	if (mem->area < B_OK) {
78 		free(mem);
79 		return NULL;
80 	}
81 	return mem;
82 }
83 
84 void
85 echo_mem_delete(echo_mem *mem)
86 {
87 	if(mem->area > B_OK)
88 		delete_area(mem->area);
89 	free(mem);
90 }
91 
92 echo_mem *
93 echo_mem_alloc(echo_dev *card, size_t size)
94 {
95 	echo_mem *mem;
96 
97 	mem = echo_mem_new(card, size);
98 	if (mem == NULL)
99 		return (NULL);
100 
101 	LIST_INSERT_HEAD(&(card->mems), mem, next);
102 
103 	return mem;
104 }
105 
106 void
107 echo_mem_free(echo_dev *card, void *ptr)
108 {
109 	echo_mem 		*mem;
110 
111 	LIST_FOREACH(mem, &card->mems, next) {
112 		if (mem->log_base != ptr)
113 			continue;
114 		LIST_REMOVE(mem, next);
115 
116 		echo_mem_delete(mem);
117 		break;
118 	}
119 }
120 
121 /*	Echo stream functions */
122 
123 extern char *      pStatusStrs[ECHOSTATUS_LAST];
124 
125 status_t
126 echo_stream_set_audioparms(echo_stream *stream, uint8 channels,
127      uint8 bitsPerSample, uint32 sample_rate)
128 {
129 	int32 			i;
130 	uint8 			sample_size, frame_size;
131 	ECHOGALS_OPENAUDIOPARAMETERS	open_params;
132 	ECHOGALS_CLOSEAUDIOPARAMETERS  close_params;
133 	ECHOGALS_AUDIOFORMAT			format_params;
134 	ECHOSTATUS status;
135 
136 	LOG(("echo_stream_set_audioparms\n"));
137 
138 	close_params.wPipeIndex = stream->pipe;
139 	status = stream->card->pEG->CloseAudio(&close_params);
140 	if(status!=ECHOSTATUS_OK && status!=ECHOSTATUS_CHANNEL_NOT_OPEN) {
141 		PRINT(("echo_stream_set_audioparms : CloseAudio failed\n"));
142 		PRINT(("  status: %s \n", pStatusStrs[status]));
143 		return B_ERROR;
144 	}
145 
146 	open_params.bIsCyclic = TRUE;
147 	open_params.Pipe.nPipe = 0;
148 	open_params.Pipe.bIsInput = stream->use == ECHO_USE_RECORD ? TRUE : FALSE;
149 	open_params.Pipe.wInterleave = channels;
150 	open_params.ProcessId = NULL;
151 
152 	status = stream->card->pEG->OpenAudio(&open_params, &stream->pipe);
153 	if(status!=ECHOSTATUS_OK) {
154 		PRINT(("echo_stream_set_audioparms : OpenAudio failed\n"));
155 		PRINT(("  status: %s \n", pStatusStrs[status]));
156 		return B_ERROR;
157 	}
158 
159 	//PRINT(("VerifyAudioOpen\n"));
160 	status = stream->card->pEG->VerifyAudioOpen(stream->pipe);
161 	if(status!=ECHOSTATUS_OK) {
162 		PRINT(("echo_stream_set_audioparms : VerifyAudioOpen failed\n"));
163 		PRINT(("  status: %s \n", pStatusStrs[status]));
164 		return B_ERROR;
165 	}
166 
167 	if ((stream->channels == channels) &&
168 		(stream->bitsPerSample == bitsPerSample) &&
169 		(stream->sample_rate == sample_rate))
170 		return B_OK;
171 
172 	format_params.wBitsPerSample = bitsPerSample;
173 	format_params.byDataAreBigEndian = 0;
174 	format_params.byMonoToStereo = 0;
175 	format_params.wDataInterleave = channels == 1 ? 1 : 2;
176 
177 	status = stream->card->pEG->QueryAudioFormat(stream->pipe, &format_params);
178 	if(status!=ECHOSTATUS_OK) {
179 		PRINT(("echo_stream_set_audioparms : bad format when querying\n"));
180 		PRINT(("  status: %s \n", pStatusStrs[status]));
181 		return B_ERROR;
182 	}
183 
184 	status = stream->card->pEG->SetAudioFormat(stream->pipe, &format_params);
185 	if(status!=ECHOSTATUS_OK) {
186 		PRINT(("echo_stream_set_audioparms : bad format when setting\n"));
187 		PRINT(("  status: %s \n", pStatusStrs[status]));
188 		return B_ERROR;
189 	}
190 
191 	/* XXXX : setting sample rate is global in this driver */
192 	status = stream->card->pEG->QueryAudioSampleRate(sample_rate);
193 	if(status!=ECHOSTATUS_OK) {
194 		PRINT(("echo_stream_set_audioparms : bad sample rate when querying\n"));
195 		PRINT(("  status: %s \n", pStatusStrs[status]));
196 		return B_ERROR;
197 	}
198 
199 	/* XXXX : setting sample rate is global in this driver */
200 	status = stream->card->pEG->SetAudioSampleRate(sample_rate);
201 	if(status!=ECHOSTATUS_OK) {
202 		PRINT(("echo_stream_set_audioparms : bad sample rate when setting\n"));
203 		PRINT(("  status: %s \n", pStatusStrs[status]));
204 		return B_ERROR;
205 	}
206 
207 	if(stream->buffer)
208 		echo_mem_free(stream->card, stream->buffer->log_base);
209 
210 	stream->bitsPerSample = bitsPerSample;
211 	stream->sample_rate = sample_rate;
212 	stream->channels = channels;
213 
214 	sample_size = stream->bitsPerSample / 8;
215 	frame_size = sample_size * stream->channels;
216 
217 	stream->buffer = echo_mem_alloc(stream->card, stream->bufframes * frame_size * stream->bufcount);
218 
219 	stream->trigblk = 1;
220 	stream->blkmod = stream->bufcount;
221 	stream->blksize = stream->bufframes * frame_size;
222 
223 	CDaffyDuck *duck = stream->card->pEG->GetDaffyDuck(stream->pipe);
224 	if(duck == NULL) {
225 		PRINT(("echo_stream_set_audioparms : Could not get daffy duck pointer\n"));
226 		return B_ERROR;
227 	}
228 
229 	uint32 dwNumFreeEntries = 0;
230 
231 	for(i=0; i<stream->bufcount; i++) {
232 		duck->AddMapping(((uint32)stream->buffer->phy_base) +
233 			i * stream->blksize, stream->blksize, 0, TRUE, dwNumFreeEntries);
234 	}
235 
236 	duck->Wrap();
237 
238 	if(stream->card->pEG->GetAudioPositionPtr(stream->pipe, stream->position)!=ECHOSTATUS_OK) {
239 		PRINT(("echo_stream_set_audioparms : Could not get audio position ptr\n"));
240 		return B_ERROR;
241 	}
242 
243 	return B_OK;
244 }
245 
246 
247 status_t
248 echo_stream_get_nth_buffer(echo_stream *stream, uint8 chan, uint8 buf,
249 					char** buffer, size_t *stride)
250 {
251 	uint8 			sample_size, frame_size;
252 	LOG(("echo_stream_get_nth_buffer\n"));
253 
254 	sample_size = stream->bitsPerSample / 8;
255 	frame_size = sample_size * stream->channels;
256 
257 	*buffer = (char*)stream->buffer->log_base + (buf * stream->bufframes * frame_size)
258 		+ chan * sample_size;
259 	*stride = frame_size;
260 
261 	return B_OK;
262 }
263 
264 
265 static uint32
266 echo_stream_curaddr(echo_stream *stream)
267 {
268 	uint32 addr = B_LENDIAN_TO_HOST_INT32(*stream->position);
269 //	TRACE(("stream_curaddr %p, phy_base %p\n", addr));
270 	return (addr / stream->blksize) % stream->blkmod;
271 }
272 
273 
274 void
275 echo_stream_start(echo_stream *stream, void (*inth) (void *), void *inthparam)
276 {
277 	LOG(("echo_stream_start\n"));
278 
279 	stream->inth = inth;
280 	stream->inthparam = inthparam;
281 
282 	stream->state |= ECHO_STATE_STARTED;
283 
284 	if(stream->card->pEG->Start(stream->pipe)!=ECHOSTATUS_OK) {
285 		PRINT(("echo_stream_start : Could not start the pipe\n"));
286 	}
287 }
288 
289 void
290 echo_stream_halt(echo_stream *stream)
291 {
292 	LOG(("echo_stream_halt\n"));
293 
294 	stream->state &= ~ECHO_STATE_STARTED;
295 
296 	if(stream->card->pEG->Stop(stream->pipe)!=ECHOSTATUS_OK) {
297 		PRINT(("echo_stream_halt : Could not stop the pipe\n"));
298 	}
299 }
300 
301 echo_stream *
302 echo_stream_new(echo_dev *card, uint8 use, uint32 bufframes, uint8 bufcount)
303 {
304 	echo_stream *stream;
305 	cpu_status status;
306 	LOG(("echo_stream_new\n"));
307 
308 	stream = (echo_stream *) malloc(sizeof(echo_stream));
309 	if (stream == NULL)
310 		return (NULL);
311 	stream->card = card;
312 	stream->use = use;
313 	stream->state = !ECHO_STATE_STARTED;
314 	stream->bitsPerSample = 0;
315 	stream->sample_rate = 0;
316 	stream->channels = 0;
317 	stream->bufframes = bufframes;
318 	stream->bufcount = bufcount;
319 	stream->inth = NULL;
320 	stream->inthparam = NULL;
321 	stream->buffer = NULL;
322 	stream->blksize = 0;
323 	stream->trigblk = 0;
324 	stream->blkmod = 0;
325 
326 	stream->pipe = card->pEG->MakePipeIndex(0, (use == ECHO_USE_RECORD));
327 
328 	stream->frames_count = 0;
329 	stream->real_time = 0;
330 	stream->update_needed = false;
331 
332 	status = lock();
333 	LIST_INSERT_HEAD((&card->streams), stream, next);
334 	unlock(status);
335 
336 	return stream;
337 }
338 
339 void
340 echo_stream_delete(echo_stream *stream)
341 {
342 	cpu_status status;
343 	LOG(("echo_stream_delete\n"));
344 
345 	echo_stream_halt(stream);
346 
347 	if(stream->buffer)
348 		echo_mem_free(stream->card, stream->buffer->log_base);
349 
350 	status = lock();
351 	LIST_REMOVE(stream, next);
352 	unlock(status);
353 
354 	free(stream);
355 }
356 
357 
358 /* Echo interrupt */
359 
360 int32 echo_int(void *arg)
361 {
362 	echo_dev	 	*card = (echo_dev*)arg;
363 	BOOL			midiReceived;
364 	ECHOSTATUS 		err;
365 
366 	echo_stream *stream;
367 	uint32       curblk;
368 
369 	err = card->pEG->ServiceIrq(midiReceived);
370 
371 	if(err != ECHOSTATUS_OK) {
372 		return B_UNHANDLED_INTERRUPT;
373 	}
374 
375 	LIST_FOREACH(stream, &card->streams, next) {
376 		if ((stream->state & ECHO_STATE_STARTED) == 0 ||
377 			(stream->inth == NULL))
378 				continue;
379 
380 		curblk = echo_stream_curaddr(stream);
381 		//TRACE(("echo_int stream %p at trigblk %lu at stream->trigblk %lu\n",
382 		//	   stream, curblk, stream->trigblk));
383 		if (curblk == stream->trigblk) {
384 			if(stream->inth)
385 				stream->inth(stream->inthparam);
386 
387 			stream->trigblk++;
388 			stream->trigblk %= stream->blkmod;
389 		}
390 	}
391 
392 	return B_INVOKE_SCHEDULER;
393 }
394 
395 /* detect presence of our hardware */
396 status_t
397 init_hardware(void)
398 {
399 	int ix=0;
400 	pci_info info;
401 	status_t err = ENODEV;
402 
403 	LOG_CREATE();
404 
405 	PRINT(("init_hardware()\n"));
406 
407 	if (get_module(pci_name, (module_info **)&pci))
408 		return ENOSYS;
409 
410 	while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) {
411 
412 		ushort card_type = info.u.h0.subsystem_id & 0xfff0;
413 
414 		if (info.vendor_id == VENDOR_ID &&
415 			((info.device_id == DEVICE_ID_56301)
416 			|| (info.device_id == DEVICE_ID_56361)) &&
417 			(info.u.h0.subsystem_vendor_id == SUBVENDOR_ID) &&
418 			(
419 #ifdef ECHOGALS_FAMILY
420 			(card_type == DARLA)
421 			|| (card_type == GINA)
422 			|| (card_type == LAYLA)
423 			|| (card_type == DARLA24)
424 #endif
425 #ifdef ECHO24_FAMILY
426 			(card_type == GINA24)
427 			|| (card_type == LAYLA24)
428 			|| (card_type == MONA)
429 			|| (card_type == MIA)
430 			|| (card_type == INDIGO)
431 #endif
432 			 )) {
433 			err = B_OK;
434 		}
435 		ix++;
436 	}
437 
438 	put_module(pci_name);
439 
440 	if(err!=B_OK) {
441 		PRINT(("no card found\n"));
442 	}
443 
444 	return err;
445 }
446 
447 
448 status_t
449 init_driver(void)
450 {
451 	int ix=0;
452 
453 	pci_info info;
454 	num_cards = 0;
455 
456 	PRINT(("init_driver()\n"));
457 	load_driver_symbols(DRIVER_NAME);
458 
459 	if (get_module(pci_name, (module_info **) &pci))
460 		return ENOSYS;
461 
462 	while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) {
463 		ushort card_type = info.u.h0.subsystem_id & 0xfff0;
464 
465 		if (info.vendor_id == VENDOR_ID &&
466 			((info.device_id == DEVICE_ID_56301)
467 			|| (info.device_id == DEVICE_ID_56361)) &&
468 			(info.u.h0.subsystem_vendor_id == SUBVENDOR_ID) &&
469 			(
470 #ifdef ECHOGALS_FAMILY
471 			(card_type == DARLA)
472 			|| (card_type == GINA)
473 			|| (card_type == LAYLA)
474 			|| (card_type == DARLA24)
475 #endif
476 #ifdef ECHO24_FAMILY
477 			(card_type == GINA24)
478 			|| (card_type == LAYLA24)
479 			|| (card_type == MONA)
480 			|| (card_type == MIA)
481 			|| (card_type == INDIGO)
482 #endif
483 			)) {
484 
485 			if (num_cards == NUM_CARDS) {
486 				PRINT(("Too many "DRIVER_NAME" cards installed!\n"));
487 				break;
488 			}
489 			memset(&cards[num_cards], 0, sizeof(echo_dev));
490 			cards[num_cards].info = info;
491 			cards[num_cards].type = card_type;
492 			if (echo_setup(&cards[num_cards])) {
493 				PRINT(("Setup of "DRIVER_NAME" %ld failed\n", num_cards+1));
494 			}
495 			else {
496 				num_cards++;
497 			}
498 		}
499 		ix++;
500 	}
501 	if (!num_cards) {
502 		PRINT(("no cards\n"));
503 		put_module(pci_name);
504 		PRINT(("no suitable cards found\n"));
505 		return ENODEV;
506 	}
507 
508 	return B_OK;
509 }
510 
511 
512 static void
513 make_device_names(
514 	echo_dev * card)
515 {
516 	sprintf(card->name, "audio/multi/"DRIVER_NAME"/%ld", card-cards+1);
517 	names[num_names++] = card->name;
518 
519 	names[num_names] = NULL;
520 }
521 
522 
523 static status_t
524 echo_setup(echo_dev * card)
525 {
526 	status_t err = B_OK;
527 	unsigned char cmd;
528 
529 	PRINT(("echo_setup(%p)\n", card));
530 
531 	(*pci->write_pci_config)(card->info.bus, card->info.device, card->info.function,
532 		PCI_latency, 1, 0xc0 );
533 
534 	make_device_names(card);
535 
536 	card->bmbar = card->info.u.h0.base_registers[0];
537 	card->irq = card->info.u.h0.interrupt_line;
538 
539 	card->pOSS = new COsSupport(card->info.device_id);
540 	if(card->pOSS == NULL)
541 		return B_ERROR;
542 
543 	switch (card->type) {
544 #ifdef ECHOGALS_FAMILY
545 		case DARLA:
546 			card->pEG = new CDarla(card->pOSS);
547 			break;
548 		case GINA:
549 			card->pEG = new CGina(card->pOSS);
550 			break;
551 		case LAYLA:
552 			card->pEG = new CLayla(card->pOSS);
553 			break;
554 		case DARLA24:
555 			card->pEG = new CDarla24(card->pOSS);
556 			break;
557 #endif
558 #ifdef ECHO24_FAMILY
559 		case GINA24:
560 			card->pEG = new CGina24(card->pOSS);
561 			break;
562 		case LAYLA24:
563 			card->pEG = new CLayla24(card->pOSS);
564 			break;
565 		case MONA:
566 			card->pEG = new CMona(card->pOSS);
567 			break;
568 		case MIA:
569 			card->pEG = new CMia(card->pOSS);
570 			break;
571 #endif
572 		default:
573 			PRINT(("card type 0x%x not supported by "DRIVER_NAME"\n", card->type));
574 			delete card->pOSS;
575 			return B_ERROR;
576 	}
577 
578 	if (card->pEG == NULL)
579 		return B_ERROR;
580 
581 	card->area_bmbar = map_mem(&card->log_bmbar, (void *)card->bmbar,
582 		card->info.u.h0.base_register_sizes[0], DRIVER_NAME" bmbar io");
583 	if (card->area_bmbar <= B_OK) {
584 		LOG(("mapping of bmbar io failed, error = %#x\n",card->area_bmbar));
585 		return B_ERROR;
586 	}
587 	LOG(("mapping of bmbar: area %#x, phys %#x, log %#x\n", card->area_bmbar, card->bmbar, card->log_bmbar));
588 
589 	cmd = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, PCI_command, 2);
590 	PRINT(("PCI command before: %x\n", cmd));
591 	(*pci->write_pci_config)(card->info.bus, card->info.device, card->info.function, PCI_command, 2, cmd | PCI_command_io);
592 	cmd = (*pci->read_pci_config)(card->info.bus, card->info.device, card->info.function, PCI_command, 2);
593 	PRINT(("PCI command after: %x\n", cmd));
594 
595 	card->pEG->AssignResources(card->log_bmbar, "no name");
596 
597 	ECHOSTATUS status;
598 	status = card->pEG->InitHw();
599 	if(status != ECHOSTATUS_OK)
600 		return B_ERROR;
601 
602 	/* Init streams list */
603 	LIST_INIT(&(card->streams));
604 
605 	/* Init mems list */
606 	LIST_INIT(&(card->mems));
607 
608 	PRINT(("installing interrupt : %x\n", card->irq));
609 	install_io_interrupt_handler(card->irq, echo_int, card, 0);
610 
611 	PRINT(("echo_setup done\n"));
612 
613 	return err;
614 }
615 
616 static void
617 echo_shutdown(echo_dev *card)
618 {
619 	PRINT(("shutdown(%p)\n", card));
620 	remove_io_interrupt_handler(card->irq, echo_int, card);
621 
622 	delete card->pEG;
623 	delete card->pOSS;
624 
625 	delete_area(card->area_bmbar);
626 }
627 
628 
629 
630 void
631 uninit_driver(void)
632 {
633 	int ix, cnt = num_cards;
634 	num_cards = 0;
635 
636 	PRINT(("uninit_driver()\n"));
637 
638 	for (ix=0; ix<cnt; ix++) {
639 		echo_shutdown(&cards[ix]);
640 	}
641 	memset(&cards, 0, sizeof(cards));
642 	put_module(pci_name);
643 }
644 
645 
646 const char **
647 publish_devices(void)
648 {
649 	int ix = 0;
650 	PRINT(("publish_devices()\n"));
651 
652 	for (ix=0; names[ix]; ix++) {
653 		PRINT(("publish %s\n", names[ix]));
654 	}
655 	return (const char **)names;
656 }
657 
658 
659 device_hooks *
660 find_device(const char * name)
661 {
662 	int ix;
663 
664 	PRINT(("find_device(%s)\n", name));
665 
666 	for (ix=0; ix<num_cards; ix++) {
667 		if (!strcmp(cards[ix].name, name)) {
668 			return &multi_hooks;
669 		}
670 	}
671 	PRINT(("find_device(%s) failed\n", name));
672 	return NULL;
673 }
674 
675 int32	api_version = B_CUR_DRIVER_API_VERSION;
676