|
|
1.1 root 1: /*++
2:
3: Copyright (c) 1993 Microsoft Corporation
4:
5: Module Name:
6:
7: mididisp.c
8:
9: Abstract:
10:
11: This module contains code for the function dispatcher.
12:
13: Author:
14:
15: Robin Speed (RobinSp) 30-Jan-1992
16:
17: Environment:
18:
19: Kernel mode
20:
21: Revision History:
22:
23:
24: --*/
25:
26: #include "sound.h"
27:
28: BOOL SoundSynthPresent(PUCHAR base, PUCHAR inbase);
29:
30:
31: #ifdef ALLOC_PRAGMA
32: #pragma alloc_text(init,SoundSynthPresent)
33: #pragma alloc_text(init,SoundSynthPortValid)
34: #pragma alloc_text(init,SoundMidiIsOpl3)
35: #endif
36:
37:
38:
39: NTSTATUS
40: SoundMidiData(
41: IN OUT PLOCAL_DEVICE_INFO pLDI,
42: IN PIRP pIrp,
43: IN PIO_STACK_LOCATION pIrpStack
44: )
45: /*++
46:
47: Routine Description:
48:
49: The user has passed in a buffer of midi data to play
50:
51: The buffer is validated and the data passed to the device
52:
53: Arguments:
54:
55: pLDI - Local wave device info
56: pIrp - The IO request packet
57: pIrpStack - The current stack location
58:
59: Return Value:
60:
61: Irp status
62:
63: --*/
64: {
65: NTSTATUS Status;
66:
67: ULONG Length;
68: PSYNTH_DATA pData;
69: PUCHAR SynthBase;
70:
71: Length = pIrpStack->Parameters.Write.Length;
72: pData = (PSYNTH_DATA)pIrp->UserBuffer;
73: SynthBase = ((PSOUND_HARDWARE)pLDI->HwContext)->SynthBase;
74:
75:
76: if (Length % sizeof(SYNTH_DATA) != 0) {
77: return STATUS_BUFFER_TOO_SMALL;
78: }
79:
80: Status = STATUS_SUCCESS;
81:
82: try {
83: for ( ; Length != 0 ; Length -= sizeof(SYNTH_DATA), pData++) {
84: USHORT IoPort;
85: IoPort = pData->IoPort;
86: if (IoPort < SYNTH_PORT ||
87: IoPort >= SYNTH_PORT + NUMBER_OF_SYNTH_PORTS) {
88: Status = STATUS_INVALID_PARAMETER;
89: break;
90: }
91:
92: WRITE_PORT_UCHAR(SynthBase + (IoPort - SYNTH_PORT),
93: (UCHAR)pData->PortData);
94:
95: //
96: // Make sure the SYNTH can keep up
97: // newer boards need 1us - older boards need
98: // 3.3 us after selecting the register and 23 us after
99: // writing the data - as we don't know which is which, we
100: // wait 23us after every write
101: //
102: if (pLDI->DeviceIndex == AdlibDevice) {
103: KeStallExecutionProcessor(23);
104: } else {
105: KeStallExecutionProcessor(10);
106: }
107: }
108:
109: } except (EXCEPTION_EXECUTE_HANDLER) {
110: Status = STATUS_ACCESS_VIOLATION;
111: }
112:
113: pIrp->IoStatus.Information = pIrpStack->Parameters.Write.Length - Length;
114:
115: return Status;
116: }
117:
118:
119: NTSTATUS
120: SoundMidiReadPort(
121: IN OUT PLOCAL_DEVICE_INFO pLDI,
122: IN PIRP pIrp,
123: IN PIO_STACK_LOCATION pIrpStack
124: )
125: /*++
126:
127: Routine Description:
128:
129: The user has passed in a buffer to return the status port value
130:
131: The buffer is validated and data returned if it's length 1
132:
133: Arguments:
134:
135: pLDI - Local wave device info
136: pIrp - The IO request packet
137: pIrpStack - The current stack location
138:
139: Return Value:
140:
141: Irp status
142:
143: --*/
144: {
145: NTSTATUS Status;
146:
147: ULONG Length;
148: PUCHAR SynthBase;
149: PUCHAR pData;
150:
151: Length = pIrpStack->Parameters.Read.Length;
152: pData = (PUCHAR)pIrp->UserBuffer;
153: SynthBase = ((PSOUND_HARDWARE)pLDI->HwContext)->SynthBase;
154:
155:
156: if (Length != sizeof(UCHAR)) {
157: return STATUS_INVALID_PARAMETER;
158: }
159:
160: Status = STATUS_SUCCESS;
161:
162: try {
163: *pData = READ_PORT_UCHAR(SynthBase);
164: pIrp->IoStatus.Information = sizeof(UCHAR);
165: } except (EXCEPTION_EXECUTE_HANDLER) {
166: Status = STATUS_ACCESS_VIOLATION;
167: }
168:
169:
170: return Status;
171: }
172:
173:
174: NTSTATUS
175: SoundMidiDispatch(
176: IN OUT PLOCAL_DEVICE_INFO pLDI,
177: IN PIRP pIrp,
178: IN PIO_STACK_LOCATION IrpStack
179: )
180: /*++
181:
182: Routine Description:
183:
184: Driver function dispatch routine
185:
186: Arguments:
187:
188: pLDI - local device info
189: pIrp - Pointer to IO request packet
190: IrpStack - current stack location
191:
192: Return Value:
193:
194: Return status from dispatched routine
195:
196: --*/
197: {
198: NTSTATUS Status;
199:
200: Status = STATUS_SUCCESS;
201:
202: //
203: // Initialize the irp information field.
204: //
205:
206: pIrp->IoStatus.Information = 0;
207:
208: switch (IrpStack->MajorFunction) {
209: case IRP_MJ_CREATE:
210: Status = SoundSetShareAccess(pLDI, IrpStack);
211:
212: if (IrpStack->FileObject->WriteAccess) {
213: // reset board to silence and to correct mode (opl3 or opl2)
214: SoundMidiQuiet(pLDI->DeviceIndex, pLDI->HwContext);
215: }
216: break;
217:
218: case IRP_MJ_WRITE:
219: if (IrpStack->FileObject->WriteAccess) {
220: Status = SoundMidiData(pLDI, pIrp, IrpStack);
221: } else {
222: Status = STATUS_ACCESS_DENIED;
223: }
224: break;
225:
226: case IRP_MJ_READ:
227: if (IrpStack->FileObject->WriteAccess) {
228: Status = SoundMidiReadPort(pLDI, pIrp, IrpStack);
229: } else {
230: Status = STATUS_ACCESS_DENIED;
231: }
232: break;
233:
234: case IRP_MJ_DEVICE_CONTROL:
235:
236:
237: //
238: // Check device access
239: //
240: if (!IrpStack->FileObject->WriteAccess &&
241: pLDI->PreventVolumeSetting) {
242: Status = STATUS_ACCESS_DENIED;
243: } else {
244: switch (IrpStack->Parameters.DeviceIoControl.IoControlCode) {
245: case IOCTL_MIDI_SET_VOLUME:
246: Status = SoundIoctlSetVolume(pLDI, pIrp, IrpStack);
247: break;
248:
249: case IOCTL_MIDI_GET_VOLUME:
250: Status = SoundIoctlGetVolume(pLDI, pIrp, IrpStack);
251: break;
252:
253: case IOCTL_SOUND_GET_CHANGED_VOLUME:
254: Status = SoundIoctlGetChangedVolume(pLDI, pIrp, IrpStack);
255: break;
256:
257:
258: default:
259: Status = STATUS_INVALID_DEVICE_REQUEST;
260: break;
261: }
262: }
263: break;
264:
265:
266: case IRP_MJ_CLOSE:
267:
268: Status = STATUS_SUCCESS;
269:
270: break;
271:
272: case IRP_MJ_CLEANUP:
273: if (IrpStack->FileObject->WriteAccess) {
274: SoundMidiQuiet(pLDI->DeviceIndex, pLDI->HwContext);
275: (*pLDI->DeviceInit->ExclusionRoutine)(pLDI, SoundExcludeClose);
276: pLDI->PreventVolumeSetting = FALSE;
277: } else {
278: Status = STATUS_SUCCESS;
279: }
280: break;
281:
282: default:
283: dprintf1(("Unimplemented major function requested: %08lXH", IrpStack->MajorFunction));
284: Status = STATUS_INVALID_DEVICE_REQUEST;
285: break;
286: }
287:
288: //
289: // Tell the IO subsystem we're done. If the Irp is pending we
290: // don't touch it as it could be being processed by another
291: // processor (or may even be complete already !).
292: //
293:
294: return Status;
295: }
296:
297:
298:
299: VOID
300: SoundMidiSendFM(
301: IN PUCHAR PortBase,
302: IN ULONG Address,
303: IN UCHAR Data
304: )
305: {
306: // these delays need to be 23us at least for old opl2 chips, even
307: // though new chips can handle 1 us delays.
308:
309: WRITE_PORT_UCHAR(PortBase + (Address < 0x100 ? 0 : 2), (UCHAR)Address);
310: KeStallExecutionProcessor(23);
311: WRITE_PORT_UCHAR(PortBase + (Address < 0x100 ? 1 : 3), Data);
312: KeStallExecutionProcessor(23);
313: }
314:
315: /*
316: * this array gives the offsets of the slots within an opl2
317: * chip. This is needed to set the attenuation for all slots to max,
318: * to ensure that the chip is silenced completely - switching off the
319: * voices alone will not do this.
320: */
321: BYTE offsetSlot[] = {
322: 0, 1, 2, 3, 4, 5,
323: 8, 9, 10, 11, 12, 13,
324: 16, 17, 18, 19, 20, 21
325: };
326:
327:
328:
329: VOID
330: SoundMidiQuiet(
331: IN UCHAR DeviceIndex,
332: IN PSOUND_HARDWARE pHw
333: )
334: /*++
335:
336: Routine Description:
337:
338: Initialize the SYNTH hardware to silence
339:
340: Arguments:
341:
342: pHw - hardware data
343:
344: Return Value:
345:
346: None
347:
348: --*/
349: {
350:
351: ULONG i;
352:
353: // D1 ("\nMidiQuietFM");
354:
355: /*
356: * sequence to initialise and silence the device
357: * depends on whether it is an Opl3 device or not
358: */
359: if (DeviceIndex == Opl3Device) {
360:
361: /* tell the FM chip to use 4-operator mode, and
362: fill in any other random variables */
363: SoundMidiSendFM (pHw->SynthBase, AD_NEW, 0x01);
364: SoundMidiSendFM (pHw->SynthBase, AD_MASK, 0x60);
365: SoundMidiSendFM (pHw->SynthBase, AD_CONNECTION, 0x3f);
366: SoundMidiSendFM (pHw->SynthBase, AD_NTS, 0x00);
367:
368:
369: /* turn off the drums, and use high vibrato/modulation */
370: SoundMidiSendFM (pHw->SynthBase, AD_DRUM, 0xc0);
371:
372: /* turn off all the oscillators */
373: for (i = 0; i < 0x15; i++) {
374: SoundMidiSendFM (pHw->SynthBase, AD_LEVEL + i, 0x3f);
375: SoundMidiSendFM (pHw->SynthBase, AD_LEVEL2 + i, 0x3f);
376: };
377:
378: /* turn off all the voices */
379: for (i = 0; i < 0x08; i++) {
380: SoundMidiSendFM (pHw->SynthBase, AD_BLOCK + i, 0x00);
381: SoundMidiSendFM (pHw->SynthBase, AD_BLOCK2 + i, 0x00);
382: };
383: } else {
384: /* base adlib hardware initialisation */
385:
386: // ensure that if we have a opl3 chip, we are in base mode
387: SoundMidiSendFM (pHw->SynthBase, AD_NEW, 0x00);
388:
389: // turn off all the slot oscillators
390: for (i = 0; i < 18; i++) {
391: SoundMidiSendFM(pHw->SynthBase, offsetSlot[i]+AD_LEVEL, 0x3f);
392: }
393:
394: /* silence all voices */
395: for (i = 0; i <= 8; i++) {
396: SoundMidiSendFM(pHw->SynthBase, (AD_FNUMBER | i), 0);
397: SoundMidiSendFM(pHw->SynthBase, (AD_BLOCK | i), 0);
398: }
399:
400: /* switch to percussive mode and silence percussion instruments */
401: SoundMidiSendFM(pHw->SynthBase, AD_DRUM, (BYTE)0x20);
402: }
403: }
404:
405:
406: BOOL
407: SoundSynthPresent(PUCHAR base, PUCHAR inbase)
408: /*++
409:
410: Routine Description:
411:
412: Detect the presence or absence of a 3812 (adlib-compatible) synthesizer
413: at the given i/o address by starting the timer and looking for an
414: overflow. Can be used to detect left and right synthesizers separately.
415:
416: Arguments:
417:
418: base - base output address
419: inbase - base input address
420:
421: Return Value:
422:
423: TRUE if a synthesizer is present at that address
424:
425: --*/
426: {
427: #define inport(port) READ_PORT_UCHAR((PUCHAR)(port))
428:
429: UCHAR t1, t2;
430:
431:
432: // check if the chip is present
433: SoundMidiSendFM(base, 4, 0x60); // mask T1 & T2
434: SoundMidiSendFM(base, 4, 0x80); // reset IRQ
435: t1 = inport(inbase); // read status register
436: SoundMidiSendFM(base, 2, 0xff); // set timer - 1 latch
437: SoundMidiSendFM(base, 4, 0x21); // unmask & start T1
438:
439: // this timer should go off in 80 us. It sometimes
440: // takes more than 100us, but will always have expired within
441: // 200 us if it is ever going to.
442: KeStallExecutionProcessor(200);
443:
444: t2 = inport(inbase); // read status register
445:
446:
447: SoundMidiSendFM(base, 4, 0x60);
448: SoundMidiSendFM(base, 4, 0x80);
449:
450:
451: if (!((t1 & 0xE0) == 0) || !((t2 & 0xE0) == 0xC0)) {
452: return(FALSE);
453: }
454:
455: return TRUE;
456:
457: #undef inport
458:
459: }
460:
461:
462:
463: NTSTATUS
464: SoundSynthPortValid(
465: IN OUT PGLOBAL_DEVICE_INFO pGDI
466: )
467: {
468: NTSTATUS Status;
469: ULONG Port;
470:
471: Port = SYNTH_PORT;
472:
473: //
474: // Check we're going to be allowed to use this port or whether
475: // some other device thinks it owns this hardware
476: //
477:
478: Status = SoundReportResourceUsage(
479: (PDEVICE_OBJECT)pGDI->DriverObject,
480: pGDI->BusType,
481: pGDI->BusNumber,
482: NULL,
483: 0,
484: FALSE,
485: NULL,
486: &Port,
487: NUMBER_OF_SYNTH_PORTS);
488:
489: if (!NT_SUCCESS(Status)) {
490: return Status;
491: }
492:
493: //
494: // Find where our device is mapped
495: //
496:
497: pGDI->Hw.SynthBase = SoundMapPortAddress(
498: pGDI->BusType,
499: pGDI->BusNumber,
500: Port,
501: NUMBER_OF_SYNTH_PORTS,
502: &pGDI->MemType);
503: {
504: PUCHAR base;
505:
506: base = pGDI->Hw.SynthBase;
507:
508: if (!SoundSynthPresent(base, base)) {
509:
510: dprintf1(("No synthesizer present"));
511: return STATUS_DEVICE_CONFIGURATION_ERROR;
512: }
513: }
514:
515: return STATUS_SUCCESS;
516: }
517:
518:
519:
520: BOOL
521: SoundMidiIsOpl3(
522: IN PSOUND_HARDWARE pHw
523: )
524: /*++
525:
526: Routine Description:
527:
528: Check if the midi synthesizer is Opl3 compatible or just adlib-compatible.
529:
530: Arguments:
531:
532: pHw - hardware data
533:
534: Return Value:
535:
536: TRUE if OPL3-compatible chip. FALSE otherwise.
537:
538: --*/
539: {
540: BOOL bReturn = FALSE;
541:
542: PUCHAR Port = pHw->SynthBase;
543:
544: /*
545: * theory: an opl3-compatible synthesizer chip looks
546: * exactly like two separate 3812 synthesizers (for left and right
547: * channels) until switched into opl3 mode. Then, the timer-control
548: * register for the right half is replaced by a channel connection register
549: * (among other changes).
550: *
551: * We can detect 3812 synthesizers by starting a timer and looking for
552: * timer overflow. So if we find 3812s at both left and right addresses,
553: * we then switch to opl3 mode and look again for the right-half. If we
554: * still find it, then the switch failed and we have an old synthesizer
555: * if the right half disappeared, we have a new opl3 synthesizer.
556: *
557: * NB we use either monaural base-level synthesis, or stereo opl3
558: * synthesis. If we discover two 3812s (as on early SB Pro and
559: * PAS), we ignore one of them.
560: */
561:
562: /*
563: * nice theory - but wrong. The timer on the right half of the
564: * opl3 chip reports its status in the left-half status register.
565: * There is no right-half status register on the opl3 chip.
566: */
567:
568:
569: /* ensure base mode */
570: SoundMidiSendFM (Port, AD_NEW, 0x00);
571: KeStallExecutionProcessor(20);
572:
573: /* look for right half of chip */
574: if (SoundSynthPresent(Port + 2, Port)) {
575: /* yes - is this two separate chips or a new opl3 chip ? */
576:
577: /* switch to opl3 mode */
578: SoundMidiSendFM (Port, AD_NEW, 0x01);
579: KeStallExecutionProcessor(20);
580:
581:
582: if (!SoundSynthPresent(Port + 2, Port)) {
583:
584: /* right-half disappeared - so opl3 */
585: bReturn = TRUE;
586: }
587: }
588:
589: /* reset to 3812 mode */
590: SoundMidiSendFM (Port, AD_NEW, 0x00);
591: KeStallExecutionProcessor(20);
592:
593:
594: return(bReturn);
595: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.