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
6#include "cmedia_pci.h"
7#include "cm_private.h"
8
9#include <string.h>
10#include <stdio.h>
11
12#if !defined(_KERNEL_EXPORT_H)
13#include <KernelExport.h>
14#endif
15
16
17#if DEBUG
18#define KPRINTF(x) kprintf x
19#else
20#define KPRINTF(x)
21#endif
22
23EXPORT status_t init_hardware(void);
24EXPORT status_t init_driver(void);
25EXPORT void uninit_driver(void);
26EXPORT const char ** publish_devices(void);
27EXPORT device_hooks * find_device(const char *);
28
29
30static char pci_name[] = B_PCI_MODULE_NAME;
31static pci_module_info	*pci;
32static char gameport_name[] = "generic/gameport/v1";
33generic_gameport_module * gameport;
34static char mpu401_name[] = B_MPU_401_MODULE_NAME;
35generic_mpu401_module * mpu401;
36
37#define DO_JOY 1
38#define DO_MIDI 1
39#define DO_PCM 1
40#define DO_MUX 0
41#define DO_MIXER 0
42
43#if DO_MIDI
44extern device_hooks midi_hooks;
45#endif /* DO_MIDI */
46#if DO_JOY
47extern device_hooks joy_hooks;
48#endif /* DO_JOY */
49#if DO_PCM
50extern device_hooks pcm_hooks;
51#endif /* DO_PCM */
52#if DO_MUX
53extern device_hooks mux_hooks;
54#endif /* DO_MUX */
55#if DO_MIXER
56extern device_hooks mixer_hooks;
57#endif /* DO_MIXER */
58
59
60int32 num_cards;
61cmedia_pci_dev cards[NUM_CARDS];
62int num_names;
63char * names[NUM_CARDS * 7 + 1];
64/* vuchar *io_base; */
65
66
67/* ----------
68	PCI_IO_RD - read a byte from pci i/o space
69----- */
70
71uint8
72PCI_IO_RD (int offset)
73{
74	return (*pci->read_io_8) (offset);
75}
76
77
78/* ----------
79	PCI_IO_RD_32 - read a 32 bit value from pci i/o space
80----- */
81
82uint32
83PCI_IO_RD_32 (int offset)
84{
85	return (*pci->read_io_32) (offset);
86}
87/* ----------
88	PCI_IO_WR - write a byte to pci i/o space
89----- */
90
91void
92PCI_IO_WR (int offset, uint8 val)
93{
94	(*pci->write_io_8) (offset, val);
95}
96
97
98/* detect presence of our hardware */
99status_t
100init_hardware(void)
101{
102	int ix=0;
103	pci_info info;
104	status_t err = ENODEV;
105
106	ddprintf(("cmedia_pci: init_hardware()\n"));
107
108	if (get_module(pci_name, (module_info **)&pci))
109		return ENOSYS;
110
111	while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) {
112		if (info.vendor_id == CMEDIA_PCI_VENDOR_ID &&
113			(info.device_id == CMEDIA_8338A_DEVICE_ID ||
114			 info.device_id == CMEDIA_8338B_DEVICE_ID ||
115			 info.device_id == CMEDIA_8738A_DEVICE_ID ||
116			 info.device_id == CMEDIA_8738B_DEVICE_ID )) {
117			err = B_OK;
118		}
119		ix++;
120	}
121#if defined(__POWERPC__) && 0
122	{
123		char		area_name [32];
124		area_info	area;
125		area_id		id;
126
127		sprintf (area_name, "pci_bus%d_isa_io", info.bus);
128		id = find_area (area_name);
129		if (id < 0)
130			err = id;
131		else if ((err = get_area_info (id, &area)) == B_OK)
132			io_base = area.address;
133	}
134#endif
135
136	put_module(pci_name);
137
138	return err;
139}
140
141
142void set_direct(cmedia_pci_dev * card, int regno, uchar value, uchar mask)
143{
144	if (mask == 0) {
145		return;
146	}
147	if (mask != 0xff) {
148		uchar old = PCI_IO_RD(card->enhanced+regno);
149		value = (value&mask)|(old&~mask);
150	}
151	PCI_IO_WR(card->enhanced+regno, value);
152	ddprintf(("cmedia_pci: CM%02x  = %02x\n", regno, value));
153}
154
155
156uchar get_direct(cmedia_pci_dev * card, int regno)
157{
158	uchar ret = PCI_IO_RD(card->enhanced+regno);
159	return ret;
160}
161
162
163
164void set_indirect(cmedia_pci_dev * card, int regno, uchar value, uchar mask)
165{
166	PCI_IO_WR(card->enhanced+0x23, regno);
167	EIEIO();
168	if (mask == 0) {
169		return;
170	}
171	if (mask != 0xff) {
172		uchar old = PCI_IO_RD(card->enhanced+0x22);
173		value = (value&mask)|(old&~mask);
174	}
175	PCI_IO_WR(card->enhanced+0x22, value);
176	EIEIO();
177	ddprintf(("cmedia_pci: CMX%02x = %02x\n", regno, value));
178}
179
180
181
182uchar get_indirect(cmedia_pci_dev * card,int regno)
183{
184	uchar ret;
185	PCI_IO_WR(card->enhanced+0x23, regno);
186	EIEIO();
187	ret = PCI_IO_RD(card->enhanced+0x22);
188	return ret;
189}
190
191
192#if 0
193void dump_card(cmedia_pci_dev * card)
194{
195	int ix;
196	dprintf("\n");
197	dprintf("CM:   ");
198	for (ix=0; ix<6; ix++) {
199		if (ix == 2 || ix == 3) dprintf("   ");
200		else dprintf(" %02x", get_direct(card, ix));
201	}
202	for (ix=0; ix<0x32; ix++) {
203		if (!(ix & 7)) {
204			dprintf("\nCMX%02x:", ix);
205		}
206		dprintf(" %02x", get_indirect(card, ix));
207	}
208	dprintf("\n");
209	dprintf("\n");
210}
211#else
212void dump_card(cmedia_pci_dev * card)
213{
214}
215#endif
216
217
218static void
219disable_card_interrupts(cmedia_pci_dev * card)
220{
221	set_direct(card, 0x0e, 0x00, 0x03);
222}
223
224
225static status_t
226setup_dma(cmedia_pci_dev * card)
227{
228	/* we're appropriating some ISA space here... */
229	/* need kernel support to do it right */
230	const uint16 base = card->enhanced + 0x80;
231	ddprintf(("cmedia_pci: dma base is 0x%04x\n", base));
232	if (base == 0)
233		return B_DEV_RESOURCE_CONFLICT;
234	card->dma_base = base;
235	return B_OK;
236}
237
238
239static void
240set_default_registers(cmedia_pci_dev * card)
241{
242	static uchar values[] = {
243#ifdef DO_JOY
244		0x04, 0x02, 0x02,	/* enable joystick */
245#endif
246
247		0x0a, 0x01, 0x01,	/* enable SPDIF inverse before SPDIF_LOOP */
248		0x04, 0x80, 0x80,	/* enable SPDIF_LOOP */
249
250		0x1a, 0x00, 0x20,	/* SPD32SEL disable */
251		0x1a, 0x00, 0x10,	/* SPDFLOOPI disable */
252
253		0x1b, 0x04, 0x04,	/* dual channel mode enable */
254		0x1a, 0x00, 0x80,	/* Double DAC structure disable */
255
256		0x24, 0x00, 0x02,	/* 3D surround disable */
257
258		0x24, 0x00, 0x01,	/* disable SPDIF_IN PCM to DAC */
259#ifdef DO_MIDI
260		0x04, 0x04, 0x04,	/* enable MPU-401 */
261		0x17, 0x00, 0x60,	/* default at 0x330 */
262#endif
263	};
264	uchar * ptr = values;
265
266	while (ptr < values+sizeof(values)) {
267		set_direct(card, ptr[0], ptr[1], ptr[2]);
268		ptr += 3;
269	}
270}
271
272
273static void
274make_device_names(cmedia_pci_dev * card)
275{
276	char * name = card->name;
277	sprintf(name, "cmedia_pci/%ld", card-cards + 1);
278
279#if DO_MIDI
280	sprintf(card->midi.name, "midi/%s", name);
281	names[num_names++] = card->midi.name;
282#endif /* DO_MIDI */
283#if DO_JOY
284	sprintf(card->joy.name1, "joystick/%s", name);
285	names[num_names++] = card->joy.name1;
286#endif /* DO_JOY */
287#if DO_PCM
288	/* cmedia_pci DMA doesn't work when physical NULL isn't NULL from PCI */
289	/* this is a hack to not export bad devices on BeBox hardware */
290	if ((*pci->ram_address)(NULL) == NULL) {
291		sprintf(card->pcm.name, "audio/raw/%s", name);
292		names[num_names++] = card->pcm.name;
293		sprintf(card->pcm.oldname, "audio/old/%s", name);
294		names[num_names++] = card->pcm.oldname;
295	}
296#endif /* DO_PCM */
297#if DO_MUX
298	sprintf(card->mux.name, "audio/mux/%s", name);
299	names[num_names++] = card->mux.name;
300#endif /* DO_MUX */
301#if DO_MIXER
302	sprintf(card->mixer.name, "audio/mix/%s", name);
303	names[num_names++] = card->mixer.name;
304#endif /* DO_MIXER */
305	names[num_names] = NULL;
306}
307
308
309/* We use the SV chip in ISA DMA addressing mode, which is 24 bits */
310/* so we need to find suitable, locked, contiguous memory in that */
311/* physical address range. */
312
313static status_t
314find_low_memory(cmedia_pci_dev * card)
315{
316	size_t low_size = (MIN_MEMORY_SIZE + (B_PAGE_SIZE - 1))
317		&~ (B_PAGE_SIZE - 1);
318	physical_entry where;
319	size_t trysize;
320	area_id curarea;
321	void * addr;
322	char name[DEVNAME];
323
324	sprintf(name, "%s_low", card->name);
325	if (low_size < MIN_MEMORY_SIZE) {
326		low_size = MIN_MEMORY_SIZE;
327	}
328	trysize = low_size;
329
330	curarea = find_area(name);
331	if (curarea >= 0) {	/* area there from previous run */
332		area_info ainfo;
333		ddprintf(("cmedia_pci: testing likely candidate...\n"));
334		if (get_area_info(curarea, &ainfo)) {
335			ddprintf(("cmedia_pci: no info\n"));
336			goto allocate;
337		}
338		/* test area we found */
339		trysize = ainfo.size;
340		addr = ainfo.address;
341		if (trysize < low_size) {
342			ddprintf(("cmedia_pci: too small (%lx)\n", trysize));
343			goto allocate;
344		}
345		if (get_memory_map(addr, trysize, &where, 1) < B_OK) {
346			ddprintf(("cmedia_pci: no memory map\n"));
347			goto allocate;
348		}
349		if ((where.address & ~(phys_addr_t)0xffffff) != 0) {
350			ddprintf(("cmedia_pci: bad physical address\n"));
351			goto allocate;
352		}
353		if (ainfo.lock < B_FULL_LOCK || where.size < low_size) {
354			ddprintf(("cmedia_pci: lock not contiguous\n"));
355			goto allocate;
356		}
357dprintf("cmedia_pci: physical %#" B_PRIxPHYSADDR "  logical %p\n",
358where.address, ainfo.address);
359		goto a_o_k;
360	}
361
362allocate:
363	if (curarea >= 0) {
364		delete_area(curarea); /* area didn't work */
365		curarea = -1;
366	}
367	ddprintf(("cmedia_pci: allocating new low area\n"));
368
369	curarea = create_area(name, &addr, B_ANY_KERNEL_ADDRESS,
370		trysize, B_LOMEM, B_READ_AREA | B_WRITE_AREA);
371	ddprintf(("cmedia_pci: create_area(%lx) returned %lx logical %p\n",
372		trysize, curarea, addr));
373	if (curarea < 0) {
374		goto oops;
375	}
376	if (get_memory_map(addr, low_size, &where, 1) < 0) {
377		delete_area(curarea);
378		curarea = B_ERROR;
379		goto oops;
380	}
381	ddprintf(("cmedia_pci: physical %p\n", where.address));
382	if ((where.address & ~(phys_addr_t)0xffffff) != 0) {
383		delete_area(curarea);
384		curarea = B_ERROR;
385		goto oops;
386	}
387	if (((where.address + low_size) & ~(phys_addr_t)0xffffff) != 0) {
388		delete_area(curarea);
389		curarea = B_ERROR;
390		goto oops;
391	}
392	/* hey, it worked! */
393	if (trysize > low_size) {	/* don't waste */
394		resize_area(curarea, low_size);
395	}
396
397oops:
398	if (curarea < 0) {
399		dprintf("cmedia_pci: failed to create low_mem area\n");
400		return curarea;
401	}
402a_o_k:
403	ddprintf(("cmedia_pci: successfully found or created low area!\n"));
404	card->low_size = low_size;
405	card->low_mem = addr;
406	card->low_phys = (vuchar *)(addr_t)where.address;
407	card->map_low = curarea;
408	return B_OK;
409}
410
411
412static status_t
413setup_cmedia_pci(cmedia_pci_dev * card)
414{
415	status_t err = B_OK;
416/*	cpu_status cp; */
417
418	ddprintf(("cmedia_pci: setup_cmedia_pci(%p)\n", card));
419
420	if ((card->pcm.init_sem = create_sem(1, "cm pcm init")) < B_OK)
421		goto bail;
422#if 1
423	if ((*mpu401->create_device)(0x330, &card->midi.driver,
424#else
425	if ((*mpu401->create_device)(card->info.u.h0.base_registers[3], &card->midi.driver,
426#endif
427		0, midi_interrupt_op, &card->midi) < B_OK)
428		goto bail3;
429#if 1
430	if ((*gameport->create_device)(0x201, &card->joy.driver) < B_OK)
431#else
432	if ((*gameport->create_device)(card->info.u.h0.base_registers[4], &card->joy.driver) < B_OK)
433#endif
434		goto bail4;
435	ddprintf(("midi %p  gameport %p\n", card->midi.driver, card->joy.driver));
436	card->midi.card = card;
437
438	err = find_low_memory(card);
439	if (err < B_OK) {
440		goto bail5;
441	}
442
443	//cp = disable_interrupts();
444	//acquire_spinlock(&card->hardware);
445
446	make_device_names(card);
447	card->enhanced = card->info.u.h0.base_registers[0];
448	ddprintf(("cmedia_pci: %s enhanced at %x\n", card->name, card->enhanced));
449
450	ddprintf(("cmedia_pci: revision %x\n", get_indirect(card, 0x15)));
451
452	disable_card_interrupts(card);
453	if (setup_dma(card) != B_OK) {
454		dprintf("cmedia pci: can't setup DMA\n");
455		goto bail6;
456	}
457
458	set_default_registers(card);
459
460	//release_spinlock(&card->hardware);
461	//restore_interrupts(cp);
462
463	return B_OK;
464
465bail6:
466	//	deallocate low memory
467bail5:
468	(*gameport->delete_device)(card->joy.driver);
469bail4:
470	(*mpu401->delete_device)(card->midi.driver);
471bail3:
472	delete_sem(card->pcm.init_sem);
473bail:
474	return err < B_OK ? err : B_ERROR;
475}
476
477
478static int
479debug_cmedia(int argc, char * argv[])
480{
481	int ix = 0;
482	if (argc == 2) {
483		ix = parse_expression(argv[1]) - 1;
484	}
485	if (argc > 2 || ix < 0 || ix >= num_cards) {
486		dprintf("cmedia_pci: dude, you gotta watch your syntax!\n");
487		return -1;
488	}
489	dprintf("%s: enhanced registers at 0x%x\n", cards[ix].name,
490		cards[ix].enhanced);
491	dprintf("%s: open %ld   dma_a at 0x%x   dma_c 0x%x\n", cards[ix].pcm.name,
492		cards[ix].pcm.open_count, cards[ix].pcm.dma_a, cards[ix].pcm.dma_c);
493	if (cards[ix].pcm.open_count) {
494		dprintf("    dma_a: 0x%lx+0x%lx   dma_c: 0x%lx+0x%lx\n",
495			PCI_IO_RD_32((int)cards[ix].pcm.dma_a), PCI_IO_RD_32((int)cards[ix].pcm.dma_a+4),
496			PCI_IO_RD_32((int)cards[ix].pcm.dma_c), PCI_IO_RD_32((int)cards[ix].pcm.dma_c+4));
497	}
498	return 0;
499}
500
501
502status_t
503init_driver(void)
504{
505	pci_info info;
506	int ix = 0;
507	status_t err;
508
509	num_cards = 0;
510
511	ddprintf(("cmedia_pci: init_driver()\n"));
512
513	if (get_module(pci_name, (module_info **)&pci))
514		return ENOSYS;
515
516	if (get_module(gameport_name, (module_info **)&gameport)) {
517		put_module(pci_name);
518		return ENOSYS;
519	}
520	ddprintf(("MPU\n"));
521	if (get_module(mpu401_name, (module_info **)&mpu401)) {
522		put_module(gameport_name);
523		put_module(pci_name);
524		return ENOSYS;
525	}
526
527	ddprintf(("MPU: %p\n", mpu401));
528
529	while ((*pci->get_nth_pci_info)(ix, &info) == B_OK) {
530		if (info.vendor_id == CMEDIA_PCI_VENDOR_ID &&
531			(info.device_id == CMEDIA_8338A_DEVICE_ID ||
532			 info.device_id == CMEDIA_8338B_DEVICE_ID ||
533			 info.device_id == CMEDIA_8738A_DEVICE_ID ||
534			 info.device_id == CMEDIA_8738B_DEVICE_ID )) {
535			if (num_cards == NUM_CARDS) {
536				dprintf("Too many C-Media cards installed!\n");
537				break;
538			}
539			memset(&cards[num_cards], 0, sizeof(cmedia_pci_dev));
540			cards[num_cards].info = info;
541#ifdef __HAIKU__
542			if ((err = (*pci->reserve_device)(info.bus, info.device, info.function,
543				DRIVER_NAME, &cards[num_cards])) < B_OK) {
544				dprintf("%s: failed to reserve_device(%d, %d, %d,): %s\n",
545					DRIVER_NAME, info.bus, info.device, info.function,
546					strerror(err));
547				continue;
548			}
549#endif
550			if (setup_cmedia_pci(&cards[num_cards])) {
551				dprintf("Setup of C-Media %ld failed\n", num_cards+1);
552#ifdef __HAIKU__
553				(*pci->unreserve_device)(info.bus, info.device, info.function,
554					DRIVER_NAME, &cards[num_cards]);
555#endif
556			}
557			else {
558				num_cards++;
559			}
560		}
561		ix++;
562	}
563	if (!num_cards) {
564		KPRINTF(("no cards\n"));
565		put_module(mpu401_name);
566		put_module(gameport_name);
567		put_module(pci_name);
568		ddprintf(("cmedia_pci: no suitable cards found\n"));
569		return ENODEV;
570	}
571
572#if DEBUG
573	add_debugger_command("cmedia", debug_cmedia, "cmedia [card# (1-n)]");
574#endif
575	return B_OK;
576}
577
578
579static void
580teardown_cmedia_pci(cmedia_pci_dev * card)
581{
582	static uchar regs[] = {
583#ifdef DO_JOY
584		0x04, 0x00, 0x02,	/* enable joystick */
585#endif
586#ifdef DO_MIDI
587		0x04, 0x00, 0x04,	/* enable MPU-401 */
588#endif
589	};
590	uchar * ptr = regs;
591	cpu_status cp;
592
593	/* remove created devices */
594	(*gameport->delete_device)(card->joy.driver);
595	(*mpu401->delete_device)(card->midi.driver);
596
597	cp = disable_interrupts();
598	acquire_spinlock(&card->hardware);
599
600	while (ptr < regs + sizeof(regs)) {
601		set_direct(card, ptr[0], ptr[1], ptr[2]);
602		ptr += 3;
603	}
604	disable_card_interrupts(card);
605
606	release_spinlock(&card->hardware);
607	restore_interrupts(cp);
608
609	delete_sem(card->pcm.init_sem);
610
611#ifdef __HAIKU__
612	(*pci->unreserve_device)(card->info.bus, card->info.device,
613		card->info.function, DRIVER_NAME, card);
614#endif
615}
616
617
618void
619uninit_driver(void)
620{
621	int ix, cnt = num_cards;
622	num_cards = 0;
623
624	ddprintf(("cmedia_pci: uninit_driver()\n"));
625	remove_debugger_command("cmedia", debug_cmedia);
626
627	for (ix = 0; ix < cnt; ix++) {
628		teardown_cmedia_pci(&cards[ix]);
629	}
630	memset(&cards, 0, sizeof(cards));
631	put_module(mpu401_name);
632	put_module(gameport_name);
633	put_module(pci_name);
634}
635
636
637const char **
638publish_devices(void)
639{
640	int ix = 0;
641	ddprintf(("cmedia_pci: publish_devices()\n"));
642
643	for (ix = 0; names[ix]; ix++) {
644		ddprintf(("cmedia_pci: publish %s\n", names[ix]));
645	}
646	return (const char **)names;
647}
648
649
650device_hooks *
651find_device(const char * name)
652{
653	int ix;
654
655	ddprintf(("cmedia_pci: find_device(%s)\n", name));
656
657	for (ix = 0; ix < num_cards; ix++) {
658#if DO_MIDI
659		if (!strcmp(cards[ix].midi.name, name)) {
660			return &midi_hooks;
661		}
662#endif /* DO_MIDI */
663#if DO_JOY
664		if (!strcmp(cards[ix].joy.name1, name)) {
665			return &joy_hooks;
666		}
667#endif /* DO_JOY */
668#if DO_PCM
669		if (!strcmp(cards[ix].pcm.name, name)) {
670			return &pcm_hooks;
671		}
672		if (!strcmp(cards[ix].pcm.oldname, name)) {
673			return &pcm_hooks;
674		}
675#endif /* DO_PCM */
676#if DO_MUX
677		if (!strcmp(cards[ix].mux.name, name)) {
678			return &mux_hooks;
679		}
680
681#endif /* DO_MUX */
682#if DO_MIXER
683		if (!strcmp(cards[ix].mixer.name, name)) {
684			return &mixer_hooks;
685		}
686#endif /* DO_MIXER */
687	}
688	ddprintf(("cmedia_pci: find_device(%s) failed\n", name));
689	return NULL;
690}
691
692int32	api_version = B_CUR_DRIVER_API_VERSION;
693
694static int32
695cmedia_pci_interrupt(void * data)
696{
697	cpu_status cp = disable_interrupts();
698	cmedia_pci_dev * card = (cmedia_pci_dev *)data;
699	uchar status;
700	int32 handled = B_UNHANDLED_INTERRUPT;
701
702/*	KTRACE(); / * */
703	acquire_spinlock(&card->hardware);
704
705	status = get_direct(card, 0x10);
706
707#if DEBUG
708/*	kprintf("%x\n", status); / * */
709#endif
710#if DO_PCM
711	if (status & 0x02) {
712		if (dma_c_interrupt(card)) {
713			handled = B_INVOKE_SCHEDULER;
714		}
715		else {
716			handled = B_HANDLED_INTERRUPT;
717		}
718		/* acknowledge interrupt */
719		set_direct(card, 0x0e, 0x00, 0x02);
720		set_direct(card, 0x0e, 0x02, 0x02);
721	}
722	if (status & 0x01) {
723		if (dma_a_interrupt(card)) {
724			handled = B_INVOKE_SCHEDULER;
725		}
726		else {
727			handled = B_HANDLED_INTERRUPT;
728		}
729		/* acknowledge interrupt */
730		set_direct(card, 0x0e, 0x00, 0x01);
731		set_direct(card, 0x0e, 0x01, 0x01);
732	}
733#endif
734#if DO_MIDI
735	status = get_direct(card, 0x12);
736	if (status & 0x01) {
737		if (midi_interrupt(card)) {
738			handled = B_INVOKE_SCHEDULER;
739		} else {
740			handled = B_HANDLED_INTERRUPT;
741		}
742	}
743#endif
744
745	/*  Sometimes, the Sonic Vibes will receive a byte of Midi data...
746	**  And duly note it in the MPU401 status register...
747	**  And generate an interrupt...
748	**  But not bother setting the midi interrupt bit in the ISR.
749	**  Thanks a lot, S3.
750	*/
751	if (handled == B_UNHANDLED_INTERRUPT) {
752		if (midi_interrupt(card)) {
753			handled = B_INVOKE_SCHEDULER;
754		}
755	}
756
757/*	KTRACE(); / * */
758	release_spinlock(&card->hardware);
759	restore_interrupts(cp);
760
761	return handled;
762//	return (handled == B_INVOKE_SCHEDULER) ? B_HANDLED_INTERRUPT : handled;
763}
764
765
766void
767increment_interrupt_handler(cmedia_pci_dev * card)
768{
769	KPRINTF(("cmedia_pci: increment_interrupt_handler()\n"));
770	if (atomic_add(&card->inth_count, 1) == 0) {
771	// !!!
772		KPRINTF(("cmedia_pci: intline %d int %p\n", card->info.u.h0.interrupt_line, cmedia_pci_interrupt));
773		install_io_interrupt_handler(card->info.u.h0.interrupt_line,
774			cmedia_pci_interrupt, card, 0);
775	}
776}
777
778
779void
780decrement_interrupt_handler(cmedia_pci_dev * card)
781{
782	KPRINTF(("cmedia_pci: decrement_interrupt_handler()\n"));
783	if (atomic_add(&card->inth_count, -1) == 1) {
784		KPRINTF(("cmedia_pci: remove_io_interrupt_handler()\n"));
785		remove_io_interrupt_handler(card->info.u.h0.interrupt_line, cmedia_pci_interrupt, card);
786	}
787}
788
789
790