1 /* 2 BeepMidi :: beep.sys MIDI player 3 4 (c) Andrew Greenwood, 2007. 5 6 Released as open-source software. You may copy, re-distribute and modify 7 this software, provided this copyright notice remains intact. 8 9 Please see the included README.TXT for more information 10 11 HISTORY : 12 16th January 2007 Started 13 17th January 2007 Polyphony support and threading added 14 18th January 2007 Made threading optional, added comments 15 */ 16 17 /* The timeslice to allocate for all playing notes (in milliseconds) */ 18 #define TIMESLICE_SIZE 60 19 20 /* 21 If this is defined, notes are added to the playing list, even if 22 they already exist. As a result, the note will sound twice during 23 each timeslice. Also each note on will require a corresponding note 24 off event. 25 */ 26 #define ALLOW_DUPLICATE_NOTES 27 28 /* 29 The maximum number of notes that may be playing at any one time. 30 Higher values result in a messier sound as all the frequencies get 31 mashed together. Do not set this below 2. Recommended = 4 32 */ 33 #define POLYPHONY 3 34 35 /* 36 Define CONTINUOUS_NOTES to perform note playback in a separate thread. 37 This was originally the intended behaviour, but after experimentation 38 doesn't sound as good for MIDI files which have a lot going on. If not 39 defined, all playing notes are output in sequence as a new note starts. 40 */ 41 #define CONTINUOUS_NOTES 42 43 #define WIN32_NO_STATUS 44 #define _INC_WINDOWS 45 #define COM_NO_WINDOWS_H 46 #include <stdarg.h> 47 #include <windef.h> 48 #include <winbase.h> 49 #define NTOS_MODE_USER 50 #include <ndk/iofuncs.h> 51 #include <ndk/obfuncs.h> 52 #include <ndk/rtlfuncs.h> 53 #include <ntddbeep.h> 54 #include <math.h> 55 #include <mmddk.h> 56 57 /*#define DPRINT printf*/ 58 #define DPRINT FakePrintf 59 60 /* A few MIDI command categories */ 61 #define MIDI_NOTE_OFF 0x80 62 #define MIDI_NOTE_ON 0x90 63 #define MIDI_CONTROL_CHANGE 0xB0 64 #define MIDI_PROGRAM 0xC0 65 #define MIDI_PITCH_BEND 0xE0 66 #define MIDI_SYSTEM 0xFF 67 68 /* Specific commands */ 69 #define MIDI_RESET 0xFF 70 71 72 typedef struct _NoteNode 73 { 74 struct _NoteNode* next; 75 struct _NoteNode* previous; 76 77 UCHAR note; 78 UCHAR velocity; /* 0 is note-off */ 79 } NoteNode; 80 81 typedef struct _DeviceInfo 82 { 83 HDRVR mme_handle; 84 HANDLE kernel_device; 85 86 DWORD callback; 87 DWORD instance; 88 DWORD flags; 89 90 UCHAR running_status; 91 92 DWORD playing_notes_count; 93 NoteNode* note_list; 94 BOOL refresh_notes; 95 96 HANDLE thread_handle; 97 BOOL terminate_thread; 98 HANDLE work_available; 99 } DeviceInfo; 100 101 DeviceInfo* the_device; 102 CRITICAL_SECTION device_lock; 103 104 void 105 FakePrintf(char* str, ...) 106 { 107 /* Just to shut the compiler up */ 108 } 109 110 111 /* 112 This is designed to be treated as a thread, however it behaves as a 113 normal function if CONTINUOUS_NOTES is not defined. 114 */ 115 116 DWORD WINAPI 117 ProcessPlayingNotes( 118 LPVOID parameter) 119 { 120 DeviceInfo* device_info = (DeviceInfo*) parameter; 121 NTSTATUS status; 122 IO_STATUS_BLOCK io_status_block; 123 DWORD arp_notes; 124 125 DPRINT("Note processing started\n"); 126 127 /* We lock the note list only while accessing it */ 128 129 #ifdef CONTINUOUS_NOTES 130 while ( WaitForSingleObject(the_device->work_available, INFINITE), !device_info->terminate_thread ) 131 #endif 132 { 133 NoteNode* node; 134 135 /* Number of notes being arpeggiated */ 136 arp_notes = 1; 137 138 EnterCriticalSection(&device_lock); 139 140 /* Calculate how much time to allocate to each playing note */ 141 142 DPRINT("%d notes active\n", (int) device_info->playing_notes_count); 143 144 node = device_info->note_list; 145 146 while ( ( node != NULL ) && ( arp_notes <= POLYPHONY ) ) 147 { 148 BEEP_SET_PARAMETERS beep_data; 149 DWORD actually_playing = 0; 150 151 double frequency = node->note; 152 153 DPRINT("playing..\n"); 154 155 frequency = frequency / 12; 156 frequency = pow(2, frequency); 157 frequency = 8.1758 * frequency; 158 159 if (device_info->playing_notes_count > POLYPHONY) 160 actually_playing = POLYPHONY; 161 else 162 actually_playing = device_info->playing_notes_count; 163 164 DPRINT("Frequency %f\n", frequency); 165 166 // TODO 167 beep_data.Frequency = (DWORD) frequency; 168 beep_data.Duration = TIMESLICE_SIZE / actually_playing; /* device_info->playing_notes_count; */ 169 170 status = NtDeviceIoControlFile(device_info->kernel_device, 171 NULL, 172 NULL, 173 NULL, 174 &io_status_block, 175 IOCTL_BEEP_SET, 176 &beep_data, 177 sizeof(BEEP_SET_PARAMETERS), 178 NULL, 179 0); 180 181 if ( ! NT_SUCCESS(status) ) 182 { 183 DPRINT("ERROR %d\n", (int) GetLastError()); 184 } 185 186 SleepEx(beep_data.Duration, TRUE); 187 188 if ( device_info->refresh_notes ) 189 { 190 device_info->refresh_notes = FALSE; 191 break; 192 } 193 194 arp_notes ++; 195 node = node->next; 196 } 197 198 LeaveCriticalSection(&device_lock); 199 } 200 201 return 0; 202 } 203 204 205 /* 206 Fills a MIDIOUTCAPS structure with information about our device. 207 */ 208 209 MMRESULT 210 GetDeviceCapabilities( 211 MIDIOUTCAPS* caps) 212 { 213 /* These are ignored for now */ 214 caps->wMid = 0; 215 caps->wPid = 0; 216 217 caps->vDriverVersion = 0x0100; 218 219 memset(caps->szPname, 0, sizeof(caps->szPname)); 220 wcscpy(caps->szPname, L"PC speaker"); 221 222 caps->wTechnology = MOD_SQSYNTH; 223 224 caps->wVoices = 1; /* We only have one voice */ 225 caps->wNotes = POLYPHONY; 226 caps->wChannelMask = 0xFFBF; /* Ignore channel 10 */ 227 228 caps->dwSupport = 0; 229 230 return MMSYSERR_NOERROR; 231 } 232 233 234 /* 235 Helper function that just simplifies calling the application making use 236 of us. 237 */ 238 239 BOOL 240 CallClient( 241 DeviceInfo* device_info, 242 DWORD_PTR message, 243 DWORD_PTR parameter1, 244 DWORD_PTR parameter2) 245 { 246 DPRINT("Calling client - callback 0x%x mmhandle 0x%x\n", device_info->callback, device_info->mme_handle); 247 return DriverCallback(device_info->callback, 248 HIWORD(device_info->flags), 249 device_info->mme_handle, 250 message, 251 device_info->instance, 252 parameter1, 253 parameter2); 254 255 } 256 257 258 /* 259 Open the kernel-mode device and allocate resources. This opens the 260 BEEP.SYS kernel device. 261 */ 262 263 MMRESULT 264 OpenDevice( 265 DeviceInfo** private_data, 266 MIDIOPENDESC* open_desc, 267 DWORD flags) 268 { 269 NTSTATUS status; 270 HANDLE heap; 271 HANDLE kernel_device; 272 UNICODE_STRING beep_device_name; 273 OBJECT_ATTRIBUTES attribs; 274 IO_STATUS_BLOCK status_block; 275 276 /* One at a time.. */ 277 if ( the_device ) 278 { 279 DPRINT("Already allocated\n"); 280 return MMSYSERR_ALLOCATED; 281 } 282 283 /* Make the device name into a unicode string and open it */ 284 285 RtlInitUnicodeString(&beep_device_name, 286 L"\\Device\\Beep"); 287 288 InitializeObjectAttributes(&attribs, 289 &beep_device_name, 290 0, 291 NULL, 292 NULL); 293 294 status = NtCreateFile(&kernel_device, 295 FILE_READ_DATA | FILE_WRITE_DATA, 296 &attribs, 297 &status_block, 298 NULL, 299 0, 300 FILE_SHARE_READ | FILE_SHARE_WRITE, 301 FILE_OPEN_IF, 302 0, 303 NULL, 304 0); 305 306 if ( ! NT_SUCCESS(status) ) 307 { 308 DPRINT("Could not connect to BEEP device - %d\n", (int) GetLastError()); 309 return MMSYSERR_ERROR; 310 } 311 312 DPRINT("Opened!\n"); 313 314 /* Allocate and initialize the device info */ 315 316 heap = GetProcessHeap(); 317 318 the_device = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(DeviceInfo)); 319 320 if ( ! the_device ) 321 { 322 DPRINT("Out of memory\n"); 323 return MMSYSERR_NOMEM; 324 } 325 326 /* Initialize */ 327 the_device->kernel_device = kernel_device; 328 the_device->playing_notes_count = 0; 329 the_device->note_list = NULL; 330 the_device->thread_handle = 0; 331 the_device->terminate_thread = FALSE; 332 the_device->running_status = 0; 333 334 // TODO 335 the_device->mme_handle = (HDRVR) open_desc->hMidi; 336 the_device->callback = open_desc->dwCallback; 337 the_device->instance = open_desc->dwInstance; 338 the_device->flags = flags; 339 340 /* Store the pointer in the user data */ 341 *private_data = the_device; 342 343 /* This is threading-related code */ 344 #ifdef CONTINUOUS_NOTES 345 the_device->work_available = CreateEvent(NULL, TRUE, FALSE, NULL); 346 347 if ( ! the_device->work_available ) 348 { 349 DPRINT("CreateEvent failed\n"); 350 HeapFree(heap, 0, the_device); 351 return MMSYSERR_NOMEM; 352 } 353 354 the_device->thread_handle = CreateThread(NULL, 355 0, 356 ProcessPlayingNotes, 357 (PVOID) the_device, 358 0, 359 NULL); 360 361 if ( ! the_device->thread_handle ) 362 { 363 DPRINT("CreateThread failed\n"); 364 CloseHandle(the_device->work_available); 365 HeapFree(heap, 0, the_device); 366 return MMSYSERR_NOMEM; 367 } 368 #endif 369 370 /* Now we call the client application to say the device is open */ 371 DPRINT("Sending MOM_OPEN\n"); 372 DPRINT("Success? %d\n", (int) CallClient(the_device, MOM_OPEN, 0, 0)); 373 374 return MMSYSERR_NOERROR; 375 } 376 377 378 /* 379 Close the kernel-mode device. 380 */ 381 382 MMRESULT 383 CloseDevice(DeviceInfo* device_info) 384 { 385 HANDLE heap = GetProcessHeap(); 386 387 /* If we're working in threaded mode we need to wait for thread to die */ 388 #ifdef CONTINUOUS_NOTES 389 the_device->terminate_thread = TRUE; 390 SetEvent(device_info->work_available); 391 392 WaitForSingleObject(the_device->thread_handle, INFINITE); 393 CloseHandle(the_device->thread_handle); 394 CloseHandle(the_device->work_available); 395 #endif 396 397 /* Let the client application know the device is closing */ 398 DPRINT("Sending MOM_CLOSE\n"); 399 CallClient(device_info, MOM_CLOSE, 0, 0); 400 401 NtClose(device_info->kernel_device); 402 403 /* Free resources */ 404 HeapFree(heap, 0, device_info); 405 406 the_device = NULL; 407 408 return MMSYSERR_NOERROR; 409 } 410 411 412 /* 413 Removes a note from the playing notes list. If the note is not playing, 414 we just pretend nothing happened. 415 */ 416 417 MMRESULT 418 StopNote( 419 DeviceInfo* device_info, 420 UCHAR note) 421 { 422 HANDLE heap = GetProcessHeap(); 423 NoteNode* node; 424 NoteNode* prev_node = NULL; 425 426 DPRINT("StopNote\n"); 427 428 EnterCriticalSection(&device_lock); 429 430 node = device_info->note_list; 431 432 while ( node != NULL ) 433 { 434 if ( node->note == note ) 435 { 436 /* Found the note - just remove the node from the list */ 437 438 DPRINT("Stopping note %d\n", (int) node->note); 439 440 if ( prev_node != NULL ) 441 prev_node->next = node->next; 442 else 443 device_info->note_list = node->next; 444 445 HeapFree(heap, 0, node); 446 447 device_info->playing_notes_count --; 448 449 DPRINT("Note stopped - now playing %d notes\n", (int) device_info->playing_notes_count); 450 451 #ifdef CONTINUOUS_NOTES 452 if (device_info->playing_notes_count == 0) 453 ResetEvent(device_info->work_available); 454 #endif 455 456 LeaveCriticalSection(&device_lock); 457 device_info->refresh_notes = TRUE; 458 459 return MMSYSERR_NOERROR; 460 } 461 462 prev_node = node; 463 node = node->next; 464 } 465 466 LeaveCriticalSection(&device_lock); 467 468 /* Hmm, a good idea? */ 469 #ifndef CONTINUOUS_NOTES 470 ProcessPlayingNotes((PVOID) device_info); 471 #endif 472 473 return MMSYSERR_NOERROR; 474 } 475 476 477 /* 478 Adds a note to the playing notes list. If the note is already playing, 479 the definition of ALLOW_DUPLICATE_NOTES determines if an existing note 480 may be duplicated. Otherwise, duplicate notes are ignored. 481 */ 482 483 MMRESULT 484 PlayNote( 485 DeviceInfo* device_info, 486 UCHAR note, 487 UCHAR velocity) 488 { 489 HANDLE heap = GetProcessHeap(); 490 491 NoteNode* node; 492 493 DPRINT("PlayNote\n"); 494 495 if ( velocity == 0 ) 496 { 497 DPRINT("Zero velocity\n"); 498 499 /* Velocity zero is effectively a "note off" */ 500 StopNote(device_info, note); 501 } 502 else 503 { 504 /* Start playing the note */ 505 NoteNode* new_node; 506 507 EnterCriticalSection(&device_lock); 508 509 node = device_info->note_list; 510 511 while ( node != NULL ) 512 { 513 #ifndef ALLOW_DUPLICATE_NOTES 514 if ( ( node->note == note ) && ( velocity > 0 ) ) 515 { 516 /* The note is already playing - do nothing */ 517 DPRINT("Duplicate note playback request ignored\n"); 518 LeaveCriticalSection(&device_lock); 519 return MMSYSERR_NOERROR; 520 } 521 #endif 522 523 node = node->next; 524 } 525 526 new_node = HeapAlloc(heap, HEAP_ZERO_MEMORY, sizeof(NoteNode)); 527 528 if ( ! new_node ) 529 { 530 LeaveCriticalSection(&device_lock); 531 return MMSYSERR_NOMEM; 532 } 533 534 new_node->note = note; 535 new_node->velocity = velocity; 536 537 /* 538 Prepend to the playing notes list. If exceeding polyphony, 539 remove the oldest note (which will be at the tail.) 540 */ 541 542 if ( device_info->note_list ) 543 device_info->note_list->previous = new_node; 544 545 new_node->next = device_info->note_list; 546 new_node->previous = NULL; 547 548 device_info->note_list = new_node; 549 device_info->playing_notes_count ++; 550 551 /* 552 if ( device_info->playing_notes_count > POLYPHONY ) 553 { 554 ASSERT(tail_node); 555 556 DPRINT("Polyphony exceeded\n"); 557 558 tail_node->previous->next = NULL; 559 560 HeapFree(heap, 0, tail_node); 561 562 device_info->playing_notes_count --; 563 } 564 */ 565 566 #ifdef CONTINUOUS_NOTES 567 SetEvent(device_info->work_available); 568 #endif 569 570 LeaveCriticalSection(&device_lock); 571 572 DPRINT("Note started - now playing %d notes\n", (int) device_info->playing_notes_count); 573 device_info->refresh_notes = TRUE; 574 } 575 576 #ifndef CONTINUOUS_NOTES 577 ProcessPlayingNotes((PVOID) device_info); 578 #endif 579 580 return MMSYSERR_NOERROR; 581 } 582 583 /* 584 Decipher a short MIDI message (which is a MIDI message packed into a DWORD.) 585 This will set "running status", but does not take this into account when 586 processing messages (is this necessary?) 587 */ 588 589 MMRESULT 590 ProcessShortMidiMessage( 591 DeviceInfo* device_info, 592 DWORD message) 593 { 594 DWORD status; 595 596 DWORD category; 597 DWORD channel; 598 DWORD data1, data2; 599 600 status = message & 0x000000FF; 601 602 /* Deal with running status */ 603 604 if ( status < MIDI_NOTE_OFF ) 605 { 606 status = device_info->running_status; 607 } 608 609 /* Ensure the status is sane! */ 610 611 if ( status < MIDI_NOTE_OFF ) 612 { 613 /* It's garbage, ignore it */ 614 return MMSYSERR_NOERROR; 615 } 616 617 /* Figure out the message category and channel */ 618 619 category = status & 0xF0; 620 channel = status & 0x0F; /* we don't use this */ 621 622 data1 = (message & 0x0000FF00) >> 8; 623 data2 = (message & 0x00FF0000) >> 16; 624 625 DPRINT("0x%x, %d, %d\n", (int) status, (int) data1, (int) data2); 626 627 /* Filter drums (which are *usually* on channel 10) */ 628 if ( channel == 10 ) 629 { 630 return MMSYSERR_NOERROR; 631 } 632 633 /* Pass to the appropriate message handler */ 634 635 switch ( category ) 636 { 637 case MIDI_NOTE_ON : 638 { 639 PlayNote(device_info, data1, data2); 640 break; 641 } 642 643 case MIDI_NOTE_OFF : 644 { 645 StopNote(device_info, data1); 646 break; 647 } 648 } 649 650 return MMSYSERR_NOERROR; 651 } 652 653 654 #define PACK_MIDI(b1, b2, b3) \ 655 ((b3 * 65536) + (b2 * 256) + b1); 656 657 658 /* 659 Processes a "long" MIDI message (ie, a MIDI message contained within a 660 buffer.) This is intended for supporting SysEx data, or blocks of MIDI 661 events. However in our case we're only interested in short MIDI messages, 662 so we scan the buffer, and each time we encounter a valid status byte 663 we start recording it as a new event. Once 3 bytes or a new status is 664 received, the event is passed to the short message handler. 665 */ 666 667 MMRESULT 668 ProcessLongMidiMessage( 669 DeviceInfo* device_info, 670 MIDIHDR* header) 671 { 672 unsigned int index = 0; 673 UCHAR* midi_bytes = (UCHAR*) header->lpData; 674 675 unsigned int msg_index = 0; 676 UCHAR msg[3]; 677 678 /* Initialize the buffer */ 679 msg[0] = msg[1] = msg[2] = 0; 680 681 if ( ! ( header->dwFlags & MHDR_PREPARED ) ) 682 { 683 DPRINT("Not prepared!\n"); 684 return MIDIERR_UNPREPARED; 685 } 686 687 DPRINT("Processing %d bytes of MIDI\n", (int) header->dwBufferLength); 688 689 while ( index < header->dwBufferLength ) 690 { 691 /* New status byte? ( = new event) */ 692 if ( midi_bytes[index] & 0x80 ) 693 { 694 DWORD short_msg; 695 696 /* Deal with the existing event */ 697 698 if ( msg[0] & 0x80 ) 699 { 700 short_msg = PACK_MIDI(msg[0], msg[1], msg[2]); 701 702 DPRINT("Complete msg is 0x%x %d %d\n", (int) msg[0], (int) msg[1], (int) msg[2]); 703 ProcessShortMidiMessage(device_info, short_msg); 704 } 705 706 /* Set new running status and start recording the event */ 707 DPRINT("Set new running status\n"); 708 device_info->running_status = midi_bytes[index]; 709 msg[0] = midi_bytes[index]; 710 msg_index = 1; 711 } 712 713 /* Unexpected data byte? ( = re-use previous status) */ 714 else if ( msg_index == 0 ) 715 { 716 if ( device_info->running_status & 0x80 ) 717 { 718 DPRINT("Retrieving running status\n"); 719 msg[0] = device_info->running_status; 720 msg[1] = midi_bytes[index]; 721 msg_index = 2; 722 } 723 else 724 DPRINT("garbage\n"); 725 } 726 727 /* Expected data ( = append to message until buffer full) */ 728 else 729 { 730 DPRINT("Next byte...\n"); 731 msg[msg_index] = midi_bytes[index]; 732 msg_index ++; 733 734 if ( msg_index > 2 ) 735 { 736 DWORD short_msg; 737 738 short_msg = PACK_MIDI(msg[0], msg[1], msg[2]); 739 740 DPRINT("Complete msg is 0x%x %d %d\n", (int) msg[0], (int) msg[1], (int) msg[2]); 741 ProcessShortMidiMessage(device_info, short_msg); 742 743 /* Reinit */ 744 msg_index = 0; 745 msg[0] = msg[1] = msg[2] = 0; 746 } 747 } 748 749 index ++; 750 } 751 752 /* 753 We're meant to clear MHDR_DONE and set MHDR_INQUEUE but since we 754 deal with everything here and now we might as well just say so. 755 */ 756 header->dwFlags |= MHDR_DONE; 757 header->dwFlags &= ~ MHDR_INQUEUE; 758 759 DPRINT("Success? %d\n", CallClient(the_device, MOM_DONE, (DWORD_PTR) header, 0)); 760 761 return MMSYSERR_NOERROR; 762 } 763 764 765 /* 766 Exported function that receives messages from WINMM (the MME API.) 767 */ 768 769 MMRESULT 770 FAR PASCAL 771 modMessage( 772 UINT device_id, 773 UINT message, 774 DWORD_PTR private_data, 775 DWORD_PTR parameter1, 776 DWORD_PTR parameter2) 777 { 778 switch ( message ) 779 { 780 case MODM_GETNUMDEVS : 781 { 782 /* Only one internal PC speaker device (and even that's too much) */ 783 DPRINT("MODM_GETNUMDEVS\n"); 784 return 1; 785 } 786 787 case MODM_GETDEVCAPS : 788 { 789 DPRINT("MODM_GETDEVCAPS\n"); 790 return GetDeviceCapabilities((MIDIOUTCAPS*) parameter1); 791 } 792 793 case MODM_OPEN : 794 { 795 DPRINT("MODM_OPEN\n"); 796 797 return OpenDevice((DeviceInfo**) private_data, 798 (MIDIOPENDESC*) parameter1, 799 parameter2); 800 } 801 802 case MODM_CLOSE : 803 { 804 DPRINT("MODM_CLOSE\n"); 805 return CloseDevice((DeviceInfo*) private_data); 806 } 807 808 case MODM_DATA : 809 { 810 return ProcessShortMidiMessage((DeviceInfo*) private_data, parameter1); 811 } 812 813 case MODM_PREPARE : 814 { 815 /* We don't bother with this */ 816 MIDIHDR* hdr = (MIDIHDR*) parameter1; 817 hdr->dwFlags |= MHDR_PREPARED; 818 return MMSYSERR_NOERROR; 819 } 820 821 case MODM_UNPREPARE : 822 { 823 MIDIHDR* hdr = (MIDIHDR*) parameter1; 824 hdr->dwFlags &= ~MHDR_PREPARED; 825 return MMSYSERR_NOERROR; 826 } 827 828 case MODM_LONGDATA : 829 { 830 DPRINT("LONGDATA\n"); 831 return ProcessLongMidiMessage((DeviceInfo*) private_data, (MIDIHDR*) parameter1); 832 } 833 834 case MODM_RESET : 835 { 836 /* TODO */ 837 break; 838 } 839 } 840 841 DPRINT("Not supported %d\n", message); 842 843 return MMSYSERR_NOTSUPPORTED; 844 } 845 846 847 /* 848 Driver entrypoint. 849 */ 850 851 LONG 852 FAR PASCAL 853 DriverProc( 854 DWORD driver_id, 855 HDRVR driver_handle, 856 UINT message, 857 LONG parameter1, 858 LONG parameter2) 859 { 860 switch ( message ) 861 { 862 case DRV_LOAD : 863 DPRINT("DRV_LOAD\n"); 864 the_device = NULL; 865 return 1L; 866 867 case DRV_FREE : 868 DPRINT("DRV_FREE\n"); 869 return 1L; 870 871 case DRV_OPEN : 872 DPRINT("DRV_OPEN\n"); 873 InitializeCriticalSection(&device_lock); 874 return 1L; 875 876 case DRV_CLOSE : 877 DPRINT("DRV_CLOSE\n"); 878 return 1L; 879 880 case DRV_ENABLE : 881 DPRINT("DRV_ENABLE\n"); 882 return 1L; 883 884 case DRV_DISABLE : 885 DPRINT("DRV_DISABLE\n"); 886 return 1L; 887 888 /* 889 We don't provide configuration capabilities. This used to be 890 for things like I/O port, IRQ, DMA settings, etc. 891 */ 892 893 case DRV_QUERYCONFIGURE : 894 DPRINT("DRV_QUERYCONFIGURE\n"); 895 return 0L; 896 897 case DRV_CONFIGURE : 898 DPRINT("DRV_CONFIGURE\n"); 899 return 0L; 900 901 case DRV_INSTALL : 902 DPRINT("DRV_INSTALL\n"); 903 return DRVCNF_RESTART; 904 }; 905 906 DPRINT("???\n"); 907 908 return DefDriverProc(driver_id, 909 driver_handle, 910 message, 911 parameter1, 912 parameter2); 913 } 914